infod 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/infod.rb +2 -3
- data/infod/Es.rb +31 -67
- data/infod/{W/source.rb → Es/code.rb} +6 -10
- data/infod/Es/css.rb +21 -0
- data/infod/{W → Es}/csv.rb +0 -0
- data/infod/Es/du.rb +16 -0
- data/infod/{W → Es}/feed.rb +13 -11
- data/infod/Es/filter.rb +75 -0
- data/infod/Es/find.rb +20 -0
- data/infod/Es/fs.rb +145 -136
- data/infod/Es/glob.rb +22 -0
- data/infod/Es/grep.rb +61 -0
- data/infod/Es/groonga.rb +47 -56
- data/infod/Es/html.rb +271 -0
- data/infod/Es/image.rb +114 -0
- data/infod/Es/in.rb +68 -0
- data/infod/Es/index.rb +183 -0
- data/infod/{W → Es}/json.rb +28 -4
- data/infod/Es/kv.rb +60 -0
- data/infod/Es/ls.rb +58 -0
- data/infod/Es/mail.rb +87 -0
- data/infod/Es/man.rb +112 -0
- data/infod/Es/mime.rb +59 -0
- data/infod/Es/out.rb +52 -0
- data/infod/{W/page.rb → Es/pager.rb} +7 -3
- data/infod/Es/pdf.rb +19 -0
- data/infod/Es/rdf.rb +35 -0
- data/infod/Es/schema.rb +99 -0
- data/infod/Es/search.rb +24 -0
- data/infod/Es/sh.rb +21 -0
- data/infod/{W → Es}/text.rb +26 -14
- data/infod/H.rb +15 -29
- data/infod/H/audio.rb +19 -0
- data/infod/H/blog.rb +15 -0
- data/infod/{W → H}/cal.rb +2 -31
- data/infod/H/edit.rb +88 -0
- data/infod/{W/examine/examine.rb → H/facets.rb} +17 -17
- data/infod/{W → H}/forum.rb +1 -0
- data/infod/{W/examine/sw.rb → H/hf.rb} +12 -12
- data/infod/H/histogram.rb +78 -0
- data/infod/H/mail.rb +92 -0
- data/infod/{W/chat.rb → H/microblog.rb} +21 -16
- data/infod/H/threads.rb +77 -0
- data/infod/H/time.rb +131 -0
- data/infod/H/who.rb +30 -0
- data/infod/{W → H}/wiki.rb +0 -0
- data/infod/K.rb +28 -60
- data/infod/N.rb +151 -74
- data/infod/Rb.rb +3 -3
- data/infod/Th.rb +27 -101
- data/infod/Th/404.rb +29 -36
- data/infod/Th/500.rb +36 -5
- data/infod/Th/GET.rb +48 -118
- data/infod/Th/POST.rb +31 -11
- data/infod/Th/perf.rb +37 -0
- data/infod/Th/util.rb +89 -0
- data/infod/Y.rb +24 -7
- data/infod/infod.rb +2 -3
- metadata +92 -64
- data/infod/Es/redis.rb +0 -3
- data/infod/Es/sqlite.rb +0 -3
- data/infod/Th/local.rb +0 -22
- data/infod/W.rb +0 -34
- data/infod/W/audio.rb +0 -56
- data/infod/W/blog.rb +0 -3
- data/infod/W/color.rb +0 -28
- data/infod/W/core.rb +0 -77
- data/infod/W/css.rb +0 -24
- data/infod/W/du.rb +0 -35
- data/infod/W/edit.rb +0 -8
- data/infod/W/examine/exhibit.rb +0 -34
- data/infod/W/examine/hist.rb +0 -55
- data/infod/W/examine/history.rb +0 -19
- data/infod/W/examine/normal.rb +0 -31
- data/infod/W/examine/protovis.rb +0 -30
- data/infod/W/examine/time/graph.rb +0 -86
- data/infod/W/examine/time/line.rb +0 -24
- data/infod/W/find.rb +0 -24
- data/infod/W/grep.rb +0 -27
- data/infod/W/html.rb +0 -143
- data/infod/W/image.rb +0 -61
- data/infod/W/kv.rb +0 -66
- data/infod/W/ls.rb +0 -50
- data/infod/W/mail.rb +0 -248
- data/infod/W/pdf.rb +0 -16
- data/infod/W/post.rb +0 -9
- data/infod/W/rdf.rb +0 -32
- data/infod/W/schema.rb +0 -172
- data/infod/W/search.rb +0 -33
- data/infod/W/shell.rb +0 -30
- data/infod/W/table.rb +0 -87
- data/infod/W/tree.rb +0 -26
- data/infod/W/vfs.rb +0 -175
data/infod/Es/glob.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
class E
|
2
|
+
|
3
|
+
# glob :: pattern -> [E]
|
4
|
+
def glob p=""
|
5
|
+
(Pathname.glob d + p).map &:E
|
6
|
+
end
|
7
|
+
|
8
|
+
fn 'set/glob',->d,e=nil,_=nil{
|
9
|
+
[d,d.pathSegment].compact.map(&:glob).flatten[0..3e3]}
|
10
|
+
|
11
|
+
fn 'req/randomFile',->e,r{
|
12
|
+
g = F['set/glob'][e]
|
13
|
+
!g.empty? ? [302, {Location: g.random.uri}, []] : [404]}
|
14
|
+
|
15
|
+
def docs
|
16
|
+
doc = self if e # directly-referenced doc
|
17
|
+
docs = docBase.glob ".{e,html,n3,nt,owl,rdf,ttl}" # basename-sharing docs
|
18
|
+
dir = (d? && uri[-1]=='/' && uri.size>1) ? c : [] # trailing slash descends
|
19
|
+
[doc,docs,dir].flatten.compact
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/infod/Es/grep.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
#watch __FILE__
|
2
|
+
class E
|
3
|
+
|
4
|
+
fn 'set/grep',->e,q,m{
|
5
|
+
q['i'] ||= true # normal people want case-insensitive, i think - comment to unstick
|
6
|
+
q['q'].do{|query|
|
7
|
+
[e,e.pathSegment].compact.select(&:e).map{|e|
|
8
|
+
grep = "grep -rl#{q.has_key?('i') && 'i'} #{query.sh} #{e.sh}" # ;puts grep
|
9
|
+
`#{grep}`}.map{|r|r.lines.to_a.map{|r|r.chomp.unpathFs}}.flatten}}
|
10
|
+
|
11
|
+
fn 'view/grep',->d,e{
|
12
|
+
w = e.q['q']
|
13
|
+
if w
|
14
|
+
# words supplied in query
|
15
|
+
w = w.scan(/[\w]+/).map(&:downcase).uniq
|
16
|
+
|
17
|
+
# word index
|
18
|
+
c={}
|
19
|
+
w.each_with_index{|w,i|
|
20
|
+
c[w] = i }
|
21
|
+
|
22
|
+
# OR pattern
|
23
|
+
a = /(#{w.join '|'})/i
|
24
|
+
# sequential pattern
|
25
|
+
p = /#{w.join '.*'}/i
|
26
|
+
|
27
|
+
[H.css('/css/search'), H.css('/css/grep'),
|
28
|
+
F['view/search/form'][e.q,e],
|
29
|
+
{_: :style, c: c.values.map{|i|
|
30
|
+
# word color
|
31
|
+
b = rand(16777216)
|
32
|
+
# keep text contrasty
|
33
|
+
f = b > 8388608 ? :black : :white
|
34
|
+
|
35
|
+
# word CSS
|
36
|
+
".w#{i} {background-color: #{'#%06x' % b}; color: #{f}}\n"}},
|
37
|
+
|
38
|
+
# each resource
|
39
|
+
d.map{|u,r|
|
40
|
+
# model to text/plain
|
41
|
+
l = F[Render+'text/plain'][{u => r},e].gsub(/<[^>]*>/,'').lines
|
42
|
+
|
43
|
+
# try sequential match
|
44
|
+
g = l.grep p
|
45
|
+
# try OR match
|
46
|
+
g = l.grep a if g.empty?
|
47
|
+
|
48
|
+
# match?
|
49
|
+
!g.empty? &&
|
50
|
+
[# link to resource
|
51
|
+
r.E.do{|e|{_: :a, href: e.url, c: e}}, '<br>',
|
52
|
+
# show 3 matches per resource
|
53
|
+
[g[-1*(g.size.max 3)..-1].map{|l|
|
54
|
+
# exerpt
|
55
|
+
l[0..403].gsub(a){|g|
|
56
|
+
H({_: :span, class: "w w#{c[g.downcase]}",c: g})}
|
57
|
+
},"<br>"]]},
|
58
|
+
{_: :a, class: :down, href: e['REQUEST_PATH']+e.q.except('view').qs, style: "background-color: #{E.cs}",c: '↓'}]
|
59
|
+
end }
|
60
|
+
|
61
|
+
end
|
data/infod/Es/groonga.rb
CHANGED
@@ -1,10 +1,51 @@
|
|
1
1
|
#watch __FILE__
|
2
2
|
class E
|
3
|
-
|
3
|
+
# ruby search-engine & column-store
|
4
4
|
# http://groonga.org/ http://ranguba.org/
|
5
|
-
|
6
|
-
#
|
7
|
-
|
5
|
+
|
6
|
+
# query
|
7
|
+
fn 'protograph/roonga',->d,e,m{
|
8
|
+
ga = E.groonga
|
9
|
+
|
10
|
+
# search expression
|
11
|
+
q = e['q']
|
12
|
+
|
13
|
+
# context
|
14
|
+
g = e["context"] || d.env['SERVER_NAME']
|
15
|
+
|
16
|
+
# exec expression
|
17
|
+
r = q ? ga.select{|r|(r['graph'] == g) & r["content"].match(q)} : # expression if exists
|
18
|
+
ga.select{|r| r['graph'] == g} # ordered set (index date-range)
|
19
|
+
|
20
|
+
# offset, size
|
21
|
+
start = e['start'].do{|c| c.to_i.max(r.size - 1).min 0 } || 0
|
22
|
+
c = (e['c']||e['count']).do{|c|c.to_i.max(10000).min(0)} || 8
|
23
|
+
|
24
|
+
# are further results traversible?
|
25
|
+
down = r.size > start+c
|
26
|
+
up = !(start<=0)
|
27
|
+
|
28
|
+
# sort results
|
29
|
+
r = r.sort(e.has_key?('score') ? [["_score"]] : [["time", "descending"]],:offset => start,:limit => c)
|
30
|
+
|
31
|
+
# pagination resources
|
32
|
+
m['prev']={'uri' => 'prev','url' => '/search','start' => start + c, 'c' => c} if down
|
33
|
+
m['next']={'uri' => 'next','url' => '/search','start' => start - c, 'c' => c} if up
|
34
|
+
|
35
|
+
# search-result identifiers
|
36
|
+
r = r.map{|r| r['.uri'].E }
|
37
|
+
|
38
|
+
# fragment identifiers
|
39
|
+
m['frag'] = {'uri' => 'frag', 'res' => r}
|
40
|
+
|
41
|
+
# containing documents
|
42
|
+
r.map(&:docs).flatten.uniq.map{|r| m[r.uri] = r.env e}
|
43
|
+
|
44
|
+
# no 404 on 0 results - searchbox view
|
45
|
+
m['/s']={'uri'=>'/s'} if m.empty?
|
46
|
+
|
47
|
+
F['docsID'][m]}
|
48
|
+
|
8
49
|
def E.groonga
|
9
50
|
@groonga ||= (require 'groonga'
|
10
51
|
E['/E/groonga'].groonga
|
@@ -14,7 +55,7 @@ class E
|
|
14
55
|
# load or create groongaDB at URI
|
15
56
|
def groonga
|
16
57
|
return Groonga::Database.open d if e # open db
|
17
|
-
dirname.
|
58
|
+
dirname.mk # create containing dir
|
18
59
|
Groonga::Database.create(:path => d) # create db
|
19
60
|
Groonga::Schema.define{|s| # create schema
|
20
61
|
s.create_table("E",:type => :hash,:key_type => "ShortText"){|t|
|
@@ -37,11 +78,9 @@ class E
|
|
37
78
|
r.uri = u # update data
|
38
79
|
r.graph = graph.to_s
|
39
80
|
r.content = i.to_s
|
40
|
-
r.time = i[E::Date][0].to_time
|
81
|
+
r.time = i[E::Date].do{|t|t[0].to_time}
|
41
82
|
}
|
42
83
|
self
|
43
|
-
rescue Exception => x
|
44
|
-
$stderr.puts x,x.message
|
45
84
|
end
|
46
85
|
|
47
86
|
# remove
|
@@ -50,52 +89,4 @@ class E
|
|
50
89
|
graph.keys.push(uri).map{|u|g[u].delete}
|
51
90
|
end
|
52
91
|
|
53
|
-
# query
|
54
|
-
fn 'graph/roonga',->d,e,m{
|
55
|
-
|
56
|
-
# db
|
57
|
-
ga = E.groonga
|
58
|
-
|
59
|
-
# search expression
|
60
|
-
q = e['q']
|
61
|
-
|
62
|
-
# context
|
63
|
-
g = e["context"] || d.env['SERVER_NAME']
|
64
|
-
|
65
|
-
# offset
|
66
|
-
start = e['start'].do{|c|c.to_i} || 0
|
67
|
-
|
68
|
-
# number of results
|
69
|
-
c = e['c'].do{|c|c.to_i.max(1000).min(0)} || 8
|
70
|
-
|
71
|
-
# exec expression
|
72
|
-
r = q ? ga.select{|r|(r['graph'] == g) & r["content"].match(q)} : # expression if exists
|
73
|
-
ga.select{|r| r['graph'] == g} # ordered set (index date-range)
|
74
|
-
|
75
|
-
# are further results traversible?
|
76
|
-
down = r.size > start+c
|
77
|
-
up = !(start<=0)
|
78
|
-
|
79
|
-
# sort results
|
80
|
-
r = r.sort(e.has_key?('score') ? [["_score"]] : [["time", "descending"]],:offset => start,:limit => c)
|
81
|
-
|
82
|
-
# pagination resources
|
83
|
-
m['prev']={'uri' => 'prev','url' => '/search','start' => start + c, 'c' => c} if down
|
84
|
-
m['next']={'uri' => 'next','url' => '/search','start' => start - c, 'c' => c} if up
|
85
|
-
|
86
|
-
# search-result identifiers
|
87
|
-
r = r.map{|r| r['.uri'].E }
|
88
|
-
|
89
|
-
# fragment identifiers
|
90
|
-
m['frag'] = {'uri' => 'frag', 'res' => r}
|
91
|
-
|
92
|
-
# containing documents
|
93
|
-
r.map(&:docs).flatten.uniq.map{|r| m[r.uri] = r.env e}
|
94
|
-
|
95
|
-
# no 404 on 0 results - searchbox view
|
96
|
-
m['/s']={'uri'=>'/s'} if m.empty?
|
97
|
-
|
98
|
-
m # result set
|
99
|
-
}
|
100
|
-
|
101
92
|
end
|
data/infod/Es/html.rb
ADDED
@@ -0,0 +1,271 @@
|
|
1
|
+
#watch __FILE__
|
2
|
+
|
3
|
+
def H _
|
4
|
+
# Ruby object-literal syntax as HTML constructors
|
5
|
+
case _
|
6
|
+
when Hash then
|
7
|
+
'<'+(_[:_]||:div).to_s+(_.keys-[:_,:c]).map{|a|
|
8
|
+
' '+a.to_s+'='+"'"+
|
9
|
+
_[a].to_s.hsub({"'"=>'%27',
|
10
|
+
'>'=>'%3E',
|
11
|
+
'<'=>'%3C'})+"'"}.join+'>'+
|
12
|
+
(_[:c] ? (H _[:c]) : '')+
|
13
|
+
(_[:_] == :link ? '' : ('</'+(_[:_]||:div).to_s+'>'))
|
14
|
+
when Array then
|
15
|
+
_.map{|n|H n}.join
|
16
|
+
else
|
17
|
+
_.to_s
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class H
|
22
|
+
|
23
|
+
def H.[] h; H h end
|
24
|
+
|
25
|
+
def H.js a,inline=false
|
26
|
+
p=a+'.js'
|
27
|
+
inline ? {_: :script, c: p.E.r} :
|
28
|
+
{_: :script, type: "text/javascript", src: p}
|
29
|
+
end
|
30
|
+
|
31
|
+
def H.once e,n,*h
|
32
|
+
return if e[n]
|
33
|
+
e[n]=true
|
34
|
+
h
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Array
|
39
|
+
def html table=true
|
40
|
+
map(&:html).join ' '
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Object
|
45
|
+
def html *a
|
46
|
+
name = self.class
|
47
|
+
href = "https://duckduckgo.com/?q=ruby+#{name}"
|
48
|
+
"<a href=#{href}><b>#{name}</b></a>"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class String
|
53
|
+
def br
|
54
|
+
gsub(/\n/,"<br>\n")
|
55
|
+
end
|
56
|
+
def href name=nil
|
57
|
+
'<a href="'+self+'">'+(name||(Fn 'abbrURI',self))+'</a>'
|
58
|
+
end
|
59
|
+
def html
|
60
|
+
if match /\A(\/|http)[\S]+\Z/
|
61
|
+
href
|
62
|
+
else
|
63
|
+
self
|
64
|
+
end
|
65
|
+
rescue
|
66
|
+
self
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class Fixnum
|
71
|
+
def html; H({_: :input, type: :number, value: to_s}) end
|
72
|
+
end
|
73
|
+
|
74
|
+
class Float
|
75
|
+
def html; H({_: :input, type: :number, value: to_s}) end
|
76
|
+
end
|
77
|
+
|
78
|
+
class TrueClass
|
79
|
+
def html; H({_: :input, type: :checkbox, title: :True, checked: :checked}) end
|
80
|
+
end
|
81
|
+
|
82
|
+
class FalseClass
|
83
|
+
def html; H({_: :input, type: :checkbox, title: :False}) end
|
84
|
+
end
|
85
|
+
|
86
|
+
class Hash
|
87
|
+
def html
|
88
|
+
H({_: :table, class: :html, c:
|
89
|
+
map{|k,v|
|
90
|
+
{_: :tr, property: k, c:
|
91
|
+
[{_: :td,
|
92
|
+
c: {_: :a, name: k,
|
93
|
+
href: (k == 'uri' ? v : k),
|
94
|
+
c: (Fn 'abbrURI',k)}, class: :key},
|
95
|
+
{_: :td,
|
96
|
+
c: (case k
|
97
|
+
when E::Content
|
98
|
+
{_: :pre, style: "white-space: pre-wrap", c: v}
|
99
|
+
when 'uri'
|
100
|
+
u = v.E
|
101
|
+
{_: :a, id: u, href: u.url, c: v}
|
102
|
+
else
|
103
|
+
v.html
|
104
|
+
end), class: :val}].cr}}.cr})
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class E
|
109
|
+
def html name=nil,l=false
|
110
|
+
(l ? url : uri).href name
|
111
|
+
end
|
112
|
+
|
113
|
+
def link
|
114
|
+
html '#',true
|
115
|
+
end
|
116
|
+
|
117
|
+
fn 'abbrURI',->u{
|
118
|
+
u.to_s.sub(/(?<scheme>[a-z]+:\/\/)?(?<abbr>.*?)(?<frag>[^#\/]*)$/,
|
119
|
+
'<div class="abbr"><div class="scheme">\k<scheme></div>\k<abbr></div><div class="frag">\k<frag></div>')}
|
120
|
+
|
121
|
+
fn 'head',->d,e{
|
122
|
+
[{_: :title, c: e.uri},
|
123
|
+
(Fn 'head.formats',e),
|
124
|
+
(Fn 'head.icon')].cr}
|
125
|
+
|
126
|
+
fn 'head.formats',->e{
|
127
|
+
formats = %w{text/plain text/n3 application/json+ld}
|
128
|
+
formats.map{|f|
|
129
|
+
{_: :link, rel: :meta, type: f,
|
130
|
+
href:'http://' + e['SERVER_NAME'] + e['REQUEST_PATH'] + e.q.merge({'format' => f}).qs}}.cr}
|
131
|
+
|
132
|
+
fn 'head.icon',->{{_: :link, href:'/css/misc/favicon.ico', rel: :icon}}
|
133
|
+
|
134
|
+
fn 'view/select',->d,e{#(Fn 'view/divine/set',d,e)||
|
135
|
+
(Fn 'view/divine/files',d,e)||
|
136
|
+
d.values.map{|r|Fn 'view/divine/resource',r,e}}
|
137
|
+
|
138
|
+
# default view
|
139
|
+
F['view'] = F['view/select']
|
140
|
+
|
141
|
+
fn 'view/base',->d,e{
|
142
|
+
[H.once(e,'base',H.css('/css/html')),
|
143
|
+
d.values.map(&:html)]}
|
144
|
+
|
145
|
+
# select a view based on RDF-type majority
|
146
|
+
fn 'view/divine/set',->d,e{
|
147
|
+
# we'd be throwing away oddball resources to select one view for all of them
|
148
|
+
# but maybe you have a specific reason to
|
149
|
+
# inbuilt views support calling on set or per-resource basis, via "once" spec of set-wide components
|
150
|
+
# so views are selected per-resource unless you hack here..
|
151
|
+
}
|
152
|
+
|
153
|
+
fn 'view/divine/files',->d,e{
|
154
|
+
d.values.map{|e|e.E.base}.do{|b|
|
155
|
+
s = b.size.to_f # size of set
|
156
|
+
t = 0.42 # threshold, max of 0.5 as file and RDF resource are separate
|
157
|
+
if b.grep(/^msg\./).size / s > t
|
158
|
+
Fn 'view/threads',d,e
|
159
|
+
|
160
|
+
elsif b.grep(AudioFile).size / s > t
|
161
|
+
Fn 'view/audio', d,e
|
162
|
+
|
163
|
+
elsif b.grep(/(gif|jpe?g|png)$/i).size / s > t
|
164
|
+
Fn 'view/th', d,e
|
165
|
+
|
166
|
+
elsif b.grep(/\.log$/).size / s > t
|
167
|
+
Fn 'view/chat', d,e
|
168
|
+
|
169
|
+
else false
|
170
|
+
end}}
|
171
|
+
|
172
|
+
# select a view for a RDF resource
|
173
|
+
fn 'view/divine/resource',->r,e{
|
174
|
+
graph = {r.uri => r}
|
175
|
+
view = F['view/base']
|
176
|
+
# find types, skipping malformed/missing info
|
177
|
+
if r.class == Hash
|
178
|
+
r[Type].do{|types|
|
179
|
+
views = types.map{|t|
|
180
|
+
# discard non-URIs
|
181
|
+
t.uri if t.respond_to? :uri}.
|
182
|
+
compact.map{|t|
|
183
|
+
subtype = t
|
184
|
+
type = subtype.split(/\//)[-2]
|
185
|
+
[F['view/' + subtype],
|
186
|
+
F['view/' + type]]}.flatten.compact
|
187
|
+
view = views[0] unless views.empty?}
|
188
|
+
end
|
189
|
+
view[graph,e]}
|
190
|
+
|
191
|
+
# multiple views (comma-separated)
|
192
|
+
fn 'view/multi',->d,e{
|
193
|
+
e.q['views'].split(',').map{|v|
|
194
|
+
F['view/'+v].do{|f|f[d,e]}}}
|
195
|
+
|
196
|
+
def triplrBlob
|
197
|
+
glob.select(&:f).do{|f|f.map{|r|
|
198
|
+
yield r.uri,Type,E('blob')
|
199
|
+
yield r.uri,Content,r.r}} end
|
200
|
+
|
201
|
+
def triplrHref enc=nil
|
202
|
+
puts "triplrHref #{uri} #{d}"
|
203
|
+
yield uri,Content,(f && read).do{|r|enc ? r.force_encoding(enc).to_utf8 : r}.hrefs
|
204
|
+
end
|
205
|
+
|
206
|
+
fn Render+'text/html',->d,e{
|
207
|
+
v = e.q['view'].to_s
|
208
|
+
h = F['head/'+v] || F['head']
|
209
|
+
v = F['view/'+v] || F['view']
|
210
|
+
H(e.q.has_key?('un') ? v[d,e] :
|
211
|
+
['<!DOCTYPE html>',
|
212
|
+
{_: :html,
|
213
|
+
c: [{_: :head,
|
214
|
+
c: ['<meta charset="utf-8" />',
|
215
|
+
h[d,e]]},
|
216
|
+
{_: :body, c: v[d,e]}].cr}].cr)}
|
217
|
+
|
218
|
+
|
219
|
+
# property-selector toolbar (requires RDFa views)
|
220
|
+
fn 'view/p',->d,e{
|
221
|
+
[H.once(e,'property.toolbar',H.once(e,'p',(H.once e,:mu,H.js('/js/mu')),
|
222
|
+
H.js('/js/p'),
|
223
|
+
H.css('/css/table')),
|
224
|
+
{_: :a, href: '#', c: '-', id: :hideP},
|
225
|
+
{_: :a, href: '#', c: '+', id: :showP},
|
226
|
+
{_: :span, id: 'properties',
|
227
|
+
c: E.graphProperties(d).map{|k|
|
228
|
+
{_: :a, class: :n, href: k, c: k.label+' '}}},
|
229
|
+
{_: :style, id: :pS},
|
230
|
+
{_: :style, id: :lS}),
|
231
|
+
(Fn 'view/'+(e.q['pv']||'table'),d,e)]}
|
232
|
+
|
233
|
+
# table-cell placement on sparse matrix of rows/columns
|
234
|
+
# cal.rb contains an example usage
|
235
|
+
fn 'view/t',->d,e,l=nil,a=nil{
|
236
|
+
[H.once(e,'table',H.css('/css/table')),
|
237
|
+
{_: :table, c:
|
238
|
+
{_: :tbody, c: (Fn 'table/'+(l||e.q['table']),d).do{|t|
|
239
|
+
rx = t.keys.max
|
240
|
+
rm = t.keys.min
|
241
|
+
c = t.values.map(&:keys)
|
242
|
+
cm = c.map(&:min).min
|
243
|
+
cx = c.map(&:max).max
|
244
|
+
(rm..rx).map{|r|
|
245
|
+
{_: :tr, c:
|
246
|
+
t[r].do{|r|
|
247
|
+
(cm..cx).map{|c|
|
248
|
+
r[c].do{|c|
|
249
|
+
{_: :td, class: :cell, c:(Fn 'view/'+(a||e.q['cellview']),c,e)}
|
250
|
+
}||{_: :td}}}}}}}}]}
|
251
|
+
|
252
|
+
fn 'view/table',->i,e{[H.css('/css/table'),(Fn 'table',i.values,e)]}
|
253
|
+
|
254
|
+
fn 'table',->es,q=nil{
|
255
|
+
ks = {} # predicate table
|
256
|
+
es.map{|e|e.respond_to?(:keys) &&
|
257
|
+
e.keys.map{|k|ks[k]=true}}
|
258
|
+
keys = ks.keys
|
259
|
+
keys.empty? ? es.html :
|
260
|
+
H({_: :table,:class => :tab,
|
261
|
+
c: [{_: :tr,
|
262
|
+
c: keys.map{|k|
|
263
|
+
{_: :th, class: :label, property: k,
|
264
|
+
c: q ? {_: :a,
|
265
|
+
href: q['REQUEST_PATH']+q.q.except('reverse').merge({'sort'=>k}).merge(q.q.member?('reverse') ? {} : {'reverse'=>true}).qs,
|
266
|
+
c: (Fn 'abbrURI',k)} : k}}},
|
267
|
+
*es.map{|e|
|
268
|
+
{_: :tr, about: e.uri, c:
|
269
|
+
keys.map{|k| {_: :td, property: k, c: e[k].send(k=='uri' ? :href : :html)} }}}]})}
|
270
|
+
|
271
|
+
end
|