ttl2html 2.3.0 → 3.0.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 946f8e7de3a456635e40617b88ce014dcd7b54442557ded55a51ca3ad6dfb5b1
4
- data.tar.gz: 4bbb58d451d13e71f84c2bd1efe9ca897b695691768337ebb45d97c19badbcfa
3
+ metadata.gz: 8120a06f3f763ca13d8b9f35c920f9129d57876a5dde434b264c052bd1aa9151
4
+ data.tar.gz: 9d1cd9d09f5a14a5d56856c00099b38abe059a9c27f775df2074b42db99e9a7a
5
5
  SHA512:
6
- metadata.gz: 0db603f9cd36379e948f3a5a30e65db301af01d8df0a98c59dffcdc125934a8f006af140fb318d439466020ac1a4f7cebc2987f7ab06b49f3724431b1541f2d3
7
- data.tar.gz: 51516e8a7b73c4260ad5d7f23a440462dda00c8300ace92bf567a955b14386981f38ed427d27a9e707873c5c83699beac709aa61aa9a81a7b53ca9167a819ebb
6
+ metadata.gz: 4935a3824f19a696ea4657d652c4afd482af6a8a2e33aa98725d4739375480654b86dd4a9781dae357d816d422dc7807a974bb7dfb38fbe1d6cb5d84a84a3792
7
+ data.tar.gz: 9e561cb7abbfc195e8a13f09af9161a0b88f301e73b040ab149707e5f47905728a9ed603210b9f2818771350fd11670072941df4b58d92e4f5c268c608845274
data/bin/catttl CHANGED
@@ -1,18 +1,39 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require "zlib"
4
+ require "getoptlong"
4
5
  require "ttl2html"
5
6
  include TTL2HTML
6
7
 
7
8
  prefix = {}
8
9
  ttl = []
10
+ noexpand_filenames = false
11
+
12
+ opts = GetoptLong.new(
13
+ ["--no-expand", "-f", GetoptLong::NO_ARGUMENT],
14
+ ["--help", "-h", GetoptLong::NO_ARGUMENT]
15
+ )
16
+
17
+ opts.each do |opt, arg|
18
+ case opt
19
+ when "--no-expand"
20
+ noexpand_filenames = true
21
+ when "--help"
22
+ puts "Usage: catttl [options] [files]"
23
+ puts "Options:"
24
+ puts " -f, --no-expand Do not expand filenames"
25
+ puts " -h, --help Show this help message"
26
+ exit
27
+ end
28
+ end
9
29
 
10
30
  ARGV.each do |file|
11
- filename = find_turtle(file)
31
+ filename = find_turtle(file, noexpand: noexpand_filenames)
12
32
  if filename.nil?
13
33
  STDERR.puts "#{file} not found. skipping."
14
34
  next
15
35
  end
36
+ STDERR.puts filename
16
37
  klass = File
17
38
  if File.extname(filename) == ".gz"
18
39
  klass = Zlib::GzipReader
@@ -29,13 +50,9 @@ ARGV.each do |file|
29
50
  end
30
51
  end
31
52
  end
32
- ARGV.each do |file|
33
- filename = find_turtle(file)
34
- STDERR.puts filename
35
- end
36
53
 
37
54
  prefix.each do |k, v|
38
- STDERR.puts "Duplicate prefixes: #{k}: #{v}" if v.uniq.size > 1
55
+ STDERR.puts "Duplicate prefixes: #{k}: #{v.uniq}" if v.uniq.size > 1
39
56
  prefix[k] = v.sort.first
40
57
  end
41
58
  dups = prefix.values.group_by{|e| e }.select{|k, v| v.size > 1 }.keys
data/bin/ttl2html CHANGED
@@ -36,6 +36,8 @@ ARGV.each do |file|
36
36
  end
37
37
  if opt_cleanup
38
38
  ttl2html.cleanup
39
+ elsif ARGV.empty?
40
+ usage
39
41
  else
40
42
  ttl2html.output_files
41
43
  end
@@ -9,7 +9,6 @@ require "nokogiri"
9
9
 
10
10
  module TTL2HTML
11
11
  class Template
12
- attr_reader :param
13
12
  include ERB::Util
14
13
  include I18n::Base
15
14
  include ActionView::Helpers::NumberHelper
@@ -20,15 +19,15 @@ module TTL2HTML
20
19
  @template_path << File.join(File.dirname(__FILE__), "..", "..", "templates")
21
20
  I18n.load_path << Dir[File.join(File.dirname(__FILE__), "..", "..", "locales") + "/*.yml"]
22
21
  I18n.load_path << Dir[File.expand_path("locales") + "/*.yml"]
23
- I18n.locale = @param[:locale] if @param[:locale]
22
+ I18n.locale = @param[:locale].to_sym if @param[:locale]
24
23
  end
25
24
  def output_to(file, param = {})
26
- @param.update(param)
27
- @param[:output_file] = file
25
+ param = @param.merge(param)
26
+ param[:output_file] = file
28
27
  dir = File.dirname(file)
29
28
  FileUtils.mkdir_p(dir) if not File.exist?(dir)
30
- open(file, "w") do |io|
31
- io.print to_html(@param)
29
+ File.open(file, "w") do |io|
30
+ io.print to_html(param)
32
31
  end
33
32
  end
34
33
  def to_html(param)
@@ -37,9 +36,9 @@ module TTL2HTML
37
36
  to_html_raw(layout_fname, param)
