alexandria-book-collection-manager 0.6.9.pre1 → 0.6.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +10 -4
- data/.rubocop_todo.yml +21 -79
- data/CHANGELOG.md +9 -1
- data/README.md +1 -1
- data/Rakefile +1 -1
- data/alexandria-book-collection-manager.gemspec +26 -4
- data/bin/alexandria +1 -1
- data/doc/AUTHORS +2 -1
- data/lib/alexandria.rb +1 -1
- data/lib/alexandria/about.rb +6 -6
- data/lib/alexandria/book_providers.rb +1 -1
- data/lib/alexandria/book_providers/adlibris.rb +4 -4
- data/lib/alexandria/book_providers/barnes_and_noble.rb +9 -5
- data/lib/alexandria/book_providers/bol_it.rb +0 -2
- data/lib/alexandria/book_providers/ibs_it.rb +0 -2
- data/lib/alexandria/book_providers/mcu.rb +3 -5
- data/lib/alexandria/book_providers/proxis.rb +1 -5
- data/lib/alexandria/book_providers/renaud.rb +0 -2
- data/lib/alexandria/book_providers/thalia.rb +10 -8
- data/lib/alexandria/book_providers/webster_it.rb +0 -2
- data/lib/alexandria/book_providers/worldcat.rb +8 -4
- data/lib/alexandria/book_providers/z3950.rb +2 -8
- data/lib/alexandria/execution_queue.rb +1 -3
- data/lib/alexandria/export_library.rb +2 -7
- data/lib/alexandria/import_library.rb +1 -6
- data/lib/alexandria/logging.rb +1 -1
- data/lib/alexandria/models/book.rb +2 -2
- data/lib/alexandria/models/library.rb +5 -7
- data/lib/alexandria/preferences.rb +1 -1
- data/lib/alexandria/scanners.rb +4 -3
- data/lib/alexandria/scanners/cuecat.rb +1 -1
- data/lib/alexandria/smart_library.rb +4 -3
- data/lib/alexandria/ui/callbacks.rb +2 -2
- data/lib/alexandria/ui/completion_models.rb +22 -19
- data/lib/alexandria/ui/dialogs/acquire_dialog.rb +2 -4
- data/lib/alexandria/ui/dialogs/barcode_animation.rb +1 -1
- data/lib/alexandria/ui/dialogs/book_properties_dialog.rb +1 -3
- data/lib/alexandria/ui/dialogs/book_properties_dialog_base.rb +0 -4
- data/lib/alexandria/ui/dialogs/export_dialog.rb +1 -3
- data/lib/alexandria/ui/dialogs/import_dialog.rb +4 -4
- data/lib/alexandria/ui/dialogs/new_book_dialog.rb +8 -11
- data/lib/alexandria/ui/dialogs/new_book_dialog_manual.rb +4 -5
- data/lib/alexandria/ui/dialogs/new_smart_library_dialog.rb +15 -9
- data/lib/alexandria/ui/dialogs/preferences_dialog.rb +20 -23
- data/lib/alexandria/ui/dialogs/smart_library_properties_dialog_base.rb +10 -14
- data/lib/alexandria/ui/dndable.rb +0 -1
- data/lib/alexandria/ui/gtk_thread_help.rb +2 -1
- data/lib/alexandria/ui/icons.rb +3 -3
- data/lib/alexandria/ui/iconview_tooltips.rb +9 -5
- data/lib/alexandria/ui/init.rb +6 -6
- data/lib/alexandria/ui/listview.rb +4 -4
- data/lib/alexandria/ui/main_app.rb +1 -1
- data/lib/alexandria/ui/multi_drag_treeview.rb +3 -5
- data/lib/alexandria/ui/sidepane.rb +26 -10
- data/lib/alexandria/ui/ui_manager.rb +5 -9
- data/lib/alexandria/undo_manager.rb +0 -2
- data/lib/alexandria/version.rb +3 -3
- data/lib/alexandria/web_themes.rb +1 -3
- data/{test/providers_test.rb → spec/alexandria/book_providers_spec.rb} +17 -15
- data/{test/book_test.rb → spec/alexandria/book_spec.rb} +4 -4
- data/spec/alexandria/library_spec.rb +189 -141
- data/spec/alexandria/scanners/cuecat_spec.rb +0 -2
- data/spec/alexandria/ui/main_app_spec.rb +13 -8
- data/spec/alexandria/ui/sidepane_spec.rb +1 -1
- data/spec/alexandria/ui/ui_utilities_spec.rb +1 -1
- data/{test → spec}/data/isbns.txt +0 -0
- data/tasks/spec.rake +2 -0
- data/util/rake/fileinstall.rb +1 -1
- metadata +32 -16
- data/tasks/test.rake +0 -38
- data/test/application_test.rb +0 -39
- data/test/isbn_test.rb +0 -68
- data/test/test_helper.rb +0 -42
@@ -77,9 +77,7 @@ module Alexandria
|
|
77
77
|
'http://www.internetbookshop.it/ser/serdsp.asp?isbn=' + book.isbn
|
78
78
|
end
|
79
79
|
|
80
|
-
#######
|
81
80
|
private
|
82
|
-
#######
|
83
81
|
|
84
82
|
def to_book(data)
|
85
83
|
raise NoResultsError if /<b>Il libro che hai cercato non è presente nel nostro catalogo<\/b><br>/.match(data)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# Copyright (C) 2004 Javier Fernandez-Sanguino
|
2
2
|
# Copyright (C) 2007 Javier Fernandez-Sanguino and Marco Costantini
|
3
|
-
#
|
3
|
+
# Copyright (C) 2011 Matijs van Zuijlen
|
4
4
|
#
|
5
5
|
# Alexandria is free software; you can redistribute it and/or
|
6
6
|
# modify it under the terms of the GNU General Public License as
|
@@ -73,7 +73,7 @@ module Alexandria
|
|
73
73
|
transport.get(URI.parse(req)).each do |line|
|
74
74
|
# line = line.convert("ISO-8859-1", "UTF-8")
|
75
75
|
print "Reading line: #{line}" if $DEBUG # for DEBUGing
|
76
|
-
if (line =~ /CMD=VERDOC.*&DOCN=([^&]*)&NDOC=([^&]*)/) and (!products[Regexp.last_match[1]]) and (book = parseBook(Regexp.last_match[1], Regexp.last_match[2]))
|
76
|
+
if (line =~ /CMD=VERDOC.*&DOCN=([^&]*)&NDOC=([^&]*)/) and (!products[Regexp.last_match[1]]) and (book = parseBook(Regexp.last_match[1], Regexp.last_match[2]))
|
77
77
|
products[Regexp.last_match[1]] = book
|
78
78
|
puts Regexp.last_match[1] if $DEBUG # for DEBUGing
|
79
79
|
end
|
@@ -91,9 +91,7 @@ module Alexandria
|
|
91
91
|
nil
|
92
92
|
end
|
93
93
|
|
94
|
-
#######
|
95
94
|
private
|
96
|
-
#######
|
97
95
|
|
98
96
|
def parseBook(docn, ndoc)
|
99
97
|
detailspage = 'http://www.mcu.es/cgi-brs/BasesHTML/isbn/BRSCGI?CMD=VERDOC&CONF=AEISPA.cnf&BASE=ISBN&DOCN=' + docn + '&NDOC=' + ndoc
|
@@ -117,7 +115,7 @@ module Alexandria
|
|
117
115
|
robotstate = 0
|
118
116
|
elsif robotstate == 1 and line =~ /^([^<]+)</
|
119
117
|
author = Regexp.last_match[1].gsub(' ', ' ').sub(/ +$/, '')
|
120
|
-
if author.length > 3
|
118
|
+
if author.length > 3
|
121
119
|
# Only add authors of appropiate length
|
122
120
|
product['authors'] << author
|
123
121
|
print "Authors are #{product['authors']}\n" if $DEBUG # for DEBUGing
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Copyright (C) 2009 Cathal Mc Ginley
|
2
|
-
# Copyright (C) 2014 Matijs van Zuijlen
|
2
|
+
# Copyright (C) 2014,2015 Matijs van Zuijlen
|
3
3
|
#
|
4
4
|
# Alexandria is free software; you can redistribute it and/or
|
5
5
|
# modify it under the terms of the GNU General Public License as
|
@@ -82,8 +82,6 @@ module Alexandria
|
|
82
82
|
def url(book)
|
83
83
|
if book.isbn.nil? or book.isbn.empty?
|
84
84
|
ISBN_REDIRECT_BASE_URL % Library.canonicalise_ean(book.isbn)
|
85
|
-
else
|
86
|
-
nil
|
87
85
|
end
|
88
86
|
end
|
89
87
|
|
@@ -133,8 +131,6 @@ module Alexandria
|
|
133
131
|
td = tr.at('td')
|
134
132
|
if td
|
135
133
|
text_of(td)
|
136
|
-
else
|
137
|
-
nil
|
138
134
|
end
|
139
135
|
end
|
140
136
|
|
@@ -81,9 +81,7 @@ module Alexandria
|
|
81
81
|
'http://www.renaud-bray.com/francais/menu/gabarit.asp?Rubrique=&Recherche=&Entete=Livre&Page=Recherche_wsc.asp&OnlyAvailable=false&Tri=ISBN&Phrase=' + book.isbn
|
82
82
|
end
|
83
83
|
|
84
|
-
#######
|
85
84
|
private
|
86
|
-
#######
|
87
85
|
|
88
86
|
def to_books(data)
|
89
87
|
data = CGI.unescapeHTML(data)
|
@@ -80,12 +80,12 @@ module Alexandria
|
|
80
80
|
book_search_results = []
|
81
81
|
results_divs = doc / 'div.articlePresentationSearchCH'
|
82
82
|
results_divs.each do |div|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
83
|
+
result = {}
|
84
|
+
title_link = div % 'div.articleText/h2/a'
|
85
|
+
result[:title] = title_link.inner_html
|
86
|
+
result[:lookup_url] = title_link['href']
|
87
|
+
book_search_results << result
|
88
|
+
end
|
89
89
|
book_search_results
|
90
90
|
end
|
91
91
|
|
@@ -187,8 +187,10 @@ module Alexandria
|
|
187
187
|
end
|
188
188
|
rescue => ex
|
189
189
|
trace = ex.backtrace.join("\n> ")
|
190
|
-
log.warn {
|
191
|
-
|
190
|
+
log.warn {
|
191
|
+
'Failed parsing search results for Thalia ' \
|
192
|
+
"#{ex.message} #{trace}"
|
193
|
+
}
|
192
194
|
raise NoResultsError
|
193
195
|
end
|
194
196
|
end
|
@@ -121,8 +121,10 @@ module Alexandria
|
|
121
121
|
end
|
122
122
|
rescue => ex
|
123
123
|
trace = ex.backtrace.join("\n> ")
|
124
|
-
log.warn {
|
125
|
-
|
124
|
+
log.warn {
|
125
|
+
'Failed parsing search results for WorldCat ' \
|
126
|
+
"#{ex.message} #{trace}"
|
127
|
+
}
|
126
128
|
end
|
127
129
|
book_search_results
|
128
130
|
end
|
@@ -241,8 +243,10 @@ module Alexandria
|
|
241
243
|
rescue => ex
|
242
244
|
raise ex if ex.instance_of? NoResultsError
|
243
245
|
trace = ex.backtrace.join("\n> ")
|
244
|
-
log.warn {
|
245
|
-
|
246
|
+
log.warn {
|
247
|
+
'Failed parsing search results for WorldCat ' \
|
248
|
+
"#{ex.message} #{trace}"
|
249
|
+
}
|
246
250
|
raise NoResultsError
|
247
251
|
end
|
248
252
|
end
|
@@ -70,9 +70,7 @@ module Alexandria
|
|
70
70
|
nil
|
71
71
|
end
|
72
72
|
|
73
|
-
#######
|
74
73
|
private
|
75
|
-
#######
|
76
74
|
|
77
75
|
def marc_to_book(marc_txt)
|
78
76
|
begin
|
@@ -157,7 +155,8 @@ module Alexandria
|
|
157
155
|
options['user'] = prefs['username']
|
158
156
|
options['password'] = prefs['password']
|
159
157
|
end
|
160
|
-
hostname
|
158
|
+
hostname = prefs['hostname']
|
159
|
+
port = prefs['port'].to_i
|
161
160
|
log.debug { "hostname #{hostname} port #{port} options #{options}" }
|
162
161
|
conn = ZOOM::Connection.new(options).connect(hostname, port)
|
163
162
|
conn.database_name = prefs['database']
|
@@ -273,9 +272,7 @@ module Alexandria
|
|
273
272
|
nil
|
274
273
|
end
|
275
274
|
|
276
|
-
#######
|
277
275
|
private
|
278
|
-
#######
|
279
276
|
|
280
277
|
def books_from_sutrs(resultset)
|
281
278
|
results = []
|
@@ -316,7 +313,6 @@ module Alexandria
|
|
316
313
|
book = Book.new(title, authors, isbn, (publisher or nil), (publish_year or nil), (edition or nil))
|
317
314
|
results << [book]
|
318
315
|
end
|
319
|
-
|
320
316
|
end
|
321
317
|
results
|
322
318
|
end
|
@@ -365,9 +361,7 @@ module Alexandria
|
|
365
361
|
nil
|
366
362
|
end
|
367
363
|
|
368
|
-
#######
|
369
364
|
private
|
370
|
-
#######
|
371
365
|
|
372
366
|
def canonicalise_isbn_with_dashes(isbn)
|
373
367
|
# The reference for the position of the dashes is
|
@@ -71,9 +71,7 @@ module Alexandria
|
|
71
71
|
@@current_queue = nil
|
72
72
|
end
|
73
73
|
|
74
|
-
#######
|
75
74
|
private
|
76
|
-
#######
|
77
75
|
|
78
76
|
def push(procedure, args, need_retval = false)
|
79
77
|
@protect_pending_calls.synchronize do
|
@@ -81,7 +79,7 @@ module Alexandria
|
|
81
79
|
@pending_calls << [@id, procedure, args, need_retval]
|
82
80
|
end
|
83
81
|
if need_retval
|
84
|
-
|
82
|
+
loop do
|
85
83
|
@protect_pending_retvals.synchronize do
|
86
84
|
ary = @pending_retvals.find { |id, _retval| id == @id }
|
87
85
|
if ary
|
@@ -128,9 +128,7 @@ module Alexandria
|
|
128
128
|
@needs_preview
|
129
129
|
end
|
130
130
|
|
131
|
-
#######
|
132
131
|
private
|
133
|
-
#######
|
134
132
|
|
135
133
|
def initialize(name, ext, message, needs_preview = false)
|
136
134
|
@name = name
|
@@ -227,7 +225,6 @@ module Alexandria
|
|
227
225
|
# we need to close the files so the iPod can be ejected/unmounted without us closing Alexandria
|
228
226
|
io.close
|
229
227
|
end
|
230
|
-
|
231
228
|
end
|
232
229
|
# Again, allow the iPod to unmount
|
233
230
|
Dir.chdir(tempdir)
|
@@ -242,9 +239,7 @@ module Alexandria
|
|
242
239
|
end
|
243
240
|
end
|
244
241
|
|
245
|
-
#######
|
246
242
|
private
|
247
|
-
#######
|
248
243
|
|
249
244
|
ONIX_DTD_URL = 'http://www.editeur.org/onix/2.1/reference/onix-international.dtd'
|
250
245
|
def to_onix_document
|
@@ -291,11 +286,11 @@ module Alexandria
|
|
291
286
|
# front cover image
|
292
287
|
elem.add_element('MediaFileTypeCode').text = '04'
|
293
288
|
elem.add_element('MediaFileFormatCode').text =
|
294
|
-
|
289
|
+
(Library.jpeg?(cover(book)) ? '03' : '02')
|
295
290
|
# filename
|
296
291
|
elem.add_element('MediaFileLinkTypeCode').text = '06'
|
297
292
|
elem.add_element('MediaFileLink').text =
|
298
|
-
|
293
|
+
File.join('images', final_cover(book))
|
299
294
|
end
|
300
295
|
if book.isbn
|
301
296
|
BookProviders.each do |provider|
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# Copyright (C) 2004-2006 Laurent Sansonetti
|
2
2
|
# Copyright (C) 2010 Cathal Mc Ginley
|
3
|
-
# Copyright (C) 2014 Matijs van Zuijlen
|
3
|
+
# Copyright (C) 2014,2015 Matijs van Zuijlen
|
4
4
|
#
|
5
5
|
# Alexandria is free software; you can redistribute it and/or
|
6
6
|
# modify it under the terms of the GNU General Public License as
|
@@ -50,9 +50,7 @@ module Alexandria
|
|
50
50
|
@on_iterate_cb, @on_error_cb)
|
51
51
|
end
|
52
52
|
|
53
|
-
#######
|
54
53
|
private
|
55
|
-
#######
|
56
54
|
|
57
55
|
def initialize(name, patterns, message)
|
58
56
|
@name = name
|
@@ -122,8 +120,6 @@ module Alexandria
|
|
122
120
|
book_elements += keys.map {|key|
|
123
121
|
if elements[key]
|
124
122
|
neaten(elements[key].text)
|
125
|
-
else
|
126
|
-
nil
|
127
123
|
end
|
128
124
|
}
|
129
125
|
# isbn
|
@@ -217,7 +213,6 @@ module Alexandria
|
|
217
213
|
if on_iterate_cb
|
218
214
|
on_iterate_cb.call(import_count, max_import)
|
219
215
|
end
|
220
|
-
|
221
216
|
end
|
222
217
|
rescue CSV::IllegalFormatError
|
223
218
|
unless failed_once
|
data/lib/alexandria/logging.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Copyright (C) 2007 Cathal Mc Ginley
|
2
|
-
#
|
2
|
+
# Copyright (C) 2011 Matijs van Zuijlen
|
3
3
|
#
|
4
4
|
# Alexandria is free software; you can redistribute it and/or
|
5
5
|
# modify it under the terms of the GNU General Public License as
|
@@ -18,8 +18,8 @@
|
|
18
18
|
module Alexandria
|
19
19
|
class Book
|
20
20
|
attr_accessor :title, :authors, :isbn, :publisher, :publishing_year,
|
21
|
-
|
22
|
-
|
21
|
+
:edition, :rating, :notes, :loaned, :loaned_since,
|
22
|
+
:loaned_to, :saved_ident, :redd, :redd_when, :own, :want, :tags, :version, :library
|
23
23
|
|
24
24
|
DEFAULT_RATING = 0
|
25
25
|
|
@@ -59,7 +59,7 @@ module Alexandria
|
|
59
59
|
i = 1
|
60
60
|
name = nil
|
61
61
|
all_libraries = existing_libraries + @@deleted_libraries
|
62
|
-
|
62
|
+
loop do
|
63
63
|
name = i == 1 ? from_base : from_base + " #{i}"
|
64
64
|
break unless all_libraries.find { |x| x.name == name }
|
65
65
|
i += 1
|
@@ -77,7 +77,6 @@ module Alexandria
|
|
77
77
|
FileUtils.mkdir_p(library.path) unless File.exist?(library.path)
|
78
78
|
Dir.chdir(library.path) do
|
79
79
|
Dir['*' + EXT[:book]].each do |filename|
|
80
|
-
|
81
80
|
test[1] = filename if test[0] == 0
|
82
81
|
|
83
82
|
unless File.size? test[1]
|
@@ -197,7 +196,10 @@ module Alexandria
|
|
197
196
|
new_yaml.sub!(/^\s*\-+\s*/, '')
|
198
197
|
text.sub!(md[0], "loaned_since: #{new_yaml}\n")
|
199
198
|
end
|
199
|
+
|
200
|
+
# TODO: Ensure book loading passes through Book#initialize
|
200
201
|
book = YAML.load(text)
|
202
|
+
|
201
203
|
unless book.isbn.class == String
|
202
204
|
# HACK
|
203
205
|
md = /isbn: (.+)/.match(text)
|
@@ -600,7 +602,7 @@ module Alexandria
|
|
600
602
|
end
|
601
603
|
|
602
604
|
def n_rated
|
603
|
-
|
605
|
+
count { |x| !x.rating.nil? and x.rating > 0 }
|
604
606
|
end
|
605
607
|
|
606
608
|
def n_unrated
|
@@ -630,9 +632,7 @@ module Alexandria
|
|
630
632
|
book.ident + (Library.jpeg?(cover(book)) ? '.jpg' : '.gif')
|
631
633
|
end
|
632
634
|
|
633
|
-
#########
|
634
635
|
protected
|
635
|
-
#########
|
636
636
|
|
637
637
|
def initialize(name)
|
638
638
|
@name = name
|
@@ -697,9 +697,7 @@ module Alexandria
|
|
697
697
|
end
|
698
698
|
end
|
699
699
|
|
700
|
-
#######
|
701
700
|
private
|
702
|
-
#######
|
703
701
|
|
704
702
|
def initialize
|
705
703
|
@all_libraries = []
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# Copyright (C) 2004-2006 Laurent Sansonetti
|
2
2
|
# Copyright (C) 2011 Cathal Mc Ginley
|
3
|
-
#
|
3
|
+
# Copyright (C) 2011 Matijs van Zuijlen
|
4
4
|
#
|
5
5
|
# Alexandria is free software; you can redistribute it and/or
|
6
6
|
# modify it under the terms of the GNU General Public License as
|
data/lib/alexandria/scanners.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# Copyright (C) 2005-2006 Christopher Cyll
|
2
|
+
# Copyright (C) 2014, 2015 Matijs van Zuijlen
|
2
3
|
#
|
3
4
|
# Alexandria is free software; you can redistribute it and/or
|
4
5
|
# modify it under the terms of the GNU General Public License as
|
@@ -23,7 +24,7 @@ module Alexandria
|
|
23
24
|
module Scanners
|
24
25
|
REGISTRY = []
|
25
26
|
|
26
|
-
def self.register
|
27
|
+
def self.register(scanner)
|
27
28
|
REGISTRY.push(scanner)
|
28
29
|
end
|
29
30
|
|
@@ -31,11 +32,11 @@ module Alexandria
|
|
31
32
|
REGISTRY.first
|
32
33
|
end
|
33
34
|
|
34
|
-
def self.find_scanner
|
35
|
+
def self.find_scanner(name)
|
35
36
|
REGISTRY.find { |scanner| scanner.name == name }
|
36
37
|
end
|
37
38
|
|
38
|
-
def each_scanner
|
39
|
+
def self.each_scanner
|
39
40
|
REGISTRY.each { |scanner| yield scanner }
|
40
41
|
end
|
41
42
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# Copyright (C) 2005-2006 Christopher Cyll
|
2
|
-
#
|
2
|
+
# Copyright (C) 2011 Matijs van Zuijlen
|
3
3
|
#
|
4
4
|
# Alexandria is free software; you can redistribute it and/or
|
5
5
|
# modify it under the terms of the GNU General Public License as
|
@@ -26,7 +26,8 @@ module Alexandria
|
|
26
26
|
extend GetText
|
27
27
|
bindtextdomain(Alexandria::TEXTDOMAIN, charset: 'UTF-8')
|
28
28
|
|
29
|
-
ALL_RULES
|
29
|
+
ALL_RULES = 1
|
30
|
+
ANY_RULE = 2
|
30
31
|
attr_reader :name
|
31
32
|
attr_accessor :rules, :predicate_operator_rule, :deleted_books
|
32
33
|
|
@@ -176,7 +177,7 @@ module Alexandria
|
|
176
177
|
filtered_library.each { |x| @cache[x] = library }
|
177
178
|
concat(filtered_library)
|
178
179
|
end
|
179
|
-
@n_rated =
|
180
|
+
@n_rated = count { |x| !x.rating.nil? and x.rating > 0 }
|
180
181
|
end
|
181
182
|
|
182
183
|
def cover(book)
|
@@ -491,7 +492,7 @@ module Alexandria
|
|
491
492
|
when 'Time'
|
492
493
|
TIME_OPERATORS.map do |x|
|
493
494
|
if x == Operators::IS_IN_LAST or
|
494
|
-
|
495
|
+
x == Operators::IS_NOT_IN_LAST
|
495
496
|
|
496
497
|
[x, Operands::DAYS]
|
497
498
|
else
|