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
@@ -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
@@ -0,0 +1,3 @@
1
+ class E
2
+
3
+ end
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('<','&lt;').gsub('>','&gt;')
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