cpee-model-management 1.1.3 → 1.2.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 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
+ }