engine2 1.0.5 → 1.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. checksums.yaml +5 -5
  2. data/app/actions.coffee +93 -58
  3. data/app/app.css +12 -0
  4. data/app/engine2.coffee +42 -24
  5. data/conf/message.yaml +1 -0
  6. data/conf/message_pl.yaml +1 -0
  7. data/config.coffee +2 -2
  8. data/engine2.gemspec +1 -1
  9. data/lib/engine2/action.rb +130 -126
  10. data/lib/engine2/action/array.rb +4 -4
  11. data/lib/engine2/action/decode.rb +13 -9
  12. data/lib/engine2/action/infra.rb +3 -3
  13. data/lib/engine2/action/list.rb +17 -10
  14. data/lib/engine2/action_node.rb +1 -2
  15. data/lib/engine2/core.rb +35 -7
  16. data/lib/engine2/model.rb +64 -15
  17. data/lib/engine2/post_bootstrap.rb +1 -1
  18. data/lib/engine2/pre_bootstrap.rb +10 -0
  19. data/lib/engine2/scheme.rb +2 -2
  20. data/lib/engine2/templates.rb +8 -0
  21. data/lib/engine2/type_info.rb +37 -15
  22. data/lib/engine2/version.rb +1 -1
  23. data/package.json +8 -5
  24. data/views/fields/blob.slim +1 -1
  25. data/views/fields/bs_select.slim +2 -2
  26. data/views/fields/bsselect_picker.slim +4 -4
  27. data/views/fields/bsselect_picker_opt.slim +5 -5
  28. data/views/fields/checkbox.slim +4 -4
  29. data/views/fields/checkbox_buttons.slim +3 -3
  30. data/views/fields/checkbox_buttons_opt.slim +3 -3
  31. data/views/fields/currency.slim +2 -2
  32. data/views/fields/date.slim +4 -4
  33. data/views/fields/date_range.slim +9 -9
  34. data/views/fields/date_time.slim +9 -9
  35. data/views/fields/datetime.slim +8 -8
  36. data/views/fields/decimal.slim +1 -1
  37. data/views/fields/decimal_date.slim +3 -3
  38. data/views/fields/decimal_time.slim +3 -3
  39. data/views/fields/email.slim +3 -3
  40. data/views/fields/file_store.slim +4 -4
  41. data/views/fields/input_text.slim +4 -4
  42. data/views/fields/integer.slim +1 -1
  43. data/views/fields/list_bsmselect.slim +20 -0
  44. data/views/fields/list_bsselect.slim +5 -5
  45. data/views/fields/list_bsselect_opt.slim +6 -6
  46. data/views/fields/list_buttons.slim +1 -1
  47. data/views/fields/list_buttons_opt.slim +2 -2
  48. data/views/fields/list_select.slim +4 -4
  49. data/views/fields/list_select_opt.slim +5 -5
  50. data/views/fields/password.slim +4 -4
  51. data/views/fields/radio_checkbox.slim +3 -3
  52. data/views/fields/scaffold.slim +1 -1
  53. data/views/fields/scaffold_picker.slim +5 -5
  54. data/views/fields/select_picker.slim +3 -3
  55. data/views/fields/select_picker_opt.slim +4 -4
  56. data/views/fields/text_area.slim +3 -3
  57. data/views/fields/time.slim +5 -4
  58. data/views/fields/typeahead_picker.slim +5 -5
  59. data/views/scaffold/fields.slim +4 -4
  60. data/views/scaffold/form.slim +1 -1
  61. data/views/scaffold/form_collapse.slim +4 -3
  62. data/views/scaffold/form_tabs.slim +3 -2
  63. data/views/scaffold/search.slim +2 -2
  64. data/views/scaffold/search_collapse.slim +6 -5
  65. data/views/scaffold/search_tabs.slim +4 -3
  66. data/views/scaffold/view.slim +2 -2
  67. data/views/scaffold/view_collapse.slim +5 -4
  68. data/views/scaffold/view_tabs.slim +4 -3
  69. data/views/search_fields/bsmselect_picker.slim +4 -4
  70. data/views/search_fields/bsselect_picker.slim +4 -4
  71. data/views/search_fields/checkbox.slim +3 -3
  72. data/views/search_fields/checkbox2.slim +5 -5
  73. data/views/search_fields/checkbox_buttons.slim +3 -3
  74. data/views/search_fields/date_range.slim +8 -8
  75. data/views/search_fields/decimal_date_range.slim +5 -5
  76. data/views/search_fields/input_text.slim +2 -2
  77. data/views/search_fields/integer.slim +1 -1
  78. data/views/search_fields/integer_range.slim +2 -2
  79. data/views/search_fields/list_bsmselect.slim +4 -4
  80. data/views/search_fields/list_bsselect.slim +4 -4
  81. data/views/search_fields/list_buttons.slim +2 -2
  82. data/views/search_fields/list_select.slim +3 -3
  83. data/views/search_fields/scaffold_picker.slim +2 -2
  84. data/views/search_fields/select_picker.slim +3 -3
  85. data/views/search_fields/typeahead_picker.slim +4 -4
  86. metadata +6 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 61ac9190b96b7053391913f8121f1df0bc1967c0
