cpee-model-management 1.1.3 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 916a9427ac9b715a51bf13d05bbe54960e4f88d2116fdb45f178b52619d4568b
4
- data.tar.gz: 950a1a9b758481af6694088939f75aa3eb2f32995af21173277d3521fb04ecdc
3
+ metadata.gz: 7844d39f8bdb0619c97fc35b4b4f030cb200cab9fbf74182db415cd8ca601ae3
4
+ data.tar.gz: 97ca85020b275d58fc533173c824f44c239dfcd967eee996e8ed85c0f86b14f9
5
5
  SHA512:
6
- metadata.gz: f92fd3bc3ccad9f06114d4f68885373a5aca274d8465082c96989a7dc4ab25fe956c574449f629978f753de35a68894762ac05d6bc3765f6acc8f7a09b3db91e
7
- data.tar.gz: a74fb8111ad4f1e501eca54d400e3dc5b62479214fc0d59030fcedcd844e02cf34be659e0ef469447a4400aa7eb90331a87abfa213970487e48ad01d86e87907
6
+ metadata.gz: fdf7e58b10267d03897965ece7f14f01c3868d1ff1a5814acc85573d52d38b2e072ebaa4fbaeeb69ab76e4dcd97fdac50355a2f514739a6a698030611352bcec
7
+ data.tar.gz: 557f02d4df9dc953dd0cb17328c2b33b9a75930bd4df4f0bea3dc3e0da322f53dfd7a54fec8aee36f5934e8b976661d9454a3a8d1ebc1f3b8a3a174dba6982cf
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "cpee-model-management"
3
- s.version = "1.1.3"
3
+ s.version = "1.2.0"
4
4
  s.platform = Gem::Platform::RUBY
5
5
  s.license = "LGPL-3.0"
6
6
  s.summary = "(Lifecycle) manage your process models in a directory or git repo."
@@ -26,6 +26,7 @@ require 'pathname'
26
26
  require 'shellwords'
27
27
  require 'securerandom'
28
28
  require 'cpee/redis'
29
+ require 'digest/sha1'
29
30
 
30
31
  module CPEE
31
32
  module ModelManagement
@@ -180,7 +181,7 @@ module CPEE
180
181
 
181
182
  class GetList < Riddl::Implementation #{{{
182
183
  def response
183
- where = @a[0] == :main ? '' : Riddl::Protocols::Utils::unescape(@r.last)
184
+ where = @a[0] == :main ? '' : @r.map{ |d| Riddl::Protocols::Utils::unescape(d) }.join('/')
184
185
  views = @a[1]
185
186
  models = @a[2]
186
187
  stage = [@p[0]&.value] || ['draft']
@@ -195,7 +196,10 @@ module CPEE
195
196
  { :type => :file, :name => File.basename(f), :creator => attrs['creator'], :author => attrs['author'], :guarded => attrs['guarded'], :guarded_id => attrs['guarded_id'], :stage => fstage, :date => File.mtime(f).xmlschema } if stage.include?(fstage)
196
197
  end.compact.uniq.sort_by{ |e| e[:name] }
197
198
 
198
- Riddl::Parameter::Complex.new('list','application/json',JSON::pretty_generate(names))
199
+ ret = JSON::pretty_generate(names)
200
+ @headers << Riddl::Header.new('CPEE-MOMA-FINGERPRINT', Digest::SHA1.hexdigest(ret))
201
+
202
+ Riddl::Parameter::Complex.new('list','application/json',ret)
199
203
  end
200
204
  end #}}}
201
205
  class GetListFull < Riddl::Implementation #{{{
@@ -226,7 +230,7 @@ module CPEE
226
230
 
227
231
  class ShiftItem < Riddl::Implementation #{{{
228
232
  def response
229
- where = @a[0] == :main ? '' : Riddl::Protocols::Utils::unescape(@r[-2])
233
+ where = @a[0] == :main ? '' : @r[0..-2].map{ |d| Riddl::Protocols::Utils::unescape(d) }.join('/')
230
234
  conns = @a[1]
231
235
  themes = @a[2]
232
236
  models = @a[3]
@@ -263,7 +267,7 @@ module CPEE
263
267
 
264
268
  class RenameItem < Riddl::Implementation #{{{
