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.
@@ -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
+
@@ -0,0 +1,10 @@
1
+ def symbolize_keys obj
2
+ if obj.is_a? Hash
3
+ return obj.inject({}) {|memo, (k, v)| memo[k.to_sym]=symbolize_keys(v); memo}
4
+ end
5
+ if obj.is_a? Array
6
+ return obj.map {|x| symbolize_keys x}
7
+ end
8
+ obj
9
+ end
10
+
@@ -0,0 +1,18 @@
1
+ module ClassesInput
2
+ def valid_class
3
+ return '' if params.valid.nil?
4
+ if params.valid
5
+ 'input-successful'
6
+ else
7
+ 'input-incorrect'
8
+ end
9
+ end
10
+
11
+ def dirty_class
12
+ if params.dirty
13
+ 'input-dirty'
14
+ else
15
+ ''
16
+ end
17
+ end
18
+ end
@@ -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