4
- data.tar.gz: b532a2ca2828607b2068450761cc55a77d43f597
2
+ SHA256:
3
+ metadata.gz: 58f23892775f4ae41feaedf7b372844c6524e8088262d51dd7b91e23ccd136c0
4
+ data.tar.gz: 3a9350c94bee76c210976c9f06edde30bfb3fec3c0ac56941f40889a193d3f7f
5
5
  SHA512:
6
- metadata.gz: 3afb80475e3f7cad3bed23163f0b03f1d54d9268a46749c5e7d9dbc5aa77cbb0859b83592094e50b8178492ff3656a98eb31123be8f56a99962bac033dc20d1a
7
- data.tar.gz: a7d20b040fd8a85373a9328508710064fa403103b2c661d2d2d71eaec148910397e2d2ddcffb0aea6712eab3bb3bf49943f496e87bd413997d6e25e1d2586bed
6
+ metadata.gz: 73eb8d90f5701a83888592a80d33874db321756f2b003c3a0359ed2a456d317d38e4c11c307760ed98a708725135b6f03b92d06e2fc2bc2759351ecd8705a939
7
+ data.tar.gz: a8000d44c3092297f5a50c29ed4e4007e7f1190aefa34cc528b0e7c5375c3d1a0b7bf17321227a159c3585aee49eaf5d13120de7c884fad66e217f255a87d4ae
@@ -134,7 +134,7 @@ angular.module('Engine2')
134
134
  E2.merge(prnt, response.data)
135
135
 
136
136
  @post_invoke(params)
137
- @scope().$eval(@meta.execute) if @meta.execute
137
+ @execute_commands() if @meta.execute
138
138
  if @meta.repeat
139
139
  @scope().$on "$destroy", => @destroyed = true
140
140
  $timeout (=> @invoke(params)), @meta.repeat unless @destroyed
@@ -203,24 +203,27 @@ angular.module('Engine2')
203
203
  ws = $websocket "ws#{l.protocol().slice(4, 5)}://#{l.host()}:#{l.port()}/#{@action_info().action_resource}", undefined, ws_meta.options
204
204
  _.each @globals().ws_methods, (method) =>
205
205
  ws_method_impl = @["ws_#{method}"]
206
- ws_method_exec = ws_meta.execute?[method]
207
- if ws_method_impl || ws_method_exec
208
- ws_method = (evt) =>
209
- is_message = method == 'message'
210
- if is_message
211
- msg = JSON.parse(evt.data)
212
- if msg.error then @globals().modal().error("WebSocket [#{evt.origin}] - #{msg.error.method}", msg.error.exception) else
213
- E2.merge(@, msg)
214
- @process_meta()
215
- else msg = evt
216
- ws_method_impl.bind(@)(msg, ws, evt) if ws_method_impl
217
- @scope().$eval(ws_method_exec) if ws_method_exec
218
-
219
- ws["on#{_.capitalize(method)}"](ws_method)
206
+ ws["on#{_.capitalize(method)}"] (evt) =>
207
+ if method == 'message'
208
+ msg = JSON.parse(evt.data)
209
+ if msg.error then @globals().modal().error("WebSocket [#{evt.origin}] - #{msg.error.method}", msg.error.exception) else
210
+ E2.merge(@, msg)
211
+ @process_meta()
212
+ else msg = evt
213
+ ws_method_impl.bind(@)(msg, ws, evt) if ws_method_impl
214
+ @execute_commands() if @meta.execute
215
+ delete @meta.execute
220
216
 
221
217
  @web_socket = -> ws
222
218
  @scope().$on "$destroy", -> ws.close()
223
219
 
220
+ execute_commands: ->
221
+ scope = @scope()
222
+ _.reduce(@meta.execute, ((pr, cmd) -> pr.then -> scope.$eval(cmd)), $q.when())
223
+
224
+ console_log: (o) ->
225
+ console.log o
226
+
224
227
  root: class RootAction extends Action
225
228
  initialize: ->
226
229
  super()
@@ -268,7 +271,10 @@ angular.module('Engine2')
268
271
  _.each response.data.actions, (act, nm) -> act.name = nm
269
272
  tree.actions ?= _.toArray(response.data.actions)
270
273
  @meta_json = response.data.meta
271
- @action_state = if @meta_json.state then _.zipObject(@meta_json.state.map (k) -> [k, localStorageService.get("#{path}/#{k}")]) else {}
274
+ if @meta_json.state
275
+ @action_state = {}
276
+ _.each @meta_json.state, (s) => @action_state[s] = localStorageService.get("#{@globals().application}/#{path.join('/')}/#{s}")
277
+ @action_state
272
278
  ,
273
279
  (err) =>
274
280
  delete @meta_json
@@ -318,7 +324,7 @@ angular.module('Engine2')
318
324
 
319
325
  traverse: (routes) ->
