epub-cfi 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +3 -0
- data/.gitignore +8 -0
- data/.gitlab-ci.yml +19 -0
- data/.yardopts +1 -0
- data/COPYING.txt +165 -0
- data/ChangeLog.md +4 -0
- data/README.md +85 -0
- data/Rakefile +27 -0
- data/epub-cfi.gemspec +41 -0
- data/lib/epub/cfi.rb +309 -0
- data/lib/epub/cfi/parser.rb +88 -0
- data/lib/epub/cfi/parser.y +184 -0
- data/lib/epub/cfi/version.rb +23 -0
- data/test/helper.rb +13 -0
- data/test/test_cfi.rb +196 -0
- data/test/test_parser.rb +53 -0
- metadata +175 -0
@@ -0,0 +1,88 @@
|
|
1
|
+
require "epub/cfi/parser.tab"
|
2
|
+
|
3
|
+
class EPUB::CFI::Parser
|
4
|
+
include Comparable
|
5
|
+
|
6
|
+
UNICODE_CHARACTER_EXCLUDING_SPECIAL_CHARS_AND_SPACE_AND_DOT_AND_COLON_AND_TILDE_AND_ATMARK_AND_SOLIDUS_AND_EXCLAMATION_MARK_PATTERN = /\u0009|\u000A|\u000D|[\u0022-\u0027]|[\u002A-\u002B]|\u002D|[\u0030-\u0039]|\u003C|[\u003E-\u0040]|[\u0041-\u005A]|\u005C|[\u005F-\u007D]|[\u007F-\uD7FF]|[\uE000-\uFFFD]|[\u10000-\u10FFFF]/ # excluding special chars and space(\u0020) and dot(\u002E) and colon(\u003A) and tilde(\u007E) and atmark(\u0040) and solidus(\u002F) and exclamation mark(\u0021)
|
7
|
+
UNICODE_CHARACTER_PATTERN = Regexp.union(UNICODE_CHARACTER_EXCLUDING_SPECIAL_CHARS_AND_SPACE_AND_DOT_AND_COLON_AND_TILDE_AND_ATMARK_AND_SOLIDUS_AND_EXCLAMATION_MARK_PATTERN, Regexp.new(Regexp.escape(EPUB::CFI::SPECIAL_CHARS), / \.:~@!/))
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def parse(string, debug: false)
|
11
|
+
new(debug: debug).parse(string)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(debug: false)
|
16
|
+
@yydebug = debug
|
17
|
+
super()
|
18
|
+
end
|
19
|
+
|
20
|
+
def parse(string)
|
21
|
+
if string.start_with? 'epubcfi('
|
22
|
+
string = string['epubcfi('.length .. -2]
|
23
|
+
end
|
24
|
+
@scanner = StringScanner.new(string, true)
|
25
|
+
@q = []
|
26
|
+
until @scanner.eos?
|
27
|
+
case
|
28
|
+
when @scanner.scan(/[1-9]/)
|
29
|
+
@q << [:DIGIT_NON_ZERO, @scanner[0]]
|
30
|
+
when @scanner.scan(/0/)
|
31
|
+
@q << [:ZERO, @scanner[0]]
|
32
|
+
when @scanner.scan(/ /)
|
33
|
+
@q << [:SPACE, @scanner[0]]
|
34
|
+
when @scanner.scan(/\^/)
|
35
|
+
@q << [:CIRCUMFLEX, @scanner[0]]
|
36
|
+
when @scanner.scan(/\[/)
|
37
|
+
@q << [:OPENING_SQUARE_BRACKET, @scanner[0]]
|
38
|
+
when @scanner.scan(/\]/)
|
39
|
+
@q << [:CLOSING_SQUARE_BRACKET, @scanner[0]]
|
40
|
+
when @scanner.scan(/\(/)
|
41
|
+
@q << [:OPENING_PARENTHESIS, @scanner[0]]
|
42
|
+
when @scanner.scan(/\)/)
|
43
|
+
@q << [:CLOSING_PARENTHESIS, @scanner[0]]
|
44
|
+
when @scanner.scan(/,/)
|
45
|
+
@q << [:COMMA, @scanner[0]]
|
46
|
+
when @scanner.scan(/;/)
|
47
|
+
@q << [:SEMICOLON, @scanner[0]]
|
48
|
+
when @scanner.scan(/=/)
|
49
|
+
@q << [:EQUAL, @scanner[0]]
|
50
|
+
when @scanner.scan(/\./)
|
51
|
+
@q << [:DOT, @scanner[0]]
|
52
|
+
when @scanner.scan(/:/)
|
53
|
+
@q << [:COLON, @scanner[0]]
|
54
|
+
when @scanner.scan(/~/)
|
55
|
+
@q << [:TILDE, @scanner[0]]
|
56
|
+
when @scanner.scan(/@/)
|
57
|
+
@q << [:ATMARK, @scanner[0]]
|
58
|
+
when @scanner.scan(/\//)
|
59
|
+
@q << [:SOLIDUS, @scanner[0]]
|
60
|
+
when @scanner.scan(/!/)
|
61
|
+
@q << [:EXCLAMATION_MARK, @scanner[0]]
|
62
|
+
when @scanner.scan(UNICODE_CHARACTER_EXCLUDING_SPECIAL_CHARS_AND_SPACE_AND_DOT_AND_COLON_AND_TILDE_AND_ATMARK_AND_SOLIDUS_AND_EXCLAMATION_MARK_PATTERN)
|
63
|
+
@q << [:UNICODE_CHARACTER_EXCLUDING_SPECIAL_CHARS_AND_SPACE_AND_DOT_AND_COLON_AND_TILDE_AND_ATMARK_AND_SOLIDUS_AND_EXCLAMATION_MARK, @scanner[0]]
|
64
|
+
else
|
65
|
+
raise 'unexpected character'
|
66
|
+
end
|
67
|
+
end
|
68
|
+
@q << [false, false]
|
69
|
+
|
70
|
+
do_parse
|
71
|
+
end
|
72
|
+
|
73
|
+
def next_token
|
74
|
+
@q.shift
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
module EPUB::CFI
|
79
|
+
module_function
|
80
|
+
|
81
|
+
def parse(string)
|
82
|
+
EPUB::CFI::Parser.parse(string)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def EPUB::CFI(string)
|
87
|
+
EPUB::CFI.parse(string)
|
88
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
class EPUB::CFI::Parser
|
2
|
+
rule
|
3
|
+
|
4
|
+
fragment : path range_zero_or_one
|
5
|
+
{
|
6
|
+
if val[1]
|
7
|
+
result = CFI::Range.from_parent_and_start_and_end(val[0], *val[1])
|
8
|
+
else
|
9
|
+
result = CFI::Location.new(val[0])
|
10
|
+
end
|
11
|
+
}
|
12
|
+
|
13
|
+
range_zero_or_one : range
|
14
|
+
|
|
15
|
+
|
16
|
+
path : step local_path
|
17
|
+
{
|
18
|
+
path, redirected_path = *val[1]
|
19
|
+
path.steps.unshift val[0]
|
20
|
+
result = val[1]
|
21
|
+
}
|
22
|
+
|
23
|
+
range : COMMA local_path COMMA local_path
|
24
|
+
{result = [val[1], val[3]]}
|
25
|
+
|
26
|
+
local_path : step_zero_or_more redirected_path
|
27
|
+
{result = [CFI::Path.new(val[0])] + val[1]}
|
28
|
+
| step_zero_or_more offset_zero_or_one
|
29
|
+
{result = [CFI::Path.new(val[0], val[1])]}
|
30
|
+
|
31
|
+
step_zero_or_more : step_zero_or_more step
|
32
|
+
{result = val[0] + [val[1]]}
|
33
|
+
| step
|
34
|
+
{result = [val[0]]}
|
35
|
+
|
|
36
|
+
{result = []}
|
37
|
+
|
38
|
+
redirected_path : EXCLAMATION_MARK offset
|
39
|
+
{result = [CFI::Path.new([], val[1])]}
|
40
|
+
| EXCLAMATION_MARK path
|
41
|
+
{result = val[1]}
|
42
|
+
|
43
|
+
step : SOLIDUS integer assertion_part_zero_or_one
|
44
|
+
{
|
45
|
+
assertion = val[2] ? CFI::IDAssertion.new(val[2][0], val[2][2]) : nil
|
46
|
+
result = CFI::Step.new(val[1].to_i, assertion)
|
47
|
+
}
|
48
|
+
|
49
|
+
offset_zero_or_one : offset
|
50
|
+
|
|
51
|
+
|
52
|
+
offset : COLON integer assertion_part_zero_or_one
|
53
|
+
{
|
54
|
+
assertion = val[2] ? CFI::TextLocationAssertion.new(*val[2]) : nil
|
55
|
+
result = CFI::CharacterOffset.new(val[1].to_i, assertion)
|
56
|
+
}
|
57
|
+
| spatial_offset assertion_part_zero_or_one
|
58
|
+
{result = CFI::TemporalSpatialOffset.new(nil, val[0][0].to_f, val[0][1].to_f, val[2])}
|
59
|
+
| TILDE number spatial_offset_zero_or_one assertion_part_zero_or_one
|
60
|
+
{
|
61
|
+
x = val[2] ? val[2][0].to_f : nil
|
62
|
+
y = val[2] ? val[2][1].to_f : nil
|
63
|
+
result = CFI::TemporalSpatialOffset.new(val[1].to_f, x, y, val[3])
|
64
|
+
}
|
65
|
+
|
66
|
+
spatial_offset_zero_or_one : spatial_offset
|
67
|
+
|
|
68
|
+
|
69
|
+
spatial_offset : ATMARK number COLON number
|
70
|
+
{result = [val[1], val[3]]}
|
71
|
+
|
72
|
+
assertion_part_zero_or_one : opening_square_bracket assertion closing_square_bracket
|
73
|
+
{result = val[1]}
|
74
|
+
|
|
75
|
+
|
76
|
+
number : DIGIT_NON_ZERO digit_zero_or_more fractional_portion_zero_or_one
|
77
|
+
{result = val.join}
|
78
|
+
| ZERO fractional_portion_zero_or_one
|
79
|
+
{result = val.join}
|
80
|
+
|
81
|
+
fractional_portion_zero_or_one : fractional_portion
|
82
|
+
|
|
83
|
+
|
84
|
+
fractional_portion : DOT digit_zero_or_more DIGIT_NON_ZERO
|
85
|
+
{result = val.join}
|
86
|
+
| DOT DIGIT_NON_ZERO
|
87
|
+
{result = val.join}
|
88
|
+
|
89
|
+
integer : ZERO
|
90
|
+
| DIGIT_NON_ZERO digit_zero_or_more
|
91
|
+
{result = val.join}
|
92
|
+
|
93
|
+
digit_zero_or_more : digit_zero_or_more digit
|
94
|
+
{result = val.join}
|
95
|
+
| digit
|
96
|
+
|
|
97
|
+
|
98
|
+
assertion : value_csv_one_or_two parameter_zero_or_more
|
99
|
+
{result = [val[0][0], val[0][1], val[1]]} # Cannot see id assertion or text location assertion when val[0]'s length is 1. It can be done by context.
|
100
|
+
| COMMA value parameter_zero_or_more
|
101
|
+
{result = [nil, val[1], val[2]]}
|
102
|
+
| parameter parameter_zero_or_more
|
103
|
+
{result = [nil, nil, val[0].merge(val[1])]} # Cannot see id assertion or text location assertion when val[0]'s length is 1. It can be done by context. In EPUBCFI 3.0.1 spec, only side-bias parameter is defined and we can say it's text location assertion of the assertion has parameters. But when the spec is extended and other parameter definitions added, we might become not able to say so.
|
104
|
+
|
105
|
+
value_csv_one_or_two : value COMMA value
|
106
|
+
{result = [val[0], val[2]]}
|
107
|
+
| value
|
108
|
+
{result = [val[0]]}
|
109
|
+
|
110
|
+
parameter_zero_or_more : parameter_zero_or_more parameter
|
111
|
+
{result = val[0].merge(val[1])}
|
112
|
+
| parameter
|
113
|
+
{result = val[0]}
|
114
|
+
|
|
115
|
+
{result = {}}
|
116
|
+
|
117
|
+
parameter : SEMICOLON value_no_space EQUAL csv
|
118
|
+
{result = {val[1] => val[3]}}
|
119
|
+
|
120
|
+
csv : csv COMMA value
|
121
|
+
{result = val[0] + [val[2]]}
|
122
|
+
| value
|
123
|
+
{result = [val[0]]}
|
124
|
+
|
125
|
+
value : string_escaped_special_chars
|
126
|
+
{result = val[0]}
|
127
|
+
|
128
|
+
value_no_space: string_escaped_special_chars_excluding_space
|
129
|
+
|
130
|
+
escaped_special_chars : CIRCUMFLEX CIRCUMFLEX
|
131
|
+
{result = val[1]}
|
132
|
+
| CIRCUMFLEX square_brackets
|
133
|
+
{result = val[1]}
|
134
|
+
| CIRCUMFLEX parentheses
|
135
|
+
{result = val[1]}
|
136
|
+
| CIRCUMFLEX COMMA
|
137
|
+
{result = val[1]}
|
138
|
+
| CIRCUMFLEX SEMICOLON
|
139
|
+
{result = val[1]}
|
140
|
+
| CIRCUMFLEX EQUAL
|
141
|
+
{result = val[1]}
|
142
|
+
|
143
|
+
character_escaped_special : character_excluding_special_chars
|
144
|
+
| escaped_special_chars
|
145
|
+
|
146
|
+
string_escaped_special_chars : string_escaped_special_chars character_escaped_special
|
147
|
+
{result = val.join}
|
148
|
+
| character_escaped_special
|
149
|
+
{result = val[0]}
|
150
|
+
|
151
|
+
string_escaped_special_chars_excluding_space : string_escaped_special_chars_excluding_space character_escaped_special_excluding_space
|
152
|
+
| character_escaped_special_excluding_space
|
153
|
+
|
154
|
+
character_escaped_special_excluding_space : character_excluding_special_chars_and_space
|
155
|
+
| escaped_special_chars
|
156
|
+
|
157
|
+
digit : ZERO
|
158
|
+
| DIGIT_NON_ZERO
|
159
|
+
|
160
|
+
square_brackets : opening_square_bracket
|
161
|
+
| closing_square_bracket
|
162
|
+
|
163
|
+
opening_square_bracket : OPENING_SQUARE_BRACKET
|
164
|
+
|
165
|
+
closing_square_bracket : CLOSING_SQUARE_BRACKET
|
166
|
+
|
167
|
+
parentheses : OPENING_PARENTHESIS
|
168
|
+
| CLOSING_PARENTHESIS
|
169
|
+
|
170
|
+
character_excluding_special_chars : character_excluding_special_chars_and_space
|
171
|
+
| SPACE
|
172
|
+
|
173
|
+
character_excluding_special_chars_and_space : character_excluding_special_chars_and_space_and_dot_and_colon_and_tilde_and_atmark_and_solidus_and_exclamation_mark
|
174
|
+
| DOT
|
175
|
+
| COLON
|
176
|
+
| TILDE
|
177
|
+
| ATMARK
|
178
|
+
| SOLIDUS
|
179
|
+
| EXCLAMATION_MARK
|
180
|
+
|
181
|
+
character_excluding_special_chars_and_space_and_dot_and_colon_and_tilde_and_atmark_and_solidus_and_exclamation_mark : UNICODE_CHARACTER_EXCLUDING_SPECIAL_CHARS_AND_SPACE_AND_DOT_AND_COLON_AND_TILDE_AND_ATMARK_AND_SOLIDUS_AND_EXCLAMATION_MARK
|
182
|
+
| digit
|
183
|
+
|
184
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#
|
2
|
+
# Copyright (c) 2017 KITAITI Makoto (KitaitiMakoto at gmail.com)
|
3
|
+
#
|
4
|
+
# EPUB CFI is free software: you can redistribute it and/or modify
|
5
|
+
# it under the terms of the GNU Lesser General Public License as published
|
6
|
+
# by the Free Software Foundation, either version 3 of the License, or
|
7
|
+
# (at your option) any later version.
|
8
|
+
#
|
9
|
+
# EPUB CFI is distributed in the hope that it will be useful,
|
10
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
# GNU Lesser General Public License for more details.
|
13
|
+
#
|
14
|
+
# You should have received a copy of the GNU Lesser General Public License
|
15
|
+
# along with EPUB CFI. If not, see <http://www.gnu.org/licenses/>.
|
16
|
+
#
|
17
|
+
|
18
|
+
module EPUB
|
19
|
+
module CFI
|
20
|
+
# epub-cfi version
|
21
|
+
VERSION = "0.1.0"
|
22
|
+
end
|
23
|
+
end
|
data/test/helper.rb
ADDED
data/test/test_cfi.rb
ADDED
@@ -0,0 +1,196 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'helper'
|
3
|
+
require 'epub/cfi'
|
4
|
+
|
5
|
+
class TestCFI < Test::Unit::TestCase
|
6
|
+
def test_escape
|
7
|
+
assert_equal '^^^[^]^(^)^,^;^=', EPUB::CFI.escape('^[](),;=')
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_unescape
|
11
|
+
assert_equal '^[](),;=', EPUB::CFI.unescape('^^^[^]^(^)^,^;^=')
|
12
|
+
end
|
13
|
+
|
14
|
+
class TestPath < self
|
15
|
+
data([
|
16
|
+
'epubcfi(/6/14[chap05ref]!/4[body01]/10/2/1:3[2^[1^]])',
|
17
|
+
'epubcfi(/6/4!/4/10/2/1:3[Ф-"spa ce"-99%-aa^[bb^]^^])',
|
18
|
+
'epubcfi(/6/4!/4/10/2/1:3[Ф-"spa%20ce"-99%25-aa^[bb^]^^])',
|
19
|
+
'epubcfi(/6/4!/4/10/2/1:3[%d0%a4-"spa%20ce"-99%25-aa^[bb^]^^])',
|
20
|
+
'epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3[yyy])',
|
21
|
+
'epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/1:3[xx,y])',
|
22
|
+
'epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3[,y])',
|
23
|
+
'epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3[;s=b])',
|
24
|
+
'epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3[yyy;s=b])',
|
25
|
+
'epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2[;s=b])',
|
26
|
+
'epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/3:10)',
|
27
|
+
'epubcfi(/6/4[chap01ref]!/4[body01]/16[svgimg])',
|
28
|
+
'epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/1:0)',
|
29
|
+
'epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2/1:0)',
|
30
|
+
'epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3)',
|
31
|
+
].reduce({}) {|data, cfi|
|
32
|
+
data[cfi] = cfi
|
33
|
+
data
|
34
|
+
})
|
35
|
+
def test_to_s(cfi)
|
36
|
+
assert_equal cfi, EPUB::CFI::Parser.parse(cfi).to_s
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_compare
|
40
|
+
assert_equal -1, epubcfi('/6/4[id]') <=> epubcfi('/6/5')
|
41
|
+
assert_equal 0, epubcfi('/6/4') <=> epubcfi('/6/4')
|
42
|
+
assert_equal 1, epubcfi('/6/4') <=> epubcfi('/4/6')
|
43
|
+
assert_equal 1, epubcfi('/6/4!/4@3:7') <=> epubcfi('/6/4!/4')
|
44
|
+
assert_equal 1,
|
45
|
+
epubcfi('/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3[yyy]') <=>
|
46
|
+
epubcfi('/6/4[chap01ref]!/4[body01]/10[para05]/1:3[xx,y]')
|
47
|
+
assert_nil epubcfi('/6/4[chap01ref]!/4[body01]/10[para05]/3:10') <=>
|
48
|
+
epubcfi('/6/4[chap01ref]!/4[body01]/10[para05]/3!:10')
|
49
|
+
assert_equal 1, epubcfi('/6/4') <=> epubcfi('/6')
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class TestRange < self
|
54
|
+
def test_attributes
|
55
|
+
parent = epubcfi('/6/4[chap01ref]!/4[body01]/10[para05]')
|
56
|
+
first = epubcfi('/6/4[chap01ref]!/4[body01]/10[para05]/2/1:1')
|
57
|
+
last = epubcfi('/6/4[chap01ref]!/4[body01]/10[para05]/3:4')
|
58
|
+
range = epubcfi('/6/4[chap01ref]!/4[body01]/10[para05],/2/1:1,/3:4')
|
59
|
+
assert_equal 0, first <=> range.first
|
60
|
+
assert_equal 0, last <=> range.last
|
61
|
+
|
62
|
+
assert_equal 0, parent <=> range.parent_path
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_to_s
|
66
|
+
cfi = '/6/4[chap01ref]!/4[body01]/10[para05],/2/1:1,/3:4'
|
67
|
+
assert_equal 'epubcfi(' + cfi + ')', epubcfi('/6/4[chap01ref]!/4[body01]/10[para05],/2/1:1,/3:4').to_s
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_cover
|
71
|
+
assert_true epubcfi('/6/4[chap01ref]!/4[body01]/10[para05],/2/1:1,/3:4').cover? epubcfi('/6/4[chap01ref]!/4[body01]/10[para05]/2/2/4')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
class TestStep < self
|
76
|
+
def test_to_s
|
77
|
+
assert_equal '/6', EPUB::CFI::Step.new(6).to_s
|
78
|
+
assert_equal '/4[id]', EPUB::CFI::Step.new(4, EPUB::CFI::IDAssertion.new('id')).to_s
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_compare
|
82
|
+
assert_equal 0, EPUB::CFI::Step.new(6) <=> EPUB::CFI::Step.new(6, 'assertion')
|
83
|
+
assert_equal -1, EPUB::CFI::Step.new(6) <=> EPUB::CFI::Step.new(7)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class TestIDAssertion < self
|
88
|
+
def test_to_s
|
89
|
+
assert_equal '[id]', EPUB::CFI::IDAssertion.new('id').to_s
|
90
|
+
assert_equal '[id;p=a]', EPUB::CFI::IDAssertion.new('id', {'p' => ['a']}).to_s
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class TestTextLocationAssertion < self
|
95
|
+
def test_to_s
|
96
|
+
assert_equal '[yyy]', EPUB::CFI::TextLocationAssertion.new('yyy').to_s
|
97
|
+
assert_equal '[xx,y]', EPUB::CFI::TextLocationAssertion.new('xx', 'y').to_s
|
98
|
+
assert_equal '[,y]', EPUB::CFI::TextLocationAssertion.new(nil, 'y').to_s
|
99
|
+
assert_equal '[;s=b]', EPUB::CFI::TextLocationAssertion.new(nil, nil, {'s' => ['b']}).to_s
|
100
|
+
assert_equal '[yyy;s=b]', EPUB::CFI::TextLocationAssertion.new('yyy', nil, {'s' => ['b']}).to_s
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class TestCharacterOffset < self
|
105
|
+
def test_to_s
|
106
|
+
assert_equal ':1', EPUB::CFI::CharacterOffset.new(1).to_s
|
107
|
+
assert_equal ':2[yyy]', EPUB::CFI::CharacterOffset.new(2, EPUB::CFI::TextLocationAssertion.new('yyy')).to_s
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_compare
|
111
|
+
assert_equal 0,
|
112
|
+
EPUB::CFI::CharacterOffset.new(3) <=>
|
113
|
+
EPUB::CFI::CharacterOffset.new(3, EPUB::CFI::TextLocationAssertion.new('yyy'))
|
114
|
+
assert_equal -1,
|
115
|
+
EPUB::CFI::CharacterOffset.new(4) <=>
|
116
|
+
EPUB::CFI::CharacterOffset.new(5)
|
117
|
+
assert_equal 1,
|
118
|
+
EPUB::CFI::CharacterOffset.new(4, EPUB::CFI::TextLocationAssertion.new(nil, 'xx')) <=>
|
119
|
+
EPUB::CFI::CharacterOffset.new(2)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class TestSpatialOffset < self
|
124
|
+
def test_to_s
|
125
|
+
assert_equal '@0.5:30.2', EPUB::CFI::TemporalSpatialOffset.new(nil, 0.5, 30.2).to_s
|
126
|
+
assert_equal '@0:100', EPUB::CFI::TemporalSpatialOffset.new(nil, 0, 100).to_s
|
127
|
+
assert_equal '@50:50.0', EPUB::CFI::TemporalSpatialOffset.new(nil, 50, 50.0).to_s
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_compare
|
131
|
+
assert_equal 0,
|
132
|
+
EPUB::CFI::TemporalSpatialOffset.new(nil, 30, 40) <=>
|
133
|
+
EPUB::CFI::TemporalSpatialOffset.new(nil, 30, 40)
|
134
|
+
assert_equal 1,
|
135
|
+
EPUB::CFI::TemporalSpatialOffset.new(nil, 30, 40) <=>
|
136
|
+
EPUB::CFI::TemporalSpatialOffset.new(nil, 40, 30)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class TestTemporalOffset < self
|
141
|
+
def test_to_s
|
142
|
+
assert_equal '~23.5', EPUB::CFI::TemporalSpatialOffset.new(23.5).to_s
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_compare
|
146
|
+
assert_equal 0,
|
147
|
+
EPUB::CFI::TemporalSpatialOffset.new(23.5) <=>
|
148
|
+
EPUB::CFI::TemporalSpatialOffset.new(23.5)
|
149
|
+
assert_equal -1,
|
150
|
+
EPUB::CFI::TemporalSpatialOffset.new(23) <=>
|
151
|
+
EPUB::CFI::TemporalSpatialOffset.new(23.5)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
class TestTemporalSpatialOffset < self
|
156
|
+
def test_to_s
|
157
|
+
assert_equal '~23.5@50:30.0', EPUB::CFI::TemporalSpatialOffset.new(23.5, 50, 30.0).to_s
|
158
|
+
end
|
159
|
+
|
160
|
+
def test_compare
|
161
|
+
assert_equal 0,
|
162
|
+
EPUB::CFI::TemporalSpatialOffset.new(23.5, 30, 40) <=>
|
163
|
+
EPUB::CFI::TemporalSpatialOffset.new(23.5, 30, 40.0)
|
164
|
+
assert_equal 1,
|
165
|
+
EPUB::CFI::TemporalSpatialOffset.new(23.5, 30, 40) <=>
|
166
|
+
EPUB::CFI::TemporalSpatialOffset.new(23.5)
|
167
|
+
assert_equal -1,
|
168
|
+
EPUB::CFI::TemporalSpatialOffset.new(nil, 30, 40) <=>
|
169
|
+
EPUB::CFI::TemporalSpatialOffset.new(23.5, 30, 40)
|
170
|
+
assert_equal -1,
|
171
|
+
EPUB::CFI::TemporalSpatialOffset.new(23.5, 30, 40) <=>
|
172
|
+
EPUB::CFI::TemporalSpatialOffset.new(23.5, 30, 50)
|
173
|
+
assert_equal 1,
|
174
|
+
EPUB::CFI::TemporalSpatialOffset.new(24, 30, 40) <=>
|
175
|
+
EPUB::CFI::TemporalSpatialOffset.new(23.5, 100, 100)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def epubcfi(string)
|
182
|
+
EPUB::CFI::Parser.new.parse(string)
|
183
|
+
end
|
184
|
+
|
185
|
+
def assert_equal_node(expected, actual, message='')
|
186
|
+
diff = AssertionMessage.delayed_diff(expected.to_s, actual.to_s)
|
187
|
+
message = build_message(message, <<EOT, expected, actual, diff)
|
188
|
+
<?>
|
189
|
+
expected but was
|
190
|
+
<?>.?
|
191
|
+
EOT
|
192
|
+
assert_block message do
|
193
|
+
expected.tdiff_equal actual
|
194
|
+
end
|
195
|
+
end
|
196
|
+
end
|