38
37
  end
39
38
  def to_html_raw(template, param)
40
- @param.update(param)
39
+ param = @param.merge(param)
41
40
  template = find_template_path(template)
42
- tmpl = open(template){|io| io.read }
41
+ tmpl = File.open(template) { |io| io.read }
43
42
  erb = ERB.new(tmpl, trim_mode: "-")
44
43
  erb.filename = template
45
44
  erb.result(binding)
@@ -121,13 +120,12 @@ module TTL2HTML
121
120
 
122
121
  # helper method:
123
122
  include TTL2HTML::Util
124
- def relative_path(dest)
123
+ def relative_path(src, dest)
125
124
  path = nil
126
125
  dest_uri = RDF::IRI.parse(dest)
127
126
  if dest_uri.absolute?
128
127
  path = dest
129
128
  else
130
- src = @param[:output_file]
131
129
  src = Pathname.new(src).relative_path_from(Pathname.new(@param[:output_dir])) if @param[:output_dir]
132
130
  path = Pathname(dest).relative_path_from(Pathname(File.dirname src))
133
131
  if @param[:output_dir] and File.directory?(Pathname.new(@param[:output_dir]) + path)
@@ -139,11 +137,11 @@ module TTL2HTML
139
137
  #p [ :relative_path, path, dest, src ]
140
138
  path
141
139
  end
142
- def relative_path_uri(dest_uri, base_uri = @param[:base_uri])
140
+ def relative_path_uri(src, dest_uri, base_uri = @param[:base_uri])
143
141
  if dest_uri.start_with? base_uri
144
142
  dest = dest_uri.sub(base_uri, "")
145
143
  dest = uri_mapping_to_path(dest, @param, "")
146
- relative_path(dest)
144
+ relative_path(src, dest)
147
145
  else
148
146
  dest_uri
149
147
  end
@@ -196,20 +194,23 @@ module TTL2HTML
196
194
  object
197
195
  end
198
196
  end
199
- def format_property(property, labels = {}, subject = nil)
200
- subject = @param[:blank_subject] if not subject and @param[:blank_subject]
201
- subject_class = @param[:data_global][subject][RDF.type.to_s]&.first if subject
202
- if subject_class and @param[:labels_with_class][subject_class] and @param[:labels_with_class][subject_class][property]
203
- @param[:labels_with_class][subject_class][property]
204
- elsif labels and labels[property]
205
- labels[property]
197
+ def format_property(property, param, subject = nil)
198
+ param = @param.merge(param)
199
+ subject = param[:blank_subject] if not subject and param[:blank_subject]
200
+ subject_class = param[:data_global][subject][RDF.type.to_s]&.first if subject
201
+ if subject_class and param[:labels_with_class][subject_class] and param[:labels_with_class][subject_class][property]
202
+ param[:labels_with_class][subject_class][property]
203
+ elsif param[:labels] and param[:labels][property]
204
+ param[:labels][property]
206
205
  else
