infod 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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