320
326
  menu_tmpl = _.template("<li {{show}} {{hide}} ui-sref-active='active'><a {{href}}>{{icon}} {{loc}}</a></li>")
321
- menu_sub_tmpl = _.template("<li {{show}} {{hide}} e2-dropdown='{{dropdown}}' href-attr='ui-sref' data-animation='{{animation}}'><a href='javascript://'>{{icon}} {{loc}}<span class='caret'></span></a></li>")
327
+ menu_sub_tmpl = _.template("<li {{show}} {{hide}} e2-dropdown='{{dropdown}}' nav='true' data-animation='{{animation}}'><a href='javascript://'>{{icon}} {{loc}}<span class='caret'></span></a></li>")
322
328
  animation = @meta.menus.menu.properties.animation
323
329
  out = routes.map (route, i) ->
324
330
  if route.menu
@@ -345,8 +351,8 @@ angular.module('Engine2')
345
351
  @ui_state = {}
346
352
  @load_state()
347
353
 
348
- delete @query.order unless @meta.info[@query.order]?.sort # _.includes(@meta.fields, @query.order)
349
- _.each @query.search, ((sv, sn) => delete @query.search[sn] unless _.includes(@meta.search_fields, sn))
354
+ delete @query.order unless @meta.fields[@query.order]?.sort # _.includes(@meta.field_list, @query.order)
355
+ _.each @query.search, ((sv, sn) => delete @query.search[sn] unless _.includes(@meta.search_field_list, sn))
350
356
 
351
357
  destroy: ->
352
358
  @save_state()
@@ -355,10 +361,13 @@ angular.module('Engine2')
355
361
  process_meta: ->
356
362
  super()
357
363
  meta = @meta
358
- meta.fields = meta.fields.filter((f) => !meta.info[f].hidden) if meta.fields
364
+ meta.field_list = meta.field_list.filter((f) => !meta.fields[f].hidden) if meta.field_list
359
365
 
360
366
  # confirm_create, view, confirm_modify, confirm_delete, assocs - implicit
361
367
 
368
+ render_table: ->
369
+ @scope().$broadcast 'render_table'
370
+
362
371
  menu_search_toggle: ->
363
372
  @ui_state.search_active = !@ui_state.search_active
364
373
  @save_state() unless @ui_state.search_active
@@ -372,7 +381,8 @@ angular.module('Engine2')
372
381
 
373
382
  menu_select_toggle: ->
374
383
  if @selection then delete @selection else @selection = {}
375
- @scope().$broadcast 'render_table'
384
+ @render_table()
385
+
376
386
 
377
387
  menu_show_meta: ->
378
388
  @globals().modal().show
@@ -394,7 +404,7 @@ angular.module('Engine2')
394
404
  E2.id_for(@current_entry(), @meta)
395
405
 
396
406
  list_cell: (e, f) ->
397
- E2.render_field(e, f, @meta)
407
+ E2.render_field(e, f, @meta, "<br>")
398
408
 
399
409
  invoke: (args = {}) ->
400
410
  @save_state()
@@ -404,7 +414,7 @@ angular.module('Engine2')
404
414
  super(query).then =>
405
415
  @ui = _.pick @query, ['order', 'asc', 'page']
406
416
  @ui.pagination_active = @ui.page != 0 || @entries.length >= @meta.config.per_page
407
- @scope().$broadcast 'render_table'
417
+ @render_table()
408
418
 
409
419
  load_new: ->
410
420
  @query.page = 0
@@ -437,7 +447,7 @@ angular.module('Engine2')
437
447
  @load_new()
438
448
 
439
449
  search_field_change: (f) ->
440
- info = @meta.info[f]
450
+ info = @meta.fields[f]
441
451
 
442
452
  @scope().$eval(info.onchange) if info.onchange
443
453
 
@@ -465,6 +475,17 @@ angular.module('Engine2')
465
475
  selected_info: ->
466
476
  @meta.loc.selected + ": " + @selected_size()
467
477
 
478
+ entry_dropped: (moved_to, render = true) ->
479
+ from = @entries[@moved_from]
480
+ @entries.splice(@moved_from, 1)
481
+ @entries.splice((if moved_to > @moved_from then moved_to - 1 else moved_to), 0, from)
482
+ delete @moved_from
483
+ @render_table() if render
484
+ true
485
+
486
+ entry_moved: (index) ->
487
+ @moved_from = index
488
+
468
489
  bulk_delete: class BulkDeleteAction extends Action
469
490
  invoke: ->
470
491
  super(ids: [_.keys(@parent().parent().selection)]).then =>
@@ -472,12 +493,12 @@ angular.module('Engine2')
472
493
 
473
494
  view: class ViewAction extends Action
474
495
  view_cell: (e, f) ->
475
- E2.render_field(e, f, @meta)
496
+ E2.render_field(e, f, @meta, "<br>")
476
497
 
477
498
  form_base_action: class FormBaseAction extends Action
478
499
  initialize: ->
479
500
  super()
480
- _.each @meta.info, (info, name) =>
501
+ _.each @meta.fields, (info, name) =>
481
502
  if info.remote_onchange
