kamishibai 0.6.2

Sign up to get free protection for your applications and to get access to all the features.
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
+ }