kamishibai 0.6.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.
Files changed (166) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/LICENSE +29 -0
  4. data/README.md +91 -0
  5. data/bin/kamishibai +59 -0
  6. data/changelogs.txt +6 -0
  7. data/images/reading.jpg +0 -0
  8. data/images/settings.jpg +0 -0
  9. data/images/view_browse.jpg +0 -0
  10. data/images/view_dir.jpg +0 -0
  11. data/kamishibai.gemspec +31 -0
  12. data/lib/kamishibai.rb +35 -0
  13. data/lib/kamishibai/book.rb +97 -0
  14. data/lib/kamishibai/config.rb +90 -0
  15. data/lib/kamishibai/database.rb +330 -0
  16. data/lib/kamishibai/functions.rb +332 -0
  17. data/lib/kamishibai/patches.rb +58 -0
  18. data/lib/kamishibai/version.rb +7 -0
  19. data/lib/kamishibai/webserver.rb +502 -0
  20. data/lib/kamishibai/webserver_cmds.rb +26 -0
  21. data/lib/kamishibai/webserver_config.rb +91 -0
  22. data/lib/kamishibai/webserver_filemanager.rb +29 -0
  23. data/lib/kamishibai/webserver_tablet.rb +259 -0
  24. data/lib/kamishibai/workers.rb +39 -0
  25. data/public/css/browse.css +390 -0
  26. data/public/css/config.css +3 -0
  27. data/public/css/reader.css +119 -0
  28. data/public/css/styles.css +30 -0
  29. data/public/css/tablet_browse.css +247 -0
  30. data/public/css/tablet_reader.css +323 -0
  31. data/public/css/tablet_reader_input.css +191 -0
  32. data/public/images/ajax-loader.gif +0 -0
  33. data/public/images/delete.png +0 -0
  34. data/public/images/folder-mini-up.png +0 -0
  35. data/public/images/folder-mini.png +0 -0
  36. data/public/images/spinner.gif +0 -0
  37. data/public/images/trash-empty-mini.png +0 -0
  38. data/public/images/trash-full-mini.png +0 -0
  39. data/public/js/browse.js +391 -0
  40. data/public/js/config.js +190 -0
  41. data/public/js/reader.js +497 -0
  42. data/public/js/swipeview-k.js +478 -0
  43. data/public/js/tablet3.js +241 -0
  44. data/public/js/tablet3_browse.js +173 -0
  45. data/public/js/tablet3_reader.js +534 -0
  46. data/public/js/utilities.js +170 -0
  47. data/public/lang/k-cn.json +82 -0
  48. data/public/lang/k-en.json +82 -0
  49. data/public/lang/k-ja.json +82 -0
  50. data/public/vendor/css/bootstrap-slider.css +252 -0
  51. data/public/vendor/css/bootstrap-theme.css +476 -0
  52. data/public/vendor/css/bootstrap-theme.css.map +1 -0
  53. data/public/vendor/css/bootstrap-theme.min.css +5 -0
  54. data/public/vendor/css/bootstrap.css +6584 -0
  55. data/public/vendor/css/bootstrap.css.map +1 -0
  56. data/public/vendor/css/bootstrap.min.css +5 -0
  57. data/public/vendor/css/jquery-ui-1.10.0.custom.css +1614 -0
  58. data/public/vendor/css/jquery.mobile-1.3.1.min.css +3 -0
  59. data/public/vendor/css/jqueryFileTree.css +91 -0
  60. data/public/vendor/fonts/glyphicons-halflings-regular.eot +0 -0
  61. data/public/vendor/fonts/glyphicons-halflings-regular.svg +288 -0
  62. data/public/vendor/fonts/glyphicons-halflings-regular.ttf +0 -0
  63. data/public/vendor/fonts/glyphicons-halflings-regular.woff +0 -0
  64. data/public/vendor/fonts/glyphicons-halflings-regular.woff2 +0 -0
  65. data/public/vendor/images/application.png +0 -0
  66. data/public/vendor/images/code.png +0 -0
  67. data/public/vendor/images/css.png +0 -0
  68. data/public/vendor/images/db.png +0 -0
  69. data/public/vendor/images/dim.png +0 -0
  70. data/public/vendor/images/directory.png +0 -0
  71. data/public/vendor/images/doc.png +0 -0
  72. data/public/vendor/images/file.png +0 -0
  73. data/public/vendor/images/film.png +0 -0
  74. data/public/vendor/images/flash.png +0 -0
  75. data/public/vendor/images/folder_open.png +0 -0
  76. data/public/vendor/images/grey.png +0 -0
  77. data/public/vendor/images/html.png +0 -0
  78. data/public/vendor/images/icons-18-black.png +0 -0
  79. data/public/vendor/images/icons-18-white.png +0 -0
  80. data/public/vendor/images/icons-36-black.png +0 -0
  81. data/public/vendor/images/icons-36-white.png +0 -0
  82. data/public/vendor/images/java.png +0 -0
  83. data/public/vendor/images/linux.png +0 -0
  84. data/public/vendor/images/music.png +0 -0
  85. data/public/vendor/images/pdf.png +0 -0
  86. data/public/vendor/images/php.png +0 -0
  87. data/public/vendor/images/picture.png +0 -0
  88. data/public/vendor/images/ppt.png +0 -0
  89. data/public/vendor/images/psd.png +0 -0
  90. data/public/vendor/images/ruby.png +0 -0
  91. data/public/vendor/images/script.png +0 -0
  92. data/public/vendor/images/spinner.gif +0 -0
  93. data/public/vendor/images/txt.png +0 -0
  94. data/public/vendor/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  95. data/public/vendor/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  96. data/public/vendor/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  97. data/public/vendor/images/ui-bg_flat_10_000000_40x100.png +0 -0
  98. data/public/vendor/images/ui-bg_flat_30_cccccc_40x100.png +0 -0
  99. data/public/vendor/images/ui-bg_flat_50_5c5c5c_40x100.png +0 -0
  100. data/public/vendor/images/ui-bg_flat_55_fbec88_40x100.png +0 -0
  101. data/public/vendor/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  102. data/public/vendor/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  103. data/public/vendor/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  104. data/public/vendor/images/ui-bg_glass_20_555555_1x400.png +0 -0
  105. data/public/vendor/images/ui-bg_glass_40_0078a3_1x400.png +0 -0
  106. data/public/vendor/images/ui-bg_glass_40_ffc73d_1x400.png +0 -0
  107. data/public/vendor/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  108. data/public/vendor/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  109. data/public/vendor/images/ui-bg_glass_75_d0e5f5_1x400.png +0 -0
  110. data/public/vendor/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  111. data/public/vendor/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  112. data/public/vendor/images/ui-bg_glass_85_dfeffc_1x400.png +0 -0
  113. data/public/vendor/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  114. data/public/vendor/images/ui-bg_gloss-wave_25_333333_500x100.png +0 -0
  115. data/public/vendor/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  116. data/public/vendor/images/ui-bg_gloss-wave_55_5c9ccc_500x100.png +0 -0
  117. data/public/vendor/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  118. data/public/vendor/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  119. data/public/vendor/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  120. data/public/vendor/images/ui-bg_highlight-soft_80_eeeeee_1x100.png +0 -0
  121. data/public/vendor/images/ui-bg_inset-hard_100_f5f8f9_1x100.png +0 -0
  122. data/public/vendor/images/ui-bg_inset-hard_100_fcfdfd_1x100.png +0 -0
  123. data/public/vendor/images/ui-bg_inset-soft_25_000000_1x100.png +0 -0
  124. data/public/vendor/images/ui-bg_inset-soft_30_f58400_1x100.png +0 -0
  125. data/public/vendor/images/ui-icons_217bc0_256x240.png +0 -0
  126. data/public/vendor/images/ui-icons_222222_256x240.png +0 -0
  127. data/public/vendor/images/ui-icons_228ef1_256x240.png +0 -0
  128. data/public/vendor/images/ui-icons_2e83ff_256x240.png +0 -0
  129. data/public/vendor/images/ui-icons_454545_256x240.png +0 -0
  130. data/public/vendor/images/ui-icons_469bdd_256x240.png +0 -0
  131. data/public/vendor/images/ui-icons_4b8e0b_256x240.png +0 -0
  132. data/public/vendor/images/ui-icons_6da8d5_256x240.png +0 -0
  133. data/public/vendor/images/ui-icons_888888_256x240.png +0 -0
  134. data/public/vendor/images/ui-icons_a83300_256x240.png +0 -0
  135. data/public/vendor/images/ui-icons_cccccc_256x240.png +0 -0
  136. data/public/vendor/images/ui-icons_cd0a0a_256x240.png +0 -0
  137. data/public/vendor/images/ui-icons_d8e7f3_256x240.png +0 -0
  138. data/public/vendor/images/ui-icons_ef8c08_256x240.png +0 -0
  139. data/public/vendor/images/ui-icons_f9bd01_256x240.png +0 -0
  140. data/public/vendor/images/ui-icons_ffd27a_256x240.png +0 -0
  141. data/public/vendor/images/ui-icons_ffffff_256x240.png +0 -0
  142. data/public/vendor/images/xls.png +0 -0
  143. data/public/vendor/images/zip.png +0 -0
  144. data/public/vendor/js/bootstrap-slider.js +1550 -0
  145. data/public/vendor/js/bootstrap.js +2317 -0
  146. data/public/vendor/js/bootstrap.min.js +7 -0
  147. data/public/vendor/js/jquery-2.1.4.js +9210 -0
  148. data/public/vendor/js/jquery-2.1.4.min.js +4 -0
  149. data/public/vendor/js/jquery-2.1.4.min.map +1 -0
  150. data/public/vendor/js/jquery-ui-1.10.2.custom.min.js +7 -0
  151. data/public/vendor/js/jquery.cookie.js +72 -0
  152. data/public/vendor/js/jquery.hammer.min.js +7 -0
  153. data/public/vendor/js/jquery.lazyload.min.js +15 -0
  154. data/public/vendor/js/jquery.localize.js +180 -0
  155. data/public/vendor/js/jquery.mobile-1.3.1.min.js +7 -0
  156. data/public/vendor/js/jquery.scrollTo-1.4.3.1.js +218 -0
  157. data/public/vendor/js/jqueryFileTree.js +95 -0
  158. data/public/vendor/js/swipeview.js +471 -0
  159. data/views/browse.haml +97 -0
  160. data/views/config.haml +115 -0
  161. data/views/layout.haml +12 -0
  162. data/views/login.haml +51 -0
  163. data/views/reader.haml +70 -0
  164. data/views/statistics.haml +108 -0
  165. data/views/tablet3.haml +80 -0
  166. metadata +334 -0