482
503
  @scope().$watch (=> @record?[name]), (n) => if n? #if typeof(n) != "undefined"
483
504
  params = value: @record[name]
@@ -488,7 +509,7 @@ angular.module('Engine2')
488
509
  @scope().$watch (=> @record?[name]), (n) => if n?
489
510
  @scope().$eval(info.onchange)
490
511
 
491
- if @meta.tabs
512
+ if @meta.tab_list
492
513
  @scope().$watch "action.activeTab", (tab) => if tab? # && tab >= 0
493
514
  @panel_shown()
494
515
 
@@ -496,36 +517,37 @@ angular.module('Engine2')
496
517
 
497
518
  post_invoke: (args) ->
498
519
  super()
499
- _.each @meta.info, (info, name) =>
520
+ _.each @meta.fields, (info, name) =>
500
521
  if _.isString(@record[name]) && !info.dont_strip
501
522
  @record[name] = @record[name].trim()
502
523
 
503
524
  panel_menu_default_action: ->
504
- _.each @meta.info, (v, n) =>
525
+ _.each @meta.fields, (v, n) =>
505
526
  @record[n] = null if @record[n] is undefined
506
527
  params = record: @record
507
528
  params.parent_id ?= @parent().query?.parent_id # and StarToManyList ?
508
529
  @invoke_action(@default_action_name, params).then =>
509
530
  dfd = $q.defer()
510
531
  if @errors
511
- if @meta.tabs
532
+ if @meta.tab_list
512
533
  [i, first, curr] = [0, null, false]
513
- for tab in @meta.tabs
514
- if _(tab.fields).find((f) => @errors[f])
534
+ for tab_name in @meta.tab_list
535
+ tab = @meta.tabs[tab_name]
536
+ if _(tab.field_list).find((f) => @errors[f])
515
537
  first = i if not first?
516
538
  act = true if @activeTab == i
517
539
  i++
518
540
  @activeTab = first unless act
519
541
 
520
542
  if @activeTab?
521
- field = _(@meta.tabs[@activeTab].fields).find((f) => @errors[f])
543
+ field = _(@meta.tabs[@meta.tab_list[@activeTab]].field_list).find((f) => @errors[f])
522
544
  # console.log field undefined ?
523
545
  else
524
546
  @activeTab = 0
525
547
  @alert = @errors
526
548
  else
527
- field = _(@meta.fields).find((f) => @errors[f])
528
- @alert = @errors if (!field || !@meta.info[field] || @meta.info[field].hidden) # ?
549
+ field = _(@meta.field_list).find((f) => @errors[f])
550
+ @alert = @errors if (!field || !@meta.fields[field] || @meta.fields[field].hidden) # ?
529
551
  $timeout => @scope().$broadcast("focus_field", field)
530
552
  #e.scope.$eval(meta.execute) if meta.execute # ?
531
553
  dfd.resolve()
@@ -534,15 +556,15 @@ angular.module('Engine2')
534
556
  dfd.promise
535
557
 
536
558
  panel_shown: ->
537
- field = if @meta.tabs
538
- tab = @meta.tabs[@activeTab]
559
+ field = if @meta.tab_list
560
+ tab = @meta.tabs[@meta.tab_list[@activeTab]]
539
561
  if @errors
540
- _(tab.fields).find((f) => @errors[f]) || _(tab.fields).find((f) => !@meta.info[f].hidden)
562
+ _(tab.field_list).find((f) => @errors[f]) || _(tab.field_list).find((f) => !@meta.fields[f].hidden)
541
563
  else
542
- tab ?= @meta.tabs[0]
543
- _(tab.fields).find((f) => !@meta.info[f].hidden && !@meta.info[f].disabled)
564
+ tab ?= @meta.tabs[@meta.tab_list[0]]
565
+ _(tab.field_list).find((f) => !@meta.fields[f].hidden && !@meta.fields[f].disabled)
544
566
  else
545
- _(@meta.fields).find((f) => !@meta.info[f].hidden && !@meta.info[f].disabled)
567
+ _(@meta.field_list).find((f) => !@meta.fields[f].hidden && !@meta.fields[f].disabled)
546
568
  $timeout (=> @scope().$broadcast("focus_field", field)), 300 # hack, on shown ?
547
569
 
548
570
  infra: class InfraAction extends Action
@@ -552,7 +574,7 @@ angular.module('Engine2')
552
574
  if @user
553
575
  @invoke_action('login_form').then (act) =>
554
576
  act.record = name: @user.name
555
- act.meta.info.name.disabled = true
577
+ act.meta.fields.name.disabled = true
556
578
  act.dont_reload_routes = !reload_routes # true
557
579
  else
558
580
  @invoke().then => @set_access(true, true, @user)
@@ -591,12 +613,7 @@ angular.module('Engine2')
591
613
  modify: class ModifyAction extends FormAction
592
614
  # invoke: (args) ->
593
615
  # super(args).then =>
