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
@@ -0,0 +1,114 @@
|
|
1
|
+
#watch __FILE__
|
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
|
7
|
+
|
8
|
+
class E
|
9
|
+
|
10
|
+
F["?"]||={}
|
11
|
+
F["?"].update({
|
12
|
+
'sw' => {
|
13
|
+
'view' => 'examine',
|
14
|
+
'ev'=>'sw',
|
15
|
+
'a'=>'Lng:49',
|
16
|
+
'minP' => 'FREQ',
|
17
|
+
'maxP' => 'FREQ',
|
18
|
+
'filter' => 'map',
|
19
|
+
'kHz:75' => 'FREQ',
|
20
|
+
'Time(UTC):93' => 'UTC',
|
21
|
+
'Station:201' => 'STATION',
|
22
|
+
}
|
23
|
+
})
|
24
|
+
|
25
|
+
fn 'view/sw/base',->d,e,c{
|
26
|
+
bands = {
|
27
|
+
120 => [2200,2500],
|
28
|
+
90 => [3100,3450],
|
29
|
+
75 => [3890,4123],
|
30
|
+
60 => [4740,5125],
|
31
|
+
49 => [800,6300],
|
32
|
+
40 => [7200,7600],
|
33
|
+
31 => [9200,9999],
|
34
|
+
25 => [11500,12160],
|
35
|
+
22 => [13500,13900],
|
36
|
+
19 => [15001,15900],
|
37
|
+
16 => [17500,17900],
|
38
|
+
13 => [21450,21850],
|
39
|
+
11 => [25700,26500],
|
40
|
+
}
|
41
|
+
band=0
|
42
|
+
e[:clr]={}
|
43
|
+
e[:fmax]=d.map{|_,r|r['FREQ'][0].to_f}.flatten.max||30000.0
|
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),
|
47
|
+
{id: :bands,
|
48
|
+
c: bands.map{|meters,bounds|
|
49
|
+
band += 1
|
50
|
+
{_: :a, class: :band,
|
51
|
+
style: "background-color:##{band % 2 == 0 ? 'fff' : 'cecece'}",
|
52
|
+
c: '<span style="font-size:1.4em">'+meters.to_s+'</span>m',
|
53
|
+
href: meters.to_s+'m.html'}}},
|
54
|
+
{id: :scales, c: %w{800 1200 1600}.map{|b|{_: :span,class: :scale, c: b}}},
|
55
|
+
{id: :spectrum, style: 'height:800px;position:absolute', c:
|
56
|
+
[{id: 't'},{class: 'loc'},{id: 'clock'},c.(),
|
57
|
+
(0..23).map{|h|
|
58
|
+
[0,15,30,45].map{|m|
|
59
|
+
t = h*60+m
|
60
|
+
left = t*4
|
61
|
+
utc="%02d%02d"%[h,m]
|
62
|
+
[(1..3).map{|l|
|
63
|
+
{_: :span, class: :u, c: utc, style: "top:#{l*25}%;left:#{left-19}px;"}},
|
64
|
+
{class: :s,style: "border-color:#{m==0 ? 'white' : '#666'};left:#{left}px;"}]}}]}]}
|
65
|
+
|
66
|
+
fn 'view/sw/item',->r,x{
|
67
|
+
min=->t{t='%04d' % (t.class==String && t.empty? ? 0 : t)
|
68
|
+
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
|
72
|
+
fi = f.to_i
|
73
|
+
n = fi / 100
|
74
|
+
x[:clr][n] ||= '#%06x' % rand(16777216)
|
75
|
+
f && b && e &&
|
76
|
+
(bmin=min.(b); emin=min.(e)
|
77
|
+
top=(x[:fmax]-f)*x[:scale]
|
78
|
+
v=->b,e{
|
79
|
+
{t: r.except('uri','UTC','FREQ').values.join(' '),:class => :bar, b: b, e: e, f: fi,style:"
|
80
|
+
background-color:#{x[:clr][n]};
|
81
|
+
top: #{top}%;
|
82
|
+
left: #{b*4.0}px;
|
83
|
+
width:#{(e-b) * 4.0}px;
|
84
|
+
",c: (e-b > 60 ?
|
85
|
+
((0..(e-b)/60).map{|h|
|
86
|
+
{_: :span, style: "position:absolute;left:#{h*120}px;top:0",c: f}}) : f)}}
|
87
|
+
(bmin > emin) ? [v.(0,emin),
|
88
|
+
v.(bmin,1440)] : v.(bmin,emin))}
|
89
|
+
|
90
|
+
fn 'view/sw',->d,e{
|
91
|
+
i=F['view/sw/item']
|
92
|
+
Fn 'view/sw/base',d,e,->{d.map{|u,r|i.(r,e)}}}
|
93
|
+
|
94
|
+
fn 'filter/gh',->o,m,_{
|
95
|
+
m.values.map{|r|
|
96
|
+
r[Content].do{|c|
|
97
|
+
c.join.lines.each_with_index{|l,i|
|
98
|
+
l.match(/^[^<]+$/) &&
|
99
|
+
(u=r.uri+'#'+i.to_s
|
100
|
+
m[u]={'uri' => u,
|
101
|
+
'big'=>[l.scan(/\b[A-Z][A-Z][A-Z]+\b/)],
|
102
|
+
Content=>[l]}
|
103
|
+
l.scan(/\d{4,}/){|d| d=d.to_i
|
104
|
+
if (d > 2400) && (d < 30000)
|
105
|
+
m[u]['FREQ']=[d]
|
106
|
+
elsif
|
107
|
+
m[u]['UTC']=["#{d}-#{d+30}"]
|
108
|
+
end}
|
109
|
+
m.delete u unless m[u].has_keys ['UTC','FREQ']
|
110
|
+
)}
|
111
|
+
m.delete r.uri
|
112
|
+
}}}
|
113
|
+
|
114
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
#watch __FILE__
|
2
|
+
class E
|
3
|
+
|
4
|
+
F["?"]||={}
|
5
|
+
F["?"].update({'taft'=>{
|
6
|
+
'graph'=>'|',
|
7
|
+
'|'=>'triplrMozHist',
|
8
|
+
'view'=>'page',
|
9
|
+
'v'=>'timegraph',
|
10
|
+
'arc'=>'referer'}})
|
11
|
+
|
12
|
+
# massage data for timegraph
|
13
|
+
fn 'filter/timegraph',->e,m,_{
|
14
|
+
|
15
|
+
e['timegraph'] ||= true
|
16
|
+
|
17
|
+
x = e['x'] || Date # x prop
|
18
|
+
y = e['y'] # y property
|
19
|
+
|
20
|
+
# 2D values
|
21
|
+
vX = m.map{|_,r|r[x]}.flatten.compact.map(&:to_time).map &:to_f
|
22
|
+
vY = m.map{|_,r|r[y]}.flatten.compact.map &:to_f
|
23
|
+
maxX = vX.max
|
24
|
+
minX = vX.min
|
25
|
+
maxY = vY.max
|
26
|
+
minY = vY.min
|
27
|
+
|
28
|
+
# scaling-ratio to normalize values to %
|
29
|
+
scaleX = 100/((maxX-minX).do{|v|v==0 ? 100 : v}||100)
|
30
|
+
scaleY = 100/((maxY-minY).do{|v|v==0 ? 100 : v}||100)
|
31
|
+
|
32
|
+
# annotate resources with positioning data
|
33
|
+
m.map{|u,r|
|
34
|
+
r['x'] = [*r[x]][0].do{|v|(maxX - v.to_time.to_f)*scaleX} || 0
|
35
|
+
r['y'] = y.do{|y|[*r[y]][0].do{|v|(maxY - v.to_f)*scaleY} || 0} || rand(100)}
|
36
|
+
}
|
37
|
+
|
38
|
+
# a linked-timeline view
|
39
|
+
fn 'view/timegraph',->d,e{
|
40
|
+
# use standard structure for examine faceted-filtering
|
41
|
+
i=F['view/timegraph/item']
|
42
|
+
Fn 'view/timegraph/base',d,e,->{d.map{|u,r|i.(r,e)}}}
|
43
|
+
|
44
|
+
# timegraph container-element
|
45
|
+
fn 'view/timegraph/base',->d,e,c{
|
46
|
+
Fn 'filter/timegraph', e.q, d, nil unless e.q['timegraph']==true
|
47
|
+
|
48
|
+
e[:graph] = d
|
49
|
+
e[:group] = {}
|
50
|
+
e[:color] = E.c
|
51
|
+
|
52
|
+
[H.css('/css/timegraph'),{class: :timegraph, c: c.()}]}
|
53
|
+
|
54
|
+
# timegraph entry
|
55
|
+
fn 'view/timegraph/item',->r,x{
|
56
|
+
# skip resources w/o x-axis field
|
57
|
+
if r[x.q['x'] || Date]
|
58
|
+
|
59
|
+
labelP = x.q['label'].expand || Creator
|
60
|
+
label = (r[labelP][0]).do{|l|
|
61
|
+
l.respond_to?(:uri) ? l.uri : l.to_s}
|
62
|
+
lc = x[:group][label] ||= E.c
|
63
|
+
|
64
|
+
[{style: "top: #{r['x']}%; left: 0", class: :date, c: r[Date][0]},
|
65
|
+
{style: "top: #{r['x']}%; left: #{r['y']}%",
|
66
|
+
c: [{_: :a, href: r.url, c: '#', class: :link},
|
67
|
+
{_: :a,
|
68
|
+
title: r[Date][0],
|
69
|
+
href: '#'+r.uri,
|
70
|
+
class: :label,
|
71
|
+
style: "background-color: #{lc}",
|
72
|
+
c: label,
|
73
|
+
}]},
|
74
|
+
|
75
|
+
# arc(s)
|
76
|
+
{_: :svg, c:
|
77
|
+
r[x.q['arc'].expand].map{|e|
|
78
|
+
# target resource
|
79
|
+
x[:graph][e.uri].do{|e|
|
80
|
+
# arc path
|
81
|
+
{_: :line, class: :arc, stroke: x[:color], 'stroke-dasharray'=>"2,2",
|
82
|
+
y1: e['x'].to_s+'%', x1: e['y'].to_s+'%',
|
83
|
+
y2: r['x'].to_s+'%', x2: r['y'].to_s+'%'}}}}]
|
84
|
+
end }
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class E
|
2
|
+
|
3
|
+
## SIMILE Timeline
|
4
|
+
# http://www.simile-widgets.org/
|
5
|
+
|
6
|
+
# JSON format
|
7
|
+
fn Render+'application/timeline',->d,e{
|
8
|
+
{dateTimeFormat: 'iso8601',
|
9
|
+
events: d.values.map{|r|
|
10
|
+
r[Date].do{|d|
|
11
|
+
{description: r.uri,
|
12
|
+
title: r[Title],
|
13
|
+
start: [*d][0],
|
14
|
+
link: r.url,
|
15
|
+
}}}.compact}.to_json}
|
16
|
+
|
17
|
+
fn 'head/timeline',->d,e{
|
18
|
+
['<script>var t="'+e['REQUEST_PATH']+e.q.except('view','?').merge({format: 'application/timeline'}).qs+'"</script>',
|
19
|
+
(H.js '/js/timeline'),
|
20
|
+
(H.js 'http://api.simile-widgets.org/timeline/2.3.1/timeline-api')]}
|
21
|
+
|
22
|
+
fn 'view/timeline',->d,e{'<div id="tl" class="timeline-default" style="height: 300px;"></div>'}
|
23
|
+
|
24
|
+
end
|
data/infod/W/feed.rb
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
#watch __FILE__
|
2
|
+
module FeedParse
|
3
|
+
def html; CGI.unescapeHTML self end
|
4
|
+
def cdata; sub /^\s*<\!\[CDATA\[(.*?)\]\]>\s*$/m,'\1'end
|
5
|
+
def guess; send (case self
|
6
|
+
when /^\s*<\!/m
|
7
|
+
:cdata
|
8
|
+
when /</m
|
9
|
+
:id
|
10
|
+
else
|
11
|
+
:html
|
12
|
+
end) end
|
13
|
+
|
14
|
+
def parse # Universal feed-parser
|
15
|
+
|
16
|
+
#xml qname table
|
17
|
+
x={}
|
18
|
+
match(/<(rdf|rss|feed)([^>]+)/i)[2].scan(/xmlns:?([a-z]+)?=["']?([^'">\s]+)/){|m|x[m[0]]=m[1]}
|
19
|
+
|
20
|
+
#items
|
21
|
+
scan(%r{<(rss:|atom:)?(item|entry)([\s][^>]*)?>(.*?)</\1?\2>}mi){|m|
|
22
|
+
|
23
|
+
#URI
|
24
|
+
u = m[2] && (u=m[2].match /about=["']?([^'">\s]+)/) && u[1] ||
|
25
|
+
m[3] && (((u=m[3].match /<(gu)?id[^>]*>([^<]+)/) || (u=m[3].match /<(link)>([^<]+)/)) && u[2])
|
26
|
+
yield u,E::Type,(E::SIOC+'Post').E
|
27
|
+
|
28
|
+
#links
|
29
|
+
m[3].scan(%r{<(link|enclosure|media)([^>]+)>}mi){|e|
|
30
|
+
yield(u, # s
|
31
|
+
E::Atom+'/link/'+((r=e[1].match(/rel=['"]?([^'">\s]+)/)) ? r[1] : e[0]), # p
|
32
|
+
e[1].match(/(href|url|src)=['"]?([^'">\s]+)/)[2].E)} # o
|
33
|
+
|
34
|
+
#elements
|
35
|
+
m[3].scan(%r{<([a-z]+:)?([a-z]+)([\s][^>]*)?>(.*?)</\1?\2>}mi){|e|
|
36
|
+
yield u, # s
|
37
|
+
(x[e[0]&&e[0].chop]||E::RSS)+e[1], # p
|
38
|
+
e[3].extend(FeedParse).guess}} # o
|
39
|
+
|
40
|
+
nil
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class E
|
45
|
+
|
46
|
+
Atom = W3+'2005/Atom'
|
47
|
+
RSS = Purl+'rss/1.0/'
|
48
|
+
RSSm = RSS+'modules/'
|
49
|
+
Feed = (E RSS+'channel')
|
50
|
+
|
51
|
+
def listFeeds; (nokogiri.css 'link[rel=alternate]').map{|u|E (URI uri).merge(u.attr :href)} end
|
52
|
+
alias_method :feeds, :listFeeds
|
53
|
+
|
54
|
+
def getFeed g; addJSON :triplrFeed,g end
|
55
|
+
def getFeedReddit g; addJSON :triplrFeedReddit,g end
|
56
|
+
|
57
|
+
# tripleStream
|
58
|
+
def triplrFeed &f
|
59
|
+
dateNorm :triplrFeedSIOCize,:triplrFeedRaw,&f
|
60
|
+
rescue Exception => x
|
61
|
+
end
|
62
|
+
|
63
|
+
def triplrFeedReddit &f
|
64
|
+
require 'nokogiri'
|
65
|
+
triplrFeed {|s,p,o|
|
66
|
+
p == Content ?
|
67
|
+
Nokogiri::HTML.parse(o).do{|o|
|
68
|
+
o.css('.md').do{|o|yield s,p,o}
|
69
|
+
yield s,Creator,o.css('a')[-4].child.to_s.strip
|
70
|
+
} : (yield s,p,o)}
|
71
|
+
end
|
72
|
+
|
73
|
+
# tripleStream
|
74
|
+
def triplrFeedRaw &f
|
75
|
+
read.extend(FeedParse).parse &f
|
76
|
+
end
|
77
|
+
|
78
|
+
# tripleStream -> tripleStream
|
79
|
+
def triplrFeedSIOCize *f
|
80
|
+
send(*f){|s,p,o|
|
81
|
+
yield s,
|
82
|
+
{ Purl+'dc/elements/1.1/creator' => Creator,
|
83
|
+
Purl+'dc/elements/1.1/subject' => SIOC+'subject',
|
84
|
+
Atom+'author' => Creator,
|
85
|
+
RSS+'description' => Content,
|
86
|
+
RSS+'encoded' => Content,
|
87
|
+
RSSm+'content/encoded' => Content,
|
88
|
+
Atom+'content' => Content,
|
89
|
+
RSS+'title' => Title,
|
90
|
+
Atom+'title' => Title,
|
91
|
+
}[p]||p,
|
92
|
+
o } end
|
93
|
+
|
94
|
+
|
95
|
+
fn Render+'application/atom+xml',->d,e{
|
96
|
+
id = 'http://' + e['SERVER_NAME'] + (CGI.escapeHTML e['REQUEST_URI'])
|
97
|
+
H(['<?xml version="1.0" encoding="utf-8"?>',
|
98
|
+
{_: :feed,xmlns: 'http://www.w3.org/2005/Atom',
|
99
|
+
c: [{_: :id, c: id},
|
100
|
+
{_: :title, c: id},
|
101
|
+
{_: :link, rel: :self, href: id},
|
102
|
+
{_: :updated, c: Time.now.iso8601},
|
103
|
+
d.map{|u,d|
|
104
|
+
d[Content] &&
|
105
|
+
{_: :entry,
|
106
|
+
c: [{_: :id, c: u},
|
107
|
+
{_: :link, href: d.url},
|
108
|
+
d[Date].do{|d|{_: :updated, c: d[0]}},
|
109
|
+
d[Title].do{|t|{_: :title, c: t}},
|
110
|
+
d[Creator].do{|c|{_: :author, c: c[0]}},
|
111
|
+
{_: :content, type: :xhtml,
|
112
|
+
c: {xmlns:"http://www.w3.org/1999/xhtml", c: d[Content]}}
|
113
|
+
].intersperse("\n")}
|
114
|
+
}.intersperse("\n")]}])}
|
115
|
+
|
116
|
+
end
|
data/infod/W/find.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
#watch __FILE__
|
2
|
+
class E
|
3
|
+
|
4
|
+
fn 'set/find',->e,q,m{
|
5
|
+
t=q['day'] && q['day'].match(/^\d+$/) && '-ctime -'+q['day']
|
6
|
+
s=q['size'] && q['size'].match(/^\d+$/) && '-size +'+q['size']+'M'
|
7
|
+
r=q['q'] && '-iregex ' + ('.*'+q['q']+'.*').sh
|
8
|
+
`find #{e.sh} #{t} #{s} #{r} | head -n 1024`.lines.map &:pathToURI}
|
9
|
+
|
10
|
+
fn 'graph/find',->e,q,m{
|
11
|
+
(Fn 'set/find', e,q,m).do{|f|
|
12
|
+
if f.size < 256
|
13
|
+
f.map{|r|r.fromStream m,:triplrInode,false}
|
14
|
+
else
|
15
|
+
f.map{|r|m[r.uri]=r}
|
16
|
+
end}}
|
17
|
+
|
18
|
+
fn 'view/find',->i,e{
|
19
|
+
{_: :form, method: :GET, action: e['REQUEST_PATH'].t, style: 'float: right',
|
20
|
+
c: [{_: :input, name: :graph, value: :find, type: :hidden},
|
21
|
+
{_: :input, name: :view, value: :ls, type: :hidden},
|
22
|
+
{_: :input, name: :q}]}}
|
23
|
+
|
24
|
+
end
|
data/infod/W/forum.rb
ADDED
data/infod/W/grep.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
#watch __FILE__
|
2
|
+
class E
|
3
|
+
|
4
|
+
fn 'set/grep',->e,q,m{
|
5
|
+
`grep -rl#{q.has_key?('i') && 'i'} #{q['q'].sh} #{e.sh}`.lines.map &:pathToURI
|
6
|
+
}
|
7
|
+
|
8
|
+
fn 'view/grep',->d,e{
|
9
|
+
w=e.q['q'].scan(/[\w]+/).map(&:downcase).uniq # split/dedupe words
|
10
|
+
c={}; w.each_with_index{|w,i|c[w]=i} # word index
|
11
|
+
a=/(#{w.join '|'})/i # OR pattern
|
12
|
+
p=/#{w.join '.*'}/i # sequential pattern
|
13
|
+
[H.css('/css/search'),{_: :style, c: c.values.map{|i| # word styles
|
14
|
+
b = rand(16777216) # random color
|
15
|
+
f = b > 8388608 ? :black : :white # keep text contrasty
|
16
|
+
".w#{i} {background-color: #{'#%06x' % b}; color: #{f}}\n"}},# CSS
|
17
|
+
d.map{|u,r| l = r.to_s.gsub(/<[^>]*>/,'').lines # plaintextify
|
18
|
+
g = l.grep p # sequential match first
|
19
|
+
g = l.grep a if g.empty? # OR match second
|
20
|
+
!g.empty? && # find anything?
|
21
|
+
[r.E.do{|e|{_: :a,href: e.url,c: e}},'<br>', # doc link
|
22
|
+
[g[-1*(g.size.max 3)..-1].map{|l| # show 3 matches per doc
|
23
|
+
l[0..404].gsub(a){|g| # create exerpt
|
24
|
+
H({_: :span, class: "w w#{c[g.downcase]}",c: g})} # style exerpt
|
25
|
+
},"<br>"]]}]}
|
26
|
+
|
27
|
+
end
|
data/infod/W/html.rb
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
#watch __FILE__
|
2
|
+
|
3
|
+
class Array
|
4
|
+
def html table=true
|
5
|
+
map(&:html).join ' '
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Object
|
10
|
+
def html *a
|
11
|
+
to_s.gsub('<','<').gsub('>','>')
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class String
|
16
|
+
def br
|
17
|
+
gsub(/\n/,"<br>\n")
|
18
|
+
end
|
19
|
+
def href name=nil
|
20
|
+
'<a href="'+self+'">'+(name||(Fn 'abbrURI',self))+'</a>'
|
21
|
+
end
|
22
|
+
def html
|
23
|
+
if match /\A(https?:\/\/)[\S]+\Z/
|
24
|
+
href
|
25
|
+
else
|
26
|
+
self
|
27
|
+
end
|
28
|
+
rescue
|
29
|
+
self
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Hash
|
34
|
+
def html
|
35
|
+
H({_: :table, class: :html, c:
|
36
|
+
map{|k,v|
|
37
|
+
{_: :tr, property: k, c:
|
38
|
+
[{_: :td,c: (Fn 'abbrURI',k), class: :key},
|
39
|
+
{_: :td,
|
40
|
+
c: (case k
|
41
|
+
when E::Content
|
42
|
+
{_: :pre, style: "white-space: pre-wrap", c: v}
|
43
|
+
when 'uri'
|
44
|
+
u = v.E
|
45
|
+
{_: :a, id: u, href: u.url, c: v}
|
46
|
+
else
|
47
|
+
v.html
|
48
|
+
end), class: :val}].cr}}.cr})
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class E
|
53
|
+
def html name=nil,l=false
|
54
|
+
(l ? url : uri).href name
|
55
|
+
end
|
56
|
+
|
57
|
+
def link
|
58
|
+
html '#',true
|
59
|
+
end
|
60
|
+
|
61
|
+
fn 'abbrURI',->u{
|
62
|
+
u.to_s.sub(/(?<scheme>[a-z]+:\/\/)?(?<abbr>.*?)(?<frag>[^#\/]+)$/,
|
63
|
+
'<span class="abbr"><span class="scheme">\k<scheme></span>\k<abbr></span><span class="frag">\k<frag></span>')
|
64
|
+
}
|
65
|
+
|
66
|
+
fn 'head',->d,e{
|
67
|
+
[{_: :title, c: d.attr(Title) || e.uri},
|
68
|
+
(Fn 'head.formats',e),
|
69
|
+
(Fn 'head.icon')].cr}
|
70
|
+
|
71
|
+
fn 'head.formats',->e{
|
72
|
+
F.keys.grep(/^render/).map{|f|
|
73
|
+
f = f[7..-1]
|
74
|
+
{_: :link, rel: :meta, type: f, href:'http://' + e['SERVER_NAME'] + e['REQUEST_PATH'] + e.q.merge({'format' => f}).qs}}.cr}
|
75
|
+
|
76
|
+
fn 'head.icon',->{{_: :link, href:'/css/i/favicon.ico', rel: :icon}}
|
77
|
+
|
78
|
+
# domain-specific view
|
79
|
+
fn 'view',->d,e{( Fn 'view/divine/set',d,e)||
|
80
|
+
d.values.map{|r|Fn 'view/divine/item',r,e}}
|
81
|
+
|
82
|
+
# no domain-specific view
|
83
|
+
fn 'view/base',->d,e{[H.css('/css/html'),d.values.map(&:html)]}
|
84
|
+
|
85
|
+
# select view - filesystem hints
|
86
|
+
fn 'view/divine/set',->d,e{
|
87
|
+
d.values.map{|e|e.E.base}.do{|b|
|
88
|
+
s = b.size.to_f
|
89
|
+
t = 0.42 # threshold
|
90
|
+
if b.grep(/^msg\./).size / s > t # email
|
91
|
+
Fn 'view/threads',d,e
|
92
|
+
elsif b.grep(/(aif|wav|flac|mp3|m4a|aac|ogg)$/i).size / s > t # audio
|
93
|
+
Fn 'view/audioplayer', d,e
|
94
|
+
elsif b.grep(/(gif|jpe?g|png)$/i).size / s > t # images
|
95
|
+
Fn 'view/th', d,e
|
96
|
+
elsif b.grep(/\.log$/).size / s > t
|
97
|
+
Fn 'view/chat', d,e
|
98
|
+
else false
|
99
|
+
end}}
|
100
|
+
|
101
|
+
Data['view/divine/item'] = "use RDF typeclass hints to choose view for a resource"
|
102
|
+
fn 'view/divine/item',->r,e{
|
103
|
+
r.class==Hash && r[Type] && r[Type][0] && r[Type][0].respond_to?(:uri) &&
|
104
|
+
(t = r[Type][0].uri; !t.empty? && # a RDF type
|
105
|
+
(F['view/'+t] ||
|
106
|
+
F['view/'+t.split(/\//)[-2]]).do{|f|
|
107
|
+
f.({r.uri => r},e)}) ||
|
108
|
+
[r.html,H.once(e,'css',H.css('/css/html'))] }
|
109
|
+
|
110
|
+
Data['view/select'] = "show a menu of all views available"
|
111
|
+
fn 'view/select',->d,e{
|
112
|
+
[{_: :style, c: 'a {min-width:22em;text-align:right}'},
|
113
|
+
F.keys.grep(/^view\/(?!application)/).map{|v|
|
114
|
+
[{_: :a, href: e['REQUEST_PATH']+e.q.merge({'view' => v[5..-1]}).qs,c: v},'<br>']}]}
|
115
|
+
F['view/?'] = F['view/select']
|
116
|
+
|
117
|
+
F['doc/view/multi'] = "display multiple comma-separated <b>views</b>"
|
118
|
+
fn 'view/multi',->d,e{e.q['views'].split(',').map{|v|Fn'view/'+v,d,e}}
|
119
|
+
|
120
|
+
def triplrBlob
|
121
|
+
glob.select(&:f).do{|f|f.map{|r|
|
122
|
+
yield r.uri,Type,E('blob')
|
123
|
+
yield r.uri,Content,r.r}} end
|
124
|
+
graphFromStream :triplrBlob
|
125
|
+
|
126
|
+
def triplrHref e=nil
|
127
|
+
yield uri,Content,read.do{|r|e ? r.force_encoding(e).to_utf8 : r}.hrefs
|
128
|
+
end
|
129
|
+
|
130
|
+
fn Render+'text/html',->d,e{
|
131
|
+
v = e.q['view'].to_s
|
132
|
+
h = F['head/'+v] || F['head']
|
133
|
+
v = F['view/'+v] || F['view']
|
134
|
+
|
135
|
+
H(e.q.has_key?('un') ? v.(d,e) :
|
136
|
+
['<!DOCTYPE html>',
|
137
|
+
{_: :html,
|
138
|
+
c: [{_: :head,
|
139
|
+
c: ['<meta charset="utf-8" />',
|
140
|
+
h.(d,e)]},
|
141
|
+
{_: :body, c: v.(d,e)}].cr}].cr)}
|
142
|
+
|
143
|
+
end
|
data/infod/W/image.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
#watch __FILE__
|
2
|
+
class E
|
3
|
+
|
4
|
+
def thumb?
|
5
|
+
mime.match(/^(image|video)/) && # is file an image?
|
6
|
+
@r.qs.match(/^[0-9]{0,3}x[0-9]{0,3}$/) && # valid dimensions?
|
7
|
+
base.match(/^[^.]/) # skip "invisible" images
|
8
|
+
end
|
9
|
+
|
10
|
+
def thumb
|
11
|
+
E['/E/images/'+
|
12
|
+
[no.stat.do{|s|
|
13
|
+
[s.ino,s.mtime]},
|
14
|
+
@r.qs].h.dive+'.png'].do{|n| n.e ||
|
15
|
+
(n.dirname.dir
|
16
|
+
mime.match(/^video/) &&
|
17
|
+
`ffmpegthumbnailer -s #{@r.qs.match(/[0-9]+/).to_s} -i #{sh} -o #{n.sh}` ||
|
18
|
+
`gm convert #{sh} -thumbnail "#{@r.qs}" #{n.sh}`)
|
19
|
+
n.env @r }
|
20
|
+
end
|
21
|
+
|
22
|
+
fn 'view/img',->i,_{
|
23
|
+
[i.values.map{|i|
|
24
|
+
[{_: :a, href: i.url,
|
25
|
+
c: {_: :img,
|
26
|
+
style:'float:left;max-width:61.8%',
|
27
|
+
src: i.url}},
|
28
|
+
i.html]},
|
29
|
+
(H.css '/css/img')
|
30
|
+
]}
|
31
|
+
|
32
|
+
fn 'view/th',->i,e{
|
33
|
+
s=e.q['s']||'233'
|
34
|
+
i.map{|u,i| u.match(/(gif|jpg|png|tiff)$/i) &&
|
35
|
+
{_: :a, href: i.url+'?view=img',
|
36
|
+
c: {_: :img, src: i.url+'?'+s+'x'+s}}}}
|
37
|
+
|
38
|
+
F['view/'+MIMEtype+'image/gif'] = F['view/th']
|
39
|
+
F['view/'+MIMEtype+'image/jpeg']= F['view/th']
|
40
|
+
F['view/'+MIMEtype+'image/png'] = F['view/th']
|
41
|
+
|
42
|
+
fn 'view/imgs',->m,e{ require 'nokogiri'
|
43
|
+
h=e.q['h'].do{|h|h.match(/^[0-9]+$/).do{|_|'height:'+h+'px'}}
|
44
|
+
seen={}
|
45
|
+
x=->i{i&&i.match(/(jpg|gif|png)$/i)&&i}
|
46
|
+
[(H.once e,:mu,H.js('/js/mu')),H.js('/js/images'),
|
47
|
+
m.values.map{|v|
|
48
|
+
[[*v[Content]].map{|c|
|
49
|
+
c.class == String &&
|
50
|
+
(Nokogiri::HTML.parse(c).do{|c|
|
51
|
+
[c.css('img').map{|i|i['src']}.compact,
|
52
|
+
c.css( 'a').map{|i|i['href']}.select(&x)]
|
53
|
+
})},
|
54
|
+
x.(v.uri),
|
55
|
+
(v.respond_to?(:values)&&v.values.flatten.map{|v|v.respond_to?(:uri)&&v.uri}.select(&x))
|
56
|
+
].flatten.uniq.compact.map{|s|
|
57
|
+
{s: s,c: ->{"<a href='#{v.uri.to_s.do{|u|u.path? ? u : u.E.url}}'><img style='float:left;#{h}' src='#{s}'></a>"}}}}.flatten.map{|i|
|
58
|
+
!seen[i[:s]] && (seen[i[:s]]=true; i[:c].())
|
59
|
+
}]}
|
60
|
+
|
61
|
+
end
|
data/infod/W/json.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
class E
|
2
|
+
|
3
|
+
def triplrJSON
|
4
|
+
yield uri, '/application/json', (JSON.parse read) if e
|
5
|
+
end
|
6
|
+
|
7
|
+
|
8
|
+
# addJSON :: tripleStream -> JSON graph (fs)
|
9
|
+
def addJSON i,g,p=[]
|
10
|
+
fromStream({},i).map{|u,r| # stream -> graph
|
11
|
+
(E u).do{|e| # resource
|
12
|
+
j = e.ef # inode
|
13
|
+
j.e || # exists?
|
14
|
+
(p.map{|p|r[p].do{|o|e.index p,o[0]}} # index properties
|
15
|
+
j.w({u => r},true) # write doc
|
16
|
+
puts "a #{e}"
|
17
|
+
# link opaque-URI docs as siblings of base-URI
|
18
|
+
e.a('.e').do{|u| (j.ln u) unless ((j.uri == u.uri) || u.e) }
|
19
|
+
e.roonga g # index content
|
20
|
+
)}}
|
21
|
+
self
|
22
|
+
rescue Exception => e
|
23
|
+
puts :addJSON,uri,e
|
24
|
+
end
|
25
|
+
|
26
|
+
fn 'view/application/json',->m,e{
|
27
|
+
m.map{|u,j|
|
28
|
+
e.q['sel'].do{|s|
|
29
|
+
c = j['/application/json'][0]
|
30
|
+
s.split(/\./).map{|s|
|
31
|
+
s = s.to_i if s.match(/^\d$/)
|
32
|
+
c = c[s] }
|
33
|
+
[{_: :a,
|
34
|
+
href: u+'?q=json&view=application/json&sel='+s,
|
35
|
+
c: {_: :b, c: u+'#'+s}}, c.html]
|
36
|
+
}||[{_: :h3, c: u},j.html]}}
|
37
|
+
|
38
|
+
def to_json *a
|
39
|
+
to_h.to_json *a
|
40
|
+
end
|
41
|
+
|
42
|
+
fn Render+'application/json',->d,_=nil{[d].to_json}
|
43
|
+
|
44
|
+
end
|