citeproc-ruby 2.2.0 → 2.3.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 +4 -4
- data/README.md +72 -64
- data/lib/citeproc/ruby/engine.rb +11 -15
- data/lib/citeproc/ruby/format.rb +35 -9
- data/lib/citeproc/ruby/formats/default.rb +3 -2
- data/lib/citeproc/ruby/formats/html.rb +42 -2
- data/lib/citeproc/ruby/renderer/date.rb +100 -16
- data/lib/citeproc/ruby/renderer/format.rb +18 -83
- data/lib/citeproc/ruby/renderer/group.rb +18 -6
- data/lib/citeproc/ruby/renderer/label.rb +2 -2
- data/lib/citeproc/ruby/renderer/layout.rb +4 -1
- data/lib/citeproc/ruby/renderer/locale.rb +63 -2
- data/lib/citeproc/ruby/renderer/macro.rb +2 -4
- data/lib/citeproc/ruby/renderer/names.rb +47 -64
- data/lib/citeproc/ruby/renderer/number.rb +2 -2
- data/lib/citeproc/ruby/renderer/observer.rb +10 -4
- data/lib/citeproc/ruby/renderer/state.rb +5 -1
- data/lib/citeproc/ruby/renderer/text.rb +16 -3
- data/lib/citeproc/ruby/renderer.rb +36 -5
- data/lib/citeproc/ruby/version.rb +1 -1
- metadata +12 -22
- data/.document +0 -5
- data/.github/workflows/ci.yml +0 -33
- data/.yardopts +0 -2
- data/AGPL +0 -662
- data/Gemfile +0 -27
- data/Rakefile +0 -47
- data/cucumber.yml +0 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 711627337c8ad301400254ed5e9143c43ac710f4aeeed58e2497384d067e7d88
|
|
4
|
+
data.tar.gz: b50e0a76599302904b67922a7799d1e88bc57e57994936a50753c45a54d74aab
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 2922fa4307ebe6a89970118a55be01322ee21fd47f7512f7b34656842c61e3ec409180bc25ae2555a48faf8c63a6d2e87aff1148085af52f011d1ce9ca2b723d
|
|
7
|
+
data.tar.gz: ff9ceb486663023a3c4fbe2d01eacb17208cb26743ba60aecf1fc707423d0c8a0a5a39e9e3f48e2e2f2fe5b4b21a759b81556ae6d0d4834b8ac3d4e574798bb4
|
data/README.md
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
CiteProc-Ruby
|
|
2
2
|
=============
|
|
3
|
-
CiteProc-Ruby is a [Citation Style Language](
|
|
4
|
-
1.0.2 cite processor written in pure Ruby.
|
|
5
|
-
processor's rendering engine only;
|
|
6
|
-
|
|
3
|
+
CiteProc-Ruby is a [Citation Style Language](https://citationstyles.org/) (CSL)
|
|
4
|
+
1.0.2 cite processor written in pure Ruby.
|
|
5
|
+
This Ruby gem contains the processor's rendering engine only;
|
|
6
|
+
for more documentation on the whole cite processor,
|
|
7
|
+
please refer to the documentation of the
|
|
7
8
|
[citeproc](https://rubygems.org/gems/citeproc) gem instead.
|
|
8
9
|
|
|
9
10
|
Quickstart
|
|
@@ -13,64 +14,65 @@ Install CiteProc-Ruby and all official CSL styles (optional).
|
|
|
13
14
|
$ [sudo] gem install citeproc-ruby
|
|
14
15
|
$ [sudo] gem install csl-styles
|
|
15
16
|
|
|
16
|
-
Start rendering
|
|
17
|
+
Start rendering your references with any CSL style!
|
|
17
18
|
|
|
18
19
|
```ruby
|
|
19
20
|
require 'citeproc'
|
|
20
21
|
require 'csl/styles'
|
|
22
|
+
require 'bibtex'
|
|
21
23
|
|
|
22
|
-
# Create a new processor
|
|
23
|
-
# format, and locale.
|
|
24
|
+
# Create a new processor
|
|
25
|
+
# with the desired style, format, and locale.
|
|
24
26
|
cp = CiteProc::Processor.new style: 'apa', format: 'text'
|
|
25
27
|
|
|
26
|
-
# To see what styles are available in your current
|
|
27
|
-
#
|
|
28
|
-
# locales as `CSL::Locale.ls'.
|
|
28
|
+
# To see what styles are available in your current environment,
|
|
29
|
+
# run `CSL::Style.ls';
|
|
30
|
+
# this also works for locales as `CSL::Locale.ls'.
|
|
29
31
|
|
|
30
|
-
# Tell the processor where to find your references.
|
|
31
|
-
# example we load them from a BibTeX bibliography
|
|
32
|
-
# bibtex-ruby gem.
|
|
32
|
+
# Tell the processor where to find your references.
|
|
33
|
+
# In this example we load them from a BibTeX bibliography
|
|
34
|
+
# using the bibtex-ruby gem (`gem install bibtex-ruby').
|
|
33
35
|
cp.import BibTeX.open('./references.bib').to_citeproc
|
|
34
36
|
|
|
35
|
-
# Now you are ready for rendering;
|
|
36
|
-
# provides
|
|
37
|
-
# `
|
|
37
|
+
# Now you are ready for rendering;
|
|
38
|
+
# the processor API provides two main rendering methods:
|
|
39
|
+
# `process' and `bibliography'.
|
|
38
40
|
|
|
39
|
-
# For simple one-off renditions,
|
|
40
|
-
# `render' in bibliography or citation mode:
|
|
41
|
+
# For simple one-off renditions,
|
|
42
|
+
# you can also call `render' in bibliography or citation mode:
|
|
41
43
|
cp.render :bibliography, id: 'knuth'
|
|
42
44
|
|
|
43
45
|
# This will return a rendered reference, like:
|
|
44
|
-
#-> Knuth, D. (1968). The art of computer programming.
|
|
45
|
-
|
|
46
|
-
# CiteProc-Ruby exposes a full CSL API to you;
|
|
47
|
-
# makes it possible to just alter CSL styles on the
|
|
48
|
-
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
# we can also make a quick adjustment at runtime:
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
#
|
|
57
|
-
#
|
|
58
|
-
#
|
|
59
|
-
|
|
46
|
+
#-> Knuth, D. (1968). The art of computer programming (Vol. 1). Addison-Wesley.
|
|
47
|
+
|
|
48
|
+
# CiteProc-Ruby exposes a full CSL API to you;
|
|
49
|
+
# this makes it possible to just alter CSL styles on the fly.
|
|
50
|
+
# For example, what if we want names not to be initialized
|
|
51
|
+
# even though APA style is configured to do so?
|
|
52
|
+
# We could change the CSL style itself,
|
|
53
|
+
# but we can also make a quick adjustment at runtime:
|
|
54
|
+
cp.engine.style.macros['author-bib'].each_descendant do |node|
|
|
55
|
+
node[:initialize] = 'false' if node.nodename == 'name'
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# What just happened?
|
|
59
|
+
# We selected the current style's 'author-bib' macro
|
|
60
|
+
# and changed all of its CSL name nodes;
|
|
61
|
+
# the cite processor output will pick-up the changes right away:
|
|
60
62
|
|
|
61
63
|
cp.render :bibliography, id: 'knuth'
|
|
62
|
-
#-> Knuth, Donald. (1968). The art of computer programming (Vol. 1).
|
|
64
|
+
#-> Knuth, Donald. (1968). The art of computer programming (Vol. 1). Addison-Wesley.
|
|
63
65
|
|
|
64
66
|
# Note that we have picked 'text' as the output format;
|
|
65
|
-
# if we want to make
|
|
66
|
-
# can switch to HTML instead:
|
|
67
|
+
# if we want to make use of richer output formats
|
|
68
|
+
# we can switch to HTML instead:
|
|
67
69
|
cp.engine.format = 'html'
|
|
68
70
|
|
|
69
71
|
cp.render :bibliography, id: 'knuth'
|
|
70
|
-
#-> Knuth, Donald. (1968). <i>The art of computer programming</i> (Vol. 1).
|
|
72
|
+
#-> Knuth, Donald. (1968). <i>The art of computer programming</i> (Vol. 1). Addison-Wesley.
|
|
71
73
|
|
|
72
74
|
# You can also render citations on the fly.
|
|
73
|
-
cp.render :citation, id: 'knuth', locator: '23'
|
|
75
|
+
cp.render :citation, id: 'knuth', locator: '23', label: 'page'
|
|
74
76
|
#-> (Knuth, 1968, p. 23)
|
|
75
77
|
|
|
76
78
|
# Pass an array if you want to render multiple citations:
|
|
@@ -80,38 +82,40 @@ cp.render :citation, [{ id: 'knuth' }, { id: 'perez' }]
|
|
|
80
82
|
|
|
81
83
|
Documentation
|
|
82
84
|
-------------
|
|
83
|
-
* [CiteProc Documentation](
|
|
84
|
-
* [CiteProc-Ruby API Documentation](
|
|
85
|
-
* [CSL-Ruby API Documentation](
|
|
85
|
+
* [CiteProc Documentation](https://rubydoc.info/gems/citeproc/)
|
|
86
|
+
* [CiteProc-Ruby API Documentation](https://rubydoc.info/gems/citeproc-ruby/)
|
|
87
|
+
* [CSL-Ruby API Documentation](https://rubydoc.info/gems/csl/)
|
|
86
88
|
|
|
87
89
|
Optional Dependencies
|
|
88
90
|
---------------------
|
|
89
91
|
CiteProc-Ruby tries to minimize hard dependencies for increased compatibility.
|
|
90
92
|
You can speed up the XML parsing by installing
|
|
91
|
-
[Nokogiri](https://rubygems.org/gems/nokogiri);
|
|
92
|
-
Ruby standard library will be used.
|
|
93
|
+
[Nokogiri](https://rubygems.org/gems/nokogiri);
|
|
94
|
+
otherwise the REXML from the Ruby standard library will be used.
|
|
93
95
|
|
|
94
|
-
Similarly, you can install
|
|
95
|
-
[EDTF](https://rubygems.org/gems/edtf)
|
|
96
|
-
|
|
97
|
-
additional inputs for date variables.
|
|
96
|
+
Similarly, you can install the
|
|
97
|
+
[EDTF](https://rubygems.org/gems/edtf)
|
|
98
|
+
gem to support a wide range of additional inputs for date variables.
|
|
98
99
|
|
|
99
100
|
CSL Styles and Locales
|
|
100
101
|
----------------------
|
|
101
|
-
You can load CSL styles and locales
|
|
102
|
-
|
|
103
|
-
|
|
102
|
+
You can load CSL styles and locales
|
|
103
|
+
by passing a respective XML string, file name, or URL.
|
|
104
|
+
You can also load styles and locales by name
|
|
105
|
+
if the corresponding files are installed
|
|
106
|
+
in your local styles and locale directories.
|
|
104
107
|
By default, CSL-Ruby looks for CSL styles and locale files in
|
|
105
108
|
|
|
106
109
|
/usr/local/share/csl/styles
|
|
107
110
|
/usr/local/share/csl/locales
|
|
108
111
|
|
|
109
|
-
You can change these locations
|
|
110
|
-
`CSL::Locale.root` respectively.
|
|
112
|
+
You can change these locations
|
|
113
|
+
by changing the value of `CSL::Style.root` and `CSL::Locale.root` respectively.
|
|
111
114
|
|
|
112
|
-
Alternatively, you can `gem install csl-styles`
|
|
113
|
-
|
|
114
|
-
|
|
115
|
+
Alternatively, you can `gem install csl-styles`
|
|
116
|
+
to install all official CSL styles and locales.
|
|
117
|
+
To make the styles and locales available,
|
|
118
|
+
simply `require 'csl/styles'`.
|
|
115
119
|
|
|
116
120
|
Development
|
|
117
121
|
-----------
|
|
@@ -125,24 +129,28 @@ To get started, install the development dependencies and run all tests:
|
|
|
125
129
|
|
|
126
130
|
$ cd citeproc-ruby
|
|
127
131
|
$ bundle install
|
|
128
|
-
$ rake
|
|
132
|
+
$ bundle exec rake
|
|
129
133
|
|
|
130
|
-
If you've found a bug or have a question,
|
|
134
|
+
If you've found a bug or have a question,
|
|
135
|
+
please open an issue on the
|
|
131
136
|
[issue tracker](https://github.com/inukshuk/citeproc-ruby/issues).
|
|
132
|
-
Or, for extra credit, clone the CiteProc-Ruby repository,
|
|
133
|
-
example, fix the bug and submit a pull request.
|
|
137
|
+
Or, for extra credit, clone the CiteProc-Ruby repository,
|
|
138
|
+
write a failing example, fix the bug and submit a pull request.
|
|
134
139
|
|
|
135
140
|
Credits
|
|
136
141
|
-------
|
|
137
142
|
Thanks to Rintze M. Zelle, Sebastian Karcher, Frank G. Bennett, Jr.,
|
|
138
|
-
and Bruce D'Arcus of CSL and citeproc-js fame
|
|
143
|
+
and Bruce D'Arcus of CSL and citeproc-js fame
|
|
144
|
+
for their support!
|
|
139
145
|
|
|
140
|
-
Thanks to Google and the Berkman Center at Harvard University
|
|
141
|
-
this project as part of
|
|
146
|
+
Thanks to Google and the Berkman Center at Harvard University
|
|
147
|
+
for supporting this project as part of
|
|
148
|
+
[Google Summer of Code](https://developers.google.com/open-source/soc/).
|
|
142
149
|
|
|
143
150
|
Copyright
|
|
144
151
|
---------
|
|
145
|
-
Copyright 2009-
|
|
152
|
+
Copyright 2009-2026 Sylvester Keil.
|
|
153
|
+
All rights reserved.
|
|
146
154
|
|
|
147
155
|
Copyright 2012 President and Fellows of Harvard College.
|
|
148
156
|
|
data/lib/citeproc/ruby/engine.rb
CHANGED
|
@@ -13,7 +13,7 @@ module CiteProc
|
|
|
13
13
|
attr_reader :renderer, :style
|
|
14
14
|
|
|
15
15
|
def_delegators :renderer,
|
|
16
|
-
:format, :format=, :locale, :locale
|
|
16
|
+
:format, :format=, :locale, :locale=, :clear_cache!
|
|
17
17
|
|
|
18
18
|
def initialize(*arguments)
|
|
19
19
|
super(*arguments)
|
|
@@ -22,8 +22,10 @@ module CiteProc
|
|
|
22
22
|
update! unless processor.nil?
|
|
23
23
|
end
|
|
24
24
|
|
|
25
|
+
# @param new_style [CSL::Style, String] a style or a style name, path,
|
|
26
|
+
# or URL; styles are used as is and never changed by the engine
|
|
25
27
|
def style=(new_style)
|
|
26
|
-
@style = CSL::Style.load
|
|
28
|
+
@style = new_style.is_a?(CSL::Style) ? new_style : CSL::Style.load(new_style)
|
|
27
29
|
end
|
|
28
30
|
|
|
29
31
|
def process(data)
|
|
@@ -35,6 +37,7 @@ module CiteProc
|
|
|
35
37
|
# populate item data
|
|
36
38
|
data.each do |item|
|
|
37
39
|
item.data = processor[item.id].dup
|
|
40
|
+
item.parse_locator!(renderer.locator_abbreviations)
|
|
38
41
|
end
|
|
39
42
|
|
|
40
43
|
# TODO implement sort in citation data
|
|
@@ -100,6 +103,7 @@ module CiteProc
|
|
|
100
103
|
|
|
101
104
|
data.each do |item|
|
|
102
105
|
item.data = processor[item.id].dup
|
|
106
|
+
item.parse_locator!(renderer.locator_abbreviations)
|
|
103
107
|
end
|
|
104
108
|
|
|
105
109
|
renderer.render_citation data, node
|
|
@@ -109,23 +113,15 @@ module CiteProc
|
|
|
109
113
|
end
|
|
110
114
|
end
|
|
111
115
|
|
|
112
|
-
|
|
116
|
+
# Applies the processor's format, locale, and style options.
|
|
117
|
+
#
|
|
118
|
+
# @return [self]
|
|
113
119
|
def update!
|
|
120
|
+
renderer.clear_cache!
|
|
114
121
|
renderer.format = processor.options[:format]
|
|
115
122
|
renderer.locale = processor.options[:locale]
|
|
116
123
|
|
|
117
|
-
|
|
118
|
-
@style = processor.options[:style]
|
|
119
|
-
else
|
|
120
|
-
@style = CSL::Style.load processor.options[:style]
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
# Preliminary locale override implementation!
|
|
124
|
-
# Does not yet reverse merge default region and default locale.
|
|
125
|
-
@style.locales.sort.reverse.each do |locale|
|
|
126
|
-
renderer.locale.merge! locale if renderer.locale.like?(locale)
|
|
127
|
-
end
|
|
128
|
-
|
|
124
|
+
self.style = processor.options[:style]
|
|
129
125
|
self
|
|
130
126
|
end
|
|
131
127
|
|
data/lib/citeproc/ruby/format.rb
CHANGED
|
@@ -7,7 +7,7 @@ module CiteProc
|
|
|
7
7
|
|
|
8
8
|
@available = []
|
|
9
9
|
|
|
10
|
-
@squeezable = /^[\s
|
|
10
|
+
@squeezable = /^[\s\.,:;!?]+$/
|
|
11
11
|
|
|
12
12
|
@stopwords = {
|
|
13
13
|
:en => %w(a about above across afore after against along alongside amid amidst among amongst
|
|
@@ -16,7 +16,7 @@ module CiteProc
|
|
|
16
16
|
forenenst from given in inside into lest like modulo near next nor notwithstanding of off
|
|
17
17
|
on onto or out over per plus pro qua sans since so than the through thru throughout thruout
|
|
18
18
|
till to toward towards under underneath until unto up upon versus vs v via vis-à-vis with
|
|
19
|
-
within without)
|
|
19
|
+
within without yet de von van)
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
class << self
|
|
@@ -140,14 +140,16 @@ module CiteProc
|
|
|
140
140
|
bibliography
|
|
141
141
|
end
|
|
142
142
|
|
|
143
|
-
|
|
143
|
+
# @param language [String] the language of the item being formatted
|
|
144
|
+
def apply(input, node, locale = nil, language = nil)
|
|
144
145
|
return '' if input.nil?
|
|
145
146
|
return input if input.empty? || node.nil?
|
|
146
147
|
|
|
147
148
|
return ArgumentError unless node.respond_to?(:formatting_options)
|
|
148
149
|
|
|
149
150
|
|
|
150
|
-
@input, @output, @node, @locale
|
|
151
|
+
@input, @output, @node, @locale, @language =
|
|
152
|
+
input, input.dup, node, locale, language
|
|
151
153
|
|
|
152
154
|
setup!
|
|
153
155
|
|
|
@@ -208,10 +210,10 @@ module CiteProc
|
|
|
208
210
|
def apply_text_case
|
|
209
211
|
case options[:'text-case']
|
|
210
212
|
when 'lowercase'
|
|
211
|
-
output.replace CiteProc.downcase
|
|
213
|
+
output.replace turkic? ? output.downcase(:turkic) : CiteProc.downcase(output)
|
|
212
214
|
|
|
213
215
|
when 'uppercase'
|
|
214
|
-
output.replace CiteProc.upcase
|
|
216
|
+
output.replace turkic? ? output.upcase(:turkic) : CiteProc.upcase(output)
|
|
215
217
|
|
|
216
218
|
when 'capitalize-first'
|
|
217
219
|
output.sub!(/^([^\p{Alnum}]*)(\p{Ll})/) { "#{$1}#{CiteProc.upcase($2)}" }
|
|
@@ -224,25 +226,34 @@ module CiteProc
|
|
|
224
226
|
output.gsub!(/\b(\p{Lu})(\p{Lu}+)\b/) { "#{$1}#{CiteProc.downcase($2)}" }
|
|
225
227
|
|
|
226
228
|
when 'title'
|
|
227
|
-
return
|
|
229
|
+
return unless english?
|
|
228
230
|
|
|
229
231
|
# TODO add support for stop words consisting of multiple words
|
|
230
232
|
#output.gsub!(/\b(\p{Lu})(\p{Lu}+)\b/) { "#{$1}#{CiteProc.downcase($2)}" }
|
|
231
233
|
|
|
232
234
|
# TODO exceptions: word followed by colon
|
|
233
235
|
first = true
|
|
234
|
-
output.gsub!(
|
|
236
|
+
output.gsub!(/(?<!['’])\b(\p{L})([\p{L}\.]+)\b/) do |word|
|
|
235
237
|
first_letter = $1
|
|
236
238
|
rest_of_word = $2
|
|
239
|
+
before, after = Regexp.last_match.pre_match, Regexp.last_match.post_match
|
|
237
240
|
result = word
|
|
238
241
|
|
|
239
|
-
|
|
242
|
+
# Stop words are capitalized at the start of hyphenated words
|
|
243
|
+
capitalize = first || !Format.stopword?(word) ||
|
|
244
|
+
after.start_with?('-') && !before.end_with?('-')
|
|
245
|
+
|
|
246
|
+
# Words in mixed case like "iPad" are left as is
|
|
247
|
+
if first_letter.match(/^\p{Ll}/) && !rest_of_word.match?(/\p{Lu}/) && capitalize
|
|
240
248
|
result = "#{CiteProc.upcase(first_letter)}#{rest_of_word}"
|
|
241
249
|
end
|
|
242
250
|
first = false
|
|
243
251
|
result
|
|
244
252
|
end
|
|
245
253
|
|
|
254
|
+
# Capitalize the first word after a colon
|
|
255
|
+
output.gsub!(/(:\s+)(\p{Ll})(?=\p{Ll}*\b)/) { "#{$1}#{CiteProc.upcase($2)}" }
|
|
256
|
+
|
|
246
257
|
output.gsub!(/(\.|\b)(\p{Ll})([\p{L}\.]+)\b$/) do |word|
|
|
247
258
|
word_boundary = $1
|
|
248
259
|
first_letter = $2
|
|
@@ -257,6 +268,21 @@ module CiteProc
|
|
|
257
268
|
end
|
|
258
269
|
end
|
|
259
270
|
|
|
271
|
+
# Title case applies only to English items: items whose language
|
|
272
|
+
# is English or, if they have no language, when the locale is English.
|
|
273
|
+
def english?
|
|
274
|
+
if @language.nil? || @language.empty?
|
|
275
|
+
locale.nil? || locale.language == :en
|
|
276
|
+
else
|
|
277
|
+
@language.match?(/\Aen(-|\z)/i)
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# @return [Boolean] whether the item's language uses Turkic case mappings
|
|
282
|
+
def turkic?
|
|
283
|
+
@language.to_s.match?(/\A(tr|az)(-|\z)/i)
|
|
284
|
+
end
|
|
285
|
+
|
|
260
286
|
def punctuation_in_quotes?
|
|
261
287
|
!locale.nil? && locale.punctuation_in_quotes?
|
|
262
288
|
end
|
|
@@ -20,14 +20,15 @@ module CiteProc
|
|
|
20
20
|
class Sort < Text
|
|
21
21
|
#A special format to use when sorting which prevents formatting of extraneous things like quotes
|
|
22
22
|
|
|
23
|
-
def apply(input, node, locale = nil)
|
|
23
|
+
def apply(input, node, locale = nil, language = nil)
|
|
24
24
|
return '' if input.nil?
|
|
25
25
|
return input if input.empty? || node.nil?
|
|
26
26
|
|
|
27
27
|
return ArgumentError unless node.respond_to?(:formatting_options)
|
|
28
28
|
|
|
29
29
|
|
|
30
|
-
@input, @output, @node, @locale
|
|
30
|
+
@input, @output, @node, @locale, @language =
|
|
31
|
+
input, input.dup, node, locale, language
|
|
31
32
|
|
|
32
33
|
setup!
|
|
33
34
|
|
|
@@ -58,7 +58,7 @@ module CiteProc
|
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
if bibliography.hanging_indent?
|
|
61
|
-
hanging_indent = "#{config[:bib_hanging_indent]}#{
|
|
61
|
+
hanging_indent = "#{config[:bib_hanging_indent]}#{unit}"
|
|
62
62
|
|
|
63
63
|
container_options['style']['padding-left'] = hanging_indent
|
|
64
64
|
entry_options['style']['text-indent'] = "-#{hanging_indent}"
|
|
@@ -118,6 +118,14 @@ module CiteProc
|
|
|
118
118
|
)
|
|
119
119
|
end
|
|
120
120
|
|
|
121
|
+
# The suffix of a layout goes into a trailing display block
|
|
122
|
+
def apply_suffix
|
|
123
|
+
close = closing_tag(config[:display])
|
|
124
|
+
return super unless node.is_a?(CSL::Style::Layout) && output.end_with?(close)
|
|
125
|
+
|
|
126
|
+
output.replace "#{squeeze_suffix(output.delete_suffix(close), suffix)}#{close}"
|
|
127
|
+
end
|
|
128
|
+
|
|
121
129
|
def prefix
|
|
122
130
|
CSL.encode_xml_text(options[:prefix])
|
|
123
131
|
end
|
|
@@ -131,7 +139,7 @@ module CiteProc
|
|
|
131
139
|
end
|
|
132
140
|
|
|
133
141
|
def strip(string)
|
|
134
|
-
string.split(/((
|
|
142
|
+
string.split(/((?:\A<\p{Alpha}[^>]*>)|(?:<\/\p{Alpha}[^>]*>))\z/, 2)
|
|
135
143
|
end
|
|
136
144
|
|
|
137
145
|
protected
|
|
@@ -194,6 +202,9 @@ module CiteProc
|
|
|
194
202
|
end
|
|
195
203
|
|
|
196
204
|
class CiteProcJS < Html
|
|
205
|
+
# The superscript characters converted to sup tags by citeproc-js
|
|
206
|
+
SUPERSCRIPTS = /[ª²-³¹-ºʰ-ʸˀ-ˁˠ-ˤۥ-ۦᴬ-ᴮᴰ-ᴺᴼ-ᵍᵏ-ᵡ⁰-ⁱ⁴-ⁿ℠™㆒-㆟]/
|
|
207
|
+
|
|
197
208
|
def initialize
|
|
198
209
|
super(
|
|
199
210
|
:bib_container => 'div',
|
|
@@ -222,6 +233,35 @@ module CiteProc
|
|
|
222
233
|
bibliography.connector = indent ? "\n" : ''
|
|
223
234
|
bibliography
|
|
224
235
|
end
|
|
236
|
+
|
|
237
|
+
def apply_vertical_align
|
|
238
|
+
case options[:'vertical-align']
|
|
239
|
+
when 'sup', 'sub'
|
|
240
|
+
output.replace content_tag(options[:'vertical-align'], output)
|
|
241
|
+
else
|
|
242
|
+
super
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
protected
|
|
247
|
+
|
|
248
|
+
def finalize!
|
|
249
|
+
super
|
|
250
|
+
|
|
251
|
+
# Use typographic apostrophes
|
|
252
|
+
output.gsub!(/(?<=\p{L})'|'(?=\d)/, '’')
|
|
253
|
+
|
|
254
|
+
output.gsub!(SUPERSCRIPTS) do |char|
|
|
255
|
+
content_tag('sup', char.unicode_normalize(:nfkc))
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
private
|
|
260
|
+
|
|
261
|
+
def style_for(options)
|
|
262
|
+
return unless options && !options.empty?
|
|
263
|
+
options.map { |k, v| "#{k}:#{v};" }.join
|
|
264
|
+
end
|
|
225
265
|
end
|
|
226
266
|
|
|
227
267
|
end
|
|
@@ -3,39 +3,84 @@ module CiteProc
|
|
|
3
3
|
|
|
4
4
|
class Renderer
|
|
5
5
|
|
|
6
|
+
# The date parts from largest to smallest
|
|
7
|
+
DATE_PARTS = %w{ year month day }.freeze
|
|
8
|
+
|
|
6
9
|
# @param item [CiteProc::CitationItem]
|
|
7
10
|
# @param node [CSL::Node]
|
|
8
|
-
# @raise
|
|
11
|
+
# @raise CSL::Error if the locale has no date in the form of the node
|
|
9
12
|
# @return [String]
|
|
10
13
|
def render_date(item, node)
|
|
11
14
|
return '' unless node.has_variable?
|
|
12
15
|
|
|
13
|
-
date
|
|
14
|
-
|
|
16
|
+
# Observers see the date as rendered:
|
|
17
|
+
# a date without the date parts used counts as empty
|
|
18
|
+
item.data.deferred_read_attribute(node.variable) do |date|
|
|
19
|
+
next '' if date.nil? || date.empty?
|
|
20
|
+
next date.to_s if date.literal?
|
|
15
21
|
|
|
16
|
-
|
|
22
|
+
parts, delimiter = node.parts_for(locale), node.delimiter_for(locale)
|
|
17
23
|
|
|
18
|
-
|
|
24
|
+
if date.range?
|
|
25
|
+
render_date_range date, parts, delimiter
|
|
26
|
+
else
|
|
27
|
+
render_date_parts date, parts, delimiter
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
19
31
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
32
|
+
# Renders a date range. The date parts which are the same for
|
|
33
|
+
# both dates are rendered only once; the date parts which differ
|
|
34
|
+
# are rendered for both dates and are joined by the range delimiter
|
|
35
|
+
# of the largest date part that differs.
|
|
36
|
+
#
|
|
37
|
+
# @param date [CiteProc::Date] the date range
|
|
38
|
+
# @param parts [Array<CSL::Style::DatePart, CSL::Locale::DatePart>]
|
|
39
|
+
# @param delimiter [String]
|
|
40
|
+
# @return [String]
|
|
41
|
+
def render_date_range(date, parts, delimiter)
|
|
42
|
+
from, to = date.parts
|
|
23
43
|
|
|
24
|
-
|
|
44
|
+
# Skip date parts without values (e.g., days in a month range)
|
|
45
|
+
parts = parts.select { |part| from[part.name] || to[part.name] }
|
|
25
46
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
47
|
+
# Open ranges are rendered with a trailing range delimiter
|
|
48
|
+
return [
|
|
49
|
+
render_date_parts(from, parts, delimiter),
|
|
50
|
+
range_delimiter_for(parts, 'year')
|
|
51
|
+
].join('') if date.open_range?
|
|
52
|
+
|
|
53
|
+
names = parts.map(&:name)
|
|
54
|
+
largest = DATE_PARTS.detect do |name|
|
|
55
|
+
names.include?(name) && from[name] != to[name]
|
|
31
56
|
end
|
|
32
57
|
|
|
58
|
+
return render_date_parts(from, parts, delimiter) if largest.nil?
|
|
59
|
+
|
|
60
|
+
# If one date is more precise than the other, all parts differ
|
|
61
|
+
differing = names.any? { |name| from[name].nil? != to[name].nil? } ?
|
|
62
|
+
DATE_PARTS :
|
|
63
|
+
DATE_PARTS.drop_while { |name| name != largest }
|
|
64
|
+
|
|
65
|
+
before, range, after = split_date_parts(parts, differing) || [[], parts, []]
|
|
66
|
+
|
|
67
|
+
[
|
|
68
|
+
render_date_parts(from, before, delimiter),
|
|
69
|
+
[
|
|
70
|
+
render_date_parts(from, without_suffix(range), delimiter),
|
|
71
|
+
render_date_parts(to, range, delimiter)
|
|
72
|
+
].join(range_delimiter_for(parts, largest)),
|
|
73
|
+
render_date_parts(from, after, delimiter)
|
|
74
|
+
].reject(&:empty?).join(delimiter)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def render_date_parts(date, parts, delimiter)
|
|
33
78
|
parts.map { |part|
|
|
34
79
|
render date, part
|
|
35
80
|
}.reject(&:empty?).join(delimiter)
|
|
36
81
|
end
|
|
37
82
|
|
|
38
|
-
# @param date [CiteProc::Date]
|
|
83
|
+
# @param date [CiteProc::Date, CiteProc::Date::DateParts]
|
|
39
84
|
# @param node [CSL::Style::DatePart, CSL::Locale::DatePart]
|
|
40
85
|
# @return [String]
|
|
41
86
|
def render_date_part(date, node)
|
|
@@ -48,7 +93,8 @@ module CiteProc
|
|
|
48
93
|
if date.day > 1 && locale.limit_day_ordinals?
|
|
49
94
|
date.day.to_s
|
|
50
95
|
else
|
|
51
|
-
|
|
96
|
+
# The ordinal uses the gender of the month
|
|
97
|
+
ordinalize date.day, :noun => date.month && 'month-%02d' % date.month
|
|
52
98
|
end
|
|
53
99
|
when node.form == 'numeric-leading-zeros'
|
|
54
100
|
'%02d' % date.day
|
|
@@ -58,6 +104,8 @@ module CiteProc
|
|
|
58
104
|
|
|
59
105
|
when node.month?
|
|
60
106
|
case
|
|
107
|
+
when date.season.is_a?(String)
|
|
108
|
+
date.season
|
|
61
109
|
when date.season?
|
|
62
110
|
translate(('season-%02d' % date.season), node.attributes_for(:form))
|
|
63
111
|
when date.month.nil?
|
|
@@ -91,6 +139,42 @@ module CiteProc
|
|
|
91
139
|
end
|
|
92
140
|
end
|
|
93
141
|
|
|
142
|
+
private
|
|
143
|
+
|
|
144
|
+
# @return [String] the range delimiter set on the date part
|
|
145
|
+
# with the given name; defaults to an en-dash
|
|
146
|
+
def range_delimiter_for(parts, name)
|
|
147
|
+
part = parts.detect { |p| p.name == name }
|
|
148
|
+
part && part[:'range-delimiter'] || '–'
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Splits the date parts into the common parts before, the
|
|
152
|
+
# differing parts, and the common parts after them.
|
|
153
|
+
#
|
|
154
|
+
# @return [Array<Array>, nil] the split parts or nil if the
|
|
155
|
+
# differing parts are not adjacent
|
|
156
|
+
def split_date_parts(parts, differing)
|
|
157
|
+
first = parts.index { |part| differing.include?(part.name) }
|
|
158
|
+
last = parts.rindex { |part| differing.include?(part.name) }
|
|
159
|
+
range = parts[first..last]
|
|
160
|
+
|
|
161
|
+
return unless range.all? { |part| differing.include?(part.name) }
|
|
162
|
+
|
|
163
|
+
[parts[0...first], range, parts[(last + 1)..]]
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# @return [Array<CSL::Node>] the date parts with a copy
|
|
167
|
+
# of the last date part without its suffix
|
|
168
|
+
def without_suffix(parts)
|
|
169
|
+
last = parts[-1]
|
|
170
|
+
return parts unless last.attribute?(:suffix)
|
|
171
|
+
|
|
172
|
+
copy = last.deep_copy
|
|
173
|
+
copy[:suffix] = nil
|
|
174
|
+
|
|
175
|
+
[*parts[0...-1], copy]
|
|
176
|
+
end
|
|
177
|
+
|
|
94
178
|
end
|
|
95
179
|
|
|
96
180
|
end
|