594
- # _.each @meta.primary_fields, (f) => @meta.info[f].disabled = true
595
-
596
- on_change: class OnChangeAction extends Action
597
- post_invoke: ->
598
- super()
599
- @parent().scope().$eval(@meta.execute) if @meta.execute
616
+ # _.each @meta.primary_fields, (f) => @meta.fields[f].disabled = true
600
617
 
601
618
  confirm: class ConfirmAction extends Action
602
619
  panel_menu_approve: ->
@@ -607,7 +624,7 @@ angular.module('Engine2')
607
624
  initialize: ->
608
625
  super()
609
626
  @decode_field = @scope().f
610
- @dinfo = @parentp().meta.info[@decode_field]
627
+ @dinfo = @parentp().meta.fields[@decode_field]
611
628
  throw "Primary and foreign key list lengths dont match: [#{@meta.primary_fields}] and [#{@dinfo.fields}]" unless @meta.primary_fields.length == @dinfo.fields.length
612
629
  @scope().$on "search_reset", => @clean()
613
630
 
@@ -626,7 +643,7 @@ angular.module('Engine2')
626
643
  @parentp().search_field_change?(@decode_field)
627
644
 
628
645
  decode_description: (entry) ->
629
- @meta.decode_fields.map((f) => E2.render_field(entry, f, @meta)).join(@meta.separator)
646
+ @meta.decode_fields.map((f) => E2.render_field(entry, f, @meta, ', ')).join(@meta.separator)
630
647
 
631
648
  parentp: ->
632
649
  @parent().parent()
@@ -653,11 +670,11 @@ angular.module('Engine2')
653
670
  if @selected.length > 0
654
671
  _.each @dinfo.fields, (fk) -> record[fk] = []
655
672
  _.each @selected, (sel) =>
656
- _(@dinfo.fields).zip(E2.split_keys(sel)).each(([fk, k]) => record[fk].push E2.parse_entry(k, @parentp().meta.info[fk])).value
673
+ _(@dinfo.fields).zip(E2.split_keys(sel)).each(([fk, k]) => record[fk].push E2.parse_entry(k, @parentp().meta.fields[fk])).value
657
674
  else @clear_record()
658
675
  else
659
676
  if @selected
660
- _(@dinfo.fields).zip(E2.split_keys(@selected)).each(([fk, k]) => record[fk] = E2.parse_entry(k, @parentp().meta.info[fk])).value
677
+ _(@dinfo.fields).zip(E2.split_keys(@selected)).each(([fk, k]) => record[fk] = E2.parse_entry(k, @parentp().meta.fields[fk])).value
661
678
  else @clear_record()
662
679
 
663
680
  @parentp().search_field_change?(@decode_field)
@@ -679,12 +696,12 @@ angular.module('Engine2')
679
696
  if @multiple
680
697
  _.each @dinfo.fields, (fk) => record[fk] = []
681
698
  _.each sel, (rec, ids) =>
682
- _(@dinfo.fields).zip(E2.split_keys(ids)).each(([k, v]) => record[k].push E2.parse_entry(v, @parentp().meta.info[k])).value
699
+ _(@dinfo.fields).zip(E2.split_keys(ids)).each(([k, v]) => record[k].push E2.parse_entry(v, @parentp().meta.fields[k])).value
683
700
  @invoke_decode _.values(sel)
684
701
  delete @decode if _.isEmpty(sel)
685
702
  else
686
703
  [ids, rec] = _(sel).toPairs().head()
687
- _(@dinfo.fields).zip(E2.split_keys(ids)).each(([k, v]) => record[k] = E2.parse_entry(v, @parentp().meta.info[k])).value
704
+ _(@dinfo.fields).zip(E2.split_keys(ids)).each(([k, v]) => record[k] = E2.parse_entry(v, @parentp().meta.fields[k])).value
688
705
  @invoke_decode [rec]
689
706
  @parentp().search_field_change?(@decode_field)
690
707
 
@@ -694,7 +711,7 @@ angular.module('Engine2')
694
711
  else
695
712
  decode_descriptions = (recs) => @decode = recs.map((fields) => @decode_description(fields)).join(' | ')
696
713
  recs = recs.map (r) => if _.isArray(r) then E2.from_id(r, @meta) else r
697
- if _(recs).every((r) => _(@meta.fields).every((f) -> r[f]?)) && !@meta.dynamic_meta then decode_descriptions(recs) else
714
+ if _(recs).every((r) => _(@meta.field_list).every((f) -> r[f]?)) && !@meta.dynamic_meta then decode_descriptions(recs) else
698
715
  @invoke(ids: [recs.map((r) => @meta.primary_fields.map (k) -> r[k])]).then => decode_descriptions(@entries)
699
716
 
700
717
  open: ->
@@ -720,7 +737,7 @@ angular.module('Engine2')
720
737
 
721
738
  @scope().$on "$typeahead.select", (e, v, index) =>
722
739
  e.stopPropagation()
