ebook_generator 0.0.2
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 +7 -0
- data/.gitignore +22 -0
- data/Changelog.md +10 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +77 -0
- data/Rakefile +2 -0
- data/ebook_generator.gemspec +23 -0
- data/lib/ebook_generator/initializer.rb +5 -0
- data/lib/ebook_generator/migration.rb +30 -0
- data/lib/ebook_generator/version.rb +3 -0
- data/lib/ebook_generator.rb +208 -0
- data/lib/generators/ebook_generator_generator.rb +17 -0
- metadata +55 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 0d00c5f445bb58f02fd5421fbc9f449b2fc8ad45
|
4
|
+
data.tar.gz: f56981f51e890a4fb2d6d5762731fb4be0df7586
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b217a3d82a044ee065111b94aa9ada5e86b5087348c35659b5b9a93898d45a4cba34aab3d30881c09756974789d736435ee1642847209f17d5c6769e37593a19
|
7
|
+
data.tar.gz: f0ef7a57ca03587fd3dd8c4343bb53900158339d7674d80b1f501c6a875320d2a37a717cf1733134215ee9545e8dfda1eb278be198477c0893be7a782dc0993d
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/Changelog.md
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Kris Quigley
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
# EbookGenerator
|
2
|
+
|
3
|
+
Rails gem to generate eBooks using the Markdown syntax to format content for sections of the book.
|
4
|
+
|
5
|
+
An example of this gem being used can be found at: [ebook-generator.affinity-tech.com](http://ebook-generator.affinity-tech.com)
|
6
|
+
|
7
|
+
Requires:
|
8
|
+
- Postgres with UUID support (uses the id to generate the ebook UUID)
|
9
|
+
- Rubyzip (to generate the .epub)
|
10
|
+
- Redcarpet (to render Markdown into HTML)
|
11
|
+
- Friendly_id (for nice slugs and for naming of generated ebook file)
|
12
|
+
|
13
|
+
## Installation
|
14
|
+
|
15
|
+
Add this line to your application's Gemfile:
|
16
|
+
|
17
|
+
gem 'ebook_generator'
|
18
|
+
|
19
|
+
And then execute:
|
20
|
+
|
21
|
+
$ bundle
|
22
|
+
|
23
|
+
Or install it yourself as:
|
24
|
+
|
25
|
+
$ gem install ebook_generator
|
26
|
+
|
27
|
+
## Usage
|
28
|
+
|
29
|
+
Generate the tables needed to process the ebooks:
|
30
|
+
|
31
|
+
`rails generate ebook_generator`
|
32
|
+
|
33
|
+
Migrate the db:
|
34
|
+
|
35
|
+
`rake db:migrate`
|
36
|
+
|
37
|
+
Require the ebook_generator module in your class:
|
38
|
+
|
39
|
+
`include 'EbookGenerator'`
|
40
|
+
|
41
|
+
Pass the id for the ebook you want to generate:
|
42
|
+
|
43
|
+
`EbookGenerator.generate_ebook(ebook.id)`
|
44
|
+
|
45
|
+
This will then generate an ebook based on the values in the db in the /tmp folder.
|
46
|
+
|
47
|
+
## Feature roadmap
|
48
|
+
|
49
|
+
### 0.1.0
|
50
|
+
- Kindle support
|
51
|
+
- Tests
|
52
|
+
|
53
|
+
### 0.2.0
|
54
|
+
- Style editing
|
55
|
+
|
56
|
+
### 0.3.0
|
57
|
+
- PDF out
|
58
|
+
|
59
|
+
### 0.4.0
|
60
|
+
- HTML out
|
61
|
+
- Image support for the front cover
|
62
|
+
|
63
|
+
### 0.5.0
|
64
|
+
- HTML to eBook conversion
|
65
|
+
|
66
|
+
### 0.6.0
|
67
|
+
- User membership to manage books
|
68
|
+
|
69
|
+
Publishing support?
|
70
|
+
|
71
|
+
## Contributing
|
72
|
+
|
73
|
+
1. Fork it ( https://github.com/[my-github-username]/ebook_generator/fork )
|
74
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
75
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
76
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
77
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "ebook_generator/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'ebook_generator'
|
7
|
+
s.version = EbookGenerator::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.summary = "Generates eBooks"
|
10
|
+
s.description = "A simple eBook (ePub) generator gem"
|
11
|
+
s.authors = ["Kris Quigley"]
|
12
|
+
s.email = 'kris@krisquigley.co.uk'
|
13
|
+
s.homepage =
|
14
|
+
'https://github.com/krisquigley/ebook_generator'
|
15
|
+
|
16
|
+
|
17
|
+
s.files = `git ls-files`.split("\n")
|
18
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
19
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
|
22
|
+
s.license = 'MIT'
|
23
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class CreateEbookGeneratorTables < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
enable_extension 'uuid-ossp'
|
4
|
+
|
5
|
+
create_table :ebooks, id: :uuid, default: "uuid_generate_v4()" do |t|
|
6
|
+
t.string "title", null: false
|
7
|
+
t.string "creator"
|
8
|
+
t.string "language", limit: 2
|
9
|
+
t.string "contributor"
|
10
|
+
t.text "description"
|
11
|
+
t.string "publisher"
|
12
|
+
t.text "rights"
|
13
|
+
t.string "subject"
|
14
|
+
t.datetime "created_at"
|
15
|
+
t.datetime "updated_at"
|
16
|
+
end
|
17
|
+
|
18
|
+
create_table "sections", force: true do |t|
|
19
|
+
t.string "title", null: false
|
20
|
+
t.text "content", null: false
|
21
|
+
t.uuid "ebook_id", null: false
|
22
|
+
t.integer "position", null: false
|
23
|
+
t.datetime "created_at"
|
24
|
+
t.datetime "updated_at"
|
25
|
+
end
|
26
|
+
|
27
|
+
add_index "sections", ["ebook_id"], name: "index_sections_on_ebook_id", using: :btree
|
28
|
+
add_index "sections", ["position"], name: "index_sections_on_position", using: :btree
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,208 @@
|
|
1
|
+
require "ebook_generator/version"
|
2
|
+
|
3
|
+
module EbookGenerator
|
4
|
+
|
5
|
+
def self.included(model_class)
|
6
|
+
model_class.extend self
|
7
|
+
end
|
8
|
+
|
9
|
+
# Move writing of files into its own class that accepts an array
|
10
|
+
def self.make_dir(path)
|
11
|
+
Dir.mkdir(path, 0777) unless File.exists?(path)
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.initialise_files(path)
|
15
|
+
metainf_path = path + "/META-INF"
|
16
|
+
|
17
|
+
Dir.mkdir(metainf_path) unless File.exists?(metainf_path)
|
18
|
+
|
19
|
+
metainf = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>
|
20
|
+
<container version=\"1.0\" xmlns=\"urn:oasis:names:tc:opendocument:xmlns:container\">
|
21
|
+
<rootfiles>
|
22
|
+
<rootfile full-path=\"OEBPS/content.opf\" media-type=\"application/oebps-package+xml\"/>
|
23
|
+
</rootfiles>
|
24
|
+
</container>"
|
25
|
+
|
26
|
+
File.open(metainf_path + "/container.xml", "w+") do |f|
|
27
|
+
f.write(metainf)
|
28
|
+
end
|
29
|
+
|
30
|
+
mimetype = "application/epub+zip"
|
31
|
+
mimetype_path = path + "/mimetype"
|
32
|
+
|
33
|
+
File.open(mimetype_path, "w+") do |f|
|
34
|
+
f.write(mimetype)
|
35
|
+
end
|
36
|
+
|
37
|
+
FileUtils.chmod 0755, mimetype_path
|
38
|
+
|
39
|
+
content_path = path + "/OEBPS"
|
40
|
+
|
41
|
+
Dir.mkdir(content_path) unless File.exists?(content_path)
|
42
|
+
|
43
|
+
Dir.mkdir(content_path + "/Text") unless File.exists?(content_path + "/Text")
|
44
|
+
|
45
|
+
Dir.mkdir(content_path + "/Styles") unless File.exists?(content_path + "/Styles")
|
46
|
+
|
47
|
+
root = Rails.root.to_s
|
48
|
+
|
49
|
+
FileUtils.cp "#{root}/app/ebook/style.css", content_path+"/Styles"
|
50
|
+
|
51
|
+
return content_path
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.generate_headers(attrs)
|
56
|
+
xml = "<title>#{attrs.title}</title>
|
57
|
+
<meta content=\"#{attrs.title}\" name=\"Title\" />
|
58
|
+
<meta content=\"#{attrs.title}\" name=\"Author\" />
|
59
|
+
<link href=\"../Styles/style.css\" rel=\"stylesheet\" type=\"text/css\" />"
|
60
|
+
return xml
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.generate_sections(path, attrs)
|
64
|
+
markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, autolink: true, tables: true)
|
65
|
+
headers = generate_headers(attrs)
|
66
|
+
|
67
|
+
attrs.sections.each do |section|
|
68
|
+
xml = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"no\"?>
|
69
|
+
<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"
|
70
|
+
\"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">
|
71
|
+
<html xmlns=\"http://www.w3.org/1999/xhtml\">
|
72
|
+
<head>
|
73
|
+
#{headers}
|
74
|
+
</head>
|
75
|
+
<body>
|
76
|
+
<div id=\"#{section.title}\">
|
77
|
+
"
|
78
|
+
xml += markdown.render(section.content)
|
79
|
+
xml += "
|
80
|
+
</div>
|
81
|
+
</body>
|
82
|
+
</html>"
|
83
|
+
|
84
|
+
File.open(path + "/Section#{section.position}.html", "w+") do |f|
|
85
|
+
f.write(xml)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.generate_content_opf(path, attrs)
|
91
|
+
xml = "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>
|
92
|
+
<package xmlns=\"http://www.idpf.org/2007/opf\" unique-identifier=\"BookId\" version=\"2.0\">
|
93
|
+
<metadata xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:opf=\"http://www.idpf.org/2007/opf\">
|
94
|
+
<dc:identifier id=\"BookId\" opf:scheme=\"UUID\">#{attrs.id}</dc:identifier>
|
95
|
+
<dc:title>#{attrs.title}</dc:title>
|
96
|
+
<dc:creator opf:role=\"aut\">#{attrs.creator}</dc:creator>
|
97
|
+
<dc:language>#{attrs.language}</dc:language>
|
98
|
+
<dc:date opf:event=\"modification\">#{attrs.updated_at}</dc:date>
|
99
|
+
<dc:description>#{attrs.description}</dc:description>
|
100
|
+
<dc:publisher>#{attrs.publisher}</dc:publisher>
|
101
|
+
<dc:rights>#{attrs.rights}</dc:rights>
|
102
|
+
<dc:subject>#{attrs.subject}</dc:subject>
|
103
|
+
<dc:contributor opf:role=\"cov\">#{attrs.contributor}</dc:contributor>
|
104
|
+
<meta content=\"0.7.2\" name=\"Sigil version\" />
|
105
|
+
</metadata>
|
106
|
+
<manifest>
|
107
|
+
<item href=\"toc.ncx\" id=\"ncx\" media-type=\"application/x-dtbncx+xml\" />
|
108
|
+
<item href=\"Styles/style.css\" id=\"style.css\" media-type=\"text/css\" />"
|
109
|
+
|
110
|
+
attrs.sections.each do |section|
|
111
|
+
xml += "<item href=\"Text/Section#{section.position}.html\" id=\"Section#{section.position}.html\" media-type=\"application/xhtml+xml\" />"
|
112
|
+
end
|
113
|
+
|
114
|
+
xml += "<item href=\"Fonts/junicode-italic-webfont.ttf\" id=\"junicode-italic-webfont.ttf\" media-type=\"application/x-font-ttf\" />
|
115
|
+
<item href=\"Fonts/junicode-webfont.ttf\" id=\"junicode-webfont.ttf\" media-type=\"application/x-font-ttf\" />
|
116
|
+
<item href=\"Fonts/junicode-bold-webfont.ttf\" id=\"junicode-bold-webfont.ttf\" media-type=\"application/x-font-ttf\" />
|
117
|
+
</manifest>
|
118
|
+
<spine toc=\"ncx\">"
|
119
|
+
|
120
|
+
attrs.sections.each do |section|
|
121
|
+
xml += "<itemref idref=\"Section#{section.position}.html\" />"
|
122
|
+
|
123
|
+
end
|
124
|
+
xml += "</spine>
|
125
|
+
<guide />
|
126
|
+
</package>"
|
127
|
+
|
128
|
+
content_path = path + "/content.opf"
|
129
|
+
|
130
|
+
File.open(content_path, "w+") do |f|
|
131
|
+
f.write(xml)
|
132
|
+
end
|
133
|
+
|
134
|
+
FileUtils.chmod 0755, content_path
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
def self.generate_toc_ncx(path, attrs)
|
139
|
+
xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\" ?>
|
140
|
+
<!DOCTYPE ncx PUBLIC \"-//NISO//DTD ncx 2005-1//EN\"
|
141
|
+
\"http://www.daisy.org/z3986/2005/ncx-2005-1.dtd\">
|
142
|
+
<ncx xmlns=\"http://www.daisy.org/z3986/2005/ncx/\" version=\"2005-1\">
|
143
|
+
<head>
|
144
|
+
<meta content=\"urn:uuid:${attrs.id}\" name=\"dtb:uid\"/>
|
145
|
+
<meta content=\"2\" name=\"dtb:depth\"/>
|
146
|
+
<meta content=\"0\" name=\"dtb:totalPageCount\"/>
|
147
|
+
<meta content=\"0\" name=\"dtb:maxPageNumber\"/>
|
148
|
+
</head>
|
149
|
+
<docTitle>
|
150
|
+
<text>#{attrs.title}</text>
|
151
|
+
</docTitle>
|
152
|
+
<navMap>"
|
153
|
+
|
154
|
+
|
155
|
+
attrs.sections.each do |section|
|
156
|
+
xml += "<navPoint id=\"navPoint-#{section.position}\" playOrder=\"#{section.position}\">
|
157
|
+
<navLabel>
|
158
|
+
<text>#{section.title}</text>
|
159
|
+
</navLabel>
|
160
|
+
<content src=\"Text/Section#{section.position}.html\"/>
|
161
|
+
</navPoint>"
|
162
|
+
end
|
163
|
+
|
164
|
+
xml += "</navMap>
|
165
|
+
</ncx>"
|
166
|
+
|
167
|
+
toc_path = path + "/toc.ncx"
|
168
|
+
|
169
|
+
File.open(toc_path, "w+") do |f|
|
170
|
+
f.write(xml)
|
171
|
+
end
|
172
|
+
|
173
|
+
FileUtils.chmod 0755, toc_path
|
174
|
+
end
|
175
|
+
|
176
|
+
def self.remove_tmp_dir(directory)
|
177
|
+
FileUtils.remove_dir(directory, true)
|
178
|
+
end
|
179
|
+
|
180
|
+
def self.generate_ebook(ebook_id)
|
181
|
+
|
182
|
+
# create tmp directory based on UUID
|
183
|
+
path = Rails.root.join "tmp/#{ebook_id}"
|
184
|
+
make_dir(path.to_s)
|
185
|
+
|
186
|
+
# set up required files and dirs
|
187
|
+
content_path = initialise_files(path.to_s)
|
188
|
+
|
189
|
+
# loop through each section loading the reference header and saving as it's own section
|
190
|
+
attrs = Ebook.find(ebook_id)
|
191
|
+
generate_sections(content_path + "/Text", attrs)
|
192
|
+
|
193
|
+
# generate toc based on the number of sections generated
|
194
|
+
generate_content_opf(content_path, attrs)
|
195
|
+
generate_toc_ncx(content_path, attrs)
|
196
|
+
|
197
|
+
# zip all files
|
198
|
+
zipfile_name = Rails.root.to_s + "/tmp/" + attrs.slug + ".epub"
|
199
|
+
|
200
|
+
zf = ZipFileProcessor.new(path.to_s, zipfile_name)
|
201
|
+
zf.write
|
202
|
+
|
203
|
+
# Clean up the tmp dir
|
204
|
+
remove_tmp_dir(path.to_s + "/")
|
205
|
+
|
206
|
+
end
|
207
|
+
|
208
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
require "rails/generators/active_record"
|
3
|
+
|
4
|
+
class EbookGeneratorGenerator < ActiveRecord::Generators::Base
|
5
|
+
argument :name, type: :string, default: 'random_name'
|
6
|
+
|
7
|
+
source_root File.expand_path('../../ebook_generator', __FILE__)
|
8
|
+
|
9
|
+
# Copies the migration template to db/migrate.
|
10
|
+
def copy_files
|
11
|
+
migration_template 'migration.rb', 'db/migrate/create_ebook_generator_tables.rb'
|
12
|
+
end
|
13
|
+
|
14
|
+
def create_initializer
|
15
|
+
copy_file 'initializer.rb', 'config/initializers/ebook_generator.rb'
|
16
|
+
end
|
17
|
+
end
|
metadata
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ebook_generator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Kris Quigley
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-04-18 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A simple eBook (ePub) generator gem
|
14
|
+
email: kris@krisquigley.co.uk
|
15
|
+
executables: []
|
16
|
+
extensions: []
|
17
|
+
extra_rdoc_files: []
|
18
|
+
files:
|
19
|
+
- .gitignore
|
20
|
+
- Changelog.md
|
21
|
+
- Gemfile
|
22
|
+
- LICENSE.txt
|
23
|
+
- README.md
|
24
|
+
- Rakefile
|
25
|
+
- ebook_generator.gemspec
|
26
|
+
- lib/ebook_generator.rb
|
27
|
+
- lib/ebook_generator/initializer.rb
|
28
|
+
- lib/ebook_generator/migration.rb
|
29
|
+
- lib/ebook_generator/version.rb
|
30
|
+
- lib/generators/ebook_generator_generator.rb
|
31
|
+
homepage: https://github.com/krisquigley/ebook_generator
|
32
|
+
licenses:
|
33
|
+
- MIT
|
34
|
+
metadata: {}
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubyforge_project:
|
51
|
+
rubygems_version: 2.1.11
|
52
|
+
signing_key:
|
53
|
+
specification_version: 4
|
54
|
+
summary: Generates eBooks
|
55
|
+
test_files: []
|