265
269
  def response
266
- where = @a[0] == :main ? '' : Riddl::Protocols::Utils::unescape(@r[-2])
270
+ where = @a[0] == :main ? '' : @r[0..-2].map{ |d| Riddl::Protocols::Utils::unescape(d) }.join('/')
267
271
  conns = @a[1]
268
272
  models = @a[2]
269
273
  name = File.basename(@r.last,'.xml')
@@ -272,7 +276,7 @@ module CPEE
272
276
  fnname = File.join(models,where,nname + '.xml')
273
277
  counter = 0
274
278
  stage = 'draft'
275
- while File.exists?(fnname)
279
+ while File.exist?(fnname)
276
280
  counter += 1
277
281
  fnname = File.join(models,where,nname + counter.to_s + '.xml')
278
282
  end
@@ -302,14 +306,15 @@ module CPEE
302
306
  end #}}}
303
307
  class RenameDir < Riddl::Implementation #{{{
304
308
  def response
309
+ where = @r[0..-2].map{ |d| Riddl::Protocols::Utils::unescape(d) }.join('/')
305
310
  conns = @a[0]
306
311
  models = @a[1]
307
- name = File.basename(@r.last,'.dir')
308
- nname = @p[0].value
312
+ name = File.join(where,File.basename(@r.last,'.dir'))
313
+ nname = File.join(where,@p[0].value)
309
314
  fname = File.join(models,name + '.dir')
310
315
  fnname = File.join(models,nname + '.dir')
311
316
  counter = 0
312
- while File.exists?(fnname)
317
+ while File.exist?(fnname)
313
318
  counter += 1
314
319
  fnname = File.join(models,nname + counter.to_s + '.dir')
315
320
  end
@@ -342,15 +347,16 @@ module CPEE
342
347
 
343
348
  class CreateDir < Riddl::Implementation #{{{
344
349
  def response
350
+ where = @a[0] == :main ? '' : @r.map{ |d| Riddl::Protocols::Utils::unescape(d) }.join('/')
345
351
  name = @p[0].value
346
- conns = @a[0]
347
- models = @a[1]
352
+ conns = @a[1]
353
+ models = @a[2]
348
354
 
349
- fname = File.join(models,name + '.dir')
355
+ fname = File.join(models,where,name + '.dir')
350
356
  counter = 0
351
- while File.exists?(fname)
357
+ while File.exist?(fname)
352
358
  counter += 1
353
- fname = File.join(models,name + counter.to_s + '.dir')
359
+ fname = File.join(models,where,name + counter.to_s + '.dir')
354
360
  end
355
361
 
356
362
  dn = CPEE::ModelManagement::get_dn @h['DN']
@@ -371,19 +377,23 @@ module CPEE
371
377
  end #}}}
372
378
  class Create < Riddl::Implementation #{{{
373
379
  def response
374
- where = @a[0] == :main ? '' : Riddl::Protocols::Utils::unescape(@r.last)
375
- stage = if @a[1] == :cre
376
- @p.shift.value
380
+ models = @a[4]
381
+ views = @a[1]
382
+ conns = @a[2]
383
+ templates = @a[3]
384
+ if @p.first.name == 'stage'
385
+ where = @a[0] == :main ? '' : @r.map{ |d| Riddl::Protocols::Utils::unescape(d) }.join('/')
386
+ stage = @p.shift.value
387
+ name = @p[0].value
388
+ source = templates[stage] ? templates[stage] : 'testset.xml'
377
389
  else
378
- nil
390
+ where = @r[0..-2].map{ |d| Riddl::Protocols::Utils::unescape(d) }.join('/')
391
+ stage = nil
392
+ name = @r[-1].sub(/\.xml$/,'')
393
+ source = File.join(models,where,name + '.xml')
394
+ where = @p[0].value
379
395
  end
380
- views = @a[2]
381
- conns = @a[3]
382
- templates = @a[4]
383
- models = @a[5]
384
396
 
385
- name = @p[0].value
386
- source = @p[1] ? File.join(models,where,@p[1].value) : (templates[stage] ? templates[stage] : 'testset.xml')
387
397
  fname = File.join(models,where,name + '.xml')
388
398
 