723
- _(@dinfo.fields).zip(E2.split_keys(@values[index].id)).each(([fk, k]) => @record()[fk] = E2.parse_entry(k, @parentp().meta.info[fk])).value
740
+ _(@dinfo.fields).zip(E2.split_keys(@values[index].id)).each(([fk, k]) => @record()[fk] = E2.parse_entry(k, @parentp().meta.fields[fk])).value
724
741
  @parentp().search_field_change?(@decode_field)
725
742
 
726
743
  @scope().$watch "action.decode", (e) => if e?
@@ -795,8 +812,23 @@ angular.module('Engine2')
795
812
  @query.changes = @changes
796
813
  super()
797
814
 
798
- current_entry_is: (mode) ->
799
- key = E2.id_for(@current_entry(), @meta)
815
+ entry_dropped: (moved_to) ->
816
+ pos_field = @meta.draggable.position_field
817
+ positions = @entries.map (e) -> e[pos_field]
818
+ super(moved_to, false)
819
+ _.each positions, (p, i) =>
820
+ if @entries[i][pos_field] != p
821
+ if entry = @current_entry_is('create', @entries[i]) ? @current_entry_is('modify', @entries[i])
822
+ entry[pos_field] = p
823
+ else
824
+ @changes.modify.push(@entries[i])
825
+ @entries[i][pos_field] = p
826
+
827
+ @render_table() # @invoke()
828
+ true
829
+
830
+ current_entry_is: (mode, entry = @current_entry()) ->
831
+ key = E2.id_for(entry, @meta)
800
832
  _.find(@changes[mode], (e) => E2.id_for(e, @meta) == key)
801
833
 
802
834
  star_to_many_field_view: class StarToManyFieldView extends ViewAction
@@ -825,6 +857,9 @@ angular.module('Engine2')
825
857
  pparent.changes.modify.push @parent().record
826
858
  else # CreateAction
827
859
  _(@parent().meta.primary_fields).each (k) => @parent().record[k] = E2.uuid(5)
860
+ if draggable = pparent.meta.draggable
861
+ max = _.maxBy(pparent.entries, (e) -> e.position)
862
+ @parent().record[draggable.position_field] = if max then max[draggable.position_field] + 1 else 1
828
863
  pparent.changes.create.push @parent().record
829
864
 
830
865
  star_to_many_field_delete: class StarToManyFieldDelete extends Action
@@ -205,3 +205,15 @@ button {
205
205
  }*/
206
206
 
207
207
  a[ng-click] {cursor:pointer;}
208
+
209
+ .dndDragging {
210
+ }
211
+
212
+ .dndDraggingSource {
213
+ }
214
+
215
+ .dndPlaceholder{
216
+ }
217
+
218
+ .dndDragover {
219
+ }
@@ -7,10 +7,12 @@ require 'angular-ui-tree'
7
7
  require '@uirouter/angularjs'
8
8
  require 'ng-file-upload'
9
9
  require 'angular-load'
10
+ require 'angular-drag-and-drop-lists'
11
+ require 'ui-select'
10
12
 
11
13
  _.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
12
14
 
13
- angular.module('Engine2', ['ngSanitize', 'ngAnimate', 'ngCookies', 'mgcrea.ngStrap', 'ngFileUpload', 'ui.tree', 'LocalStorageModule', 'angularLoad', 'ngWebSocket', 'ui.router']) # 'draggabilly'
15
+ angular.module('Engine2', ['ngSanitize', 'ngAnimate', 'ngCookies', 'mgcrea.ngStrap', 'ngFileUpload', 'ui.tree', 'LocalStorageModule', 'angularLoad', 'ngWebSocket', 'ui.router', 'dndLists', 'ui.select'])
14
16
  .config ($httpProvider, $compileProvider, localStorageServiceProvider, $logProvider, $qProvider, $locationProvider, $provide) ->
15
17
  $httpProvider.interceptors.push 'e2HttpInterceptor'
16
18
  $provide.decorator '$httpBackend', ($delegate) ->