@@ -0,0 +1,58 @@
1
+ # encoding: utf-8
2
+
3
+ # License: refer to LICENSE file
4
+
5
+ require 'addressable/uri' #using addressable because default ruby uri is badly implemented, causing InvalidURIError on many situations, especially unicode string
6
+
7
+ #
8
+ # bug fix
9
+ # the addressable/uri has a bug that is not escaping the '
10
+ #
11
+ class Addressable::URI
12
+ def self.escape2(str)
13
+ s = Addressable::URI.escape(str)
14
+ s = s.gsub("#",'%23')
15
+ s = s.gsub("'",'%27')
16
+ s = s.gsub('+','%2B')
17
+ s
18
+ end
19
+ end
20
+
21
+ class Addressable::URI
22
+ def self.unescape2(str)
23
+ s = str.gsub('%2B','+')
24
+ s = s.gsub('%27',"'")
25
+ s = s.gsub('%23',"#")
26
+ s = Addressable::URI.unescape(s)
27
+ s
28
+ end
29
+ end
30
+
31
+
32
+ #
33
+ # bug fix
34
+ # fixing bug when client post to server it get 405 "Method Not Allowed" Error
35
+ # https://github.com/bachue/rack-contrib/commit/f5f4ffebc20277903f6013adfb0429d12b4a3050
36
+ #
37
+ module Rack
38
+ class TryStatic
39
+
40
+ def initialize(app, options)
41
+ @app = app
42
+ @try = ['', *options[:try]]
43
+ @static = ::Rack::Static.new(
44
+ lambda { |_| [404, {}, []] },
45
+ options)
46
+ end
47
+
48
+ def call(env)
49
+ orig_path = env['PATH_INFO']
50
+ found = nil
51
+ @try.each do |path|
52
+ resp = @static.call(env.merge!({'PATH_INFO' => orig_path + path}))
53
+ break if !(403..405).include?(resp[0]) && found = resp
54
+ end
55
+ found or @app.call(env.merge!('PATH_INFO' => orig_path))
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,7 @@
1
+ # encoding: utf-8
2
+
3
+ # License: refer to LICENSE file
4
+
5
+ class Kamishibai
6
+ VERSION = "0.6.2"
7
+ end
@@ -0,0 +1,502 @@
1
+ # encoding: utf-8
2
+
3
+ # License: refer to LICENSE file
4
+
5
+ require 'haml'
6
+ require 'pp' if $debug
7
+ require 'sinatra'
8
+ require 'sinatra/base'
9
+ require 'sinatra/reloader' if development? and Gem::Specification::find_all_by_name('sinatra-reloader').any?
10
+ #require 'rack/contrib/try_static'
11
+ require 'rbconfig'
12
+
13
+ #
14
+ # Kamishibai
15
+ # A manga reading webapp
16
+ #
17
+
18
+ module Kamishibai
19
+ class Webserver < Sinatra::Base
20
+ # shutdown hook, save database and bookmarks when exiting
21
+ at_exit do
22
+ $db.save
23
+ $db.save_bookmarks
24
+ end
25
+
26
+ # smaller, quicker web server
27
+ set :server, 'thin' unless RUBY_PLATFORM == 'java'
28
+
29
+ if $debug
30
+ set :environment, :development
31
+ else
32
+ set :environment, :production
33
+ end
34
+
35
+ configure :development do
36
+ enable :logging
37
+ if Gem::Specification::find_all_by_name('sinatra-reloader').any?
38
+ register Sinatra::Reloader
39
+
40
+ also_reload 'kamishibai/functions'
41
+ also_reload 'kamishibai/patches'
42
+ also_reload 'kamishibai/book'
43
+ also_reload 'kamishibai/database'
44
+
45
+ # reload webserver plug-ins
46
+ Dir.glob( settings.root + '/../**/webserver_*.rb' ) { |f|
47
+ also_reload f.gsub('.rb','')
48
+ }
49
+ end
50
+
51
+ # enable caching for public directory
52
+ set :static_cache_control, [:public, :max_age => 1]
53
+ end
54
+
55
+ configure :production do
56
+ disable :logging
57
+ disable :raise_errors
58
+
59
+ # enable caching for public directory
60
+ set :static_cache_control, [:public, :max_age => 1] # 300
61
+ end
62
+
63
+ configure do
64
+ # listen to all interface
65
+ set :bind, $settings.bind
66
+
67
+ # setup port
68
+ set :port, $settings.port
69
+
70
+
71
+ # enable session support
72
+ enable :sessions
73
+ use Rack::Session::Pool, :expire_after => 60*60*24*365
74
+
75
+ # enable http gzip
76
+ use Rack::Deflater
77
+
78
+ # setup template location
79
+ set :views, File.expand_path( settings.root + '/../../views' )
80
+
81
+ # setup public location
82
+ set :public_folder, File.expand_path(settings.views + '/../public')
83
+
84
+ # setup 2nd public location for vendor libraries
85
+ # use Rack::TryStatic,
86
+ # :root => File.expand_path(settings.views + '/../public/vendor'),
87
+ # :urls => %w[/], try: ['.html', 'index.html', '/index.html']
88
+
89
+
90
+ # authentication
91
+ use Rack::Auth::Basic, "Restricted Area" do |username, password|
92
+ [username, password] == [$settings.username, $settings.password]
93
+ end
94
+
95
+ # register mime type for static file
96
+ mime_type :jpeg, 'image/jpeg'
97
+ mime_type :png, 'image/png'
98
+ mime_type :gif, 'image/gif'
99
+ mime_type :css, 'text/css'
100
+ mime_type :javascript, 'application/javascript'
101
+ end
102
+
103
+ # setup instance variable
104
+ before do
105
+ # global instance var
106
+ end
107
+
108
+ # helper functions
109
+ helpers do
110
+ # precheck the input from url
111
+ def input_check( bookcode, page )
112
+ page = page.to_i
113
+
114
+ if $db.has_bookcode?( bookcode )
115
+ @book = $db.get_book( bookcode )
116
+ else
117
+ not_found "No such book code. #{ bookcode }"
118
+ end
119
+
120
+ unless @book.pages
121
+ not_found "Book contain no images. #{ bookcode } #{ @book.fullpath }"
122
+ end
123
+
124
+ if page < 1 or page > @book.pages
125
+ not_found "No such page. #{ page } #{ bookcode } #{ @book.fullpath }"
126
+ end
127
+
128
+ if ! FileTest.exists?( @book.fullpath )
129
+ not_found "File don't exists. #{ bookcode } #{ @book.fullpath }"
130
+ end
131
+
132
+ if ! FileTest.file?( @book.fullpath )
133
+ not_found "Not a file. #{ bookcode } #{ @book.fullpath }"
134
+ end
135
+ end
136
+
137
+ # regular expression from POST keyword search
138
+ def pregex
139
+ keyword = request['keyword'].untaint
140
+ keyword = keyword.gsub(' ','.+')
141
+ return Regexp.new( keyword, Regexp::IGNORECASE )
142
+ end
143
+ end
144
+
145
+
146
+ # detect screen dimension
147
+ post '/screen' do
148
+ session[:width] = params[:width].to_i
149
+ session[:height] = params[:height].to_i
150
+
151
+ puts "screen dimension. #{session[:width]} x #{session[:height]}" if $debug
152
+ end
153
+
154
+ # redirect index page
155
+ get '/' do
156
+ redirect '/browse/'
157
+ # if request.user_agent =~ /(android|tablet|iphone|ipad)/i
158
+ # redirect '/browse_tablet/'
159
+ # else
160
+ # redirect '/browse/'
161
+ # end
162
+ end
163
+
164
+ get '/statistics' do
165
+ haml :statistics, :layout => false
166
+ end
167
+
168
+ # browse page with folder/file navigation
169
+ get '/browse/' do
170
+ haml :browse, :layout => false
171
+ end
172
+
173
+ get '/bookinfo/*' do |bookcode|
174
+ input_check( bookcode, 1)
175
+
176
+ content_type :javascript
177
+
178
+ page = @book.page ? @book.page : 'null'
179
+
180
+ "var book = {
181
+ bookcode: '#{@book.bookcode}',
182
+ basename: '#{File.basename( @book.fullpath ).gsub(/\.cbz$/i,'').escape_html}',
183
+ title: '#{@book.title.to_s.escape_html}',
184
+ author: '#{@book.author.to_s.escape_html}',
185
+ page: #{page},
186
+ pages: #{@book.pages}
187
+ };
188
+ "
189
+ end
190
+
191
+ get '/setbookmark/*/*' do |bookcode, page|
192
+ page = page.to_i
193
+ input_check( bookcode, page )
194
+
195
+ $db.set_bookmark(bookcode, page)
196
+
197
+ "bookmarked"
198
+ end
199
+
200
+ def self.get_or_post(path, opts={}, &block)
201
+ get(path, opts, &block)
202
+ post(path, opts, &block)
203
+ end
204
+
205
+ # reader page
206
+ get '/reader/' do
207
+ cache_control :public, :must_revalidate, :max_age => 1
208
+
209
+ haml :reader, :layout => false
210
+ end
211
+
212
+ # list sources
213
+ get '/list_sources' do
214
+ content_type :javascript
215
+
216
+ %Q{
217
+ sources = [
218
+ #{$settings.srcs.collect{ |x| " \"#{x}\"" }.join(",\n")}
219
+ ]
220
+ }
221
+ end
222
+
223
+ # directory browse page
224
+ get_or_post '/lists_dir' do
225
+ content_type :text
226
+ path = File.expand_path( request['dir'].untaint )
227
+ order_by = request['order_by'].untaint
228
+
229
+ unless FileTest.exists?(path)
230
+ errmsg = "Error. No such path. #{path}"
231
+ puts errmsg if $debug
232
+ # not_found errmsg
233
+
234
+ return errmsg
235
+ end
236
+
237
+ unless File.stat(path).readable_real?
238
+ errmsg = "Error. Not readable path. #{path}"
239
+ puts errmsg if $debug
240
+ # not_found errmsg
241
+
242
+ return errmsg
243
+ end
244
+
245
+ # check and add new books, existing books will not be added
246
+ $db.add_books( [ path ], false)
247
+ # refresh db, make sure book filepath is valid
248
+ $db.refresh_bookcodes
249
+
250
+
251
+ html = "<ul id=\"ul-lists\" class=\"ul-lists\">\n"
252
+
253
+ html << "\t<li class=\"directory collapsed updir\"><a href=\"#dir=#{File.dirname(path)}\" rel=\"#{File.dirname(path)}/\"><img src=\"/images/folder-mini-up.png\" /><span>..</span></a></li>\n"
254
+
255
+
256
+
257
+ # poulate the lists with files(books) and directory
258
+ lists = []
259
+ Dir.glob(path.escape_glob + '/*').entries.sort.each { |fp|
260
+ next unless File.stat(fp).readable_real?
261
+
262
+ dir = File.dirname(fp)
263
+ f = File.basename(fp)
264
+ ext = File.extname(fp)[1..-1]
265
+
266
+ next unless pregex.match( f ) # skip unless file/dir name match the searchbox's keyword
267
+
268
+ if FileTest.directory?(fp) and File.stat(fp).executable_real?
269
+ # a directory
270
+ lists << fp
271
+
272
+ elsif ext == 'cbz' and File.stat(fp).readable_real?
273
+ # a file
274
+
275
+ bookcode = $db.get_bookcode_byfilename( fp )
276
+ unless bookcode
277
+ puts "ERROR: bookcode #{bookcode} not found! skipping to next book..."
278
+ next
279
+ end
280
+
281
+ book = $db.get_book( bookcode )
282
+
283
+ lists << book
284
+ end
285
+ }
286
+
287
+ # sort by order
288
+ lists2 = {}
289
+ case order_by
290
+ when 'size'
291
+ lists.each { |x|
292
+
293
+ if x.class == Book
294
+ lists2[x] = x.size
295
+ else
296
+ # x == full path of file or directory
297
+ lists2[x] = 0
298
+ end
299
+ }
300
+
301
+ lists2 = lists2.sort { |a, b| b[1] <=> a[1] }
302
+
303
+ when 'date'
304
+ lists.each { |x|
305
+ if x.class == Book
306
+ lists2[x] = x.mtime
307
+ else
308
+ # x == full path of file or directory
309
+ lists2[x] = File.stat(x).mtime.to_i
310
+ end
311
+ }
312
+ lists2 = lists2.sort { |a, b| b[1] <=> a[1] }
313
+
314
+ else
315
+ # by name, default
316
+ lists.each { |x|
317
+ if x.class == Book
318
+ lists2[x] = File.basename( x.fullpath )
319
+ else
320
+ # x == full path of file or directory
321
+ lists2[x] = File.basename( x )
322
+ end
323
+ }
324
+ lists2 = lists2.sort { |a, b| a[1] <=> b[1] }
325
+ end
326
+
327
+ # create html from lists(2)
328
+ lists2.each { |item, dat|
329
+
330
+ if item.class == String
331
+ # a directory
332
+
333
+ li_classes = ['directory', 'collapsed']
334
+
335
+ if item == 'Trash'
336
+ el_id = 'id="trash"'
337
+ li_classes << 'trash'
338
+ if Dir.glob(item.escape_glob + '/*.cbz').entries.length > 0
339
+ icon = 'trash-full-mini.png'
340
+ else
341
+ icon = 'trash-empty-mini.png'
342
+ end
343
+ else
344
+ icon = 'folder-mini.png'
345
+ end
346
+
347
+ html << "\t<li class=\"" + li_classes.join(' ') + "\" #{el_id}><a href=\"#dir=#{item}\" rel=\"#{item}/\"><img src=\"/images/" + icon + "\" /><span>#{File.basename(item)}</span></a></li>\n"
348
+
349
+ elsif item.class == Book
350
+ # a file
351
+
352
+ book = item
353
+ bookcode = book.bookcode
354
+ fp = book.fullpath
355
+ f = File.basename(fp)
356
+ ext = File.extname(fp)[1..-1]
357
+
358
+ img = "<img class='lazy fadeIn fadeIn-1s fadeIn-Delay-Xs' data-original='/thumbnail/#{bookcode}' alt='Loading...' />"
359
+
360
+ if book.page
361
+ # prepare the value for the visual read progress
362
+ page = book.page
363
+ pages = book.pages
364
+
365
+ bn = (1.0*page/pages)*100
366
+ pc = bn - bn % 10
367
+
368
+ # read percentage css class
369
+ if pc > 0
370
+ readstate = 'read' + pc.to_i.to_s
371
+ else
372
+ readstate = 'read5'
373
+ end
374
+
375
+ href = '/tablet#book=' + bookcode + '&page=' + page.to_s
376
+ else
377
+ readstate = 'read0'
378
+ href = '/tablet#book=' + bookcode
379
+ end
380
+
381
+ html << "\t<li class=\"file ext_#{ext}\"><a href=\"#{href}\" bookcode=\"#{bookcode}\" rel=\"#{fp}\">#{img}<span class=\"#{readstate}\">#{f.escape_html}</span><span class=\"badge badge-info bookpages\">#{book.pages}</span></a></li>\n"
382
+ end
383
+ }
384
+
385
+ return html
386
+ end
387
+
388
+ # directory browse page
389
+ post '/jqueryFileTree' do
390
+ content_type :text
391
+ path = request['dir'].untaint
392
+
393
+ html = "<ul class=\"jqueryFileTree\" style=\"display: none;\">\n"
394
+
395
+ if FileTest.exists?(File.expand_path(path))
396
+
397
+ # chdir() to user requested dir (root + "/" + dir)
398
+ Dir.chdir(File.expand_path(path).untaint);
399
+
400
+ #loop through all directories
401
+ Dir.glob("*") { |x|
402
+ next unless File.directory?(x.untaint)
403
+ next unless File.stat(x.untaint).readable_real? and File.stat(x.untaint).executable_real?
404
+
405
+ fp = path + x
406
+ html << "\t<li class=\"directory collapsed\"><a href=\"#\" rel=\"#{fp}/\" onclick=\"selected_dir('#{fp}');\">#{x.escape_html}</a></li>\n";
407
+ }
408
+
409
+ end
410
+
411
+ html << "</ul>\n"
412
+
413
+ return html
414
+ end
415
+
416
+ # cbz thumbnail
417
+ get '/thumbnail/*' do |bookcode|
418
+ cache_control :public, :must_revalidate, :max_age => 3600
419
+
420
+ # check and setup the variable
421
+ input_check( bookcode, 1 )
422
+
423
+ content_type :jpeg
424
+
425
+ # generate thumbnail
426
+ image = mk_thumb( @book.fullpath )
427
+
428
+ return image
429
+ end
430
+
431
+ # cbz file loader
432
+ get '/cbz/*/*' do |bookcode, page|
433
+ # # fake delay
434
+ # if $imgNum
435
+ # $imgNum += 1
436
+ # else
437
+ # $imgNum = 1
438
+ # end
439
+ # pp $imgNum
440
+
441
+ page = page.to_i
442
+ cache_control :public, :must_revalidate, :max_age => 3600
443
+
444
+ input_check( bookcode, page )
445
+
446
+ # width/height
447
+ quality = $settings.default_image_quality
448
+ width = session[:width].to_i
449
+ height = session[:height].to_i
450
+ # hard set to hd
451
+ # width = 1080
452
+ # height = 1920
453
+
454
+ # fix/prevent invalid input
455
+ quality = 0 if quality < 0
456
+ quality = 100 if quality > 100
457
+ width = 0 if width < 0 # 0 will be treated as do not resize
458
+ height = 0 if height < 0
459
+
460
+ # image type, image data
461
+ image = open_cbz( @book.fullpath, page)
462
+
463
+ # set content type, png/jpeg/png/gif/etc
464
+ itype = image_type( image )
465
+ content_type itype
466
+
467
+ # # fake delay
468
+ # Thread.new {
469
+ # sleep 7
470
+ # $imgNum = 0
471
+ # sleep 7
472
+ # $imgNum = 0
473
+ # }
474
+ # sleep 1.5 * $imgNum
475
+ # $imgNum -= 1
476
+
477
+ if width > 0 and height > 0 and $settings.image_resize
478
+ # resize image
479
+ img_resize( image, width, height, { :quality => quality })
480
+ else
481
+ # give raw
482
+ image
483
+ end
484
+ end
485
+
486
+ get '/list_db' do
487
+ html = "<html><body>\n"
488
+ p $db.books.length
489
+ $db.books.each { |bookcode, book|
490
+ html << "<pre>#{bookcode} #{book.page.to_s.rjust(3)}/#{book.pages} #{book.fullpath}</pre>\n"
491
+ }
492
+ html << "</body></html>"
493
+ html
494
+ end
495
+ end
496
+ end
497
+
498
+
499
+ # load webserver plug-ins
500
+ Dir.glob( settings.root + '/../**/webserver_*.rb' ) { |f|
501
+ require f.gsub('.rb','')
502
+ }