389
399
  attrs = JSON::load File.open(fname + '.attrs') rescue {}
@@ -391,7 +401,7 @@ module CPEE
391
401
  stage = views[stage] if views && views[stage]
392
402
 
393
403
  counter = 0
394
- while File.exists?(fname)
404
+ while File.exist?(fname)
395
405
  counter += 1
396
406
  fname = File.join(models,where,name + counter.to_s + '.xml')
397
407
  end
@@ -436,11 +446,11 @@ module CPEE
436
446
 
437
447
  class GetItem < Riddl::Implementation #{{{
438
448
  def response
439
- where = @a[0] == :main ? '' : Riddl::Protocols::Utils::unescape(@r[-2])
449
+ where = @a[0] == :main ? '' : @r[0..-2].map{ |d| Riddl::Protocols::Utils::unescape(d) }.join('/')
440
450
  models = @a[1]
441
451
  name = File.basename(@r[-1],'.xml')
442
452
  fname = File.join(models,where,name + '.xml')
443
- if File.exists? fname
453
+ if File.exist? fname
444
454
  Riddl::Parameter::Complex.new('content','application/xml',File.read(fname))
445
455
  else
446
456
  @status = 400
@@ -449,7 +459,7 @@ module CPEE
449
459
  end #}}}
450
460
  class OpenItem < Riddl::Implementation #{{{
451
461
  def response
452
- where = @a[0] == :main ? '' : Riddl::Protocols::Utils::unescape(@r[-3])
462
+ where = @a[0] == :main ? '' : @r[0..-3].map{ |d| Riddl::Protocols::Utils::unescape(d) }.join('/')
453
463
  name = File.basename(@r[-2],'.xml')
454
464
  insta = @a[1]
455
465
  cock = @a[2]
@@ -462,7 +472,7 @@ module CPEE
462
472
 
463
473
  inst = nil
464
474
  begin
