bull 0.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/bin/bull +8 -0
- data/lib/bull/bcaptcha.rb +0 -0
- data/lib/bull/client.rb +280 -0
- data/lib/bull/encode_times.rb +35 -0
- data/lib/bull/mrelogin.rb +8 -0
- data/lib/bull/mreport.rb +26 -0
- data/lib/bull/notification.rb +87 -0
- data/lib/bull/reactive_var.rb +94 -0
- data/lib/bull/server.rb +468 -0
- data/lib/bull/start.rb +43 -0
- data/lib/bull/symbolize.rb +10 -0
- data/lib/bull/ui_common.rb +18 -0
- data/lib/bull/ui_core.rb +796 -0
- data/lib/bull/utils.rb +45 -0
- data/template/client/main.rb +8 -0
- metadata +115 -0
data/lib/bull/start.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
#$LOAD_PATH.unshift '..'
|
2
|
+
require 'eventmachine'
|
3
|
+
require 'em-websocket'
|
4
|
+
require 'rethinkdb'
|
5
|
+
require 'time'
|
6
|
+
require_relative 'mreport'
|
7
|
+
|
8
|
+
def start app_controller, key_path
|
9
|
+
puts Time.now
|
10
|
+
#MReport.load_reports
|
11
|
+
|
12
|
+
$r = RethinkDB::RQL.new
|
13
|
+
conn = $r.connect()
|
14
|
+
|
15
|
+
EM.run do
|
16
|
+
EM::WebSocket.run(:host => "0.0.0.0",
|
17
|
+
:port => 3000,
|
18
|
+
:secure => true,
|
19
|
+
:tls_options => {
|
20
|
+
:private_key_file => File.join(key_path, 'privateKey.key'), # "../../privateKey.key",
|
21
|
+
:cert_chain_file => File.join(key_path, 'certificate.crt') #"../../certificate.crt"
|
22
|
+
}
|
23
|
+
) do |ws|
|
24
|
+
controller = nil
|
25
|
+
|
26
|
+
ws.onopen { |handshake| controller = app_controller.new ws, conn}
|
27
|
+
|
28
|
+
ws.onmessage do |msg|
|
29
|
+
begin
|
30
|
+
controller.notify msg
|
31
|
+
rescue Exception => e
|
32
|
+
puts 'message: ', e.message
|
33
|
+
puts 'trace: ', e.backtrace.inspect
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
ws.onclose { controller.close; controller = nil }
|
38
|
+
|
39
|
+
ws.onerror { |e| puts "Error: #{e.message}"}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
data/lib/bull/ui_core.rb
ADDED
@@ -0,0 +1,796 @@
|
|
1
|
+
require 'reactive-ruby'
|
2
|
+
require 'set'
|
3
|
+
require_relative 'reactive_var'
|
4
|
+
require_relative 'utils'
|
5
|
+
require_relative 'bcaptcha'
|
6
|
+
require_relative 'ui_common'
|
7
|
+
require 'time'
|
8
|
+
|
9
|
+
class DisplayList < React::Component::Base
|
10
|
+
|
11
|
+
before_mount do
|
12
|
+
state.docs! []
|
13
|
+
@predicate_id = nil
|
14
|
+
end
|
15
|
+
|
16
|
+
def watch_(name, *args)
|
17
|
+
#reactives = args.pop
|
18
|
+
reactives = args.select{|v| v.is_a? RVar}
|
19
|
+
@rvs = reactive(*reactives) do
|
20
|
+
clear
|
21
|
+
$controller.stop_watch(@predicate_id) if @predicate_id != nil
|
22
|
+
args_ = args.collect do |arg|
|
23
|
+
if arg.is_a? RVar
|
24
|
+
arg.value
|
25
|
+
else
|
26
|
+
arg
|
27
|
+
end
|
28
|
+
end
|
29
|
+
#if args_.empty?
|
30
|
+
# @predicate_id = $controller.watch(name) {|data| consume data}
|
31
|
+
#else
|
32
|
+
@predicate_id = $controller.watch(name, *args_) {|data| consume data}
|
33
|
+
#end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def consume data
|
38
|
+
if data.nil?
|
39
|
+
clear
|
40
|
+
return
|
41
|
+
end
|
42
|
+
docs = state.docs.dup
|
43
|
+
if data['new_val'] == nil
|
44
|
+
index = docs.index {|x| x['id'] == data['old_val']['id']}
|
45
|
+
docs.delete_at index
|
46
|
+
elsif data['old_val'] == nil
|
47
|
+
docs << data['new_val']
|
48
|
+
else
|
49
|
+
index = docs.index {|x| x['id'] == data['old_val']['id']}
|
50
|
+
doc = docs.fetch index
|
51
|
+
doc.merge! data['new_val']
|
52
|
+
end
|
53
|
+
state.docs! sort(docs)
|
54
|
+
end
|
55
|
+
|
56
|
+
def clear
|
57
|
+
state.docs! []
|
58
|
+
end
|
59
|
+
|
60
|
+
def sort docs
|
61
|
+
docs
|
62
|
+
end
|
63
|
+
|
64
|
+
before_unmount do
|
65
|
+
$controller.stop_watch @predicate_id if @predicate_id != nil
|
66
|
+
@rvs.each_pair {|k, v| v.remove k} if @rvs
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class DisplayDoc < React::Component::Base
|
71
|
+
|
72
|
+
before_mount do
|
73
|
+
@predicate_id = nil
|
74
|
+
end
|
75
|
+
|
76
|
+
def watch_ selected
|
77
|
+
@rvs = reactive(selected) do
|
78
|
+
value = selected.value
|
79
|
+
clear
|
80
|
+
$controller.stop_watch(@predicate_id) if @predicate_id != nil
|
81
|
+
@predicate_id = $controller.watch(@@table, value) do |data|
|
82
|
+
clear
|
83
|
+
data['new_val'].each {|k, v| state.__send__(k+'!', v)} if !(data.nil? || data['new_val'].nil?)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
before_unmount do
|
89
|
+
$controller.stop_watch @predicate_id if @predicate_id != nil
|
90
|
+
@rvs.each_pair {|k, v| v.remove k} if @rvs
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
module AbstractStringInput
|
95
|
+
include ClassesInput
|
96
|
+
def render
|
97
|
+
span do
|
98
|
+
input(placeholder: params.placeholder, class: valid_class + ' ' + dirty_class,
|
99
|
+
type: type_attr, value: params.value){}.on(:change) do |event|
|
100
|
+
params.on_change event.target.value
|
101
|
+
end.on(:keyDown) do |event|
|
102
|
+
if event.key_code == 13
|
103
|
+
params.on_enter.call event.target.value
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class StringInput < React::Component::Base
|
111
|
+
include AbstractStringInput
|
112
|
+
|
113
|
+
param :on_change, type: Proc
|
114
|
+
param :value, type: String
|
115
|
+
param :placeholder
|
116
|
+
param :on_enter
|
117
|
+
param :valid
|
118
|
+
param :dirty
|
119
|
+
|
120
|
+
def type_attr
|
121
|
+
:text
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
class PasswordInput < React::Component::Base
|
126
|
+
include AbstractStringInput
|
127
|
+
|
128
|
+
param :on_change, type: Proc
|
129
|
+
param :value, type: String
|
130
|
+
param :placeholder
|
131
|
+
param :on_enter
|
132
|
+
param :valid
|
133
|
+
param :dirty
|
134
|
+
|
135
|
+
def type_attr
|
136
|
+
:password
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
class MultiLineInput < React::Component::Base
|
141
|
+
param :on_change, type: Proc
|
142
|
+
param :on_enter
|
143
|
+
param :value
|
144
|
+
param :placeholder
|
145
|
+
param :valid
|
146
|
+
param :dirty
|
147
|
+
|
148
|
+
include ClassesInput
|
149
|
+
|
150
|
+
def render
|
151
|
+
textarea(placeholder: params.placeholder, class: valid_class + ' ' + dirty_class,
|
152
|
+
value: params.value){}.on(:change) do |event|
|
153
|
+
params.on_change event.target.value
|
154
|
+
end#.on(:keyDown) do |event|
|
155
|
+
# if event.key_code == 13
|
156
|
+
# params.on_enter.call event.target.value
|
157
|
+
# end
|
158
|
+
#end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
=begin
|
163
|
+
class DateInput < React::Component::Base
|
164
|
+
param :on_change, type: Proc
|
165
|
+
param :value
|
166
|
+
param :format
|
167
|
+
param :valid
|
168
|
+
param :dirty
|
169
|
+
|
170
|
+
include ClassesInput
|
171
|
+
|
172
|
+
before_mount do
|
173
|
+
state.value! ''
|
174
|
+
state.year! ''
|
175
|
+
end
|
176
|
+
|
177
|
+
def date(value, year)
|
178
|
+
begin # opal: undefined method `strptime' for Time
|
179
|
+
Time.strptime(value+year, params.format+'%Y'){|y| y < 100 ? (y >= 69 ? y + 1900 : y + 2000) : y}
|
180
|
+
rescue
|
181
|
+
nil
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
|
186
|
+
def render
|
187
|
+
if params.value.nil?
|
188
|
+
value = state.value
|
189
|
+
year = state.year
|
190
|
+
else
|
191
|
+
value = params.value.format params.format
|
192
|
+
year = params.value.year.to_s
|
193
|
+
end
|
194
|
+
span do
|
195
|
+
input(class: valid_class + ' ' + dirty_class, value: value).on(:change) do |event|
|
196
|
+
state.value! event.target.value
|
197
|
+
params.on_change date(event.target.value, year)
|
198
|
+
end
|
199
|
+
input(value: year).on(:change) do |event|
|
200
|
+
state.year! event.target.value
|
201
|
+
params.on_change date(value, event.target.value)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
=end
|
207
|
+
|
208
|
+
module AbstractNumeric
|
209
|
+
include ClassesInput
|
210
|
+
|
211
|
+
def format val
|
212
|
+
val.to_s
|
213
|
+
end
|
214
|
+
|
215
|
+
def render
|
216
|
+
if parse(state.value) != params.value
|
217
|
+
value = params.value.to_s
|
218
|
+
else
|
219
|
+
value = state.value
|
220
|
+
end
|
221
|
+
|
222
|
+
span do
|
223
|
+
input(placeholder: params.placeholder, class: valid_class + ' ' + dirty_class, type: :text, value: format(value)){}.on(:change) do |event|
|
224
|
+
state.value! event.target.value
|
225
|
+
if event.target.value == ''
|
226
|
+
params.on_change nil
|
227
|
+
else
|
228
|
+
update_state event
|
229
|
+
end
|
230
|
+
end.on(:keyDown) do |event|
|
231
|
+
if event.key_code == 13
|
232
|
+
params.on_enter.call event.target.value
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
class IntegerInput < React::Component::Base
|
240
|
+
include AbstractNumeric
|
241
|
+
|
242
|
+
param :on_change, type: Proc
|
243
|
+
param :value, type: Integer
|
244
|
+
param :valid
|
245
|
+
param :on_enter
|
246
|
+
param :placeholder
|
247
|
+
param :dirty
|
248
|
+
|
249
|
+
def parse val
|
250
|
+
begin
|
251
|
+
Integer(val)
|
252
|
+
rescue
|
253
|
+
nil
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
def update_state event
|
258
|
+
params.on_change parse(event.target.value)
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
def format_integer value
|
263
|
+
path = value.to_s.reverse.split(/(\d\d\d)/).select{|v| v != ''}
|
264
|
+
if path[-1] == '-'
|
265
|
+
path.pop
|
266
|
+
'-' + path.join(',').reverse
|
267
|
+
else
|
268
|
+
path.join(',').reverse
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
def format_float value
|
273
|
+
e, d = value.to_s.split('.')
|
274
|
+
d = '' if value.to_s.end_with?('.')
|
275
|
+
return '' if e.nil?
|
276
|
+
v = format_integer e
|
277
|
+
if d.nil?
|
278
|
+
v
|
279
|
+
else
|
280
|
+
v + '.' + d
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
class IntegerCommaInput < React::Component::Base
|
285
|
+
include AbstractNumeric
|
286
|
+
|
287
|
+
param :on_change, type: Proc
|
288
|
+
param :value, type: Integer
|
289
|
+
param :valid
|
290
|
+
param :on_enter
|
291
|
+
param :placeholder
|
292
|
+
param :dirty
|
293
|
+
|
294
|
+
def format value
|
295
|
+
format_integer value
|
296
|
+
end
|
297
|
+
|
298
|
+
def parse val
|
299
|
+
begin
|
300
|
+
Integer(val.gsub(',', ''))
|
301
|
+
rescue
|
302
|
+
nil
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
def update_state event
|
307
|
+
params.on_change parse(event.target.value)
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
class FloatInput < React::Component::Base
|
312
|
+
include AbstractNumeric
|
313
|
+
|
314
|
+
param :on_change, type: Proc
|
315
|
+
param :value, type: Float
|
316
|
+
param :valid
|
317
|
+
param :on_enter
|
318
|
+
param :placeholder
|
319
|
+
param :dirty
|
320
|
+
|
321
|
+
def parse val
|
322
|
+
begin
|
323
|
+
Float(val)
|
324
|
+
rescue
|
325
|
+
nil
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
def update_state event
|
330
|
+
params.on_change parse(event.target.value)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
class FloatCommaInput < React::Component::Base
|
335
|
+
include AbstractNumeric
|
336
|
+
|
337
|
+
param :on_change, type: Proc
|
338
|
+
param :value, type: Float
|
339
|
+
param :valid
|
340
|
+
param :on_enter
|
341
|
+
param :placeholder
|
342
|
+
param :dirty
|
343
|
+
|
344
|
+
def format value
|
345
|
+
format_float value
|
346
|
+
end
|
347
|
+
|
348
|
+
def parse val
|
349
|
+
begin
|
350
|
+
Float(val.gsub(',', ''))
|
351
|
+
rescue
|
352
|
+
nil
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
def update_state event
|
357
|
+
params.on_change parse(event.target.value)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
class CheckInput < React::Component::Base
|
362
|
+
param :value
|
363
|
+
param :on_change
|
364
|
+
|
365
|
+
def render
|
366
|
+
input(type: :checkbox, checked: params.value).on(:change){params.on_change.call !params.value}
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
class RadioInput < React::Component::Base
|
371
|
+
param :value
|
372
|
+
param :values
|
373
|
+
param :name
|
374
|
+
param :on_change
|
375
|
+
|
376
|
+
def render
|
377
|
+
span do
|
378
|
+
params.values.each do |v|
|
379
|
+
input(type: :radio, name: params.name, value: v, checked: v == params.value){v}.on(:change){params.on_change.call v}
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
class HashInput < React::Component::Base
|
386
|
+
param :value
|
387
|
+
param :on_change
|
388
|
+
|
389
|
+
before_mount do
|
390
|
+
state.key! ''
|
391
|
+
state.value! ''
|
392
|
+
end
|
393
|
+
|
394
|
+
def render
|
395
|
+
span do
|
396
|
+
input(placeholder: 'key', value: state.key).on(:change){|event| state.key! event.target.value}
|
397
|
+
input(placeholder: 'value',value: state.value).on(:change){|event| state.value! event.target.value}
|
398
|
+
button{'add'}.on(:click) do
|
399
|
+
hsh = params.value.dup
|
400
|
+
hsh[state.key] = state.value
|
401
|
+
params.on_change.call hsh
|
402
|
+
state.key! ''
|
403
|
+
state.value! ''
|
404
|
+
end
|
405
|
+
table do
|
406
|
+
tr do
|
407
|
+
th{'key'}
|
408
|
+
th{'value'}
|
409
|
+
th{' '}
|
410
|
+
end
|
411
|
+
params.value.each_pair do |k, v|
|
412
|
+
tr do
|
413
|
+
td{k}
|
414
|
+
td{v}
|
415
|
+
td{i(class: 'fa fa-times')}.on(:click) do
|
416
|
+
hsh = params.value.dup
|
417
|
+
hsh.delete k
|
418
|
+
params.on_change.call hsh
|
419
|
+
end
|
420
|
+
end
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
class ArrayInput < React::Component::Base
|
428
|
+
param :value
|
429
|
+
param :on_change
|
430
|
+
|
431
|
+
before_mount do
|
432
|
+
state.v! ''
|
433
|
+
end
|
434
|
+
|
435
|
+
def render
|
436
|
+
span do
|
437
|
+
input(value: state.v).on(:change) do |event|
|
438
|
+
state.v! event.target.value
|
439
|
+
end.on(:keyDown) do |event|
|
440
|
+
if event.key_code == 13
|
441
|
+
list = params.value.dup
|
442
|
+
list << event.target.value
|
443
|
+
params.on_change.call list
|
444
|
+
state.v! ''
|
445
|
+
end
|
446
|
+
end
|
447
|
+
table do
|
448
|
+
tr do
|
449
|
+
th{' '}
|
450
|
+
th{' '}
|
451
|
+
end
|
452
|
+
params.value.each do |v|
|
453
|
+
tr do
|
454
|
+
td{v}
|
455
|
+
td{i(class: 'fa fa-times')}.on(:click) do
|
456
|
+
list = params.value.dup
|
457
|
+
list.delete v
|
458
|
+
params.on_change.call list
|
459
|
+
end
|
460
|
+
end
|
461
|
+
end
|
462
|
+
end
|
463
|
+
end
|
464
|
+
end
|
465
|
+
end
|
466
|
+
|
467
|
+
class SelectInput < React::Component::Base
|
468
|
+
param :on_change
|
469
|
+
param :value
|
470
|
+
param :options
|
471
|
+
param :dirty
|
472
|
+
|
473
|
+
include ClassesInput
|
474
|
+
|
475
|
+
def render
|
476
|
+
span do
|
477
|
+
select(class: 'select ' + dirty_class, value: params.value) do
|
478
|
+
option{''}
|
479
|
+
params.options.each {|val| option(value: val){val}}
|
480
|
+
end.on(:change) {|event| params.on_change.call event.target.value}
|
481
|
+
end
|
482
|
+
end
|
483
|
+
end
|
484
|
+
|
485
|
+
class SelectObjectInput < React::Component::Base
|
486
|
+
param :on_change
|
487
|
+
param :value
|
488
|
+
param :options
|
489
|
+
param :dirty
|
490
|
+
param :display
|
491
|
+
|
492
|
+
include ClassesInput
|
493
|
+
|
494
|
+
before_mount do
|
495
|
+
@map = {}
|
496
|
+
end
|
497
|
+
|
498
|
+
def render
|
499
|
+
@map = {}
|
500
|
+
params.options.each {|val| @map[val[params.display]] = val}
|
501
|
+
span do
|
502
|
+
select(class: 'select ' + dirty_class, value: params.value) do
|
503
|
+
option{''}
|
504
|
+
params.options.each {|val| option(value: val[params.display]){val[params.display]}}
|
505
|
+
end.on(:change) {|event| params.on_change.call @map[event.target.value]}
|
506
|
+
end
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
class MultipleSelectInput < React::Component::Base
|
511
|
+
param :on_change
|
512
|
+
param :options
|
513
|
+
param :value
|
514
|
+
|
515
|
+
def render
|
516
|
+
span do
|
517
|
+
select(class: 'select ', multiple: true, value: params.value) do
|
518
|
+
option{''}
|
519
|
+
params.options.each {|val| option(value: val){val}} #(selected: params.values.include? val){val}}
|
520
|
+
end.on(:change) do |event|
|
521
|
+
list = params.value.dup
|
522
|
+
if list.include? event.target.value
|
523
|
+
list.delete event.target.value
|
524
|
+
else
|
525
|
+
list << event.target.value
|
526
|
+
end
|
527
|
+
params.on_change.call list
|
528
|
+
end
|
529
|
+
=begin
|
530
|
+
table do
|
531
|
+
tr do
|
532
|
+
th{' '}
|
533
|
+
th{' '}
|
534
|
+
end
|
535
|
+
params.values.each do |v|
|
536
|
+
tr do
|
537
|
+
td{v}
|
538
|
+
td{i(class: 'fa fa-times fa-2x')}.on(:click) do
|
539
|
+
list = params.values.dup
|
540
|
+
list.delete v
|
541
|
+
params.on_change.call list
|
542
|
+
end
|
543
|
+
end
|
544
|
+
end
|
545
|
+
end
|
546
|
+
=end
|
547
|
+
end
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
551
|
+
class FormButtons < React::Component::Base
|
552
|
+
param :valid
|
553
|
+
param :dirty
|
554
|
+
param :save
|
555
|
+
param :discard
|
556
|
+
|
557
|
+
before_mount do
|
558
|
+
state.discard! false
|
559
|
+
end
|
560
|
+
|
561
|
+
def render
|
562
|
+
div do
|
563
|
+
i(class: 'save fa fa-floppy-o fa-2x').on(:click){params.save.call} if params.valid && params.dirty
|
564
|
+
i(class: 'discard fa fa-times fa-2x').on(:click) do
|
565
|
+
if params.dirty
|
566
|
+
state.discard! true
|
567
|
+
else
|
568
|
+
params.discard.call
|
569
|
+
end
|
570
|
+
end if !state.discard
|
571
|
+
i(class: 'rdiscard fa fa-times fa-4x').on(:click) {params.discard.call; state.discard! false} if state.discard
|
572
|
+
end
|
573
|
+
end
|
574
|
+
end
|
575
|
+
|
576
|
+
class Form < React::Component::Base
|
577
|
+
|
578
|
+
include MNotification
|
579
|
+
|
580
|
+
before_mount do
|
581
|
+
@dirty = Set.new
|
582
|
+
@refs = {}
|
583
|
+
state.discard! false
|
584
|
+
state.dirty! false
|
585
|
+
end
|
586
|
+
|
587
|
+
before_unmount do
|
588
|
+
@rvs.each_pair {|k, v| v.remove k; v.remove_form self} if @rvs
|
589
|
+
end
|
590
|
+
|
591
|
+
def dirty?
|
592
|
+
!@dirty.empty?
|
593
|
+
end
|
594
|
+
|
595
|
+
def expand_attr(hsh={})
|
596
|
+
lambda do |value|
|
597
|
+
value.each_pair do |k, v|
|
598
|
+
if !hsh[k].nil?
|
599
|
+
k = hsh[k]
|
600
|
+
end
|
601
|
+
@dirty.add k
|
602
|
+
state.__send__(k+'!', v)
|
603
|
+
state.__send__('dirty_' + k + '!', true)
|
604
|
+
state.dirty! true
|
605
|
+
end
|
606
|
+
end
|
607
|
+
end
|
608
|
+
|
609
|
+
def change_attr(attr)
|
610
|
+
lambda do |value|
|
611
|
+
@dirty.add attr
|
612
|
+
doc = state.__send__(attr.split('.')[0])
|
613
|
+
set_nested(attr, value, doc){|r, v| state.__send__(r+'!', v)}
|
614
|
+
state.__send__('dirty_' + attr.gsub('.', '_') + '!', true)
|
615
|
+
state.dirty! true
|
616
|
+
end
|
617
|
+
end
|
618
|
+
|
619
|
+
def hash_from_state
|
620
|
+
ret = {}
|
621
|
+
@dirty.each do |attr|
|
622
|
+
get_nested!(ret, attr) {|r| state.__send__(r)}
|
623
|
+
end
|
624
|
+
@@constants.each do |cte|
|
625
|
+
get_nested!(ret, cte) {|r| params.__send__(r)}
|
626
|
+
end if @@constants
|
627
|
+
ret
|
628
|
+
end
|
629
|
+
|
630
|
+
def save
|
631
|
+
if state.id
|
632
|
+
update
|
633
|
+
else
|
634
|
+
insert
|
635
|
+
end
|
636
|
+
state.discard! false
|
637
|
+
state.dirty! false
|
638
|
+
end
|
639
|
+
|
640
|
+
def clear_dirty
|
641
|
+
@dirty.each {|attr| state.__send__('dirty_' + attr.gsub('.', '_')+'!', false)}
|
642
|
+
@dirty.clear
|
643
|
+
end
|
644
|
+
|
645
|
+
def discard
|
646
|
+
@selected.value = nil
|
647
|
+
clear
|
648
|
+
#@dirty.each {|attr| state.__send__('dirty_' + attr+'!', false)}
|
649
|
+
#@dirty.clear
|
650
|
+
clear_dirty
|
651
|
+
state.discard! false
|
652
|
+
state.dirty! false
|
653
|
+
end
|
654
|
+
|
655
|
+
def insert
|
656
|
+
$controller.insert(@@table, hash_from_state).then do |response|
|
657
|
+
if response.nil?
|
658
|
+
#$notifications.add ['error', 'form: data not inserted', 1] if $notifications
|
659
|
+
notify_error 'form: data not inserted', 1
|
660
|
+
else
|
661
|
+
params.selected.value = response
|
662
|
+
#$notifications.add ['ok', 'form: data inserted', 1] if $notifications
|
663
|
+
notify_ok 'form: data inserted', 1
|
664
|
+
end
|
665
|
+
end
|
666
|
+
#@dirty.each {|attr| state.__send__('dirty_' + attr+'!', false)}
|
667
|
+
#@dirty.clear
|
668
|
+
clear_dirty
|
669
|
+
end
|
670
|
+
|
671
|
+
def update
|
672
|
+
$controller.update(@@table, state.id, hash_from_state).then do |count|
|
673
|
+
if count == 0
|
674
|
+
#$notifications.add ['error', 'form: data not updated', 1] if $notifications
|
675
|
+
notify_error 'form: data not updated', 1
|
676
|
+
elsif count == 1
|
677
|
+
#$notifications.add ['ok', 'form: data updated', 1] if $notifications
|
678
|
+
notify_ok 'form: data updated', 1
|
679
|
+
end
|
680
|
+
end
|
681
|
+
#@dirty.each {|attr| state.__send__('dirty_' + attr.gsub('.', '_')+'!', false)}
|
682
|
+
#@dirty.clear
|
683
|
+
clear_dirty
|
684
|
+
end
|
685
|
+
|
686
|
+
def get selected
|
687
|
+
@selected = selected
|
688
|
+
selected.add_form self
|
689
|
+
@rvs = reactive(selected) do
|
690
|
+
@dirty.each {|attr| state.__send__('dirty_' + attr+'!', false)}
|
691
|
+
@dirty.clear
|
692
|
+
clear
|
693
|
+
$controller.rpc('get_' + @@table, selected.value).then do|response|
|
694
|
+
response.each do |k, v|
|
695
|
+
state.__send__(k+'!', v)
|
696
|
+
end
|
697
|
+
end
|
698
|
+
end
|
699
|
+
end
|
700
|
+
|
701
|
+
def get_unique kw
|
702
|
+
k, selected = kw.shift
|
703
|
+
@selected = selected
|
704
|
+
selected.add_form self
|
705
|
+
@rvs = reactive(selected) do
|
706
|
+
@dirty.each {|attr| state.__send__('dirty_' + attr+'!', false)}
|
707
|
+
@dirty.clear
|
708
|
+
clear
|
709
|
+
$controller.rpc('get_unique_' + @@table, k, selected.value).then do|response|
|
710
|
+
response.each do |k, v|
|
711
|
+
state.__send__(k+'!', v)
|
712
|
+
end
|
713
|
+
end
|
714
|
+
end
|
715
|
+
end
|
716
|
+
end
|
717
|
+
|
718
|
+
module Modal
|
719
|
+
def render
|
720
|
+
div(class: 'modal') do
|
721
|
+
div(class: 'modal-center') do
|
722
|
+
content
|
723
|
+
end
|
724
|
+
end
|
725
|
+
end
|
726
|
+
end
|
727
|
+
|
728
|
+
module AbstractPopover
|
729
|
+
def render
|
730
|
+
klass = 'inactive animated fadeOut'
|
731
|
+
if params.show == 'visible'
|
732
|
+
klass = 'active animated fadeIn'
|
733
|
+
|
734
|
+
h = $document[params.target].height
|
735
|
+
x = $document[params.target].position.x
|
736
|
+
y = $document[params.target].position.y
|
737
|
+
|
738
|
+
$document[params.id].offset.x = x
|
739
|
+
$document[params.id].offset.y = y+h
|
740
|
+
$window.after(5){params.close.call}
|
741
|
+
end
|
742
|
+
div(id: params.id) do
|
743
|
+
div(class: 'popover ' + klass) do
|
744
|
+
div(class: 'arrow-up')
|
745
|
+
div(class: 'box') do
|
746
|
+
div(class: 'close'){i(class: 'fa fa-times')}.on(:click){params.close.call}
|
747
|
+
div(class: 'content'){content}
|
748
|
+
end
|
749
|
+
end
|
750
|
+
end
|
751
|
+
end
|
752
|
+
end
|
753
|
+
|
754
|
+
#example
|
755
|
+
=begin
|
756
|
+
class Popover < React::Component::Base
|
757
|
+
param :show
|
758
|
+
param :close
|
759
|
+
param :id
|
760
|
+
param :target_id
|
761
|
+
include AbstractPopover
|
762
|
+
|
763
|
+
def content
|
764
|
+
div do
|
765
|
+
b{'hello'}
|
766
|
+
div{' there'}
|
767
|
+
end
|
768
|
+
end
|
769
|
+
end
|
770
|
+
|
771
|
+
Popover(id:'popover', target_id: 'my_input', show: state.show_popup, close: lambda{state.show_popup! 'hidden'})
|
772
|
+
#where show_popup can be 'visible' or 'hidden'
|
773
|
+
=end
|
774
|
+
|
775
|
+
class HorizontalMenu < React::Component::Base
|
776
|
+
param :options
|
777
|
+
param :set_page
|
778
|
+
param :page
|
779
|
+
param :language
|
780
|
+
|
781
|
+
def active page
|
782
|
+
params.page == page ? 'active': ''
|
783
|
+
end
|
784
|
+
|
785
|
+
def render
|
786
|
+
div(class: 'no-print') do
|
787
|
+
ul(class: 'menu') do
|
788
|
+
params.options.each_pair do |k, v|
|
789
|
+
li(class: 'menu-item ' + active(k)){a(href: '#'){v}.on(:click){params.set_page.call k}}
|
790
|
+
end
|
791
|
+
li{a(href: '#'){'en'}.on(:click){params.language.value = 'en'}}
|
792
|
+
li{a(href: '#'){'es'}.on(:click){params.language.value = 'es'}}
|
793
|
+
end if params.options.length > 1
|
794
|
+
end
|
795
|
+
end
|
796
|
+
end
|