@@ -29,6 +31,16 @@ angular.module('Engine2', ['ngSanitize', 'ngAnimate', 'ngCookies', 'mgcrea.ngStr
29
31
  # $qProvider.errorOnUnhandledRejections(false)
30
32
  # $locationProvider.hashPrefix('')
31
33
  # $locationProvider.html5Mode(false)
34
+ $provide.decorator 'ngModelDirective', ($delegate) ->
35
+ directive = $delegate[0]
36
+ compile = directive.compile
37
+ directive.compile = (elem, attrs, trans) ->
38
+ comp = compile(elem, attrs, trans)
39
+ pre: comp.pre
40
+ post: (scope, element, attr, ctrls) ->
41
+ ctrls[0].$parsers.push (vw) -> if vw == "" then null else vw
42
+ comp.post(scope, element, attr, ctrls)
43
+ $delegate
32
44
 
33
45
  .factory 'PushJS', -> require 'push.js'
34
46
  .factory 'MetaCache', ($cacheFactory) -> $cacheFactory('MetaCache')
@@ -212,11 +224,14 @@ angular.module('Engine2', ['ngSanitize', 'ngAnimate', 'ngCookies', 'mgcrea.ngStr
212
224
  when render.true_value then E2Snippets.boolean_true_value
213
225
  when render.false_value then E2Snippets.boolean_false_value
214
226
  else "?"
215
- list_select: (value, render) ->
216
- render.list_hash ||= render.list.reduce(((h, [a, b]) -> h[a] = b; h), {})
217
- render.list_hash[value] ? value
227
+ list_select: (value, render, separator) ->
228
+ render.list_hash ||= render.values.reduce(((h, [a, b]) -> h[a] = b; h), {})
229
+ if render.multiselect && _.isArray(value)
230
+ value.map((v) -> render.list_hash[v] ? ":#{value}:").join(separator)
231
+ else
232
+ render.list_hash[value] ? ":#{value}:"
218
233
  datetime: (value, render) ->
219
- value.split('\.')[0]
234
+ value.split('\.')[0].split(' ', 2).join(' ')
220
235
  # $dateFormatter.formatDate(value, "yyyy-MM-dd", $dateFormatter.getDefaultLocale())
221
236
  integer: (value, render) -> # ?
222
237
  value.toString()
@@ -231,12 +246,12 @@ angular.module('Engine2', ['ngSanitize', 'ngAnimate', 'ngCookies', 'mgcrea.ngStr
231
246
  value.toString()
232
247
 
233
248
 
234
- render_field: (entry, name, meta) ->
249
+ render_field: (entry, name, meta, separator) ->
235
250
  value = entry[name]
236
- if value? && info = meta.info
251
+ if value? && info = meta.fields
237
252
  f_info = info[name]
238
253
  if f_info? && type = f_info.type
239
- @renderers[type](value, f_info.render)
254
+ @renderers[type](value, f_info.render, separator)
240
255
  else
241
256
  if f_info?.escape == false then value else
242
257
  (value + "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
@@ -260,7 +275,7 @@ angular.module('Engine2', ['ngSanitize', 'ngAnimate', 'ngCookies', 'mgcrea.ngStr
260
275
 
261
276
  name = attrs.e2Field || scope.f
262
277
  meta = scope.action.meta
263
- info = meta.info[name]
278
+ info = meta.fields[name]
264
279
  scope.$on "focus_field", (event, n, mode) ->
265
280
  if n == name
266
281
  elem[0].focus()
@@ -293,7 +308,7 @@ angular.module('Engine2', ['ngSanitize', 'ngAnimate', 'ngCookies', 'mgcrea.ngStr
293
308
  link: (scope, elem, attrs, controller) ->
294
309
  name = attrs.e2Field || scope.f
295
310
  meta = scope.action.meta
296
- info = meta.info[name]
311
+ info = meta.fields[name]
297
312
  if info.filter
298
313
  filter = $filter(info.filter)
299
314
  controller.$parsers.push (value) ->
@@ -317,7 +332,7 @@ angular.module('Engine2', ['ngSanitize', 'ngAnimate', 'ngCookies', 'mgcrea.ngStr
317
332
  elem.after($compile(response.data)(scope))
318
333
 
319
334
  .directive 'e2TableBody', ($parse, $compile) ->
320
- table_tmpl = _.template("<thead><tr>{{thead}}</tr></thead><tbody>{{tbody}}</tbody>")
335
+ table_tmpl = _.template("<thead><tr>{{thead}}</tr></thead><tbody {{tbody_attrs}}>{{tbody}}</tbody>")
321
336
  scope: false
322
337
  restrict: 'A'
323
338
  link: (scope, elem, attrs) ->
@@ -325,15 +340,16 @@ angular.module('Engine2', ['ngSanitize', 'ngAnimate', 'ngCookies', 'mgcrea.ngStr
325
340
  # ev.stopPropagation()
326
341
  action = scope.action
327
342
  meta = action.meta
343
+ draggable = meta.draggable
328
344
  position = meta.menus.item_menu.properties.position ? 0
329
- right_style = if position >= meta.fields.length then "style=\"text-align: right\"" else ""
345
+ right_style = if position >= meta.field_list.length then "style=\"text-align: right\"" else ""
330
346
 
331
347
  thead = ""
332
- fields = meta.fields.slice()
348
+ fields = meta.field_list.slice()
333
349
  fields.splice(position, 0, null)
334
350
  _.each fields, (f) ->
335
351
  if f
336
- info = meta.info[f]
352
+ info = meta.fields[f]
337
353
  thead += "<th>"
338
354
  title = if info.title then "title=\"#{info.title}\"" else ""
339
355
  if info.sort
@@ -351,25 +367,27 @@ angular.module('Engine2', ['ngSanitize', 'ngAnimate', 'ngCookies', 'mgcrea.ngStr
351
367
  _.each action.entries, (e, i) ->
352
368
  tbody += if action.selection then "<tr ng-class=\"action.selected_class(#{i})\" class=\"tr_hover\" ng-click=\"action.select(#{i}, $event)\">" else
353
369
  row_cls = e.$row_info?.class
354
- if row_cls then "<tr class=\"#{row_cls}\">" else "<tr>"
370
+ tr_attrs = if draggable then "dnd-draggable=\"action.entries[#{i}]\" dnd-dragstart=\"action.entry_moved(#{i})\"" else ''
371
+ if row_cls then "<tr class=\"#{row_cls}\" #{tr_attrs}>" else "<tr #{tr_attrs}>"
355
372
  _.each fields, (f) ->
356
373
  if f
357
- tbody += if col_cls = meta.info[f].column_class then "<td class=\"#{col_cls}\">" else "<td>"
374
+ tbody += if col_cls = meta.fields[f].column_class then "<td class=\"#{col_cls}\">" else "<td>"
358
375
  tbody += action.list_cell(e, f) ? ''
359
376
  tbody += "</td>"
360
377
  else
361
378
  tbody += "<td #{right_style}><div e2-button-set=\"action.meta.menus.item_menu\" index=\"#{i}\"></div></td>"
362
379
  tbody += "</tr>"
363
380
 
381
+ tbody_attrs = if draggable then 'dnd-list=\"action.entries\" dnd-drop=\"action.entry_dropped(index, external, type)\"' else ''
364
382
  elem.empty()
365
- elem.append($compile(table_tmpl thead: thead, tbody: tbody)(scope))
383
+ elem.append($compile(table_tmpl thead: thead, tbody: tbody, tbody_attrs: tbody_attrs)(scope))
366
384
 
367
385
  .directive 'e2Dropdown', ($parse, $dropdown, $timeout, E2Snippets) ->
368
386
  event_num = 0
369
387
  dropdown_sub_tmpl = _.template("<li class='dropdown-submenu' {{show}} {{hide}}><a href=''> {{icon}} {{loc}}</a>{{sub}}</li>")
370
- dropdown_tmpl = _.template("<li {{clazz}} {{show}} {{hide}}> <a {{href}} {{click}}> {{icon}} {{loc}}</a></li>")
388
+ dropdown_tmpl = _.template("<li {{clazz}} {{show}} {{hide}} {{active}}> <a {{href}} {{click}}> {{icon}} {{loc}}</a></li>")
371
389
 
372
- render = (menu, href) ->
390
+ render = (menu, nav) ->
373
391
  out = menu.map (m) ->
374
392
  switch
375
393
  when m.divider
@@ -380,13 +398,14 @@ angular.module('Engine2', ['ngSanitize', 'ngAnimate', 'ngCookies', 'mgcrea.ngStr
380
398
  loc: m.menu.loc
381
399
  show: m.menu.show && "ng-show=\"#{m.menu.show}\"" || ''
382
400
  hide: m.menu.hide && "ng-hide=\"#{m.menu.hide}\"" || ''
383
- sub: render(m.menu.entries)
401
+ sub: render(m.menu.entries, nav)
384
402
  else
385
403
  dropdown_tmpl
386
404
  clazz: E2Snippets.make_ng_class(m)
387
405
  show: m.show && "ng-show=\"#{m.show}\"" || ''
388
406
  hide: m.hide && "ng-hide=\"#{m.hide}\"" || ''
389
- href: m.href && "#{href}=\"#{m.href}\"" || ''
407
+ href: m.href && "#{if nav then 'ui-sref' else 'href'}=\"#{m.href}\"" || ''
408
+ active: nav && "ui-sref-active='active'" || ''
390
409
  click: m.click && "ng-click=\"#{m.click}\"" || ''
391
410
  icon: m.icon && E2Snippets.icon(m.icon) || ''
392
411
  loc: m.loc
@@ -402,8 +421,7 @@ angular.module('Engine2', ['ngSanitize', 'ngAnimate', 'ngCookies', 'mgcrea.ngStr
402
421
  # event.preventDefault()
403
422
  # event.stopPropagation()
404
423
  menu = $parse(attrs.e2Dropdown)(scope)
405
- href = attrs.hrefAttr ? 'href'
406
- dropdown = $dropdown(elem, (scope: scope, template: render(menu, href), animation: attrs.animation || 'am-flip-x', prefixEvent: "#{event_num}.tooltip")) # , delay: 1
424
+ dropdown = $dropdown(elem, (scope: scope, template: render(menu, attrs.nav?), animation: attrs.animation || 'am-flip-x', prefixEvent: "#{event_num}.tooltip")) # , delay: 1
407
425
  dropdown.$promise.then ->
408
426
  event_hide = scope.$on "#{event_num}.tooltip.hide", (e) ->
409
427
  e.stopPropagation()
@@ -504,7 +522,7 @@ angular.module('Engine2', ['ngSanitize', 'ngAnimate', 'ngCookies', 'mgcrea.ngStr
504
522
  action = scope.action
505
523
  mode = attr.e2Datepicker
506
524
  field = scope.other_date ? scope.other_time ? scope.f
507
- info = action.meta.info[field]
525
+ info = action.meta.fields[field]
508
526
 
509
527
  if action.query
510
528
  scope.value[mode] = parse(action.query.search[scope.f][mode], info)