465
- inst = if File.exists?(fname + '.active') && File.exists?(fname + '.active-uuid') && !force
475
+ inst = if File.exist?(fname + '.active') && File.exist?(fname + '.active-uuid') && !force
466
476
  t = {
467
477
  'CPEE-INSTANCE-URL' => File.read(fname + '.active'),
468
478
  'CPEE-INSTANCE-UUID' => File.read(fname + '.active-uuid')
@@ -530,7 +540,7 @@ module CPEE
530
540
 
531
541
  class MoveItem < Riddl::Implementation #{{{
532
542
  def response
533
- where = @a[0] == :main ? '' : Riddl::Protocols::Utils::unescape(@r[-2])
543
+ where = @a[0] == :main ? '' : @r[0..-2].map{ |d| Riddl::Protocols::Utils::unescape(d) }.join('/')
534
544
  conns = @a[1]
535
545
  models = @a[2]
536
546
 
@@ -560,7 +570,7 @@ module CPEE
560
570
  end #}}}
561
571
  class PutItem < Riddl::Implementation #{{{
562
572
  def response
563
- where = @a[0] == :main ? '' : Riddl::Protocols::Utils::unescape(@r[-2])
573
+ where = @a[0] == :main ? '' : @r[0..-2].map{ |d| Riddl::Protocols::Utils::unescape(d) }.join('/')
564
574
  conns = @a[1]
565
575
  models = @a[2]
566
576
  name = File.basename(@r.last,'.xml')
@@ -569,7 +579,9 @@ module CPEE
569
579
 
570
580
  fname = File.join(models,where,name + '.xml')
571
581
 
572
- if File.exists?(fname)
582
+ p fname
583
+
584
+ if File.exist?(fname)
573
585
  author = dn['GN'] + ' ' + dn['SN']
574
586
  attrs = {}
575
587
  XML::Smart.string(cont) do |doc|
@@ -599,7 +611,7 @@ module CPEE
599
611
  end #}}}
600
612
  class DeleteItem < Riddl::Implementation #{{{
601
613
  def response
602
- where = @a[0] == :main ? '' : Riddl::Protocols::Utils::unescape(@r[-2])
614
+ where = @a[0] == :main ? '' : @r[0..-2].map{ |d| Riddl::Protocols::Utils::unescape(d) }.join('/')
603
615
  conns = @a[1]
604
616
  models = @a[2]
605
617
  name = File.basename(@r.last,'.xml')
@@ -614,15 +626,16 @@ module CPEE
614
626
  end #}}}
615
627
  class DeleteDir < Riddl::Implementation #{{{
616
628
  def response
629
+ where = @r[0..-2].map{ |d| Riddl::Protocols::Utils::unescape(d) }.join('/')
617
630
  conns = @a[0]
618
631
  models = @a[1]
619
632
  name = File.basename(@r.last,'.dir')
620
- fname = File.join(models,name + '.dir')
633
+ fname = File.join(models,where,name + '.dir')
621
634
 
622
635
  dn = CPEE::ModelManagement::get_dn @h['DN']
623
636
  author = dn['GN'] + ' ' + dn['SN']
624
637
 
625
- CPEE::ModelManagement::op author, 'rm', models, File.join(name + '.dir')
638
+ CPEE::ModelManagement::op author, 'rm', models, File.join(where,name + '.dir')
626
639
  CPEE::ModelManagement::notify conns, 'delete', models, fname
627
640
  end
628
641
  end #}}}
@@ -856,30 +869,47 @@ module CPEE
856
869
 
857
870
  CPEE::redis_connect opts, 'Server Main'
858
871
 
872
+ opts[:sse_keepalive_frequency] ||= 10
873
+
859
874
  Proc.new do
875
+
876
+ parallel do
877
+ EM.add_periodic_timer(opts[:sse_keepalive_frequency]) do
878
+ opts[:management_receivers].each do |sse|
879
+ sse.send_with_id('heartbeat', '42') unless sse&.closed?
880
+ end
881
+ opts[:stat_receivers].each do |sse|
882
+ sse.send_with_id('heartbeat', '42') unless sse&.closed?
883
+ end
884
+ end
885
+ end
886
+
860
887
  interface 'events' do
861
888
  run StatReceive, opts[:redis], opts[:stat_receivers] if post 'event'
862
889
  end
890
+
863
891
  interface 'implementation' do
864
892
  run GetList, :main, opts[:views], opts[:models] if get 'stage'
865
893
  run GetListFull, opts[:views], opts[:models] if get 'full'
866
894
  run GetStages, opts[:themes] if get 'stages'
867
- run Create, :main, :cre, opts[:views], opts[:management_receivers], opts[:templates], opts[:models] if post 'item'
868
- run Create, :main, :dup, opts[:views], opts[:management_receivers], opts[:templates], opts[:models] if post 'duplicate'
869
- run CreateDir, opts[:management_receivers], opts[:models] if post 'dir'
870
- run ManagementSend, opts[:management_receivers] if sse
895
+ run Create, :main, opts[:views], opts[:management_receivers], opts[:templates], opts[:models] if post 'item'
896
+ run CreateDir, :main, opts[:management_receivers], opts[:models] if post 'dir'
897
+ on resource 'management' do
898
+ run ManagementSend, opts[:management_receivers] if sse
899
+ end
871
900
  on resource '[a-zA-Z0-9öäüÖÄÜ _-]+\.dir' do
872
901
  run GetList, :sub, opts[:views], opts[:models] if get 'stage'
873
- run Create, :sub, :cre, opts[:views], opts[:management_receivers], opts[:templates], opts[:models] if post 'item'
874
- run Create, :sub, :dup, opts[:views], opts[:management_receivers], opts[:templates], opts[:models] if post 'duplicate'
902
+ run Create, :sub, opts[:views], opts[:management_receivers], opts[:templates], opts[:models] if post 'item'
875
903
  run DeleteDir, opts[:management_receivers], opts[:models] if delete
876
904
  run RenameDir, opts[:management_receivers], opts[:models] if put 'name'
905
+ run CreateDir, :sub, opts[:management_receivers], opts[:models] if post 'dir'
877
906
  on resource '[a-zA-Z0-9öäüÖÄÜ _-]+\.xml' do
878
907
  run DeleteItem, :sub, opts[:management_receivers], opts[:models] if delete
879
908
  run GetItem, :sub, opts[:models] if get
880
909
  run PutItem, :sub, opts[:management_receivers], opts[:models] if put 'content'
881
910
  run RenameItem, :sub, opts[:management_receivers], opts[:models] if put 'name'
882
- run MoveItem, :sub, opts[:management_receivers], opts[:models] if put 'dirname'
911
+ run MoveItem, :sub, opts[:management_receivers], opts[:models] if put 'movedir'
912
+ run Create, :sub, opts[:views], opts[:management_receivers], opts[:templates], opts[:models] if put 'dupdir'
883
913
  run ShiftItem, :sub, opts[:management_receivers], opts[:themes], opts[:models] if put 'newstage'
884
914
  on resource 'open' do
885
915
  run OpenItem, :sub, opts[:instantiate], opts[:cockpit], opts[:views], false, opts[:models] if get 'stage'
@@ -894,7 +924,8 @@ module CPEE
894
924
  run GetItem, :main, opts[:models] if get
895
925
  run PutItem, :main, opts[:management_receivers], opts[:models] if put 'content'
896
926
  run RenameItem, :main, opts[:management_receivers], opts[:models] if put 'name'
897
- run MoveItem, :main, opts[:management_receivers], opts[:models] if put 'dirname'
927
+ run MoveItem, :main, opts[:management_receivers], opts[:models] if put 'movedir'
928
+ run Create, :main, opts[:views], opts[:management_receivers], opts[:templates], opts[:models] if put 'dupdir'
898
929
  run ShiftItem, :main, opts[:management_receivers], opts[:themes], opts[:models] if put 'newstage'
899
930
  on resource 'open' do
900
931
  run OpenItem, :main, opts[:instantiate], opts[:cockpit], opts[:views], false, opts[:models] if get 'stage'
@@ -32,17 +32,14 @@
32
32
  <param name="pattern">([a-zA-Z0-9öäüÖÄÜ _-]+)|</param>
33
33
  </parameter>
34
34
  </message>
35
- <message name="dirname">
36
- <parameter name="dir" type="string">
37
- <param name="pattern">([a-zA-Z0-9öäüÖÄÜ _-]+)\.dir|</param>
35
+ <message name="movedir">
36
+ <parameter name="movedir" type="string">
37
+ <param name="pattern">(([a-zA-Z0-9öäüÖÄÜ _-]+)\.dir(\/([a-zA-Z0-9öäüÖÄÜ _-]+)\.dir)*)(\/?)|</param>
38
38
  </parameter>
39
39
  </message>
40
- <message name="duplicate">
41
- <parameter name="new" type="string">
42
- <param name="pattern">[a-zA-Z0-9öäüÖÄÜ _-]+</param>
43
- </parameter>
44
- <parameter name="old" type="string">
45
- <param name="pattern">[a-zA-Z0-9öäüÖÄÜ _-]+\.xml</param>
40
+ <message name="dupdir">
41
+ <parameter name="dupdir" type="string">
42
+ <param name="pattern">(([a-zA-Z0-9öäüÖÄÜ _-]+)\.dir(\/([a-zA-Z0-9öäüÖÄÜ _-]+)\.dir)*)(\/?)|</param>
46
43
  </parameter>
47
44
  </message>
48
45
  <message name="stages">
@@ -77,14 +74,15 @@
77
74
  <resource>
78
75
  <post in="item"/>
79
76
  <post in="dir"/>
80
- <post in="duplicate"/>
81
77
  <get in="stage" out="list"/>
82
78
  <get in="full" out="list"/>
83
79
  <get in="stages" out="list"/>
84
- <sse/>
85
- <resource relative="[a-zA-Z0-9&#xF6;&#xE4;&#xFC;&#xD6;&#xC4;&#xDC; _-]+\.dir">
80
+ <resource relative="management">
81
+ <sse/>
82
+ </resource>
83
+ <resource relative="[a-zA-Z0-9&#xF6;&#xE4;&#xFC;&#xD6;&#xC4;&#xDC; _-]+\.dir" recursive="true">
86
84
  <post in="item"/>
87
- <post in="duplicate"/>
85
+ <post in="dir"/>
88
86
  <get in="stage" out="list"/>
89
87
  <delete/>
90
88
  <put in="name"/> <!-- rename -->
@@ -93,8 +91,9 @@
93
91
  <delete/>
94
92
  <put in="content"/>
95
93
  <put in="name"/> <!-- rename -->
96
- <put in="dirname"/> <!-- move -->
94
+ <put in="movedir"/> <!-- move -->
97
95
  <put in="newstage"/> <!-- shift -->
96
+ <put in="dupdir"/> <!-- duplicate -->
98
97
  <resource relative="open"><get in="stage"/></resource>
99
98
  <resource relative="open-new"><get in="stage"/></resource>
100
99
  </resource>
@@ -104,7 +103,8 @@
104
103
  <delete/>
105
104
  <put in="content"/>
106
105
  <put in="name"/> <!-- rename -->
107
- <put in="dirname"/> <!-- move -->
106
+ <put in="movedir"/> <!-- move -->
107
+ <put in="dupdir"/> <!-- duplicate -->
108
108
  <put in="newstage"/> <!-- shift -->
109
109
  <resource relative="open"><get in="stage"/></resource>
110
110
  <resource relative="open-new"><get in="stage"/></resource>
data/server/redis.rdb ADDED
Binary file
data/tools/cpee-moma CHANGED
@@ -80,7 +80,7 @@ p2 = ARGV[2]
80
80
  ui = "#{curpath}/../ui/"
81
81
 
82
82
  if command == 'cpui'
83
- if !File.exists?(p1)
83
+ if !File.exist?(p1)
84
84
  FileUtils.cp_r(ui,p1)
85
85
  else
86
86
  FileUtils.cp_r(Dir.glob(File.join(ui,'*')),p1,remove_destination: true)
@@ -178,8 +178,8 @@ elsif command == 'convert'
178
178
  end
179
179
  Dir['*.dir'].each do |f|
180
180
  attrs = JSON::load File.open(f + '.attrs') rescue {}
181
- attrs['creator'] = File.read(f + '.creator') if File.exists?(f + '.creator')
182
- attrs['author'] = File.read(f + '.author') if File.exists?(f + '.author')
181
+ attrs['creator'] = File.read(f + '.creator') if File.exist?(f + '.creator')
182
+ attrs['author'] = File.read(f + '.author') if File.exist?(f + '.author')
183
183
  File.write(f + '.attrs',JSON::pretty_generate(attrs))
184
184
 
185
185
  File.unlink(f + '.creator') rescue nil
@@ -241,12 +241,12 @@ elsif command == 'consistent'
241
241
  end
242
242
  end
243
243
  elsif command == 'new'
244
- if !File.exists?(p1)
244
+ if !File.exist?(p1)
245
245
  FileUtils.mkdir(File.join(p1)) rescue nil
246
- FileUtils.cp_r("#{curpath}/../server/moma",p1) unless File.exists?(File.join('p1','moma'))
247
- FileUtils.cp_r("#{curpath}/../server/moma.conf",p1) unless File.exists?(File.join('p1','moma.conf'))
248
- FileUtils.cp_r("#{curpath}/../server/testset.xml",p1) unless File.exists?(File.join('p1','testset.xml'))
249
- FileUtils.cp_r("#{curpath}/../server/model.xml",p1) unless File.exists?(File.join('p1','model.xml'))
246
+ FileUtils.cp_r("#{curpath}/../server/moma",p1) unless File.exist?(File.join('p1','moma'))
247
+ FileUtils.cp_r("#{curpath}/../server/moma.conf",p1) unless File.exist?(File.join('p1','moma.conf'))
248
+ FileUtils.cp_r("#{curpath}/../server/testset.xml",p1) unless File.exist?(File.join('p1','testset.xml'))
249
+ FileUtils.cp_r("#{curpath}/../server/model.xml",p1) unless File.exist?(File.join('p1','model.xml'))
250
250
  FileUtils.mkdir(File.join(p1,'models')) rescue nil
251
251
  else
252
252
  puts 'Directory already exists.'
data/ui/css/moma.css ADDED
@@ -0,0 +1,114 @@
1
+ :root {
2
+ --x-ui-selection-background-light: #ef29297f;
3
+ --x-ui-selection-background: #ef2929;
4
+ }
5
+
6
+ [data-class=model] { min-width: 1.5em; text-align: center; font-size: 1.7em; cursor: pointer; }
7
+ [data-class=folder] { min-width: 1.5em; text-align: center; font-size: 1.7em; }
8
+ [data-class=model]:hover { background-color: var(--x-ui-selection-background-light) !important; }
9
+ [data-class=ops] { cursor: pointer; }
10
+ [data-class=special] { cursor: pointer; min-width: 1.5em; text-align: center !important; font-size: 1.7em; }
11
+ [data-class=special].invisible { cursor: auto; visibility: hidden; }
12
+ [data-class=special]:hover { background-color: var(--x-ui-selection-background-light) !important; }
13
+
14
+ [data-class=special] {
15
+ position: relative;
16
+ z-index: 0;
17
+ animation: important 0.2s infinite;
18
+ }
19
+ @keyframes important {
20
+ 0% { transform: rotate(0deg); }
21
+
22
+ 8% { transform: rotate(3deg); }
23
+ 16% { transform: rotate(6deg); }
24
+ 24% { transform: rotate(9deg); }
25
+ 32% { transform: rotate(6deg); }
26
+ 38% { transform: rotate(3deg); }
27
+
28
+ 50% { transform: rotate(0deg); }
29
+
30
+ 58% { transform: rotate(-3deg); }
31
+ 66% { transform: rotate(-6deg); }
32
+ 74% { transform: rotate(-9deg); }
33
+ 82% { transform: rotate(-6deg); }
34
+ 88% { transform: rotate(-3deg); }
35
+
36
+ 100% { transform: rotate(0deg); }
37
+ }
38
+
39
+ td.selected {
40
+ background-color: var(--x-ui-selection-background) !important;
41
+ }
42
+
43
+ div.fixed {
44
+ position: sticky;
45
+ z-index: 1;
46
+ top: 0;
47
+ width: 100%;
48
+ background-color: var(--x-ui-background-color);
49
+ }
50
+ div.text { padding-top: 1em; }
51
+ div.breadcrumb { padding-top: 0.5em; padding-bottom: 1em; }
52
+ div.breadcrumb span.separator {
53
+ padding-left: 1em;
54
+ padding-right: 1em;
55
+ }
56
+ div.breadcrumb span.crumb:last-child { }
57
+ div.breadcrumb span.crumb:not(:last-child) {
58
+ color: var(--x-ui-link-color);
59
+ cursor: pointer;
60
+ }
61
+ div.breadcrumb span.crumb:not(:last-child):hover {
62
+ color: var(--x-ui-light-text-color);
63
+ text-decoration: underline;
64
+ }
65
+
66
+ form input {
67
+ width: 45em !important;
68
+ }
69
+
70
+ ui-behind {
71
+ text-transform: capitalize;
72
+ font-weight: bold;
73
+ color: #d0d0d0;
74
+ }
75
+
76
+ ui-behind span {
77
+ cursor: pointer;
78
+ }
79
+
80
+ [is="x-ui-"] table.ui-table {
81
+ border-collapse: collapse;
82
+ }
83
+ [is="x-ui-"] table.ui-table thead th {
84
+ font-weight: bold;
85
+ text-align: left;
86
+ padding: 0.3em 0.5em;
87
+ }
88
+
89
+ [is="x-ui-"] table.ui-table tbody td {
90
+ padding: 0.1em 0.5em;
91
+ }
92
+ [is="x-ui-"] table.ui-table tbody tr:nth-child(odd) td {
93
+ background-color: var(--x-ui-content-light-background);
94
+ }
95
+ [is="x-ui-"] table.ui-table tbody tr:hover td {
96
+ background-color: var(--x-ui-content-hover-background);
97
+ }
98
+ [is="x-ui-"] table.ui-table tbody td[data-class=ops] {
99
+ text-align: center;
100
+ }
101
+ [is="x-ui-"] table.ui-table tbody tr:nth-child(odd) td[data-class=ops] {
102
+ background-color: #d0d0d0;
103
+ }
104
+ [is="x-ui-"] table.ui-table tbody tr:hover td[data-class=ops] {
105
+ background-color: #99cee6b0;
106
+ }
107
+
108
+ [is="x-ui-"] ui-rest > ui-content > ui-area {
109
+ padding: 0em 1em 1em 1em;
110
+ }
111
+
112
+ tr.contextmenuitem .capitalized {
113
+ text-transform: capitalize;
114
+ }