infod 0.0.1 → 0.0.2
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.
- 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
|