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
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d8aca7ef76271077d0c4026b20d05df02926a9ca
|
4
|
+
data.tar.gz: 14dabd7b660a4047585ffa63cb2ac31bd109125f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8eb1f1a5a5a5ba3e6fca187ffcadad7b796bc0ea680b5a2ef5b2659a4c5ed95986b57593c9622a0450033d084e00cf3ef6bef36c9e73ea15cf2b80ab4a011502
|
7
|
+
data.tar.gz: 213f294bb142f9ef4dd5402d0d3d8a702cdca7156d0cd7b298a0d0f512b1ac239593f9908b12d1b9235fc5b24205237c557308b81d8c702726c382104814b9d0
|
data/.document
ADDED
data/.gitignore
ADDED
data/.gitlab-ci.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
before_script:
|
2
|
+
- ruby -v
|
3
|
+
- which ruby
|
4
|
+
- gem install racc rake rubygems-tasks yard test-unit test-unit-notify pretty_backtrace simplecov
|
5
|
+
|
6
|
+
test:2.2:
|
7
|
+
image: ruby:2.2
|
8
|
+
script:
|
9
|
+
rake test
|
10
|
+
|
11
|
+
test:2.3:
|
12
|
+
image: ruby:2.3
|
13
|
+
script:
|
14
|
+
rake test
|
15
|
+
|
16
|
+
test:2.4:
|
17
|
+
image: ruby:2.4
|
18
|
+
script:
|
19
|
+
rake test
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--markup markdown --title "EPUB CFI Documentation" --protected
|
data/COPYING.txt
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
GNU LESSER GENERAL PUBLIC LICENSE
|
2
|
+
Version 3, 29 June 2007
|
3
|
+
|
4
|
+
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
|
5
|
+
Everyone is permitted to copy and distribute verbatim copies
|
6
|
+
of this license document, but changing it is not allowed.
|
7
|
+
|
8
|
+
|
9
|
+
This version of the GNU Lesser General Public License incorporates
|
10
|
+
the terms and conditions of version 3 of the GNU General Public
|
11
|
+
License, supplemented by the additional permissions listed below.
|
12
|
+
|
13
|
+
0. Additional Definitions.
|
14
|
+
|
15
|
+
As used herein, "this License" refers to version 3 of the GNU Lesser
|
16
|
+
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
17
|
+
General Public License.
|
18
|
+
|
19
|
+
"The Library" refers to a covered work governed by this License,
|
20
|
+
other than an Application or a Combined Work as defined below.
|
21
|
+
|
22
|
+
An "Application" is any work that makes use of an interface provided
|
23
|
+
by the Library, but which is not otherwise based on the Library.
|
24
|
+
Defining a subclass of a class defined by the Library is deemed a mode
|
25
|
+
of using an interface provided by the Library.
|
26
|
+
|
27
|
+
A "Combined Work" is a work produced by combining or linking an
|
28
|
+
Application with the Library. The particular version of the Library
|
29
|
+
with which the Combined Work was made is also called the "Linked
|
30
|
+
Version".
|
31
|
+
|
32
|
+
The "Minimal Corresponding Source" for a Combined Work means the
|
33
|
+
Corresponding Source for the Combined Work, excluding any source code
|
34
|
+
for portions of the Combined Work that, considered in isolation, are
|
35
|
+
based on the Application, and not on the Linked Version.
|
36
|
+
|
37
|
+
The "Corresponding Application Code" for a Combined Work means the
|
38
|
+
object code and/or source code for the Application, including any data
|
39
|
+
and utility programs needed for reproducing the Combined Work from the
|
40
|
+
Application, but excluding the System Libraries of the Combined Work.
|
41
|
+
|
42
|
+
1. Exception to Section 3 of the GNU GPL.
|
43
|
+
|
44
|
+
You may convey a covered work under sections 3 and 4 of this License
|
45
|
+
without being bound by section 3 of the GNU GPL.
|
46
|
+
|
47
|
+
2. Conveying Modified Versions.
|
48
|
+
|
49
|
+
If you modify a copy of the Library, and, in your modifications, a
|
50
|
+
facility refers to a function or data to be supplied by an Application
|
51
|
+
that uses the facility (other than as an argument passed when the
|
52
|
+
facility is invoked), then you may convey a copy of the modified
|
53
|
+
version:
|
54
|
+
|
55
|
+
a) under this License, provided that you make a good faith effort to
|
56
|
+
ensure that, in the event an Application does not supply the
|
57
|
+
function or data, the facility still operates, and performs
|
58
|
+
whatever part of its purpose remains meaningful, or
|
59
|
+
|
60
|
+
b) under the GNU GPL, with none of the additional permissions of
|
61
|
+
this License applicable to that copy.
|
62
|
+
|
63
|
+
3. Object Code Incorporating Material from Library Header Files.
|
64
|
+
|
65
|
+
The object code form of an Application may incorporate material from
|
66
|
+
a header file that is part of the Library. You may convey such object
|
67
|
+
code under terms of your choice, provided that, if the incorporated
|
68
|
+
material is not limited to numerical parameters, data structure
|
69
|
+
layouts and accessors, or small macros, inline functions and templates
|
70
|
+
(ten or fewer lines in length), you do both of the following:
|
71
|
+
|
72
|
+
a) Give prominent notice with each copy of the object code that the
|
73
|
+
Library is used in it and that the Library and its use are
|
74
|
+
covered by this License.
|
75
|
+
|
76
|
+
b) Accompany the object code with a copy of the GNU GPL and this license
|
77
|
+
document.
|
78
|
+
|
79
|
+
4. Combined Works.
|
80
|
+
|
81
|
+
You may convey a Combined Work under terms of your choice that,
|
82
|
+
taken together, effectively do not restrict modification of the
|
83
|
+
portions of the Library contained in the Combined Work and reverse
|
84
|
+
engineering for debugging such modifications, if you also do each of
|
85
|
+
the following:
|
86
|
+
|
87
|
+
a) Give prominent notice with each copy of the Combined Work that
|
88
|
+
the Library is used in it and that the Library and its use are
|
89
|
+
covered by this License.
|
90
|
+
|
91
|
+
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
92
|
+
document.
|
93
|
+
|
94
|
+
c) For a Combined Work that displays copyright notices during
|
95
|
+
execution, include the copyright notice for the Library among
|
96
|
+
these notices, as well as a reference directing the user to the
|
97
|
+
copies of the GNU GPL and this license document.
|
98
|
+
|
99
|
+
d) Do one of the following:
|
100
|
+
|
101
|
+
0) Convey the Minimal Corresponding Source under the terms of this
|
102
|
+
License, and the Corresponding Application Code in a form
|
103
|
+
suitable for, and under terms that permit, the user to
|
104
|
+
recombine or relink the Application with a modified version of
|
105
|
+
the Linked Version to produce a modified Combined Work, in the
|
106
|
+
manner specified by section 6 of the GNU GPL for conveying
|
107
|
+
Corresponding Source.
|
108
|
+
|
109
|
+
1) Use a suitable shared library mechanism for linking with the
|
110
|
+
Library. A suitable mechanism is one that (a) uses at run time
|
111
|
+
a copy of the Library already present on the user's computer
|
112
|
+
system, and (b) will operate properly with a modified version
|
113
|
+
of the Library that is interface-compatible with the Linked
|
114
|
+
Version.
|
115
|
+
|
116
|
+
e) Provide Installation Information, but only if you would otherwise
|
117
|
+
be required to provide such information under section 6 of the
|
118
|
+
GNU GPL, and only to the extent that such information is
|
119
|
+
necessary to install and execute a modified version of the
|
120
|
+
Combined Work produced by recombining or relinking the
|
121
|
+
Application with a modified version of the Linked Version. (If
|
122
|
+
you use option 4d0, the Installation Information must accompany
|
123
|
+
the Minimal Corresponding Source and Corresponding Application
|
124
|
+
Code. If you use option 4d1, you must provide the Installation
|
125
|
+
Information in the manner specified by section 6 of the GNU GPL
|
126
|
+
for conveying Corresponding Source.)
|
127
|
+
|
128
|
+
5. Combined Libraries.
|
129
|
+
|
130
|
+
You may place library facilities that are a work based on the
|
131
|
+
Library side by side in a single library together with other library
|
132
|
+
facilities that are not Applications and are not covered by this
|
133
|
+
License, and convey such a combined library under terms of your
|
134
|
+
choice, if you do both of the following:
|
135
|
+
|
136
|
+
a) Accompany the combined library with a copy of the same work based
|
137
|
+
on the Library, uncombined with any other library facilities,
|
138
|
+
conveyed under the terms of this License.
|
139
|
+
|
140
|
+
b) Give prominent notice with the combined library that part of it
|
141
|
+
is a work based on the Library, and explaining where to find the
|
142
|
+
accompanying uncombined form of the same work.
|
143
|
+
|
144
|
+
6. Revised Versions of the GNU Lesser General Public License.
|
145
|
+
|
146
|
+
The Free Software Foundation may publish revised and/or new versions
|
147
|
+
of the GNU Lesser General Public License from time to time. Such new
|
148
|
+
versions will be similar in spirit to the present version, but may
|
149
|
+
differ in detail to address new problems or concerns.
|
150
|
+
|
151
|
+
Each version is given a distinguishing version number. If the
|
152
|
+
Library as you received it specifies that a certain numbered version
|
153
|
+
of the GNU Lesser General Public License "or any later version"
|
154
|
+
applies to it, you have the option of following the terms and
|
155
|
+
conditions either of that published version or of any later version
|
156
|
+
published by the Free Software Foundation. If the Library as you
|
157
|
+
received it does not specify a version number of the GNU Lesser
|
158
|
+
General Public License, you may choose any version of the GNU Lesser
|
159
|
+
General Public License ever published by the Free Software Foundation.
|
160
|
+
|
161
|
+
If the Library as you received it specifies that a proxy can decide
|
162
|
+
whether future versions of the GNU Lesser General Public License shall
|
163
|
+
apply, that proxy's public statement of acceptance of any version is
|
164
|
+
permanent authorization for you to choose that version for the
|
165
|
+
Library.
|
data/ChangeLog.md
ADDED
data/README.md
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
EPUB CFI
|
2
|
+
========
|
3
|
+
|
4
|
+
* [Homepage](https://gitlab.com/KitaitiMakoto/epub-cfi)
|
5
|
+
* [Documentation](http://rubydoc.info/gems/epub-cfi/frames)
|
6
|
+
* [Email](mailto:KitaitiMakoto at gmail.com)
|
7
|
+
|
8
|
+
Description
|
9
|
+
-----------
|
10
|
+
|
11
|
+
Parser and builder implementation for EPUB CFI defined at http://www.idpf.org/epub/linking/cfi/
|
12
|
+
|
13
|
+
Extracted from [EPUB Parser][].
|
14
|
+
|
15
|
+
[EPUB Parser]: http://www.rubydoc.info/gems/epub-parser/file/docs/Home.markdown
|
16
|
+
|
17
|
+
Features
|
18
|
+
--------
|
19
|
+
|
20
|
+
* Parses EPUB CFI string.
|
21
|
+
* Builds EPUB CFI(implemented but currently useless).
|
22
|
+
* Converts EPUB CFI object to a string.
|
23
|
+
|
24
|
+
Examples
|
25
|
+
--------
|
26
|
+
|
27
|
+
require 'epub/cfi'
|
28
|
+
|
29
|
+
location = EPUB::CFI.parse("epubcfi(/6/14[chap05ref]!/4[body01]/10/2/1:3[2^[1^]])")
|
30
|
+
# Or location = EPUB::CFI("epubcfi(/6/14[chap05ref]!/4[body01]/10/2/1:3[2^[1^]])")
|
31
|
+
# => #<EPUB::CFI::Location:/6/14[chap05ref]!/4[body01]/10/2/1:3[2^[1^]]>
|
32
|
+
location.to_s # => "epubcfi(/6/14[chap05ref]!/4[body01]/10/2/1:3[2^[1^]])"
|
33
|
+
|
34
|
+
location1 = EPUB::CFI("/6/4[chap01ref]!/4[body01]/10[para05]/1:3[xx,y]")
|
35
|
+
location2 = EPUB::CFI("/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3[yyy]")
|
36
|
+
location1 < location2 # => true
|
37
|
+
location1 <=> location1 # => -1
|
38
|
+
# location2 appears earlier than location1
|
39
|
+
|
40
|
+
range = EPUB::CFI("/6/4[chap01ref]!/4[body01]/10[para05],/2/1:1,/3:4")
|
41
|
+
# => #<EPUB::CFI::Location:/6/4[chap01ref]!/4[body01]/10[para05]/2/1:1>..#<EPUB::CFI::Location:/6/4[chap01ref]!/4[body01]/10[para05]/3:4>
|
42
|
+
range.kind_of? ::Range # => true
|
43
|
+
range.begin
|
44
|
+
# => #<EPUB::CFI::Location:/6/4[chap01ref]!/4[body01]/10[para05]/2/1:1>
|
45
|
+
range.end
|
46
|
+
# => #<EPUB::CFI::Location:/6/4[chap01ref]!/4[body01]/10[para05]/3:4>
|
47
|
+
range.start_subpath
|
48
|
+
# => "/2/1:1"
|
49
|
+
range.end_subpath
|
50
|
+
# => "/3:4"
|
51
|
+
range.parent_path
|
52
|
+
# => #<EPUB::CFI::Location:/6/4[chap01ref]!/4[body01]/10[para05]>
|
53
|
+
range.to_s # => "epubcfi(/6/4[chap01ref]!/4[body01]/10[para05],/2/1:1,/3:4)"
|
54
|
+
|
55
|
+
builtin_range = location2..location1
|
56
|
+
# => #<EPUB::CFI::Location:/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3[yyy]>..#<EPUB::CFI::Location:/6/4[chap01ref]!/4[body01]/10[para05]/1:3[xx,y]>
|
57
|
+
builtin_range.to_s
|
58
|
+
# => "epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3[yyy])..epubcfi(/6/4[chap01ref]!/4[body01]/10[para05]/1:3[xx,y])"
|
59
|
+
builtin_range.kind_of? ::Range # => true
|
60
|
+
builtin_range.kind_of? EPUB::CFI::Range # => false
|
61
|
+
cfi_range = EPUB::CFI::Range.new(location2, location1)
|
62
|
+
# => #<EPUB::CFI::Location:/6/4[chap01ref]!/4[body01]/10[para05]/2/1:3[yyy]>..#<EPUB::CFI::Location:/6/4[chap01ref]!/4[body01]/10[para05]/1:3[xx,y]>
|
63
|
+
cfi_range.kind_of? ::Range # => true
|
64
|
+
cfi_range.kind_of? EPUB::CFI::Range # => true
|
65
|
+
|
66
|
+
Install
|
67
|
+
-------
|
68
|
+
|
69
|
+
$ gem install epub-cfi
|
70
|
+
|
71
|
+
See also
|
72
|
+
--------
|
73
|
+
|
74
|
+
* [EPUB Canonical Fragment Identifiers 1.1][spec]
|
75
|
+
* [EPUB Parser][]
|
76
|
+
|
77
|
+
[spec]: http://www.idpf.org/epub/linking/cfi/
|
78
|
+
[EPUB Parser]: http://www.rubydoc.info/gems/epub-parser/file/docs/Home.markdown
|
79
|
+
|
80
|
+
Copyright
|
81
|
+
---------
|
82
|
+
|
83
|
+
Copyright (c) 2017 KITAITI Makoto
|
84
|
+
|
85
|
+
See {file:COPYING.txt} for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
task :default => "lib/epub/cfi/parser.tab.rb"
|
5
|
+
|
6
|
+
file "lib/epub/cfi/parser.tab.rb" do |target|
|
7
|
+
sh "racc #{target.name.sub("tab.rb", "y")}"
|
8
|
+
end
|
9
|
+
|
10
|
+
require "rake/clean"
|
11
|
+
CLEAN.include "lib/epub/cfi/parser.tab.rb"
|
12
|
+
|
13
|
+
require 'rubygems/tasks'
|
14
|
+
Gem::Tasks.new
|
15
|
+
task :build => [:clean, "lib/epub/cfi/parser.tab.rb"]
|
16
|
+
|
17
|
+
require 'rake/testtask'
|
18
|
+
Rake::TestTask.new do |test|
|
19
|
+
test.libs << 'test'
|
20
|
+
test.pattern = 'test/**/test_*.rb'
|
21
|
+
test.verbose = true
|
22
|
+
end
|
23
|
+
task :test => "lib/epub/cfi/parser.tab.rb"
|
24
|
+
|
25
|
+
require 'yard'
|
26
|
+
YARD::Rake::YardocTask.new
|
27
|
+
task :doc => :yard
|
data/epub-cfi.gemspec
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'epub/cfi/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |gem|
|
8
|
+
gem.name = "epub-cfi"
|
9
|
+
gem.version = EPUB::CFI::VERSION
|
10
|
+
gem.summary = %q{EPUB CFI parser and builder}
|
11
|
+
gem.description = %q{Parser and builder implementation for EPUB CFI defined at http://www.idpf.org/epub/linking/cfi/}
|
12
|
+
gem.license = "LGPL"
|
13
|
+
gem.authors = ["KITAITI Makoto"]
|
14
|
+
gem.email = "KitaitiMakoto@gmail.com"
|
15
|
+
gem.homepage = "https://gitlab.com/KitaitiMakoto/epub-cfi"
|
16
|
+
|
17
|
+
gem.files = `git ls-files`.split($/) << "lib/epub/cfi/parser.tab.rb"
|
18
|
+
|
19
|
+
`git submodule --quiet foreach --recursive pwd`.split($/).each do |submodule|
|
20
|
+
submodule.sub!("#{Dir.pwd}/",'')
|
21
|
+
|
22
|
+
Dir.chdir(submodule) do
|
23
|
+
`git ls-files`.split($/).map do |subpath|
|
24
|
+
gem.files << File.join(submodule,subpath)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
29
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
30
|
+
gem.require_paths = ['lib']
|
31
|
+
|
32
|
+
gem.add_runtime_dependency "racc"
|
33
|
+
|
34
|
+
gem.add_development_dependency 'rake'
|
35
|
+
gem.add_development_dependency 'rubygems-tasks'
|
36
|
+
gem.add_development_dependency 'yard'
|
37
|
+
gem.add_development_dependency "test-unit"
|
38
|
+
gem.add_development_dependency "test-unit-notify"
|
39
|
+
gem.add_development_dependency "pretty_backtrace"
|
40
|
+
gem.add_development_dependency "simplecov"
|
41
|
+
end
|
data/lib/epub/cfi.rb
ADDED
@@ -0,0 +1,309 @@
|
|
1
|
+
require 'epub/cfi/version'
|
2
|
+
|
3
|
+
module EPUB
|
4
|
+
module CFI
|
5
|
+
SPECIAL_CHARS = '^[](),;=' # "5E", "5B", "5D", "28", "29", "2C", "3B", "3D"
|
6
|
+
RE_ESCAPED_SPECIAL_CHARS = Regexp.escape(SPECIAL_CHARS)
|
7
|
+
|
8
|
+
class << self
|
9
|
+
def escape(string)
|
10
|
+
string.gsub(/([#{RE_ESCAPED_SPECIAL_CHARS}])/o, '^\1')
|
11
|
+
end
|
12
|
+
|
13
|
+
def unescape(string)
|
14
|
+
string.gsub(/\^([#{RE_ESCAPED_SPECIAL_CHARS}])/o, '\1')
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Location
|
19
|
+
include Comparable
|
20
|
+
|
21
|
+
attr_reader :paths
|
22
|
+
|
23
|
+
def initialize(paths=[])
|
24
|
+
@paths = paths
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize_copy(original)
|
28
|
+
@paths = original.paths.collect(&:dup)
|
29
|
+
end
|
30
|
+
|
31
|
+
def <=>(other)
|
32
|
+
index = 0
|
33
|
+
other_paths = other.paths
|
34
|
+
cmp = nil
|
35
|
+
paths.each do |path|
|
36
|
+
other_path = other_paths[index]
|
37
|
+
return 1 unless other_path
|
38
|
+
cmp = path <=> other_path
|
39
|
+
break unless cmp == 0
|
40
|
+
index += 1
|
41
|
+
end
|
42
|
+
|
43
|
+
unless cmp == 0
|
44
|
+
if cmp == 1 and paths[index].offset and other_paths[index + 1]
|
45
|
+
return nil
|
46
|
+
else
|
47
|
+
return cmp
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
return nil if paths.last.offset && other_paths[index]
|
52
|
+
|
53
|
+
return -1 if other_paths[index]
|
54
|
+
|
55
|
+
0
|
56
|
+
end
|
57
|
+
|
58
|
+
def path_string
|
59
|
+
paths.join('!')
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_s
|
63
|
+
"epubcfi(#{path_string})"
|
64
|
+
end
|
65
|
+
|
66
|
+
def inspect
|
67
|
+
"#<#{self.class}:#{path_string}>"
|
68
|
+
end
|
69
|
+
|
70
|
+
def join(*other_paths)
|
71
|
+
new_paths = paths.dup
|
72
|
+
other_paths.each do |path|
|
73
|
+
new_paths << path
|
74
|
+
end
|
75
|
+
self.class.new(new_paths)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Path
|
80
|
+
attr_reader :steps, :offset
|
81
|
+
|
82
|
+
def initialize(steps=[], offset=nil)
|
83
|
+
@steps, @offset = steps, offset
|
84
|
+
end
|
85
|
+
|
86
|
+
def initialize_copy(original)
|
87
|
+
@steps = original.steps.collect(&:dup)
|
88
|
+
@offset = original.offset.dup if original.offset
|
89
|
+
end
|
90
|
+
|
91
|
+
def to_s
|
92
|
+
@string_cache ||= (steps.join + offset.to_s)
|
93
|
+
end
|
94
|
+
|
95
|
+
def <=>(other)
|
96
|
+
other_steps = other.steps
|
97
|
+
index = 0
|
98
|
+
steps.each do |step|
|
99
|
+
other_step = other_steps[index]
|
100
|
+
return 1 unless other_step
|
101
|
+
cmp = step <=> other_step
|
102
|
+
return cmp unless cmp == 0
|
103
|
+
index += 1
|
104
|
+
end
|
105
|
+
|
106
|
+
return -1 if other_steps[index]
|
107
|
+
|
108
|
+
other_offset = other.offset
|
109
|
+
if offset
|
110
|
+
if other_offset
|
111
|
+
offset <=> other_offset
|
112
|
+
else
|
113
|
+
1
|
114
|
+
end
|
115
|
+
else
|
116
|
+
if other_offset
|
117
|
+
-1
|
118
|
+
else
|
119
|
+
0
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def each_step_with_instruction
|
125
|
+
yield [step, nil]
|
126
|
+
local_path.each_step_with_instruction do |s, instruction|
|
127
|
+
yield [s, instruction]
|
128
|
+
end
|
129
|
+
self
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class Range < ::Range
|
134
|
+
attr_accessor :parent_path, :start_subpath, :end_subpath
|
135
|
+
|
136
|
+
# @todo consider the case subpaths are redirected path
|
137
|
+
# @todo FIXME: too dirty
|
138
|
+
class << self
|
139
|
+
def from_parent_and_start_and_end(parent_path, start_subpath, end_subpath)
|
140
|
+
start_str = start_subpath.join
|
141
|
+
end_str = end_subpath.join
|
142
|
+
|
143
|
+
first_paths = parent_path.collect(&:dup)
|
144
|
+
if start_subpath
|
145
|
+
offset_of_first = start_subpath.last.offset
|
146
|
+
offset_of_first = offset_of_first.dup if offset_of_first
|
147
|
+
last_of_first_paths = first_paths.pop
|
148
|
+
first_paths << last_of_first_paths
|
149
|
+
last_of_first_paths.steps.concat start_subpath.shift.steps
|
150
|
+
first_paths.concat start_subpath
|
151
|
+
first_paths.last.instance_variable_set :@offset, offset_of_first
|
152
|
+
end
|
153
|
+
offset_of_last = end_subpath.last.offset
|
154
|
+
offset_of_last = offset_of_last.dup if offset_of_last
|
155
|
+
last_paths = parent_path.collect(&:dup)
|
156
|
+
last_of_last_paths = last_paths.pop
|
157
|
+
last_paths << last_of_last_paths
|
158
|
+
last_of_last_paths.steps.concat end_subpath.shift.steps
|
159
|
+
last_paths.concat end_subpath
|
160
|
+
last_paths.last.instance_variable_set :@offset, offset_of_last
|
161
|
+
|
162
|
+
first = CFI::Location.new(first_paths)
|
163
|
+
last = CFI::Location.new(last_paths)
|
164
|
+
|
165
|
+
new_range = new(first, last)
|
166
|
+
|
167
|
+
new_range.parent_path = Location.new(parent_path)
|
168
|
+
new_range.start_subpath = start_str
|
169
|
+
new_range.end_subpath = end_str
|
170
|
+
|
171
|
+
new_range
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
def to_s
|
176
|
+
@string_cache ||= "epubcfi(#{@parent_path.path_string},#{@start_subpath},#{@end_subpath})"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
class Step
|
181
|
+
attr_reader :value, :assertion
|
182
|
+
alias step value
|
183
|
+
|
184
|
+
def initialize(value, assertion=nil)
|
185
|
+
@value, @assertion = value, assertion
|
186
|
+
@string_cache = nil
|
187
|
+
end
|
188
|
+
|
189
|
+
def initialize_copy(original)
|
190
|
+
@value = original.value
|
191
|
+
@assertion = original.assertion.dup if original.assertion
|
192
|
+
end
|
193
|
+
|
194
|
+
def to_s
|
195
|
+
@string_cache ||= "/#{value}#{assertion}" # need escape?
|
196
|
+
end
|
197
|
+
|
198
|
+
def <=>(other)
|
199
|
+
value <=> other.value
|
200
|
+
end
|
201
|
+
|
202
|
+
def element?
|
203
|
+
value.even?
|
204
|
+
end
|
205
|
+
|
206
|
+
def character_data?
|
207
|
+
value.odd?
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
class IDAssertion
|
212
|
+
attr_reader :id, :parameters
|
213
|
+
|
214
|
+
def initialize(id, parameters={})
|
215
|
+
@id, @parameters = id, parameters
|
216
|
+
@string_cache = nil
|
217
|
+
end
|
218
|
+
|
219
|
+
def to_s
|
220
|
+
return @string_cache if @string_cache
|
221
|
+
string_cache = '['
|
222
|
+
string_cache << CFI.escape(id) if id
|
223
|
+
parameters.each_pair do |key, values|
|
224
|
+
value = values.join(',')
|
225
|
+
string_cache << ";#{CFI.escape(key)}=#{CFI.escape(value)}"
|
226
|
+
end
|
227
|
+
string_cache << ']'
|
228
|
+
@string_cache = string_cache
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
class TextLocationAssertion
|
233
|
+
attr_reader :preceded, :followed, :parameters
|
234
|
+
|
235
|
+
def initialize(preceded=nil, followed=nil, parameters={})
|
236
|
+
@preceded, @followed, @parameters = preceded, followed, parameters
|
237
|
+
@string_cache = nil
|
238
|
+
end
|
239
|
+
|
240
|
+
def to_s
|
241
|
+
return @string_cache if @string_cache
|
242
|
+
string_cache = '['
|
243
|
+
string_cache << CFI.escape(preceded) if preceded
|
244
|
+
string_cache << ',' << CFI.escape(followed) if followed
|
245
|
+
parameters.each_pair do |key, values|
|
246
|
+
value = values.join(',')
|
247
|
+
string_cache << ";#{CFI.escape(key)}=#{CFI.escape(value)}"
|
248
|
+
end
|
249
|
+
string_cache << ']'
|
250
|
+
@string_cache = string_cache
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
class CharacterOffset
|
255
|
+
attr_reader :value, :assertion
|
256
|
+
alias offset value
|
257
|
+
|
258
|
+
def initialize(value, assertion=nil)
|
259
|
+
@value, @assertion = value, assertion
|
260
|
+
@string_cache = nil
|
261
|
+
end
|
262
|
+
|
263
|
+
def to_s
|
264
|
+
@string_cache ||= ":#{value}#{assertion}" # need escape?
|
265
|
+
end
|
266
|
+
|
267
|
+
def <=>(other)
|
268
|
+
value <=> other.value
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
class TemporalSpatialOffset
|
273
|
+
attr_reader :temporal, :x, :y, :assertion
|
274
|
+
|
275
|
+
def initialize(temporal=nil, x=nil, y=nil, assertion=nil)
|
276
|
+
raise RangeError, "dimension must be in 0..100 but passed #{x}" unless (0.0..100.0).cover?(x) if x
|
277
|
+
raise RangeError, "dimension must be in 0..100 but passed #{y}" unless (0.0..100.0).cover?(y) if y
|
278
|
+
warn "Assertion is passed to #{__class__} but cannot know how to handle with it: #{assertion}" if assertion
|
279
|
+
@temporal, @x, @y, @assertion = temporal, x, y, assertion
|
280
|
+
@string_cache = nil
|
281
|
+
end
|
282
|
+
|
283
|
+
def to_s
|
284
|
+
return @string_cache if @string_cache
|
285
|
+
string_cache = ''
|
286
|
+
string_cache << "~#{temporal}" if temporal
|
287
|
+
string_cache << "@#{x}:#{y}" if x or y
|
288
|
+
@string_cache = string_cache
|
289
|
+
end
|
290
|
+
|
291
|
+
# @note should split the class to spatial offset and temporal-spatial offset?
|
292
|
+
def <=>(other)
|
293
|
+
return -1 if temporal.nil? and other.temporal
|
294
|
+
return 1 if temporal and other.temporal.nil?
|
295
|
+
cmp = temporal <=> other.temporal
|
296
|
+
return cmp unless cmp == 0
|
297
|
+
return -1 if y.nil? and other.y
|
298
|
+
return 1 if y and other.y.nil?
|
299
|
+
cmp = y <=> other.y
|
300
|
+
return cmp unless cmp == 0
|
301
|
+
return -1 if x.nil? and other.x
|
302
|
+
return 1 if x and other.x.nil?
|
303
|
+
cmp = x <=> other.x
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
require "epub/cfi/parser"
|