iodine 0.7.56 → 0.7.57
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 +4 -4
- data/CHANGELOG.md +4 -0
- data/examples/bates/README.md +3 -0
- data/examples/bates/config.ru +342 -0
- data/examples/bates/david+bold.pdf +0 -0
- data/examples/bates/public/drop-pdf.png +0 -0
- data/examples/bates/public/index.html +600 -0
- data/ext/iodine/extconf.rb +1 -1
- data/ext/iodine/iodine.c +1 -1
- data/lib/iodine/version.rb +1 -1
- data/lib/iodine.rb +1 -1
- metadata +8 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8efbe9762562b087f91eae2c69bf2d79e574b4119af4c7163f34b45b2d0aaa8e
|
|
4
|
+
data.tar.gz: 8ae978cabf57c0a622e97b3f8128a68a02150fca5c11ee4d00c52f179aaa20bb
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: c14ddb0e815d811fda11d87cc52631d2b63639d39ff3f1c4382b7eeee39886ad5f44a809f20b488983fdd62ed239c761293e832e22653af950154880667b2dd3
|
|
7
|
+
data.tar.gz: 644e50ef7a3d213c0d44aa73dfebebf7db5c6410b39be8714844632bab6d000e608bd25160fb76c8aea66f9afd0257d50036d4f4cd6c967c4c7976aa5d3d9893
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,10 @@ Please notice that this change log contains changes for upcoming releases as wel
|
|
|
6
6
|
|
|
7
7
|
## Changes:
|
|
8
8
|
|
|
9
|
+
#### Change log v.0.7.57 (2023-09-04)
|
|
10
|
+
|
|
11
|
+
**Fix**: Fixes possible name collision when loading gem (`.rb` vs. `.so` loading). Credit to @noraj (Alexandre ZANNI) for opening issue #148. Credit to @janbiedermann (Jan Biedermann) for discovering the root cause and offering a solution.
|
|
12
|
+
|
|
9
13
|
#### Change log v.0.7.56 (2023-07-07)
|
|
10
14
|
|
|
11
15
|
**Support**: Adds teapot support (HTTP code 418). Credit to Aleksandar N. Kostadinov (@akostadinov) for issue #144 and PR #145.
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
# Bates numbering with CombinePDF and Rack
|
|
2
|
+
|
|
3
|
+
This folder holds a demo application combining Iodine's `X-Sendfile` support with the [`combine_pdf` gem](https://github.com/boazsegev/combine_pdf) to create a "bates numbering" tool that is common to the one used by some court systems (such as Israeli courts).
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
require 'combine_pdf'
|
|
2
|
+
require 'rack'
|
|
3
|
+
require 'base64'
|
|
4
|
+
# require_relative 'pdf_controller'
|
|
5
|
+
|
|
6
|
+
module BatesAPP
|
|
7
|
+
# This is the HTTP response object according to the Rack specification.
|
|
8
|
+
STATIC_RESPONSE = [200, { 'Content-Type' => 'text/html', 'X-Sendfile' => File.expand_path('./public/index.html') }.freeze, []].freeze
|
|
9
|
+
WS_RESPONSE = [403, { 'Content-Type' => 'text/html' }, "Forbidden"]
|
|
10
|
+
|
|
11
|
+
# this is function will be called by the Rack server (iodine) for every request.
|
|
12
|
+
def self.call env
|
|
13
|
+
req = Rack::Request.new(env)
|
|
14
|
+
# check if this is an upgrade request (WebsSocket / SSE).
|
|
15
|
+
return WS_RESPONSE if(env['rack.upgrade?'.freeze])
|
|
16
|
+
# simply return the RESPONSE object, no matter what request was received.
|
|
17
|
+
return STATIC_RESPONSE if req.params.empty?
|
|
18
|
+
return bates(req.params)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def self.bates(params)
|
|
23
|
+
params['file'] = params['file'].values # convert to Array
|
|
24
|
+
# catch any exception and tell the user which PDF caused the exception.
|
|
25
|
+
begin
|
|
26
|
+
# set the file_name variable
|
|
27
|
+
# this will be used in case an exception is caught
|
|
28
|
+
# to state which file caused the exception.
|
|
29
|
+
file_name = ''
|
|
30
|
+
|
|
31
|
+
# container for the complete pdf
|
|
32
|
+
# the complete pdf container will hold all
|
|
33
|
+
# the merged pdf data.
|
|
34
|
+
completed_pdf = CombinePDF.new
|
|
35
|
+
|
|
36
|
+
# get the output file name
|
|
37
|
+
# this will be used to name the download for the client's browser
|
|
38
|
+
output_name = params['bates']['output'].to_s + '.pdf'
|
|
39
|
+
output_name = "unknown.pdf" if output_name == '.pdf'
|
|
40
|
+
|
|
41
|
+
# get some paramaters that will be used while combining pages
|
|
42
|
+
params['bates'] ||= {}
|
|
43
|
+
first_page_number = params['bates']['first_page_number'].to_i
|
|
44
|
+
first_index_number = params['bates']['first_index_number'].to_i
|
|
45
|
+
|
|
46
|
+
# we will add an option for the stamping to ignore the first pdf
|
|
47
|
+
# this is useful for the court cases that use bates numbering
|
|
48
|
+
# (the "cover PDF" will be the briefs of submissions that contain exhibits to be bates stamped)
|
|
49
|
+
ignore_first_file = params['bates']['first_pdf_is_cover']
|
|
50
|
+
first_page = nil
|
|
51
|
+
|
|
52
|
+
# we will pick some data up while running combining the different pdf files.
|
|
53
|
+
# this will be used for the table of contents later on.
|
|
54
|
+
pdfs_pages_count = []
|
|
55
|
+
pdf_dates = []
|
|
56
|
+
pdf_titles = []
|
|
57
|
+
|
|
58
|
+
# we will be creating title pages before each PDF file.
|
|
59
|
+
# the title pages will be sized using the mediabox variable
|
|
60
|
+
# wich will be set with the dimentions of each pdf file's first page.
|
|
61
|
+
mediabox = nil
|
|
62
|
+
|
|
63
|
+
# register UNICODE fonts if necessary
|
|
64
|
+
# in this example I will register the Hebrew font David from an existing PDF file.
|
|
65
|
+
unless CombinePDF::Fonts.get_font :my_new_david
|
|
66
|
+
# if your running Rails, consider Rails.root instead of Root
|
|
67
|
+
fonts = CombinePDF.new(File.expand_path('./david+bold.pdf').to_s).fonts(true)
|
|
68
|
+
# I know the first font is David regular, after using the
|
|
69
|
+
# ruby console and looking at the fonts array for the file.
|
|
70
|
+
CombinePDF.register_font_from_pdf_object :my_new_david, fonts[0]
|
|
71
|
+
# the second font of the array was the latin font for a newline and space... useless
|
|
72
|
+
# the third was the david bold. I will now add that font.
|
|
73
|
+
CombinePDF.register_font_from_pdf_object :my_new_david_bold, fonts[2]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# iterate through the different files sent from the client's browser's web form
|
|
77
|
+
params['file'].each do |v|
|
|
78
|
+
# set the file_name variable in case an exception will be raised
|
|
79
|
+
file_name = v['name']
|
|
80
|
+
|
|
81
|
+
# parse the pdf data
|
|
82
|
+
# we will use the CombinePDF.parse method which allows us
|
|
83
|
+
# to parse data without saving the PDF to the file system.
|
|
84
|
+
# our javascript encoded the file data using base64, which we will need to decode.
|
|
85
|
+
# (this is specific to my form which uses the HTML5 File API in this specific manner)
|
|
86
|
+
# begin
|
|
87
|
+
puts "parsing file: #{file_name}"
|
|
88
|
+
pdf_file = CombinePDF.parse(
|
|
89
|
+
Base64.urlsafe_decode64(
|
|
90
|
+
v['data'].slice('data:application/pdf;base64,'.length,
|
|
91
|
+
v['data'].length)
|
|
92
|
+
)
|
|
93
|
+
)
|
|
94
|
+
# rescue
|
|
95
|
+
# puts "Failed when parsing #{file_name} with data:\n#{v['data'].inspect}"
|
|
96
|
+
# raise
|
|
97
|
+
# end
|
|
98
|
+
|
|
99
|
+
# we will use the pages array a few times, so in order to avoid
|
|
100
|
+
# recomputing the array every time, we will save it to a local variable.
|
|
101
|
+
pdf_file_pages = pdf_file.pages
|
|
102
|
+
|
|
103
|
+
# we will add the page count to the page count array,
|
|
104
|
+
# used by the table of contents
|
|
105
|
+
pdfs_pages_count << pdf_file_pages.length
|
|
106
|
+
|
|
107
|
+
######
|
|
108
|
+
# create and add title page to arrays.
|
|
109
|
+
|
|
110
|
+
if ignore_first_file && first_page.nil?
|
|
111
|
+
# if the first PDF file is a "cover page" PDF,
|
|
112
|
+
# we will not add a title, nor add the file.
|
|
113
|
+
# instead we will save the data in a variable to add it after we're done
|
|
114
|
+
pdf_dates << ''
|
|
115
|
+
pdf_titles << ''
|
|
116
|
+
first_page = pdf_file
|
|
117
|
+
else
|
|
118
|
+
# set title page mediabox size
|
|
119
|
+
# the mediabox data (page size) is contained in the page's
|
|
120
|
+
# :CropBox or :MediaBox keys (pages are Hash objects).
|
|
121
|
+
mediabox ||= pdf_file_pages[0][:CropBox] || pdf_file_pages[0][:MediaBox]
|
|
122
|
+
|
|
123
|
+
# create a title page unless we're only merging or there is no indexing
|
|
124
|
+
if params['bates']['should_index'] && params['bates']['numbering'] != 3
|
|
125
|
+
|
|
126
|
+
# create an empty page object
|
|
127
|
+
title_page = CombinePDF.create_page mediabox
|
|
128
|
+
|
|
129
|
+
# write the content to the title page.
|
|
130
|
+
# we will be using the I18n.t shortcut to write some of the data.
|
|
131
|
+
# the rest of the data, like the title, we got from the form.
|
|
132
|
+
title_page.textbox("#{params['bates']['title_type']} #{pdfs_pages_count.length + first_index_number - (ignore_first_file ? 2 : 1)}",
|
|
133
|
+
max_font_size: 34,
|
|
134
|
+
font: :my_new_david,
|
|
135
|
+
y: (mediabox[3] - mediabox[1]) / 2) unless params['bates']['title_type'].to_s.empty?
|
|
136
|
+
title_page.textbox v['title'].to_s, max_font_size: 36, font: :my_new_david_bold
|
|
137
|
+
title_page.textbox v['date'].to_s, max_font_size: 24, font: :my_new_david, height: (mediabox[3] - mediabox[1]) / 2
|
|
138
|
+
|
|
139
|
+
# we will add the page object to the completed pdf object.
|
|
140
|
+
# notice that page objects are created as "floating" pages,
|
|
141
|
+
# not attached to any specific PDF file/object.
|
|
142
|
+
completed_pdf << title_page
|
|
143
|
+
|
|
144
|
+
# we will add some data that will be used to create the
|
|
145
|
+
# table of contents at a later stage.
|
|
146
|
+
page_count = pdfs_pages_count.pop + 1
|
|
147
|
+
pdfs_pages_count << page_count
|
|
148
|
+
pdf_dates << v['date'].to_s
|
|
149
|
+
pdf_titles << v['title'].to_s
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# now we are ready to add the pdf file data to the completed pdf object.
|
|
153
|
+
# there is no need to add each page, we can add the pdf as a whole.
|
|
154
|
+
# (it's actually faster, as the PDF page catalog isn't recomputed for each page)
|
|
155
|
+
completed_pdf << pdf_file
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
##########
|
|
160
|
+
# create the index pdf...
|
|
161
|
+
# ...unless we're only merging or there is no indexing
|
|
162
|
+
if params['bates']['should_index'] && params['bates']['numbering'].to_i != 3
|
|
163
|
+
|
|
164
|
+
# set the fonts and formatting for the table of contents.
|
|
165
|
+
#
|
|
166
|
+
# also, add an empty array for the table data.
|
|
167
|
+
#
|
|
168
|
+
# the table data array will contain arrays of String objects, each one
|
|
169
|
+
# corresponding to a row in the table.
|
|
170
|
+
table_options = { font: :my_new_david,
|
|
171
|
+
header_font: :my_new_david_bold,
|
|
172
|
+
max_font_size: 12,
|
|
173
|
+
column_widths: (params['bates']['date_header'].to_s.empty? ? [3, 40, 4] : [3, 10, 30, 4]),
|
|
174
|
+
table_data: [] }
|
|
175
|
+
|
|
176
|
+
# set the table header array.
|
|
177
|
+
# this is an array of strings.
|
|
178
|
+
# to chose the localized strings
|
|
179
|
+
table_options[:headers] = [params['bates']['number_header'],
|
|
180
|
+
(params['bates']['date_header'].to_s.empty? ? nil : params['bates']['date_header']),
|
|
181
|
+
params['bates']['title_header'].to_s,
|
|
182
|
+
params['bates']['page_header'].to_s]
|
|
183
|
+
table_options[:headers].compact!
|
|
184
|
+
|
|
185
|
+
# by default, there are 25 rows per page for table pdf created by CombinePDF
|
|
186
|
+
# we can override this in the formatting (but we didn't).
|
|
187
|
+
#
|
|
188
|
+
# the 25 rows include 1 header row per page - so there are only 24 effective rows.
|
|
189
|
+
#
|
|
190
|
+
# we will calculate how many pages the table of contents pdf will have once completes,
|
|
191
|
+
# so we can add the count to the page numbers in the index.
|
|
192
|
+
index_page_length = pdfs_pages_count.length / 24
|
|
193
|
+
index_page_length += 1 if pdfs_pages_count.length % 24 > 0
|
|
194
|
+
|
|
195
|
+
# set the page number for the first entry in the table of contents.
|
|
196
|
+
page_number = first_page_number + index_page_length
|
|
197
|
+
|
|
198
|
+
# set the index count to 0, we will use it to change the index for each entry.
|
|
199
|
+
# we need a different variable in case the first PDF file is a "cover page".
|
|
200
|
+
index_count = 0
|
|
201
|
+
|
|
202
|
+
# iterate over the data we collected before and add it to the table data.
|
|
203
|
+
pdfs_pages_count.each_index do |i|
|
|
204
|
+
# add the data unless it is set to be ignored
|
|
205
|
+
unless ignore_first_file
|
|
206
|
+
|
|
207
|
+
# add an array of strings to the :table_data array,
|
|
208
|
+
# representing a row in our table.
|
|
209
|
+
# remember there might not be a date column.
|
|
210
|
+
if params['bates']['date_header'].to_s.empty?
|
|
211
|
+
table_options[:table_data] << [(first_index_number + index_count).to_s,
|
|
212
|
+
pdf_titles[i], page_number]
|
|
213
|
+
else
|
|
214
|
+
table_options[:table_data] << [(first_index_number + index_count).to_s,
|
|
215
|
+
pdf_dates[i],
|
|
216
|
+
pdf_titles[i], page_number]
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
# if the data was added to the index table, bump the index count
|
|
220
|
+
index_count += 1
|
|
221
|
+
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
# make sure future data will not be ignored
|
|
225
|
+
ignore_first_file = false
|
|
226
|
+
|
|
227
|
+
# add the page count to the page number, so that the next
|
|
228
|
+
# index's page number is up to date.
|
|
229
|
+
page_number += pdfs_pages_count[i]
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# if out current locale is hebrew, which is a right to left language,
|
|
233
|
+
# set the direction for the table to Right-To-left (:rtl).
|
|
234
|
+
#
|
|
235
|
+
# notice that RTL text should be automatically recognized, but that
|
|
236
|
+
# feature isn't available (and shouldn't be available) for tables.
|
|
237
|
+
table_options[:direction] = :rtl if params['bates']['dir'] == 'rtl'
|
|
238
|
+
|
|
239
|
+
# if there is table data, we will create an index pdf.
|
|
240
|
+
unless table_options[:table_data].empty?
|
|
241
|
+
|
|
242
|
+
# create the index PDF from the table data and options we have.
|
|
243
|
+
index_pdf = CombinePDF.create_table table_options
|
|
244
|
+
|
|
245
|
+
# We will now add the words "Table of Contents" (or the I18n equivilant)
|
|
246
|
+
# to the first page of our new index_pdf PDF object.
|
|
247
|
+
#
|
|
248
|
+
# the table PDF object was created by CombinePDF using writable PDF pages,
|
|
249
|
+
# so we have properties like .mediabox and methods like .textbox
|
|
250
|
+
# at our disposal.
|
|
251
|
+
|
|
252
|
+
# get the first page of the index_pdf object, we will use this reference a lot.
|
|
253
|
+
title_page = index_pdf.pages[0]
|
|
254
|
+
|
|
255
|
+
# write the textbox, using the mediabox page data [x,y,width,height] to place
|
|
256
|
+
# the text we want to write.
|
|
257
|
+
#
|
|
258
|
+
# we will use the I18n.t shortcut to choose the text to write down.
|
|
259
|
+
title_page.textbox params['bates']['index_title'],
|
|
260
|
+
y: ((title_page.mediabox[3] - title_page.mediabox[1]) * 0.91),
|
|
261
|
+
height: ((title_page.mediabox[3] - title_page.mediabox[1]) * 0.03),
|
|
262
|
+
font: :my_new_david,
|
|
263
|
+
max_font_size: 36,
|
|
264
|
+
text_valign: :bottom
|
|
265
|
+
|
|
266
|
+
# now we will add the index_pdf to the BEGINING of the completed pdf.
|
|
267
|
+
# for this we will use the >> operator instead of the << operator.
|
|
268
|
+
completed_pdf >> index_pdf
|
|
269
|
+
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# add first file if it was skipped
|
|
275
|
+
completed_pdf >> first_page unless first_page.nil?
|
|
276
|
+
|
|
277
|
+
#####
|
|
278
|
+
# number pages
|
|
279
|
+
# unless no numbering
|
|
280
|
+
if params['bates']['numbering'].to_i != 3
|
|
281
|
+
# list the numbering options
|
|
282
|
+
numbering_options = [[:top, :bottom], [:top_left, :bottom_left], [:top_right, :bottom_right]]
|
|
283
|
+
|
|
284
|
+
# set the first visible page number to the page where numbering starts
|
|
285
|
+
# this assumes that the bates numbering include the numbering of the "cover page",
|
|
286
|
+
# yet at the same time the numbering isn't visible on the "cover page"
|
|
287
|
+
first_page_number += pdfs_pages_count[0] if params['bates']['first_pdf_is_cover'] && params['bates']['skip_cover']
|
|
288
|
+
|
|
289
|
+
# call the page numbering method and
|
|
290
|
+
# add the special properties we want for the textbox
|
|
291
|
+
completed_pdf.number_pages(start_at: first_page_number,
|
|
292
|
+
page_range: params['bates']['skip_cover'] ? (pdfs_pages_count[0].to_i..-1) : nil,
|
|
293
|
+
font_name: :my_new_david,
|
|
294
|
+
font_size: 14,
|
|
295
|
+
font_color: [0, 0, 0.4],
|
|
296
|
+
box_color: [0.8, 0.8, 0.8],
|
|
297
|
+
border_width: 1,
|
|
298
|
+
border_color: [0.3, 0.3, 0.3],
|
|
299
|
+
box_radius: 8,
|
|
300
|
+
number_location: numbering_options[params['bates']['numbering'].to_i],
|
|
301
|
+
opacity: 0.75)
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
# send the completed PDF to the client.
|
|
305
|
+
# if the completed PDF is empty, raise an error.
|
|
306
|
+
if completed_pdf.pages.empty?
|
|
307
|
+
# inform the client there was an unknown error.
|
|
308
|
+
response = STATIC_RESPONSE.dup
|
|
309
|
+
response[1] = response[1].dup
|
|
310
|
+
response[1]['set-cookie'] = "Unknown error - 0 pages."
|
|
311
|
+
return response
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# make sure the PDF version is high enough for the opacity we used in the page numbering.
|
|
315
|
+
completed_pdf.version = [completed_pdf.version, 1.6].max
|
|
316
|
+
|
|
317
|
+
# we will format the PDF to a pdf file WITHOUT saving it to the file system,
|
|
318
|
+
# using the .to_pdf method (instead of the .save method).
|
|
319
|
+
# we will send the raw PDF data stream.
|
|
320
|
+
return [200, {'content-type' => "application/pdf",
|
|
321
|
+
'content-disposition' => "attachment; filename=\"#{output_name}\""}, completed_pdf.to_pdf]
|
|
322
|
+
|
|
323
|
+
rescue Exception => e
|
|
324
|
+
puts e.message, e.backtrace
|
|
325
|
+
puts "The file causing the exception is: #{file_name}"
|
|
326
|
+
# if an exception was raised, tell the user which PDF caused the exception
|
|
327
|
+
response = STATIC_RESPONSE.dup
|
|
328
|
+
response[1] = response[1].dup
|
|
329
|
+
response[1]['set-cookie'] = "notice=Unsupported or error reading file: #{file_name}"
|
|
330
|
+
return response
|
|
331
|
+
end
|
|
332
|
+
end
|
|
333
|
+
end
|
|
334
|
+
|
|
335
|
+
Iodine.workers = 1
|
|
336
|
+
Iodine.threads = 5
|
|
337
|
+
Iodine::DEFAULT_SETTINGS[:public] ||= './public'
|
|
338
|
+
|
|
339
|
+
p Iodine::DEFAULT_SETTINGS
|
|
340
|
+
Iodine::DEFAULT_SETTINGS[:max_body] = 70 if Iodine::DEFAULT_SETTINGS[:max_body].to_i < 5
|
|
341
|
+
|
|
342
|
+
run BatesAPP
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en"><head>
|
|
3
|
+
<title>Bates Numbering for Court</title>
|
|
4
|
+
<meta charset='utf-8'>
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=no, minimal-ui=yes">
|
|
6
|
+
<style>
|
|
7
|
+
html, body {
|
|
8
|
+
height: 100%; width: 100%;
|
|
9
|
+
padding: 0; margin: 0;
|
|
10
|
+
color: #111;
|
|
11
|
+
background-color: #99A3CE;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
h2, h3 {
|
|
15
|
+
font-size: 1.2em;
|
|
16
|
+
background-color: #44518E;
|
|
17
|
+
color: #C6CCE7;
|
|
18
|
+
}
|
|
19
|
+
h2 a, h3 a
|
|
20
|
+
{
|
|
21
|
+
color: inherit;
|
|
22
|
+
text-decoration: none;
|
|
23
|
+
}
|
|
24
|
+
h2 a:hover, h3 a:hover
|
|
25
|
+
{
|
|
26
|
+
color: #00f;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
h1 {
|
|
30
|
+
font-size: 1.5em;
|
|
31
|
+
text-align: center;
|
|
32
|
+
background-color: #1B2864;
|
|
33
|
+
color: #99A3CE;
|
|
34
|
+
margin:0; padding: 0.5em;
|
|
35
|
+
}
|
|
36
|
+
h2 {
|
|
37
|
+
border-radius: 0.5em;
|
|
38
|
+
text-align: center;
|
|
39
|
+
}
|
|
40
|
+
h3 {
|
|
41
|
+
text-align: left;
|
|
42
|
+
}
|
|
43
|
+
.hide {
|
|
44
|
+
display: none !important;
|
|
45
|
+
}
|
|
46
|
+
span.en {
|
|
47
|
+
direction: ltr;
|
|
48
|
+
display: inline-block;
|
|
49
|
+
text-align: left;
|
|
50
|
+
width: auto;
|
|
51
|
+
}
|
|
52
|
+
#notice
|
|
53
|
+
{
|
|
54
|
+
z-index: 99999;
|
|
55
|
+
position: absolute;
|
|
56
|
+
top: 0; left: 0;
|
|
57
|
+
background-color: rgba(0, 0, 0, 0.75);
|
|
58
|
+
box-sizing: border-box;
|
|
59
|
+
padding: 1em 0;
|
|
60
|
+
text-align: center;
|
|
61
|
+
height: 100px;
|
|
62
|
+
width: 100%;
|
|
63
|
+
overflow-y: auto;
|
|
64
|
+
color: #fff; font-weight: bold;
|
|
65
|
+
transition:All 0.25s ease;
|
|
66
|
+
-webkit-transition:All 0.25s ease;
|
|
67
|
+
-moz-transition:All 0.25s ease;
|
|
68
|
+
-o-transition:All 0.25s ease;
|
|
69
|
+
-ms-transition:All 0.25s ease;
|
|
70
|
+
transform-origin: 0 50%;
|
|
71
|
+
-webkit-transform-origin: 50% 0; /* Chrome, Safari, Opera */
|
|
72
|
+
-ms-transform-origin: 50% 0; /* IE 9 */
|
|
73
|
+
-moz-transform-origin: 50% 0;
|
|
74
|
+
-o-transform-origin: 50% 0;
|
|
75
|
+
transform: scale(1, 1);
|
|
76
|
+
-webkit-transform: scale(1, 1);
|
|
77
|
+
-moz-transform: scale(1, 1);
|
|
78
|
+
-o-transform: scale(1, 1);
|
|
79
|
+
-ms-transform: scale(1, 1);
|
|
80
|
+
}
|
|
81
|
+
#notice.hidden
|
|
82
|
+
{
|
|
83
|
+
transform: scale(1, 0);
|
|
84
|
+
-webkit-transform: scale(1, 0);
|
|
85
|
+
-moz-transform: scale(1, 0);
|
|
86
|
+
-o-transform: scale(1, 0);
|
|
87
|
+
-ms-transform: scale(1, 0);
|
|
88
|
+
}
|
|
89
|
+
#notice .notice_close
|
|
90
|
+
{
|
|
91
|
+
display:block;
|
|
92
|
+
position: absolute;
|
|
93
|
+
top:0; left:0;
|
|
94
|
+
padding:0.5em;
|
|
95
|
+
height:100%;
|
|
96
|
+
width:5em;
|
|
97
|
+
text-align:left;
|
|
98
|
+
cursor: pointer;
|
|
99
|
+
border-right:1px solid #eee;
|
|
100
|
+
color: #ccc;
|
|
101
|
+
}
|
|
102
|
+
#notice .notice_close:hover
|
|
103
|
+
{
|
|
104
|
+
color: #eee;
|
|
105
|
+
background-color: rgba(255, 255, 255, 0.25);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
#content {
|
|
109
|
+
background-color: #fff;
|
|
110
|
+
color: #111;
|
|
111
|
+
min-height: 100%;
|
|
112
|
+
padding: 4em 1.5em;
|
|
113
|
+
margin: 0 1em;
|
|
114
|
+
overflow-x: hidden;
|
|
115
|
+
box-sizing: border-box;
|
|
116
|
+
}
|
|
117
|
+
#content h1, #content h2, #content h3 {
|
|
118
|
+
background-color: #1B2864;
|
|
119
|
+
color: #99A3CE;
|
|
120
|
+
padding: 0.2em 1em;
|
|
121
|
+
margin: 0.2em 0;
|
|
122
|
+
}
|
|
123
|
+
#content h1 {
|
|
124
|
+
padding: 0.5em;
|
|
125
|
+
margin: 0;
|
|
126
|
+
}
|
|
127
|
+
#content p {
|
|
128
|
+
padding: 0.5em;
|
|
129
|
+
margin: 0;
|
|
130
|
+
}
|
|
131
|
+
#content a {
|
|
132
|
+
color: #0000aa;
|
|
133
|
+
text-decoration: none;
|
|
134
|
+
}
|
|
135
|
+
#content a:hover {
|
|
136
|
+
color: blue;
|
|
137
|
+
text-decoration: underline;
|
|
138
|
+
background: white;
|
|
139
|
+
position: relative;
|
|
140
|
+
top: 1px;
|
|
141
|
+
left: 2px;
|
|
142
|
+
}
|
|
143
|
+
#content h1 a, #content h2 a, #content h3 a, #content table th a {
|
|
144
|
+
color: #aaaaff;
|
|
145
|
+
}
|
|
146
|
+
#content h1 a:hover, #content h2 a:hover, #content h3 a:hover, #content table th a:hover {
|
|
147
|
+
background: inherit;
|
|
148
|
+
color: #ddddff;
|
|
149
|
+
}
|
|
150
|
+
#content #notice {
|
|
151
|
+
background: #ffaadd;
|
|
152
|
+
color: #551111;
|
|
153
|
+
margin: 0.3em 15%;
|
|
154
|
+
padding: 1em 0.5em;
|
|
155
|
+
text-align: center;
|
|
156
|
+
border-radius: 1em;
|
|
157
|
+
font-weight: bold;
|
|
158
|
+
font-size: 1.1em;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.desc, form {
|
|
162
|
+
text-align: justify;
|
|
163
|
+
display: block;
|
|
164
|
+
margin: 0.5em 15%; padding: 0 0 0.5em 0;
|
|
165
|
+
background: #eeeefa;
|
|
166
|
+
color: #111111;
|
|
167
|
+
border-radius: 1em;
|
|
168
|
+
}
|
|
169
|
+
.with_border
|
|
170
|
+
{
|
|
171
|
+
border: 1px solid #aab;
|
|
172
|
+
}
|
|
173
|
+
.desc p, form p {
|
|
174
|
+
padding: 0.2em;
|
|
175
|
+
margin: 0.5em 0;
|
|
176
|
+
}
|
|
177
|
+
form input[type='submit'] {
|
|
178
|
+
margin: 0.5em 20%;
|
|
179
|
+
width: 60%;
|
|
180
|
+
color: #000033;
|
|
181
|
+
background: #eeeeff;
|
|
182
|
+
}
|
|
183
|
+
form table {
|
|
184
|
+
width: 100%;
|
|
185
|
+
table-layout: fixed;
|
|
186
|
+
border-collapse: collapse;
|
|
187
|
+
}
|
|
188
|
+
form table th {
|
|
189
|
+
background: #bbb;
|
|
190
|
+
color: #111111;
|
|
191
|
+
}
|
|
192
|
+
form table td {
|
|
193
|
+
width: 2%;
|
|
194
|
+
border-collapse: collapse;
|
|
195
|
+
border-top: #bbbbcc dotted 1px;
|
|
196
|
+
border-bottom: #bbbbcc dotted 1px;
|
|
197
|
+
padding: 1px 2px;
|
|
198
|
+
}
|
|
199
|
+
form table tr {
|
|
200
|
+
border-collapse: collapse;
|
|
201
|
+
}
|
|
202
|
+
form table .en {
|
|
203
|
+
direction: ltr;
|
|
204
|
+
}
|
|
205
|
+
form table input, form table select {
|
|
206
|
+
width: 100%;
|
|
207
|
+
margin: 0.5em 0;
|
|
208
|
+
padding: 0;
|
|
209
|
+
color: #000033;
|
|
210
|
+
background: #eeeeff;
|
|
211
|
+
}
|
|
212
|
+
form table .no_resize input, form table .no_resize select {
|
|
213
|
+
width: auto;
|
|
214
|
+
margin: 0.5em 0;
|
|
215
|
+
padding: inherit;
|
|
216
|
+
}
|
|
217
|
+
form .field {
|
|
218
|
+
width: 100%;
|
|
219
|
+
display: block;
|
|
220
|
+
text-align: justify;
|
|
221
|
+
padding: 0.5em 0;
|
|
222
|
+
margin: 0;
|
|
223
|
+
margin-bottom: 0;
|
|
224
|
+
vertical-align: top;
|
|
225
|
+
border-collapse: collapse;
|
|
226
|
+
border-top: #bbbbcc dotted 1px;
|
|
227
|
+
border-bottom: #bbbbcc dotted 1px;
|
|
228
|
+
}
|
|
229
|
+
form .field label {
|
|
230
|
+
width: 34%;
|
|
231
|
+
margin: 0 2%;
|
|
232
|
+
padding: 0;
|
|
233
|
+
display: inline-block;
|
|
234
|
+
vertical-align: top;
|
|
235
|
+
}
|
|
236
|
+
form .field input {
|
|
237
|
+
width: 60%;
|
|
238
|
+
margin: 0;
|
|
239
|
+
padding: 0;
|
|
240
|
+
display: inline-block;
|
|
241
|
+
vertical-align: top;
|
|
242
|
+
color: #000033;
|
|
243
|
+
background: #eeeeff;
|
|
244
|
+
}
|
|
245
|
+
form .field select {
|
|
246
|
+
width: 60%;
|
|
247
|
+
margin: 0;
|
|
248
|
+
padding: 0;
|
|
249
|
+
}
|
|
250
|
+
form .field .no_resize input, form .field .no_resize select {
|
|
251
|
+
width: auto;
|
|
252
|
+
margin: inherit;
|
|
253
|
+
padding: inherit;
|
|
254
|
+
color: #000033;
|
|
255
|
+
background: #eeeeff;
|
|
256
|
+
}
|
|
257
|
+
form .field textarea {
|
|
258
|
+
width: 98%;
|
|
259
|
+
padding: 0;
|
|
260
|
+
margin: 0;
|
|
261
|
+
height: 5em;
|
|
262
|
+
display: block;
|
|
263
|
+
color: #000033;
|
|
264
|
+
background: #eeeeff;
|
|
265
|
+
}
|
|
266
|
+
form .en input, form .en textarea {
|
|
267
|
+
text-align: left;
|
|
268
|
+
direction: ltr;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
table.one_liners {
|
|
272
|
+
table-layout: fixed !important;
|
|
273
|
+
max-width: 100% !important;
|
|
274
|
+
width: 100% !important;
|
|
275
|
+
text-overflow: ellipsis;
|
|
276
|
+
overflow: none;
|
|
277
|
+
}
|
|
278
|
+
table.one_liners td, table.one_liners th {
|
|
279
|
+
width: inherit;
|
|
280
|
+
max-width: 10%;
|
|
281
|
+
white-space: nowrap;
|
|
282
|
+
text-overflow: ellipsis;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
h1#dropzone {
|
|
286
|
+
font-size: 2em;
|
|
287
|
+
text-align: center;
|
|
288
|
+
background-color: #44518E;
|
|
289
|
+
color: #C6CCE7;
|
|
290
|
+
padding: 0.5em 0;
|
|
291
|
+
background-image: url(/drop-pdf.png);
|
|
292
|
+
background-repeat: no-repeat;
|
|
293
|
+
background-position: 10% center;
|
|
294
|
+
background-size: auto 100%;
|
|
295
|
+
border-radius: 0.5em 0.5em 0 0;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
ul.bates_file_table_list, ol.bates_file_table_list {
|
|
299
|
+
display: block;
|
|
300
|
+
margin: 0;
|
|
301
|
+
padding: 0;
|
|
302
|
+
list-style-position: inside;
|
|
303
|
+
width: 100%;
|
|
304
|
+
}
|
|
305
|
+
ul.bates_file_table_list li, ol.bates_file_table_list li {
|
|
306
|
+
border-top: 1px dotted #111111;
|
|
307
|
+
border-bottom: 1px dotted #111111;
|
|
308
|
+
border-collapse: collapse;
|
|
309
|
+
}
|
|
310
|
+
ul.bates_file_table_list li div, ol.bates_file_table_list li div {
|
|
311
|
+
display: inline-block;
|
|
312
|
+
width: 28%;
|
|
313
|
+
}
|
|
314
|
+
ul.bates_file_table_list input, ol.bates_file_table_list input {
|
|
315
|
+
width: 90%;
|
|
316
|
+
color: #000033;
|
|
317
|
+
background: #eeeeff;
|
|
318
|
+
}
|
|
319
|
+
ul.bates_file_table_list {
|
|
320
|
+
list-style-type: circle;
|
|
321
|
+
}
|
|
322
|
+
ul.bates_file_table_list li:first-child {
|
|
323
|
+
background: #aab;
|
|
324
|
+
color: #111111;
|
|
325
|
+
}
|
|
326
|
+
input[type=checkbox]:checked#bates_first_pdf_is_cover ~ ol.bates_file_table_list li:first-child {
|
|
327
|
+
background: #bbc;
|
|
328
|
+
color: #551111;
|
|
329
|
+
}
|
|
330
|
+
input[type=checkbox]
|
|
331
|
+
{
|
|
332
|
+
width: auto;
|
|
333
|
+
display: inline;
|
|
334
|
+
}
|
|
335
|
+
#file_remover {
|
|
336
|
+
float: left;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/* RESPONSIVE RELATED */
|
|
340
|
+
@media screen and (max-width: 46em) {
|
|
341
|
+
/* STYLING CONTENT (none at the moment)*/
|
|
342
|
+
/* line 800, /Users/2Be/Ruby/combine_pdf_demo/app/assets/stylesheets/welcome.css.scss */
|
|
343
|
+
body, html {
|
|
344
|
+
height: auto;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
/* line 803, /Users/2Be/Ruby/combine_pdf_demo/app/assets/stylesheets/welcome.css.scss */
|
|
348
|
+
#content {
|
|
349
|
+
padding: 3em 0;
|
|
350
|
+
margin: 0;
|
|
351
|
+
}
|
|
352
|
+
#content #notice {
|
|
353
|
+
margin: 0.2em 1em;
|
|
354
|
+
}
|
|
355
|
+
/* line 814, /Users/2Be/Ruby/combine_pdf_demo/app/assets/stylesheets/welcome.css.scss */
|
|
356
|
+
#content .hide_when_small {
|
|
357
|
+
display: none;
|
|
358
|
+
}
|
|
359
|
+
form, .desc {
|
|
360
|
+
margin: 0.3em 1em;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
</style>
|
|
364
|
+
<script>
|
|
365
|
+
function show_notice() { document.getElementById("notice").classList.remove('hidden'); }
|
|
366
|
+
function hide_notice() { document.getElementById("notice").classList.add('hidden'); }
|
|
367
|
+
</script>
|
|
368
|
+
</head><body>
|
|
369
|
+
<h1>Bates Numbering - PDF file merge and number</h1>
|
|
370
|
+
<div id="notice" class="hidden"><a class='notice_close'onclick='hide_notice()'>X</a><div id="notice_text"></div></div>
|
|
371
|
+
<div id="contents">
|
|
372
|
+
<ol class='bates_file_table_list' id='demo' style='display:none;'>
|
|
373
|
+
<li id='file_NUM'>
|
|
374
|
+
<div>
|
|
375
|
+
<input id="file_NUM_name" name="file[NUM][name]" readonly="readonly" type="text" value="FILE_NAME">
|
|
376
|
+
<input id="file_NUM_data" name="file[NUM][data]" type="hidden" value="NUM">
|
|
377
|
+
</div>
|
|
378
|
+
<div>
|
|
379
|
+
<input id="file_NUM_date" name="file[NUM][date]" type="text" value="DATE">
|
|
380
|
+
</div><div>
|
|
381
|
+
<input id="file_NUM_title" name="file[NUM][title]" type="text" value="TITLE">
|
|
382
|
+
</div>
|
|
383
|
+
|||
|
|
384
|
+
<a href="#" class="file_remover" onclick='this.parentNode.parentNode.removeChild(this.parentNode); return false;'>[x]</a>
|
|
385
|
+
</li>
|
|
386
|
+
</ol>
|
|
387
|
+
<form accept-charset="UTF-8" enctype="multipart/form-data" method="post" onsubmit="onSubmit()">
|
|
388
|
+
<h1 id='dropzone'>Drag PDF files here</h1>
|
|
389
|
+
<p>
|
|
390
|
+
<ul class='bates_file_table_list'>
|
|
391
|
+
<li>
|
|
392
|
+
<div>File Name</div>
|
|
393
|
+
<div>Date</div>
|
|
394
|
+
<div>Title</div>
|
|
395
|
+
</li>
|
|
396
|
+
</ul>
|
|
397
|
+
<input checked="checked" id="bates_first_pdf_is_cover" name="bates[first_pdf_is_cover]" type="checkbox" value="true">
|
|
398
|
+
<label for="bates_first_pdf_is_cover">Place the first PDF file before the Index page.</label>
|
|
399
|
+
<ol class='bates_file_table_list' id='file_list'></ol>
|
|
400
|
+
<input type="submit" value="Submit">
|
|
401
|
+
</p>
|
|
402
|
+
<h3>Settings</h3>
|
|
403
|
+
<table>
|
|
404
|
+
<tr><td>Output file name:</td><td><input type='text' id="output_filename" name='bates[output]' value="combined"></td></tr><tr><td>First page number:</td><td><input type='number' name='bates[first_page_number]' value="1">
|
|
405
|
+
</td></tr><tr><td>First index number:</td><td><input type='number' name='bates[first_index_number]' value="1">
|
|
406
|
+
</td></tr><tr><td>Numbering:</td><td><select id='numbering' name='bates[numbering]'>
|
|
407
|
+
<option value='0'>Center</option>
|
|
408
|
+
<option value='1'>Left</option>
|
|
409
|
+
<option value='2'>Right</option>
|
|
410
|
+
<option value='3'>None</option>
|
|
411
|
+
</select>
|
|
412
|
+
</td></tr><tr><td><input checked="checked" id="bates_should_index_files" name="bates[should_index]" type="checkbox" value="true"><label for='bates_should_index_files'>Index Files?</label></td><td><label for='bates_should_index_files'>un-check to avoid an index and title pages.</label>
|
|
413
|
+
</td></tr><tr><td><input id="bates_skip_cover" name="bates[skip_cover]" type="checkbox" value="true"><label for='bates_skip_cover'>Skip Cover?</label></td><td><label for='bates_skip_cover'>check to skip numbering the cover page.</label>
|
|
414
|
+
</td></tr><tr><td>Language:</td><td><select id='dir' name='bates[dir]'>
|
|
415
|
+
<option value='rtl'>עברית</option>
|
|
416
|
+
<option value='ltr'>English</option>
|
|
417
|
+
</select>
|
|
418
|
+
</td></tr><tr><td>Index Title:</td><td>
|
|
419
|
+
<input type='text' id='bates_index_title' name='bates[index_title]' value="Index">
|
|
420
|
+
</td></tr><tr><td>"Number" Header:</td><td>
|
|
421
|
+
<input type='text' id='bates_number_header' name='bates[number_header]' value="#">
|
|
422
|
+
</td></tr><tr><td>"Date" Header (Leave empty to ignore):</td><td>
|
|
423
|
+
<input type='text' id='bates_date_header' name='bates[date_header]' value="Date">
|
|
424
|
+
</td></tr><tr><td>"Title" Header:</td><td>
|
|
425
|
+
<input type='text' id='bates_title_header' name='bates[title_header]' value="Title">
|
|
426
|
+
</td></tr><tr><td> "Page Number" Header:</td><td>
|
|
427
|
+
<input type='text' id='bates_page_header' name='bates[page_header]' value="Page">
|
|
428
|
+
</td></tr><tr><td>Cover Page Title:</td><td>
|
|
429
|
+
<input type='text' id='bates_title_type' name='bates[title_type]' value="Exhibit">
|
|
430
|
+
<input type="submit" value="Submit">
|
|
431
|
+
</form>
|
|
432
|
+
</div>
|
|
433
|
+
<script>
|
|
434
|
+
function onSubmit()
|
|
435
|
+
{
|
|
436
|
+
return true;
|
|
437
|
+
}
|
|
438
|
+
file_i=0
|
|
439
|
+
files_read = 0
|
|
440
|
+
|
|
441
|
+
template = document.getElementById('demo').innerHTML
|
|
442
|
+
|
|
443
|
+
function transform_file(file)
|
|
444
|
+
{
|
|
445
|
+
// Get Time and Title if the name matches standard naming
|
|
446
|
+
obj_date_string = file.name.match("^[0-9\\-–]*") + ""
|
|
447
|
+
if(obj_date_string.length == 8) {
|
|
448
|
+
obj_date = obj_date_string.substr(6,2) + "." + obj_date_string.substr(4,2) + "." + obj_date_string.substr(0,4)
|
|
449
|
+
obj_description = file.name.substring(9, file.name.toLowerCase().search("\.pdf") )
|
|
450
|
+
} else if(obj_date_string.length == 10 && obj_date_string[4] == '-' && obj_date_string[7] == '-') {
|
|
451
|
+
obj_date = obj_date_string.substr(8,2) + "." + obj_date_string.substr(5,2) + "." + obj_date_string.substr(0,4)
|
|
452
|
+
obj_description = file.name.substring(11, file.name.toLowerCase().search("\.pdf") )
|
|
453
|
+
} else {
|
|
454
|
+
obj_date = ""
|
|
455
|
+
obj_description = file.name.substring(0, file.name.toLowerCase().search("\.pdf") )
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// copy template and adjust details
|
|
459
|
+
|
|
460
|
+
document.getElementById('file_list').innerHTML += template.replace(RegExp("NUM", "g"), "" + file_i).replace(RegExp("FILE_NAME", "g"), file.name).replace(RegExp("DATE", "g"), obj_date).replace(RegExp("TITLE", "g"), obj_description)
|
|
461
|
+
|
|
462
|
+
// set reader for file
|
|
463
|
+
reader = new FileReader();
|
|
464
|
+
reader.futureObject = document.getElementById("file_" + file_i + "_data")
|
|
465
|
+
|
|
466
|
+
file_i += 1;
|
|
467
|
+
|
|
468
|
+
reader.onloadend = function(e)
|
|
469
|
+
{
|
|
470
|
+
//var id = this.futureObject.value
|
|
471
|
+
console.log("adding result to: " + this.futureObject.id )
|
|
472
|
+
document.getElementById(this.futureObject.id).value = e.target.result
|
|
473
|
+
files_read +=1
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
//reader.readAsText(file)
|
|
477
|
+
reader.readAsDataURL(file)
|
|
478
|
+
true
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// make drop_zone droppable and set callbacks
|
|
482
|
+
var dropzone = document.getElementById('dropzone')
|
|
483
|
+
|
|
484
|
+
dropzone.ondragend = function (event) {event.stopPropagation();return false; };
|
|
485
|
+
dropzone.ondragover = function (event) {event.stopPropagation();return false; };
|
|
486
|
+
dropzone.ondragenter = function (event) {event.stopPropagation();return false; };
|
|
487
|
+
|
|
488
|
+
dropzone.ondrop = function(event) {
|
|
489
|
+
event.preventDefault()
|
|
490
|
+
event.stopPropagation();
|
|
491
|
+
var files = event.dataTransfer.files; // FileList object.
|
|
492
|
+
i = 0
|
|
493
|
+
while(files[i])
|
|
494
|
+
{
|
|
495
|
+
console.log('transforming:' + files[i] + ' name: ' + files[i].name.toLowerCase())
|
|
496
|
+
if( files[i].name.toLowerCase().search("\.pdf") != -1) transform_file(files[i])
|
|
497
|
+
i++
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
set_list_sotrable()
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
// make drag-drop and sortable list from file_list
|
|
505
|
+
var Dragged_id = ''
|
|
506
|
+
|
|
507
|
+
function start_drag_list_item(e) {
|
|
508
|
+
Dragged_id = this.id
|
|
509
|
+
e.dataTransfer.setData('text/plain', this.id)
|
|
510
|
+
this.style.opacity = '0.3'
|
|
511
|
+
}
|
|
512
|
+
function end_drag_list_item(e) {
|
|
513
|
+
this.style.opacity = '1'
|
|
514
|
+
Dragged_id = ''
|
|
515
|
+
}
|
|
516
|
+
function enter_drag_list_item(e) {
|
|
517
|
+
if(Dragged_id == '') return false;
|
|
518
|
+
if(this.id == Dragged_id) return false; //this.style.opacity = '0.7'
|
|
519
|
+
this.parentNode.insertBefore(this.parentNode.removeChild(document.getElementById(Dragged_id)), this);
|
|
520
|
+
}
|
|
521
|
+
function leave_drag_list_item(e) {
|
|
522
|
+
if(Dragged_id == '') return false;
|
|
523
|
+
if(this.id != Dragged_id) this.style.opacity = '1'
|
|
524
|
+
}
|
|
525
|
+
function drop_list_item(e)
|
|
526
|
+
{
|
|
527
|
+
e.stopPropagation()
|
|
528
|
+
e.preventDefault()
|
|
529
|
+
alert('move ' + Dragged_id + ' before ' + this.id)
|
|
530
|
+
if (this.id == Dragged_id) return false;
|
|
531
|
+
}
|
|
532
|
+
function set_list_sotrable()
|
|
533
|
+
{
|
|
534
|
+
all_list_items = document.querySelectorAll('#file_list li');
|
|
535
|
+
for(i=0; i < all_list_items.length; i++)
|
|
536
|
+
{
|
|
537
|
+
all_list_items[i].setAttribute("draggable","true");
|
|
538
|
+
all_list_items[i].ondragstart = start_drag_list_item
|
|
539
|
+
all_list_items[i].ondragenter = enter_drag_list_item
|
|
540
|
+
all_list_items[i].ondragleave = leave_drag_list_item
|
|
541
|
+
all_list_items[i].ondragend = end_drag_list_item
|
|
542
|
+
all_list_items[i].ondrop = drop_list_item
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function get_today_as_str(){
|
|
547
|
+
const n = new Date();
|
|
548
|
+
return (n.getUTCFullYear().toString() + (n.getUTCMonth() < 10 ? ("0" + n.getUTCMonth().toString()) : n.getUTCMonth().toString()) + (n.getUTCMonth() < 10 ? ("0" + n.getUTCDate().toString()) : n.getUTCDate().toString()));
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
// set some initial values
|
|
552
|
+
function set_common_values(o){
|
|
553
|
+
for (let [key, value] of Object.entries(o)) {
|
|
554
|
+
console.log(key, value);
|
|
555
|
+
var tmp = document.getElementById(key);
|
|
556
|
+
if(tmp){
|
|
557
|
+
if(tmp.value)
|
|
558
|
+
tmp.value = value;
|
|
559
|
+
else
|
|
560
|
+
tmp.innerHTML = value;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
function set_en_values(){
|
|
565
|
+
|
|
566
|
+
set_common_values({
|
|
567
|
+
output_filename: get_today_as_str() + " name",
|
|
568
|
+
bates_index_title: "Index",
|
|
569
|
+
bates_number_header: "Ex.",
|
|
570
|
+
bates_date_header: "Date",
|
|
571
|
+
bates_title_header: "Title",
|
|
572
|
+
bates_page_header: "Page",
|
|
573
|
+
bates_title_type: "Exhibit",
|
|
574
|
+
});
|
|
575
|
+
}
|
|
576
|
+
function set_he_values(){
|
|
577
|
+
set_common_values({
|
|
578
|
+
output_filename: get_today_as_str() + " שם",
|
|
579
|
+
bates_index_title: "תוכן עניינים",
|
|
580
|
+
bates_number_header: "נספח",
|
|
581
|
+
bates_date_header: "תאריך",
|
|
582
|
+
bates_title_header: "כותרת",
|
|
583
|
+
bates_page_header: "עמוד",
|
|
584
|
+
bates_title_type: "נספח",
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function update_defaults(e) {
|
|
589
|
+
if(e.target.value == 'ltr') set_en_values();
|
|
590
|
+
else set_he_values();
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
function init_page(e){
|
|
594
|
+
set_he_values();
|
|
595
|
+
var tmp = document.getElementById("dir");
|
|
596
|
+
tmp.onchange = update_defaults;
|
|
597
|
+
}
|
|
598
|
+
init_page();
|
|
599
|
+
</script>
|
|
600
|
+
</body></html>
|
data/ext/iodine/extconf.rb
CHANGED
data/ext/iodine/iodine.c
CHANGED
|
@@ -1325,7 +1325,7 @@ Ruby loads the library and invokes the Init_<lib_name> function...
|
|
|
1325
1325
|
Here we connect all the C code to the Ruby interface, completing the bridge
|
|
1326
1326
|
between Lib-Server and Ruby.
|
|
1327
1327
|
***************************************************************************** */
|
|
1328
|
-
void
|
|
1328
|
+
void Init_iodine_ext(void) {
|
|
1329
1329
|
/* common Symbol objects in use by Iodine */
|
|
1330
1330
|
#define IODINE_MAKE_SYM(name) \
|
|
1331
1331
|
do { \
|
data/lib/iodine/version.rb
CHANGED
data/lib/iodine.rb
CHANGED
|
@@ -3,7 +3,7 @@ require 'socket' # TCPSocket is used internally for Hijack support
|
|
|
3
3
|
# require 'openssl' # For SSL/TLS support using OpenSSL
|
|
4
4
|
|
|
5
5
|
require_relative './iodine/version'
|
|
6
|
-
require_relative './iodine/
|
|
6
|
+
require_relative './iodine/iodine_ext' # loading a binary C extension
|
|
7
7
|
|
|
8
8
|
# Iodine is an HTTP / WebSocket server as well as an Evented Network Tool Library. In essense, Iodine is a Ruby port for the [facil.io](http://facil.io) C library.
|
|
9
9
|
#
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: iodine
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.7.
|
|
4
|
+
version: 0.7.57
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Boaz Segev
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-
|
|
11
|
+
date: 2023-09-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rake
|
|
@@ -143,6 +143,11 @@ files:
|
|
|
143
143
|
- bin/poc/gemfile
|
|
144
144
|
- bin/poc/www/index.html
|
|
145
145
|
- examples/async_task.ru
|
|
146
|
+
- examples/bates/README.md
|
|
147
|
+
- examples/bates/config.ru
|
|
148
|
+
- examples/bates/david+bold.pdf
|
|
149
|
+
- examples/bates/public/drop-pdf.png
|
|
150
|
+
- examples/bates/public/index.html
|
|
146
151
|
- examples/config.ru
|
|
147
152
|
- examples/echo.ru
|
|
148
153
|
- examples/etag.ru
|
|
@@ -246,7 +251,7 @@ licenses:
|
|
|
246
251
|
metadata:
|
|
247
252
|
allowed_push_host: https://rubygems.org
|
|
248
253
|
post_install_message: |-
|
|
249
|
-
Thank you for installing Iodine 0.7.
|
|
254
|
+
Thank you for installing Iodine 0.7.57.
|
|
250
255
|
Remember: if iodine supports your business, it's only fair to give value back (code contributions / donations).
|
|
251
256
|
rdoc_options: []
|
|
252
257
|
require_paths:
|