relaton 3.0.6 → 3.0.8
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 +4 -4
- data/bin/relaton +20 -0
- data/lib/relaton/bib/model/docidentifier.rb +17 -2
- data/lib/relaton/bibcollection.rb +146 -0
- data/lib/relaton/bibdata.rb +92 -0
- data/lib/relaton/bipm/data_outcomes_parser.rb +6 -1
- data/lib/relaton/cli/base_convertor.rb +134 -0
- data/lib/relaton/cli/command.rb +390 -0
- data/lib/relaton/cli/data_fetcher.rb +20 -0
- data/lib/relaton/cli/frontend/dist/404.html +18 -0
- data/lib/relaton/cli/frontend/dist/app.iife.js +1 -0
- data/lib/relaton/cli/frontend/dist/style.css +3 -0
- data/lib/relaton/cli/frontend/index.html +14 -0
- data/lib/relaton/cli/frontend/package-lock.json +2945 -0
- data/lib/relaton/cli/frontend/package.json +29 -0
- data/lib/relaton/cli/frontend/public/404.html +18 -0
- data/lib/relaton/cli/frontend/src/App.test.ts +501 -0
- data/lib/relaton/cli/frontend/src/App.vue +506 -0
- data/lib/relaton/cli/frontend/src/app.ts +90 -0
- data/lib/relaton/cli/frontend/src/components/DocumentDetail.test.ts +142 -0
- data/lib/relaton/cli/frontend/src/components/DocumentDetail.vue +228 -0
- data/lib/relaton/cli/frontend/src/components/DocumentRow.vue +88 -0
- data/lib/relaton/cli/frontend/src/components/Icon.test.ts +44 -0
- data/lib/relaton/cli/frontend/src/components/Icon.vue +55 -0
- data/lib/relaton/cli/frontend/src/components/Pagination.vue +26 -0
- data/lib/relaton/cli/frontend/src/dev/main.ts +100 -0
- data/lib/relaton/cli/frontend/src/dev/sample-data.json +71 -0
- data/lib/relaton/cli/frontend/src/lib/filter.test.ts +88 -0
- data/lib/relaton/cli/frontend/src/lib/filter.ts +95 -0
- data/lib/relaton/cli/frontend/src/lib/hydrate.test.ts +102 -0
- data/lib/relaton/cli/frontend/src/lib/hydrate.ts +55 -0
- data/lib/relaton/cli/frontend/src/lib/shards.test.ts +319 -0
- data/lib/relaton/cli/frontend/src/lib/shards.ts +183 -0
- data/lib/relaton/cli/frontend/src/lib/types.ts +148 -0
- data/lib/relaton/cli/frontend/src/lib/url.test.ts +151 -0
- data/lib/relaton/cli/frontend/src/lib/url.ts +111 -0
- data/lib/relaton/cli/frontend/src/shims-vue.d.ts +5 -0
- data/lib/relaton/cli/frontend/src/styles/index.css +27 -0
- data/lib/relaton/cli/frontend/src/test/fetch.ts +58 -0
- data/lib/relaton/cli/frontend/tsconfig.json +20 -0
- data/lib/relaton/cli/frontend/vite.config.ts +42 -0
- data/lib/relaton/cli/frontend/vitest.config.ts +11 -0
- data/lib/relaton/cli/frontend_assets.rb +58 -0
- data/lib/relaton/cli/full_text_search.rb +98 -0
- data/lib/relaton/cli/i18n.yaml +1 -0
- data/lib/relaton/cli/index_item_normalizer.rb +380 -0
- data/lib/relaton/cli/index_site_generator.rb +1015 -0
- data/lib/relaton/cli/relaton_file.rb +290 -0
- data/lib/relaton/cli/subcommand_collection.rb +263 -0
- data/lib/relaton/cli/subcommand_db.rb +81 -0
- data/lib/relaton/cli/templates/_document.liquid +111 -0
- data/lib/relaton/cli/templates/_index.liquid +61 -0
- data/lib/relaton/cli/templates/index/page.liquid +20 -0
- data/lib/relaton/cli/templates/index-style.css +1017 -0
- data/lib/relaton/cli/util.rb +8 -0
- data/lib/relaton/cli/version.rb +11 -0
- data/lib/relaton/cli/xml_convertor.rb +47 -0
- data/lib/relaton/cli/xml_to_html_renderer.rb +117 -0
- data/lib/relaton/cli/yaml_convertor.rb +102 -0
- data/lib/relaton/cli.rb +113 -0
- data/lib/relaton/doi/CLAUDE.md +1 -1
- data/lib/relaton/element_finder.rb +29 -0
- data/lib/relaton/gb/CLAUDE.md +1 -1
- data/lib/relaton/ietf/CLAUDE.md +1 -1
- data/lib/relaton/iho/CLAUDE.md +3 -3
- data/lib/relaton/iho/bibliography.rb +41 -27
- data/lib/relaton/iho/processor.rb +3 -2
- data/lib/relaton/nist/CLAUDE.md +11 -1
- data/lib/relaton/nist/README.adoc +7 -1
- data/lib/relaton/nist/hit_collection.rb +25 -1
- data/lib/relaton/nist/mods_parser.rb +32 -8
- data/lib/relaton/nist/pubs_export.rb +69 -11
- data/lib/relaton/ogc/CLAUDE.md +1 -1
- data/lib/relaton/omg/CLAUDE.md +1 -1
- data/lib/relaton/plateau/CLAUDE.md +1 -1
- data/lib/relaton/render/general.rb +319 -0
- data/lib/relaton/render/iso690/csl_import.rb +129 -0
- data/lib/relaton/render/iso690/element.rb +153 -0
- data/lib/relaton/render/iso690/elements/access.rb +44 -0
- data/lib/relaton/render/iso690/elements/authorizer.rb +45 -0
- data/lib/relaton/render/iso690/elements/citeid.rb +39 -0
- data/lib/relaton/render/iso690/elements/component_part.rb +97 -0
- data/lib/relaton/render/iso690/elements/creator.rb +267 -0
- data/lib/relaton/render/iso690/elements/edition.rb +97 -0
- data/lib/relaton/render/iso690/elements/extent.rb +71 -0
- data/lib/relaton/render/iso690/elements/identifier.rb +46 -0
- data/lib/relaton/render/iso690/elements/location.rb +73 -0
- data/lib/relaton/render/iso690/elements/medium.rb +32 -0
- data/lib/relaton/render/iso690/elements/numeration.rb +26 -0
- data/lib/relaton/render/iso690/elements/production.rb +111 -0
- data/lib/relaton/render/iso690/elements/pub_date.rb +63 -0
- data/lib/relaton/render/iso690/elements/series.rb +66 -0
- data/lib/relaton/render/iso690/elements/size.rb +45 -0
- data/lib/relaton/render/iso690/elements/status.rb +30 -0
- data/lib/relaton/render/iso690/elements/stddoc.rb +40 -0
- data/lib/relaton/render/iso690/elements/title.rb +56 -0
- data/lib/relaton/render/iso690/elements/updated.rb +38 -0
- data/lib/relaton/render/iso690/elements.rb +67 -0
- data/lib/relaton/render/iso690/fields.rb +142 -0
- data/lib/relaton/render/iso690/i18n/en.yml +48 -0
- data/lib/relaton/render/iso690/i18n/fr.yml +14 -0
- data/lib/relaton/render/iso690/i18n/ja.yml +38 -0
- data/lib/relaton/render/iso690/i18n.rb +83 -0
- data/lib/relaton/render/iso690/identifier_kinds.rb +29 -0
- data/lib/relaton/render/iso690/index_renderer.rb +57 -0
- data/lib/relaton/render/iso690/kinds.rb +53 -0
- data/lib/relaton/render/iso690/renderer.rb +150 -0
- data/lib/relaton/render/iso690/rules/bipm.rb +186 -0
- data/lib/relaton/render/iso690/rules/jis.rb +103 -0
- data/lib/relaton/render/iso690/rules/nist.rb +384 -0
- data/lib/relaton/render/iso690/rules.rb +227 -0
- data/lib/relaton/render/iso690/style/locale.rb +59 -0
- data/lib/relaton/render/iso690/style/name_form.rb +87 -0
- data/lib/relaton/render/iso690/style/scheme.rb +33 -0
- data/lib/relaton/render/iso690/style/sort_rule.rb +25 -0
- data/lib/relaton/render/iso690/style/template_map.rb +40 -0
- data/lib/relaton/render/iso690/style/type_template.rb +45 -0
- data/lib/relaton/render/iso690/style.rb +227 -0
- data/lib/relaton/render/iso690/styles/apa.yml +45 -0
- data/lib/relaton/render/iso690/styles/author-date.yml +31 -0
- data/lib/relaton/render/iso690/styles/chicago.yml +49 -0
- data/lib/relaton/render/iso690/styles/ieee-sa.yml +45 -0
- data/lib/relaton/render/iso690/styles/jis-en.yml +68 -0
- data/lib/relaton/render/iso690/styles/jis-ja.yml +67 -0
- data/lib/relaton/render/iso690/styles/lncs.yml +54 -0
- data/lib/relaton/render/iso690/styles/mla.yml +56 -0
- data/lib/relaton/render/iso690/template.rb +248 -0
- data/lib/relaton/render/iso690.rb +28 -0
- data/lib/relaton/render/version.rb +5 -0
- data/lib/relaton/un/CLAUDE.md +1 -1
- data/lib/relaton/version.rb +1 -1
- data/lib/relaton-cli.rb +17 -0
- data/lib/relaton-render.rb +13 -0
- metadata +192 -21
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Name(s) of creator(s) (ISO 690 clause 7.2). The first creator is
|
|
8
|
+
# given inverted per the style's name form (SURNAME, forenames),
|
|
9
|
+
# subsequent creators in direct order (forenames SURNAME); name
|
|
10
|
+
# parts come from the typed model, never decomposed from a
|
|
11
|
+
# rendered string.
|
|
12
|
+
class Creator < Element
|
|
13
|
+
def present?
|
|
14
|
+
!creators.empty? || !fallback_name.empty?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def render
|
|
18
|
+
return plain_render if @short
|
|
19
|
+
|
|
20
|
+
joined = if (truncated = truncated_creators)
|
|
21
|
+
truncated
|
|
22
|
+
elsif creators.empty?
|
|
23
|
+
fallback_name
|
|
24
|
+
elsif (form = @style.templates.creators) && !form.empty?
|
|
25
|
+
::Relaton::Render::Iso690::Template.new(form).evaluate(
|
|
26
|
+
"names" => Template::Field[true, join(names)],
|
|
27
|
+
)
|
|
28
|
+
else
|
|
29
|
+
join(names)
|
|
30
|
+
end
|
|
31
|
+
case role_placement
|
|
32
|
+
when "afterPeriod"
|
|
33
|
+
# the in-slot trailing space separates the (eds.) marker
|
|
34
|
+
# from the next element, which follows directly in the
|
|
35
|
+
# template; a lone editor is unmarked unless declared
|
|
36
|
+
if editors? && marked?
|
|
37
|
+
"#{period(joined)}#{role_space}#{role_parenthesis} "
|
|
38
|
+
else
|
|
39
|
+
"#{period(joined)} "
|
|
40
|
+
end
|
|
41
|
+
else
|
|
42
|
+
marker = editors? && marked? ? "#{role_space}#{role_parenthesis}" : ''
|
|
43
|
+
"#{joined}#{marker}"
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# The short cite splits after the creators: they render as a
|
|
48
|
+
# plain join, without the wrap and sentence period (1.x
|
|
49
|
+
# short-cite templates carried no wrap)
|
|
50
|
+
def plain_render
|
|
51
|
+
marker = editors? && marked? ? "#{role_space}#{role_parenthesis}" : ''
|
|
52
|
+
"#{join(names)}#{marker}"
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# The editor role marker's parentheses follow the locale
|
|
56
|
+
# (ASCII for Latin scripts, fullwidth for CJK)
|
|
57
|
+
def role_parenthesis
|
|
58
|
+
open = @i18n.punct_fetch("open-paren", "(")
|
|
59
|
+
close = @i18n.punct_fetch("close-paren", ")")
|
|
60
|
+
"#{open}#{role_word}#{close}"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# CJK cites the role marker tight against the name list
|
|
64
|
+
def role_space
|
|
65
|
+
@i18n.punct_fetch("marker-space", " ")
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# The template-level role slot ("{{role}}"): the marker with
|
|
69
|
+
# its locale spacing, when the creators are all editors
|
|
70
|
+
def role_marker
|
|
71
|
+
editors? ? "#{role_space}#{role_parenthesis}" : ""
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def role_placement
|
|
75
|
+
@style.scheme.name_form.role_placement || "glue"
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def period(text)
|
|
79
|
+
text.end_with?(".") ? text : "#{text}."
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def marked?
|
|
83
|
+
name_form.editors_marked &&
|
|
84
|
+
(creators.size > 1 || name_form.lone_editor_marked)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def role_word
|
|
88
|
+
@i18n.label(creators.one? ? "ed" : "eds")
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# In-text (name-and-date) form: the principal creator only;
|
|
92
|
+
# at the style's et-al threshold the cite truncates
|
|
93
|
+
# ("Aluffi <em>et al.</em>")
|
|
94
|
+
def in_text
|
|
95
|
+
return in_text_etal if in_text_etal?
|
|
96
|
+
|
|
97
|
+
name = creators.first ? in_text_name(creators.first) : ""
|
|
98
|
+
etal? ? "#{name}#{etal_suffix}" : name
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# The in-text cite truncates at its own threshold: at
|
|
102
|
+
# inTextEtalCount creators it shows the first
|
|
103
|
+
# inTextEtalDisplay surnames ("Aluffi <em>et al.</em>")
|
|
104
|
+
def in_text_etal
|
|
105
|
+
names = creators.first(name_form.in_text_etal_display)
|
|
106
|
+
.map { |c| in_text_name(c) }
|
|
107
|
+
"#{join(names)}#{etal_suffix}"
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# The et-al suffix is the locale's label (roman by default; a
|
|
111
|
+
# pack's `others` label may carry its own presentation), joined
|
|
112
|
+
# per the style's list style — the serial comma before it is the
|
|
113
|
+
# MLA form ("Fielding, Roy, et al.")
|
|
114
|
+
def etal_suffix
|
|
115
|
+
sep = name_form.list_style == "serial" ? "," : ""
|
|
116
|
+
"#{sep} #{@i18n.label('others')}"
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# The in-text cite names by surname alone (the 1.x
|
|
120
|
+
# authorcitetemplate), never by initials
|
|
121
|
+
def in_text_name(contributor)
|
|
122
|
+
person = contributor.person or return org_name(contributor)
|
|
123
|
+
complete = completename(person)
|
|
124
|
+
return complete unless complete.nil?
|
|
125
|
+
|
|
126
|
+
raw = surname(person).to_s
|
|
127
|
+
name_form.surname_upcase ? raw.upcase : raw
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def in_text_etal?
|
|
131
|
+
count = name_form.in_text_etal_count
|
|
132
|
+
count.positive? && creators.size >= count
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def etal?
|
|
136
|
+
count = name_form.etal_count
|
|
137
|
+
count.positive? && creators.size >= count
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# The principal creator's given names, as the name form declares
|
|
141
|
+
def principal_given
|
|
142
|
+
principal = creators.first or return ""
|
|
143
|
+
person = principal.person or return ""
|
|
144
|
+
complete = completename(person)
|
|
145
|
+
return "" if complete
|
|
146
|
+
|
|
147
|
+
given_names(person)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
private
|
|
151
|
+
|
|
152
|
+
def creators
|
|
153
|
+
@creators ||= begin
|
|
154
|
+
found = contributors("author")
|
|
155
|
+
found = contributors("editor") if found.empty?
|
|
156
|
+
found
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# The declared creator fallback for items with no personal
|
|
161
|
+
# creators: the publishers' abbreviation or full name — all
|
|
162
|
+
# publishers cite, joined per the style's list style ("ISO and
|
|
163
|
+
# IEC")
|
|
164
|
+
def fallback_name
|
|
165
|
+
orgs = contributors("publisher")
|
|
166
|
+
name = case name_form.creator_fallback
|
|
167
|
+
when "publisher_abbrev"
|
|
168
|
+
orgs.map do |c|
|
|
169
|
+
localized(c.organization&.abbreviation)
|
|
170
|
+
end
|
|
171
|
+
when "publisher_name"
|
|
172
|
+
orgs.map do |c|
|
|
173
|
+
localized(Array(c.organization&.name).first)
|
|
174
|
+
end
|
|
175
|
+
else
|
|
176
|
+
[]
|
|
177
|
+
end
|
|
178
|
+
join_names(name.reject(&:empty?))
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def names
|
|
182
|
+
creators.each_with_index
|
|
183
|
+
.map do |contributor, index|
|
|
184
|
+
format(contributor,
|
|
185
|
+
first: index.zero?)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# The style's et-al truncation ("Aluffi, P., D. Anderson,
|
|
190
|
+
# M. Hering <em>et al.</em>"): at etalCount creators, the
|
|
191
|
+
# first etalDisplay names cite, comma-joined without the
|
|
192
|
+
# terminal and-join
|
|
193
|
+
def truncated_creators
|
|
194
|
+
count = name_form.etal_count
|
|
195
|
+
display = name_form.etal_display
|
|
196
|
+
return nil if count <= 0 || display <= 0 ||
|
|
197
|
+
creators.size < count
|
|
198
|
+
|
|
199
|
+
creators.first(display).each_with_index
|
|
200
|
+
.map do |contributor, index|
|
|
201
|
+
format(contributor, first: index.zero?)
|
|
202
|
+
end.join(", ") + etal_suffix
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def format(contributor, first:)
|
|
206
|
+
person = contributor.person or return org_name(contributor)
|
|
207
|
+
name = completename_name(person, first: first) ||
|
|
208
|
+
personal_name(person, first: first)
|
|
209
|
+
name.empty? ? org_name(contributor) : name
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def completename_name(person, first:)
|
|
213
|
+
complete = completename(person) or return nil
|
|
214
|
+
return complete unless name_form.completename_upcase
|
|
215
|
+
|
|
216
|
+
first ? @style.render_name(surname: complete, given: "") :
|
|
217
|
+
complete.upcase
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def personal_name(person, first:)
|
|
221
|
+
surname = surname(person).to_s
|
|
222
|
+
given = given_names(person)
|
|
223
|
+
if (first || name_form.inverted_all) && !name_form.given_name_first
|
|
224
|
+
@style.render_name(surname: surname, given: given)
|
|
225
|
+
else
|
|
226
|
+
# the direct form initializes per the style's initials
|
|
227
|
+
# knob ("S. Payne" over "Sam Payne")
|
|
228
|
+
shown = name_form.subsequent_surname_upcase ? surname.upcase :
|
|
229
|
+
surname
|
|
230
|
+
[person_given(person), shown].reject(&:empty?).join(" ")
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def name_form
|
|
235
|
+
@style.scheme.name_form
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
def join(names)
|
|
239
|
+
join_names(names)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def editors?
|
|
243
|
+
!creators.empty? && creators.all? { |c| has_role?(c, "editor") }
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
def completename(person)
|
|
247
|
+
value = person_completename(person)
|
|
248
|
+
value.empty? ? nil : value
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
def surname(person)
|
|
252
|
+
value = person_surname(person)
|
|
253
|
+
value.empty? ? nil : value
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def given_names(person)
|
|
257
|
+
person_given(person)
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
def org_name(contributor)
|
|
261
|
+
super
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Edition and version (ISO 690 clause 7.6). The model numbers the
|
|
8
|
+
# edition through Edition#number (falling back to its content).
|
|
9
|
+
class Edition < Element
|
|
10
|
+
def present?
|
|
11
|
+
!raw.to_s.empty?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def render
|
|
15
|
+
return "#{@i18n.label('version')} #{raw}" unless word_edition?
|
|
16
|
+
|
|
17
|
+
# The exact worded forms first ("first edition", "第1版"),
|
|
18
|
+
# then the generic ordinal template, the cardinal last: a
|
|
19
|
+
# specific label outranks a template that would apply to
|
|
20
|
+
# any number
|
|
21
|
+
word = @i18n.label("edition_#{number}")
|
|
22
|
+
return word unless word == "edition_#{number}"
|
|
23
|
+
|
|
24
|
+
ordinal = locale_template("edition_ordinal", number)
|
|
25
|
+
return ordinal if ordinal
|
|
26
|
+
|
|
27
|
+
cardinal_form || "#{ordinalize(number)} #{@i18n.label('edition')}"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# The plain cardinal form ("edition 7")
|
|
31
|
+
def cardinal_form
|
|
32
|
+
locale_template("edition_cardinal", number) ||
|
|
33
|
+
locale_template("version_cardinal", number)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
|
|
38
|
+
# A locale template carrying {{ var1 }} (optionally through
|
|
39
|
+
# the ordinal_word filter) populated with the edition number;
|
|
40
|
+
# a bare label (no placeholder) is not one
|
|
41
|
+
def locale_template(key, value)
|
|
42
|
+
raw = @i18n.label(key)
|
|
43
|
+
return nil if raw == key || raw !~ /\{{/
|
|
44
|
+
|
|
45
|
+
if raw.include?("ordinal_word")
|
|
46
|
+
word = @i18n.label("ordinal_word_#{value}")
|
|
47
|
+
word = ordinalize(value) if word == "ordinal_word_#{value}"
|
|
48
|
+
raw.gsub(/\{\{\s*var1[^}]*\}\}/, word)
|
|
49
|
+
else
|
|
50
|
+
raw.gsub("{{ var1 }}", value.to_s)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
# Worded editions are document editions; online resources
|
|
57
|
+
# carry numbered builds rendered as versions. A standard is a
|
|
58
|
+
# document edition too (its report kind carries the clause 8.11
|
|
59
|
+
# fallback templates, but its numbered editions read as
|
|
60
|
+
# "Nth edition" — the TS contract renders them so).
|
|
61
|
+
def word_edition?
|
|
62
|
+
number.positive? &&
|
|
63
|
+
%w[monograph component_part serial_part report]
|
|
64
|
+
.include?(item_kind)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def raw
|
|
68
|
+
(@model.edition&.number || @model.edition&.content).to_s
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def number
|
|
72
|
+
Integer(raw)
|
|
73
|
+
rescue ArgumentError, TypeError
|
|
74
|
+
0
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def ordinalize(num)
|
|
78
|
+
"#{num}#{ordinal_suffix(num)}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def ordinal_suffix(num)
|
|
82
|
+
case num % 100
|
|
83
|
+
when 11, 12, 13 then "th"
|
|
84
|
+
else
|
|
85
|
+
case num % 10
|
|
86
|
+
when 1 then "st"
|
|
87
|
+
when 2 then "nd"
|
|
88
|
+
when 3 then "rd"
|
|
89
|
+
else "th"
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Extent of the item (ISO 690 clause 7.11): page localities.
|
|
8
|
+
class Extent < Element
|
|
9
|
+
def present?
|
|
10
|
+
!render.empty?
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def render
|
|
14
|
+
groups = [numeration, page_range].reject { |g| g.empty? }
|
|
15
|
+
groups.join(group_sep)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
# An extent label is a template over its value: "vol. %" in
|
|
21
|
+
# the Latin pack, "巻%" / "%頁" in the CJK packs
|
|
22
|
+
def unit(type, value)
|
|
23
|
+
label = @i18n.label(type)
|
|
24
|
+
return "#{label} #{value}" unless label.include?("%")
|
|
25
|
+
|
|
26
|
+
label.sub("%", value)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def cjk?(label)
|
|
30
|
+
label.match?(/\p{Han}|\p{Hiragana}|\p{Katakana}/)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def group_sep
|
|
34
|
+
sep = @i18n.label("extent_group_separator")
|
|
35
|
+
return sep unless sep == "extent_group_separator"
|
|
36
|
+
|
|
37
|
+
cjk?(@i18n.label("page")) ? "、 " : ", "
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def numeration
|
|
41
|
+
%w[volume issue].filter_map do |type|
|
|
42
|
+
loc = localities.find { |l| l.type == type }
|
|
43
|
+
next if loc.nil? || loc.reference_from.to_s.empty?
|
|
44
|
+
|
|
45
|
+
unit(type, loc.reference_from)
|
|
46
|
+
end.join(" ")
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def page_range
|
|
50
|
+
loc = localities.find { |l| l.type == "page" } or return ""
|
|
51
|
+
from = loc.reference_from.to_s
|
|
52
|
+
to = loc.reference_to.to_s
|
|
53
|
+
if to.empty? || to == from
|
|
54
|
+
unit("page", from)
|
|
55
|
+
else
|
|
56
|
+
range = "#{from}#{@i18n.label('date_range')}#{to}"
|
|
57
|
+
unit("pages", range)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def localities
|
|
62
|
+
Array(@model.extent).flat_map do |e|
|
|
63
|
+
Array(e.locality) +
|
|
64
|
+
Array(e.locality_stack).flat_map { |s| Array(s.locality) }
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Standard and persistent identifiers, rendered by kind
|
|
8
|
+
class Identifier < Element
|
|
9
|
+
INTERNAL_TYPES = %w[metanorma metanorma-ordinal metanorma-objid]
|
|
10
|
+
.freeze
|
|
11
|
+
|
|
12
|
+
def present?
|
|
13
|
+
!identifiers.empty?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def render
|
|
18
|
+
return "" if identifiers.empty?
|
|
19
|
+
|
|
20
|
+
ids = @style.templates.identifier_mode == "first" ?
|
|
21
|
+
[identifiers.first] : identifiers
|
|
22
|
+
ids.map { |d| render_id(d) }
|
|
23
|
+
.join(@i18n.punct_fetch("identifier_join", ". "))
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# A flavor's identifier kinds subclass this element and
|
|
27
|
+
# override here
|
|
28
|
+
def render_id(docidentifier)
|
|
29
|
+
IdentifierKinds.render(docidentifier)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
# Scoped identifiers (anchor, biblio-tag) are pipeline
|
|
35
|
+
# artefacts, not cited identifiers
|
|
36
|
+
def identifiers
|
|
37
|
+
Array(@model.docidentifier).reject do |d|
|
|
38
|
+
INTERNAL_TYPES.include?(d.type) ||
|
|
39
|
+
!d.scope.to_s.empty? || d.content.to_s.strip.empty?
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Availability and location (ISO 690 clause 7.12): access location
|
|
8
|
+
# (URI) of the resource
|
|
9
|
+
class Location < Element
|
|
10
|
+
def present?
|
|
11
|
+
!uri.empty?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def render
|
|
15
|
+
text = uri
|
|
16
|
+
form = uri_form
|
|
17
|
+
# an access location cites its own label ("At: Library"); a
|
|
18
|
+
# uri carries the availability label
|
|
19
|
+
if @from_accesslocation
|
|
20
|
+
return "#{@i18n.label('at_url')} #{text}".strip
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
text = if form && !form.empty?
|
|
24
|
+
::Relaton::Render::Iso690::Template.new(form).evaluate(
|
|
25
|
+
"uri" => Template::Field[!text.empty?, text],
|
|
26
|
+
)
|
|
27
|
+
else
|
|
28
|
+
text
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
label = @short ? "" : "#{@i18n.label('available_from')} "
|
|
32
|
+
"#{label}#{text}".strip
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
# The short cite renders the bare uri (1.x short templates),
|
|
38
|
+
# the reference the declared form
|
|
39
|
+
def uri_form
|
|
40
|
+
if @short
|
|
41
|
+
short_form = @style.templates.uri_short
|
|
42
|
+
return :bare if short_form.empty?
|
|
43
|
+
|
|
44
|
+
return short_form
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
@style.templates.uri
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def uri
|
|
51
|
+
from_accesslocation =
|
|
52
|
+
Array(@model.accesslocation).map(&:to_s).reject(&:empty?)
|
|
53
|
+
unless from_accesslocation.empty?
|
|
54
|
+
@from_accesslocation = true
|
|
55
|
+
return from_accesslocation.first.to_s
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
uris = Array(@model.source).reject do |u|
|
|
59
|
+
u.content.to_s.strip.empty?
|
|
60
|
+
end
|
|
61
|
+
# the citation uri outranks the rest (1.x uri extraction)
|
|
62
|
+
preferred = uris.find { |u| u.type == "citation" } ||
|
|
63
|
+
uris.find { |u| u.type == "attachment" } ||
|
|
64
|
+
uris.find { |u| u.type == "src" } || uris.first
|
|
65
|
+
# the uri lands inside a markup attribute: escape it
|
|
66
|
+
require "cgi"
|
|
67
|
+
CGI.escapeHTML(preferred&.content.to_s)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Medium designation (ISO 690 clause 7.5), bracketed per the
|
|
8
|
+
# standard's worked examples, e.g. "[online]"
|
|
9
|
+
class Medium < Element
|
|
10
|
+
def present?
|
|
11
|
+
!medium.empty?
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def render
|
|
15
|
+
medium
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def medium
|
|
21
|
+
genre = @model.medium&.genre.to_s
|
|
22
|
+
return genre.sub(/^\w/) { |c| c.upcase } unless genre.empty?
|
|
23
|
+
|
|
24
|
+
key = "medium_#{@model.type}"
|
|
25
|
+
label = @i18n.label(key)
|
|
26
|
+
label == key ? "" : label
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Relaton
|
|
4
|
+
module Render
|
|
5
|
+
module Iso690
|
|
6
|
+
module Elements
|
|
7
|
+
# Numeration within a series (ISO 690 clause 7.9): report numbers
|
|
8
|
+
class Numeration < Element
|
|
9
|
+
def present?
|
|
10
|
+
!docnumber.to_s.empty?
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def render
|
|
14
|
+
"#{@i18n.label('report_no')} #{docnumber}"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def docnumber
|
|
20
|
+
@model.docnumber
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|