207
206
  property.split(/[\/\#]/).last.capitalize
208
207
  end
209
208
  end
210
- def format_object(object, data, type = {})
209
+ def format_object(object, param)
210
+ type = param[:type] || {}
211
+ data = param[:data] || {}
211
212
  if /\Ahttps?:\/\// =~ object.to_s
212
- rel_path = relative_path_uri(object, param[:base_uri])
213
+ rel_path = relative_path_uri(param[:output_file], object)
213
214
  if param[:data_global][object]
214
215
  result = "<a href=\"#{rel_path}\">#{get_title(param[:data_global][object]) or ERB::Util.html_escape(object)}</a>"
215
216
  subtitle = get_subtitle(param[:data_global][object])
@@ -223,16 +224,17 @@ module TTL2HTML
223
224
  end
224
225
  elsif /\A_:/ =~ object.to_s and param[:data_global][object]
225
226
  if type[:inverse] and param[:data_inverse_global][object]
226
- format_triples(param[:data_inverse_global][object], inverse: true, blank: true)
227
+ format_triples(param[:data_inverse_global][object], param, inverse: true, blank: true)
227
228
  else
228
- format_triples(param[:data_global][object], blank: true)
229
+ format_triples(param[:data_global][object], param, blank: true)
229
230
  end
230
231
  else
231
232
  ERB::Util.html_escape object
232
233
  end
233
234
  end
234
- def format_triples(triples, type = {})
235
- param_local = @param.dup.merge(data: triples)
235
+ def format_triples(triples, param, type = {})
236
+ param_local = @param.dup.merge(param)
237
+ param_local = param_local.merge(data: triples)
236
238
  param_local[:type] = type
237
239
  if @param[:labels_with_class] and triples["http://www.w3.org/1999/02/22-rdf-syntax-ns#type"]
238
240
  @param[:labels_with_class].reverse_each do |k, v|
@@ -324,6 +326,7 @@ module TTL2HTML
324
326
  value && !(value.respond_to?(:empty?) && value.empty?)
325
327
  end
326
328
  def sort_criteria(val, data_global)
329
+ raise "sort_criteria got nil." if data_global.nil?
327
330
  resource = data_global[val]
328
331
  results = []
329
332
  [
@@ -350,4 +353,4 @@ module TTL2HTML
350
353
  results
351
354
  end
352
355
  end
353
- end
356
+ end
data/lib/ttl2html/util.rb CHANGED
@@ -1,29 +1,44 @@
1
1
  module TTL2HTML
2
2
  module Util
3
- def uri_mapping_to_path(uri, param, suffix = ".html")
4
- path = nil
3
+ def make_mapping_uris_cache(param)
4
+ @path_cache = Set.new
5
+ data = @data || @param[:data_global] || @param[:data] || {}
6
+ data.keys.each do |uri|
7
+ local_path = _uri_mapping_to_path(uri, param)
8
+ # 親パスをすべて抽出してSetに追加
9
+ idx = 0
10
+ while (idx = local_path.index("/", idx))
11
+ parent = local_path[0...idx]
12
+ @path_cache << parent unless parent.empty?
13
+ idx += 1
14
+ end
15
+ end
16
+ @path_cache
17
+ end
18
+ def _uri_mapping_to_path(uri, param, suffix = ".html")
19
+ local_path = uri.sub(param[:base_uri], "")
5
20
  if param[:uri_mappings]
6
21
  param[:uri_mappings].each do |mapping|
7
- local_file = uri.sub(param[:base_uri], "")
8
- if mapping["regexp"] =~ local_file
9
- path = local_file.sub(mapping["regexp"], mapping["path"])
22
+ if mapping["regexp"] =~ local_path
23
+ #p [mapping["regexp"], local_path]
24
+ local_path = local_path.sub(mapping["regexp"], mapping["path"])
25
+ #p [mapping["regexp"], local_path]
10
26
  end
11
27
  end
12
28
  end
13
- if path.nil?
14
- if suffix == ".html"
15
- if @data.keys.find{|e| e.start_with?(uri + "/") }
16
- path = uri + "/index"
17
- elsif uri.end_with?("/")
18
- path = uri + "index"
19
- else
20
- path = uri
21
- end
22
- else
23
- path = uri
29
+ local_path
30
+ end
31
+ def uri_mapping_to_path(uri, param, suffix = ".html")
32
+ path = nil
33
+ @path_cache ||= make_mapping_uris_cache(param)
34
+ path = _uri_mapping_to_path(uri, param, suffix)
35
+ if suffix == ".html"
36
+ if @path_cache.include? path
37
+ path += "/index"
38
+ elsif path.end_with?("/")
39
+ path += "index"
24
40
  end
25
41
  end
26
- path = path.sub(param[:base_uri], "")
27
42
  path << suffix
28
43
  #p [uri, path]
29
44
  path
@@ -1 +1 @@
1
- TTL2HTML::VERSION = "2.3.0"
1
+ TTL2HTML::VERSION = "3.0.0"
data/lib/ttl2html.rb CHANGED
@@ -27,7 +27,7 @@ module TTL2HTML
27
27
 
28
28
  def load_config(file)
29
29
  config = { output_turtle: true }
30
- open(file) do |io|
30
+ File.open(file) do |io|
31
31
  YAML.safe_load(io, permitted_classes: [Regexp]).each do |k, v|
32
32
  config[k.intern] = v
33
33
  end
@@ -135,7 +135,9 @@ module TTL2HTML
135
135
  format: "(%t) %a %e %P% Processed: %c from %C"
136
136
  }
137
137
  data = @data.keys.sort_by do|uri|
138
- [ uri.count("/"), uri.size, uri ]
138
+ local_path = uri_mapping_to_path(uri, @config, ".html")
139
+ #p [ local_path.size, local_path.count("/"), local_path ]
140
+ [ local_path.size, local_path.count("/"), local_path ]
139
141
  end.reverse
140
142
  Parallel.each(data, progress: progressbar_options) do |uri|
141
143
  next if not uri.start_with? @config[:base_uri]
@@ -168,8 +170,8 @@ module TTL2HTML
168
170
  end
169
171
  @config[:orders_with_class] = shapes2orders(shapes)
170
172
  Dir.mkdir @config[:output_dir] if @config[:output_dir] and not File.exist? @config[:output_dir]
173
+ template = Template.new("default.html.erb", @config)
171
174
  each_data(:output_html_files) do |uri, v|
172
- template = Template.new("default.html.erb", @config)
173
175
  param = @config.dup
174
176
  param[:uri] = uri
175
177
  param[:turtle_uri] = uri + ".ttl"
@@ -185,6 +187,7 @@ module TTL2HTML
185
187
  param[:breadcrumbs_items] = build_breadcrumbs(uri, template)
186
188
  end
187
189
  file = uri_mapping_to_path(uri, @config, ".html")
190
+ #p [:each_data, uri, file]
188
191
  if @config[:output_dir]
189
192
  file = File.join(@config[:output_dir], file)
190
193
  end
@@ -209,6 +212,7 @@ module TTL2HTML
209
212
  template = Template.new("index.html.erb", @config)
210
213
  param = @config.dup
211
214
  param[:class_label] = template.get_title(@data[@config[:top_class]], nil)
215
+ param[:class_label] ||= @config[:top_class].split(/[\/\#]/).last.capitalize
212
216
  param[:data_global] = @data
213
217
  param[:data_inverse_global] = @data_inverse
214
218
  param[:versions] = versions
@@ -261,7 +265,7 @@ module TTL2HTML
261
265
  label = template.get_title(@data[target_class], nil)
262
266
  comment = template.get_language_literal(@data[target_class]["http://www.w3.org/2000/01/rdf-schema#comment"]) if @data[target_class]["http://www.w3.org/2000/01/rdf-schema#comment"]
263
267
  else
264
- label = template.format_property(target_class)
268
+ label = template.format_property(target_class, param)
265
269
  end
266
270
  else
267
271
  label = template.get_title(@data[subject.to_s])
@@ -486,12 +490,21 @@ module TTL2HTML
486
490
  if data["http://rdfs.org/ns/void#sparqlEndpoint"]
487
491
  endpoint = data["http://rdfs.org/ns/void#sparqlEndpoint"].first
488
492
  end
493
+ if data["http://www.w3.org/ns/dcat#accessService"]
494
+ service = data["http://www.w3.org/ns/dcat#accessService"].first
495
+ service_data = @data[service]
496
+ if service_data
497
+ endpoint = service_data["http://www.w3.org/ns/dcat#endpointURL"]&.first
498
+ endpoint_landingpage = service_data["http://www.w3.org/ns/dcat#landingPage"]&.first
499
+ end
500
+ end
489
501
  result = {
490
502
  uri: toplevel.to_s,
491
503
  description: data["http://purl.org/dc/terms/description"],
492
504
  license: license,
493
505
  contact: contact,
494
506
  endpoint: endpoint,
507
+ endpoint_landingpage: endpoint_landingpage,
495
508
  derivedfrom: derivedfrom,
496
509
  }
497
510
  end
@@ -522,6 +535,7 @@ module TTL2HTML
522
535
  end
523
536
 
524
537
  def cleanup
538
+ dirs = []
525
539
  @data.select do |uri, v|
526
540
  uri.start_with? @config[:base_uri]
527
541
  end.sort_by do |uri, v|
@@ -529,25 +543,41 @@ module TTL2HTML
529
543
  end.each do |uri, v|
530
544
  html_file = uri_mapping_to_path(uri, @config, ".html")
531
545
  html_file = File.join(@config[:output_dir], html_file) if @config[:output_dir]
546
+ dirs << File.dirname(html_file)
532
547
  File.unlink html_file if File.exist? html_file
533
548
  ttl_file = uri_mapping_to_path(uri, @config, ".ttl")
534
549
  ttl_file = File.join(@config[:output_dir], ttl_file) if @config[:output_dir]
535
550
  File.unlink ttl_file if File.exist? ttl_file
536
551
  dir = uri.sub(@config[:base_uri], "")
537
552
  dir = File.join(@config[:output_dir], dir) if @config[:output_dir]
538
- Dir.rmdir dir if File.exist? dir
553
+ dirs << dir
539
554
  end
540
555
  index_html = "index.html"
541
556
  index_html = File.join(@config[:output_dir], "index.html") if @config[:output_dir]
542
557
  if @config[:top_class] and File.exist? index_html
543
558
  File.unlink index_html
544
559
  end
560
+ about_html = (@config[:about_file] || "about.html")
561
+ about_html = File.join(@config[:output_dir], about_html) if @config[:output_dir]
562
+ File.unlink about_html if File.exist? about_html
563
+
564
+ dirs = dirs.uniq.sort_by{|e| -(e.size) }
565
+ #p dirs
566
+ dirs.each do |dir|
567
+ next if dir == "." # failsafe...
568
+ next if dir == @config[:output_dir] # failsafe...
569
+ FileUtils.remove_entry_secure(dir) if File.exist? dir
570
+ end
545
571
  end
546
572
  end
547
573
 
548
574
  def find_turtle(filename, params = {})
549
575
  if params[:noexpand] == true
550
- filename if File.exists? filename
576
+ if File.exist? filename
577
+ filename
578
+ else
579
+ nil
580
+ end
551
581
  else
552
582
  file = nil
553
583
  basename = File.basename(filename, ".ttl")
data/locales/en.yml CHANGED
@@ -16,6 +16,7 @@ en:
16
16
  default:
17
17
  details: Details
18
18
  inverse_data: Referred resources
19
+ copy-link: "Copy Link"
19
20
  index:
20
21
  derivedfrom-text: "This dataset is created by editing <a href=\"%{url}\">\"%{label}\"</a>."
21
22
  list: "List of %{resource}"
@@ -23,7 +24,8 @@ en:
23
24
  past-versions: "Past versions"
24
25
  license-text: "This dataset is freely usable as <a href=\"%{url}\">%{label}</a>."
25
26
  sparql-endpoint: "SPARQL Endpoint"
26
- sparql-endpoint-text: "The SPARQL endpoint for the dataset is available at"
27
+ sparql-endpoint-text: "The SPARQL endpoint for the dataset is available at:"
28
+ sparql-endpoint-landingpage: "Search page"
27
29
  dataset:
28
30
  derivedfrom-text: "This dataset is created by editing <a href=\"%{url}\">\"%{label}\"</a>."
29
31
  license-text: "This dataset is freely usable as <a href=\"%{url}\">%{label}</a>."
data/locales/ja.yml CHANGED
@@ -16,6 +16,7 @@ ja:
16
16
  default:
17
17
  details: 詳細情報
18
18
  inverse_data: 被参照情報
19
+ copy-link: "リンクをコピー"
19
20
  index:
20
21
  derivedfrom-text: "このデータセットは<a href=\"%{url}\">「%{label}」</a>を加工して作成。"
21
22
  list: "%{resource} 一覧"
@@ -23,7 +24,8 @@ ja:
23
24
  past-versions: 過去の更新履歴
24
25
  license-text: "このデータセットは<a href=\"%{url}\">%{label}</a>として自由に利用できます。"
25
26
  sparql-endpoint: "SPARQLエンドポイント"
26
- sparql-endpoint-text: "このデータセットに対するSPARQLエンドポイントは以下のURLからアクセスできます"
27
+ sparql-endpoint-text: "このデータセットに対するSPARQLエンドポイントは以下のURLからアクセスできます:"
28
+ sparql-endpoint-landingpage: "クエリ画面"
27
29
  dataset:
28
30
  derivedfrom-text: "このデータセットは<a href=\"%{url}\">「%{label}」</a>を加工して作成。"
29
31
  license-text: "<a href=\"%{url}\">%{label}</a>として自由に利用できます。"
@@ -36,7 +36,7 @@
36
36
  <%- if param[:shapes].size > 0 -%>
37
37
  <h2 id="shapes"><%=h t("about.shape-heading") %></h2>
38
38
  <%- param[:shapes].keys.sort_by{|k| param[:shapes][k][:order] }.each do |shape| -%>
39
- <h3 id="<%=h relative_path_uri(shape) %>"><%=h param[:shapes][shape][:label] %></h3>
39
+ <h3 id="<%=h relative_path_uri(param[:output_file], shape) %>"><%=h param[:shapes][shape][:label] %></h3>
40
40
  <%- if param[:shapes][shape][:target_class] -%>
41
41
  <ul><li><%= t("about.shape-target") %>: <a href="<%=h param[:shapes][shape][:target_class] %>"><%=h param[:shapes][shape][:target_class] %></a></li></ul>
42
42
  <%- end -%>
@@ -55,9 +55,13 @@
55
55
  <%- end -%>
56
56
  <%- if param[:toplevel][:endpoint] -%>
57
57
  <h2 id="sparql-endpoint"><%=h t("index.sparql-endpoint") %> <i class="bi bi-database-add"></i></h2>
58
- <p>
59
- <%= t("index.sparql-endpoint-text") %>: <a href="<%=h param[:toplevel][:endpoint] %>"><%=h param[:toplevel][:endpoint] %></a>
60
- </p>
58
+ <p><%= t("index.sparql-endpoint-text") %></p>
59
+ <ul>
60
+ <%- if param[:toplevel][:endpoint_landingpage] -%>
61
+ <li><%= t("index.sparql-endpoint-landingpage") %>: <a href="<%=h param[:toplevel][:endpoint_landingpage] %>"><%=h param[:toplevel][:endpoint_landingpage] %></a></li>
62
+ <%- end -%>
63
+ <li><%= t("index.sparql-endpoint") %>: <a href="<%=h param[:toplevel][:endpoint] %>"><%=h param[:toplevel][:endpoint] %></a></li>
64
+ </ul>
61
65
  <%- end -%>
62
66
  <%- if param[:toplevel] and param[:toplevel][:contact] -%>
63
67
  <h2 id="contact"><%=h t("about.contact") %></h2>
@@ -4,7 +4,69 @@
4
4
  <%- if param[:subtitle] -%>
5
5
  <p class="lead"><%= param[:subtitle] %></p>
6
6
  <%- end -%>
7
- <p><i class="bi bi-link-45deg"></i> <a href="<%=h param[:uri] %>"><%=h param[:uri] %></a></p>
7
+ <p>
8
+ <i class="bi bi-link-45deg"></i> <a href="<%=h param[:uri] %>"><%=h param[:uri] %></a>
9
+ <%- if param[:enable_copy_link_button] -%>
10
+ <button id="copy-link" class="btn btn-sm btn-outline-secondary" aria-live="polite">
11
+ <i class="bi bi-copy"></i> <span class="copy-link-text"><%=h t("default.copy-link") %></span>
12
+ </button>
13
+ <%- end -%>
14
+ </p>
15
+ <%- if param[:enable_copy_link_button] -%>
16
+ <script>
17
+ (() => {
18
+ const btn = document.getElementById("copy-link");
19
+ const url = '<%=h param[:uri] %>';
20
+ const originalHTML = btn.innerHTML;
21
+ let timerId = null;
22
+ async function copyText(text) {
23
+ // Clipboard API, or fallback
24
+ if (navigator.clipboard && window.isSecureContext) {
25
+ await navigator.clipboard.writeText(text);
26
+ return;
27
+ }
28
+ const ta = document.createElement("textarea");
29
+ ta.value = text;
30
+ ta.style.position = "fixed";
31
+ ta.style.left = "-9999px";
32
+ document.body.appendChild(ta);
33
+ ta.select();
34
+ document.execCommand("copy");
35
+ document.body.removeChild(ta);
36
+ }
37
+ function setCopiedState() {
38
+ btn.innerHTML = `<i class="bi bi-check2"></i> <span class="btn-label">Copied!</span>`;
39
+ btn.classList.remove("btn-outline-secondary");
40
+ btn.classList.add("btn-outline-success");
41
+ }
42
+ function resetState() {
43
+ btn.innerHTML = originalHTML;
44
+ btn.classList.remove("btn-outline-success");
45
+ btn.classList.add("btn-outline-secondary");
46
+ }
47
+ btn.addEventListener("click", async () => {
48
+ btn.disabled = true;
49
+ try {
50
+ await copyText(url);
51
+ setCopiedState();
52
+ clearTimeout(timerId);
53
+ timerId = setTimeout(() => {
54
+ resetState();
55
+ btn.disabled = false;
56
+ }, 1500);
57
+ } catch (e) {
58
+ // fail...
59
+ btn.innerHTML = `<span class="btn-label">Copy failed...</span>`;
60
+ clearTimeout(timerId);
61
+ timerId = setTimeout(() => {
62
+ resetState();
63
+ btn.disabled = false;
64
+ }, 1500);
65
+ }
66
+ });
67
+ })();
68
+ </script>
69
+ <%- end -%>
8
70
  </div>
9
71
  </div>
10
72
  <div class="container">
@@ -13,11 +75,11 @@
13
75
  <div class="col-md-12">
14
76
  <nav aria-label="breadcrumb">
15
77
  <ol class="breadcrumb">
16
- <li class="breadcrumb-item"><a href="<%=h relative_path_uri(param[:base_uri]) %>"><i class="bi bi-house-door-fill"></i> Home</a></li>
78
+ <li class="breadcrumb-item"><a href="<%=h relative_path_uri(param[:output_file], param[:base_uri]) %>"><i class="bi bi-house-door-fill"></i> Home</a></li>
17
79
  <%- param[:breadcrumbs_items].reverse.each_with_index do |e, i| -%>
18
80
  <li class="breadcrumb-item<%= ' active' if i == param[:breadcrumbs_items].size-1 %>" aria-current="page">
19
81
  <%- if e[:uri] -%>
20
- <a href="<%=h relative_path_uri(e[:uri]) %>"><%=h e[:label] %></a>
82
+ <a href="<%=h relative_path_uri(param[:output_file], e[:uri]) %>"><%=h e[:label] %></a>
21
83
  <%- else -%>
22
84
  <%=h e[:label] %>
23
85
  <%- end -%>
@@ -31,7 +93,7 @@
31
93
  <div class="row">
32
94
  <div class="col-md-12">
33
95
  <h2><%=h t("default.details") %></h2>
34
- <%= format_triples(param[:data]) %>
96
+ <%= format_triples(param[:data], param) %>
35
97
  </div>
36
98
  </div>
37
99
  <%- if param[:additional_content] -%>
@@ -41,7 +103,7 @@
41
103
  <div class="row inverse">
42
104
  <div class="col-md-12">
43
105
  <h2><%=h t("default.inverse_data") %></h2>
44
- <%= format_triples(param[:data_inverse], inverse: true) %>
106
+ <%= format_triples(param[:data_inverse], param, inverse: true) %>
45
107
  </div>
46
108
  </div>
47
109
  <%- end -%>
@@ -4,11 +4,11 @@
4
4
  <ul>
5
5
  <%- param[:index_data].each do |e| -%>
6
6
  <%- e.each do |s, o| -%>
7
- <li><%= format_object(s, param[:data_global]) %>
7
+ <li><%= format_object(s, param) %>
8
8
  <%- if o and o.size > 0 -%>
9
9
  <ul>
10
10
  <%- o.each do |o2| -%>
11
- <li><%= format_object(o2.to_s, param[:data_global]) %></li>
11
+ <li><%= format_object(o2.to_s, param) %></li>
12
12
  <%- end -%>
13
13
  </ul>
14
14
  <%- end -%>
@@ -9,7 +9,7 @@
9
9
  <%- end -%>
10
10
  <p><i class="bi bi-link-45deg"></i> <a href="<%=h param[:base_uri] %>"><%=h param[:base_uri] %></a></p>
11
11
  <%- if param[:about_file] -%>
12
- <p><a class="btn btn-info" href="<%=h relative_path(param[:about_file]) %>"><%=h t("about.title", title: param[:site_title]) %> &raquo;</a></p>
12
+ <p><a class="btn btn-info" href="<%=h relative_path(param[:output_file], param[:about_file]) %>"><%=h t("about.title", title: param[:site_title]) %> &raquo;</a></p>
13
13
  <%- end -%>
14
14
  </div>
15
15
  </div>
@@ -23,7 +23,7 @@
23
23
  <%= format_version_info(param[:versions].last) %>
24
24
  </dl>
25
25
  <%- if param[:about_file] and param[:versions].size > 1 -%>
26
- <p><a href="<%=h relative_path(param[:about_file]) %>#versions">&raquo; <%=h t("index.past-versions") %></a></p>
26
+ <p><a href="<%=h relative_path(param[:output_file], param[:about_file]) %>#versions">&raquo; <%=h t("index.past-versions") %></a></p>
27
27
  <%- end -%>
28
28
  <%- if param[:toplevel] and not param[:toplevel][:license].empty? -%>
29
29
  <p class="license">
@@ -40,11 +40,19 @@
40
40
  <%- end -%>
41
41
  </div>
42
42
  </div>
43
- <div class="col-md">
44
- <h2 id="sparql-endpoint"><%=h t("index.sparql-endpoint") %> <i class="bi bi-database-add"></i></h2>
45
- <p>
46
- <%= t("index.sparql-endpoint-text") %>: <a href="<%=h param[:toplevel][:endpoint] %>"><%=h param[:toplevel][:endpoint] %></a>
47
- </p>
43
+ <%- end -%>
44
+ <%- if param[:toplevel][:endpoint] -%>
45
+ <div class="row">
46
+ <div class="col-md">
47
+ <h2 id="sparql-endpoint"><%=h t("index.sparql-endpoint") %> <i class="bi bi-database-add"></i></h2>
48
+ <p><%= t("index.sparql-endpoint-text") %></p>
49
+ <ul>
50
+ <%- if param[:toplevel][:endpoint_landingpage] -%>
51
+ <li><%= t("index.sparql-endpoint-landingpage") %>: <a href="<%=h param[:toplevel][:endpoint_landingpage] %>"><%=h param[:toplevel][:endpoint_landingpage] %></a></li>
52
+ <%- end -%>
53
+ <li><%= t("index.sparql-endpoint") %>: <a href="<%=h param[:toplevel][:endpoint] %>"><%=h param[:toplevel][:endpoint] %></a></li>
54
+ </ul>
55
+ </div>
48
56
  </div>
49
57
  <%- end -%>
50
58
  </div>
@@ -9,7 +9,7 @@
9
9
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/afeld/bootstrap-toc/dist/bootstrap-toc.min.css">
10
10
  <%- end -%>
11
11
  <%- Array(param[:css_file]).each do |file| -%>
12
- <link rel="stylesheet" href="<%=h relative_path(file) %>">
12
+ <link rel="stylesheet" href="<%=h relative_path(param[:output_file], file) %>">
13
13
  <%- end -%>
14
14
  <%- if param[:custom_css] -%>
15
15
  <style type="text/css"><%=h param[:custom_css] %></style>
@@ -51,8 +51,8 @@
51
51
  <body<%= ' data-spy="scroll" data-target="#toc"' if @template == "about.html.erb" and param[:about_toc] %>>
52
52
  <nav class="navbar navbar-expand-lg <%=h param[:navbar_class] || "navbar-light" %>">
53
53
  <%- if param[:logo] -%>
54
- <a class="navbar-brand" href="<%=h relative_path_uri(param[:base_uri]) %>">
55
- <img src="<%=h relative_path(param[:logo]) %>" style="max-height: 54px" alt="<%=h param[:site_title] %>">
54
+ <a class="navbar-brand" href="<%=h relative_path_uri(param[:output_file], param[:base_uri]) %>">
55
+ <img src="<%=h relative_path(param[:output_file], param[:logo]) %>" style="max-height: 54px" alt="<%=h param[:site_title] %>">
56
56
  </a>
57
57
  <%- end -%>
58
58
  <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
@@ -61,16 +61,16 @@
61
61
  <div class="collapse navbar-collapse" id="navbarSupportedContent">
62
62
  <ul class="navbar-nav mr-auto">
63
63
  <li class="nav-item<%= ' active' if @template == "index.html.erb" %>">
64
- <a class="nav-link" href="<%=h relative_path_uri(param[:base_uri]) %>">Home</a>
64
+ <a class="nav-link" href="<%=h relative_path_uri(param[:output_file], param[:base_uri]) %>">Home</a>
65
65
  </li>
66
66
  <%- if param[:about_file] -%>
67
67
  <li class="nav-item<%= ' active' if @template == "about.html.erb" %>">
68
- <a class="nav-link" href="<%=h relative_path(param[:about_file]) %>">About</a>
68
+ <a class="nav-link" href="<%=h relative_path(param[:output_file], param[:about_file]) %>">About</a>
69
69
  </li>
70
70
  <%- end -%>
71
71
  <%- if param[:additional_link] -%>
72
72
  <%- param[:additional_link].each do |link| -%>
73
- <li class="nav-item"><a class="nav-link" href="<%=h link["href"] %>"><%=h link["label"] %></a></li>
73
+ <li class="nav-item"><a class="nav-link" href="<%=h relative_path_uri(param[:output_file], link["href"]) %>"><%=h link["label"] %></a></li>
74
74
  <%- end -%>
75
75
  <%- end -%>
76
76
  </ul>
@@ -103,7 +103,7 @@
103
103
  <script src="https://cdn.jsdelivr.net/gh/afeld/bootstrap-toc/dist/bootstrap-toc.min.js"></script>
104
104
  <%- end -%>
105
105
  <%- Array(param[:javascript_file]).each do |file| -%>
106
- <script src="<%=h relative_path(file) %>"></script>
106
+ <script src="<%=h relative_path(param[:output_file], file) %>"></script>
107
107
  <%- end -%>
108
108
  </body>
109
109
  </html>
@@ -1,4 +1,4 @@
1
1
  <dl class="row border">
2
- <dt class="col-sm-3"><%= format_object(param[:blank_subject], param[:data]) %></dt>
3
- <dd class="col-sm-9"><%= format_triples(param[:blank_triples]) %></dd>
2
+ <dt class="col-sm-3"><%= format_object(param[:blank_subject], param) %></dt>
3
+ <dd class="col-sm-9"><%= format_triples(param[:blank_triples], param) %></dd>
4
4
  </dl>
@@ -4,17 +4,17 @@
4
4
  order = Float::INFINITY if order.nil?
5
5
  [ order, k ]
6
6
  }.each do |k, v| -%>
7
- <dt class="col-sm-3"><%=h t('triples.inverse_refered', property: format_property(k, param[:labels], v.first)) %></dt>
7
+ <dt class="col-sm-3"><%=h t('triples.inverse_refered', property: format_property(k, param, v.first)) %></dt>
8
8
  <%- if v.respond_to? :has_key? -%>
9
9
  <% v.each_with_index do |v2, idx| %>
10
- <dd class="col-sm-9<%= ' offset-sm-3' if idx > 0 %>" lang="<%=h v2[0] %>"><%= format_object v2[1], param[:data], param[:type] %></dd>
10
+ <dd class="col-sm-9<%= ' offset-sm-3' if idx > 0 %>" lang="<%=h v2[0] %>"><%= format_object v2[1], param %></dd>
11
11
  <% end %>
12
12
  <%- elsif v.size > 1 -%>
13
13
  <%- v.each_with_index do |v2, idx| -%>
14
- <dd class="col-sm-9<%= ' offset-sm-3' if idx > 0 %>"><%= format_object v2, param[:data], param[:type] %></dd>
14
+ <dd class="col-sm-9<%= ' offset-sm-3' if idx > 0 %>"><%= format_object v2, param %></dd>
15
15
  <%- end -%>
16
16
  <%- else -%>
17
- <dd class="col-sm-9"><%= format_object v.first, param[:data], param[:type] %></dd>
17
+ <dd class="col-sm-9"><%= format_object v.first, param %></dd>
18
18
  <%- end -%>
19
19
  <%- end -%>
20
20
  </dl>
@@ -1,21 +1,23 @@
1
+ <%- itemtypes = param[:data][RDF.type.to_s].to_a.join(" ") -%>
1
2
  <%- if param[:type][:blank] -%>
2
- <dl class="row<%= if param[:type][:blank] then ' border' end %>" itemscope>
3
+ <dl class="row<%= if param[:type][:blank] then ' border' end %>" itemscope<% unless itemtypes.empty? %> itemtype="<%=h itemtypes %>"<% end %>>
3
4
  <%- else -%>
4
- <dl class="row<%= if param[:type][:blank] then ' border' end %>" itemscope itemid="<%=h param[:uri] %>">
5
+ <dl class="row<%= if param[:type][:blank] then ' border' end %>" itemscope itemid="<%=h param[:uri] %>"<% unless itemtypes.empty? %> itemtype="<%=h itemtypes %>"<% end %>>
5
6
  <%- end -%>
6
7
  <%- param[:data].sort_by{|k, v|
7
8
  order = param[:orders][k] if param[:orders] and param[:orders][k]
8
9
  order = Float::INFINITY if order.nil?
9
10
  [ order, k ]
10
11
  }.each do |k, v| -%>
11
- <dt class="col-sm-3"><%=h format_property(k, param[:labels]) %></dt>
12
+ <dt class="col-sm-3"><%=h format_property(k, param) %></dt>
12
13
  <%- v.sort_by{|e| sort_criteria(e, param[:data_global]) }.each_with_index do |v2, idx| -%>
13
14
  <%- if v2.respond_to? :language and v2.language? -%>
14
- <dd class="col-sm-9<%= ' offset-sm-3' if idx > 0 %>" lang="<%=h v2.language %>" itemprop="<%=h k %>"><%= format_object v2, param[:data], param[:type] %></dd>
15
+ <dd class="col-sm-9<%= ' offset-sm-3' if idx > 0 %>" lang="<%=h v2.language %>" itemprop="<%=h k %>"><%= format_object v2, param %></dd>
15
16
  <%- elsif /\Ahttps?:\/\// =~ v2.to_s -%>
16
- <dd class="col-sm-9<%= ' offset-sm-3' if idx > 0 %>" itemscope itemprop="<%=h k %>" itemid="<%=h v2.to_s %>"><%= format_object v2, param[:data], param[:type] %></dd>
17
+ <%- itemtypes = param[:data_global][v2.to_s] ? param[:data_global][v2.to_s][RDF.type.to_s].to_a.join(" ") : "" -%>
18
+ <dd class="col-sm-9<%= ' offset-sm-3' if idx > 0 %>" itemscope itemprop="<%=h k %>" itemid="<%=h v2.to_s %>"<% unless itemtypes.empty? %> itemtype="<%=h itemtypes %>"<% end %>><%= format_object v2, param %></dd>
17
19
  <%- else -%>
18
- <dd class="col-sm-9<%= ' offset-sm-3' if idx > 0 %>" itemprop="<%=h k %>"><%= format_object v2, param[:data], param[:type] %></dd>
20
+ <dd class="col-sm-9<%= ' offset-sm-3' if idx > 0 %>" itemprop="<%=h k %>"><%= format_object v2, param %></dd>
19
21
  <%- end -%>
20
22
  <%- end -%>
21
23
  <%- end -%>
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ttl2html
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 3.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Masao Takaku
8
8
  bindir: bin
9
9
  cert_chain: []
10
- date: 2025-12-22 00:00:00.000000000 Z
10
+ date: 2026-04-08 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
13
  name: nokogiri
@@ -180,9 +180,9 @@ dependencies:
180
180
  description: Static site generator for RDF/Turtle
181
181
  email: tmasao@acm.org
182
182
  executables:
183
+ - catttl
183
184
  - ttl2html
184
185
  - xlsx2shape
185
- - catttl
186
186
  extensions: []
187
187
  extra_rdoc_files: []
188
188
  files:
@@ -225,7 +225,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
225
225
  - !ruby/object:Gem::Version
226
226
  version: '0'
227
227
  requirements: []
228
- rubygems_version: 3.6.2
228
+ rubygems_version: 4.0.3
229
229
  specification_version: 4
230
230
  summary: ttl2html
231
231
  test_files: []