Skip to content

Commit 21ab3b1

Browse files
committed
First try at linking full manifests
1 parent d647611 commit 21ab3b1

File tree

4 files changed

+224
-27
lines changed

4 files changed

+224
-27
lines changed

source/_includes/code_example.html

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{% assign _title = include.title | default: 'Full JSON example' %}
2+
{% assign _target = include.target | default: "_blank" %}
3+
4+
<div class="code-example">
5+
<div class="code-example__header">
6+
<a href="example/{{ include.src }}" target="{{ _target }}">{{ _title }}</a></div>
7+
{% highlight json %}
8+
{% include_lines presentation/4.0/example/{{ include.src }} from:{{ include.from }} to:{{ include.to }} %}
9+
{% endhighlight %}
10+
</div>

source/_plugins/include_lines.rb

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
require 'liquid'
2+
3+
module Jekyll
4+
class IncludeLinesTag < Liquid::Tag
5+
def initialize(tag_name, markup, tokens)
6+
super
7+
@markup = markup.to_s.strip
8+
end
9+
10+
def render(context)
11+
site = context.registers[:site]
12+
source = site.source
13+
14+
rendered_markup = Liquid::Template.parse(@markup).render(context)
15+
path, options = parse_markup(rendered_markup)
16+
raise ArgumentError, "include_lines: missing file path" if path.nil? || path.empty?
17+
18+
from = integer_option(options, 'from')
19+
to = integer_option(options, 'to')
20+
start = integer_option(options, 'start')
21+
finish = integer_option(options, 'end')
22+
23+
from ||= start
24+
to ||= finish
25+
26+
format = true
27+
if options.key?('format')
28+
format = boolean_option(options, 'format')
29+
end
30+
indent = integer_option(options, 'indent')
31+
32+
unless from && to
33+
raise ArgumentError, "include_lines: must specify from/to (1-indexed), e.g. {% include_lines path from:11 to:35 %}"
34+
end
35+
36+
if from < 1 || to < 1
37+
raise ArgumentError, "include_lines: from/to must be >= 1 (got from:#{from} to:#{to})"
38+
end
39+
40+
if to < from
41+
raise ArgumentError, "include_lines: to must be >= from (got from:#{from} to:#{to})"
42+
end
43+
44+
absolute_path = File.expand_path(path, source)
45+
unless absolute_path.start_with?(File.expand_path(source) + File::SEPARATOR)
46+
raise ArgumentError, "include_lines: path must be within the site source directory"
47+
end
48+
49+
unless File.file?(absolute_path)
50+
raise ArgumentError, "include_lines: file not found: #{path}"
51+
end
52+
53+
lines = File.read(absolute_path, encoding: 'UTF-8').split("\n", -1)
54+
max_line = lines.length
55+
56+
if from > max_line
57+
raise ArgumentError, "include_lines: from (#{from}) is beyond end of file (#{max_line} lines): #{path}"
58+
end
59+
60+
to = [to, max_line].min
61+
62+
selected = lines[(from - 1)..(to - 1)]
63+
64+
if format
65+
selected = dedent_lines(selected)
66+
end
67+
68+
if indent && indent > 0
69+
prefix = ' ' * indent
70+
selected = selected.map { |l| l.strip.empty? ? l : (prefix + l) }
71+
end
72+
73+
selected.join("\n")
74+
rescue StandardError => e
75+
if defined?(Jekyll) && Jekyll.respond_to?(:logger) && Jekyll.logger
76+
Jekyll.logger.error("include_lines:", e.message)
77+
end
78+
raise
79+
end
80+
81+
private
82+
83+
def parse_markup(markup)
84+
tokens = markup.scan(/\"[^\"]+\"|\'[^\']+\'|\S+/)
85+
return [nil, {}] if tokens.empty?
86+
87+
path_token = tokens.shift
88+
path = unquote(path_token)
89+
90+
options = {}
91+
tokens.each do |t|
92+
key, value = t.split(':', 2)
93+
next if value.nil?
94+
options[key] = unquote(value)
95+
end
96+
97+
[path, options]
98+
end
99+
100+
def unquote(value)
101+
v = value.to_s
102+
if (v.start_with?('"') && v.end_with?('"')) || (v.start_with?("'") && v.end_with?("'"))
103+
v[1..-2]
104+
else
105+
v
106+
end
107+
end
108+
109+
def integer_option(options, key)
110+
return nil unless options.key?(key)
111+
Integer(options[key])
112+
rescue ArgumentError
113+
raise ArgumentError, "include_lines: #{key} must be an integer (got #{options[key].inspect})"
114+
end
115+
116+
def boolean_option(options, key)
117+
return nil unless options.key?(key)
118+
119+
v = options[key].to_s.strip.downcase
120+
return true if %w[1 true yes y on].include?(v)
121+
return false if %w[0 false no n off].include?(v)
122+
123+
raise ArgumentError, "include_lines: #{key} must be a boolean (got #{options[key].inspect})"
124+
end
125+
126+
def dedent_lines(lines)
127+
non_empty = lines.reject { |l| l.strip.empty? }
128+
return lines if non_empty.empty?
129+
130+
min_indent = non_empty.map { |l| l[/\A[ \t]*/].length }.min
131+
return lines if min_indent.nil? || min_indent.zero?
132+
133+
lines.map do |l|
134+
l.strip.empty? ? l : l[min_indent..]
135+
end
136+
end
137+
end
138+
end
139+
140+
Liquid::Template.register_tag('include_lines', Jekyll::IncludeLinesTag)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
{
2+
"@context": "http://iiif.io/api/presentation/4/context.json",
3+
"id": "https://iiif.io/api/presentation/4.0/example/02_timeline.json",
4+
"type": "Manifest",
5+
"label": {
6+
"en": [
7+
"Simplest Audio Example (IIIF Presentation v4)"
8+
]
9+
},
10+
"items": [
11+
{
12+
"id": "https://iiif.io/api/presentation/4.0/example/02",
13+
"type": "Timeline",
14+
"duration": 1985.024,
15+
"items": [
16+
{
17+
"id": "https://iiif.io/api/presentation/4.0/example/02/page",
18+
"type": "AnnotationPage",
19+
"items": [
20+
{
21+
"id": "https://iiif.io/api/presentation/4.0/example/02/page/anno",
22+
"type": "Annotation",
23+
"motivation": "painting",
24+
"body": {
25+
"id": "https://fixtures.iiif.io/audio/indiana/mahler-symphony-3/CD1/medium/128Kbps.mp4",
26+
"type": "Sound",
27+
"format": "audio/mp4",
28+
"duration": 1985.024
29+
},
30+
"target": "https://iiif.io/api/presentation/4.0/example/02"
31+
}
32+
]
33+
}
34+
]
35+
}
36+
]
37+
}

source/presentation/4.0/index.md

Lines changed: 37 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,42 @@ a:hover > code {
7373
text-decoration: underline;
7474
}
7575

76+
.code-example {
77+
margin: 0 0 1em 0;
78+
}
79+
.code-example__header {
80+
background-color: #edf0f0;
81+
border-top-left-radius: 1em;
82+
border-top-right-radius: 1em;
83+
padding: 0.6em 1.5em;
84+
font-weight: bold;
85+
}
86+
.code-example__header a {
87+
color: #171717;
88+
text-decoration: underline;
89+
}
90+
.code-example__header + .highlight pre {
91+
border-top-left-radius: 0;
92+
border-top-right-radius: 0;
93+
}
94+
figure.highlight pre {
95+
border-bottom-left-radius: 1em;
96+
border-bottom-right-radius: 1em;
97+
}
98+
.code-example > figure.highlight {
99+
margin-left: 0px;
100+
margin-right: 0px;
101+
margin-top: 0px;
102+
margin-bottom: 0px;
103+
text-align: left;
104+
105+
}
106+
107+
code.language-json {
108+
font-size: 0.9rem;
109+
line-height: 1.0;
110+
font-family: "Courier Prime", monospace;
111+
}
76112
</style>
77113

78114
# Status of this Document
@@ -170,33 +206,7 @@ A Container that represents a bounded temporal range, without any spatial coordi
170206

171207
Timelines have an additional required property of [`duration`][prezi-40-model-duration], which gives the extent of the Timeline as a floating point number of seconds.
172208

173-
```json
174-
{
175-
"id": "https://example.org/iiif/presentation/examples/manifest-with-containers/timeline",
176-
"type": "Timeline",
177-
"duration": 32.76,
178-
"items": [
179-
{
180-
"id": "https://example.org/iiif/presentation/examples/manifest-with-containers/page/p1",
181-
"type": "AnnotationPage",
182-
"items": [
183-
{
184-
"id": "https://example.org/iiif/presentation/examples/manifest-with-containers/annotation/t1",
185-
"type": "Annotation",
186-
"motivation": [ "painting" ],
187-
"body": {
188-
"id": "https://iiif.io/api/presentation/example-content-resources/audio/clip.mp3",
189-
"type": "Audio",
190-
"format": "audio/mp3",
191-
"duration": 32.76
192-
},
193-
"target": "https://example.org/iiif/presentation/examples/manifest-with-containers/timeline"
194-
}
195-
]
196-
}
197-
]
198-
}
199-
```
209+
{% include code_example.html src="02_timeline.json" from=11 to=35 %}
200210

201211
### Canvas
202212

0 commit comments

Comments
 (0)