engine2 1.0.5 → 1.0.6

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.
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)