engine2 1.0.4 → 1.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (131) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +0 -0
  3. data/Rakefile +4 -4
  4. data/app/{engine2actions.coffee → actions.coffee} +341 -215
  5. data/app/app.coffee +0 -0
  6. data/app/app.css +17 -0
  7. data/app/engine2.coffee +158 -208
  8. data/app/modal.coffee +138 -0
  9. data/bower.json +4 -2
  10. data/conf/message.yaml +5 -0
  11. data/conf/message_pl.yaml +7 -2
  12. data/config.coffee +24 -12
  13. data/engine2.gemspec +8 -8
  14. data/lib/engine2.rb +12 -10
  15. data/lib/engine2/action.rb +1338 -133
  16. data/lib/engine2/action/array.rb +189 -0
  17. data/lib/engine2/{meta/decode_meta.rb → action/decode.rb} +52 -21
  18. data/lib/engine2/action/delete.rb +64 -0
  19. data/lib/engine2/action/form.rb +16 -0
  20. data/lib/engine2/{meta/infra_meta.rb → action/infra.rb} +123 -89
  21. data/lib/engine2/action/link.rb +117 -0
  22. data/lib/engine2/action/list.rb +333 -0
  23. data/lib/engine2/action/save.rb +28 -0
  24. data/lib/engine2/action/view.rb +8 -0
  25. data/lib/engine2/action_node.rb +221 -0
  26. data/lib/engine2/core.rb +175 -87
  27. data/lib/engine2/handler.rb +14 -13
  28. data/lib/engine2/model.rb +85 -43
  29. data/lib/engine2/models/Files.rb +4 -1
  30. data/lib/engine2/models/UserInfo.rb +6 -3
  31. data/lib/engine2/post_bootstrap.rb +4 -3
  32. data/lib/engine2/pre_bootstrap.rb +10 -6
  33. data/lib/engine2/scheme.rb +107 -65
  34. data/lib/engine2/templates.rb +41 -6
  35. data/lib/engine2/type_info.rb +51 -23
  36. data/lib/engine2/version.rb +2 -1
  37. data/package.json +22 -16
  38. data/public/favicon.ico +0 -0
  39. data/public/img/ajax-loader-dark.gif +0 -0
  40. data/public/img/ajax-loader.gif +0 -0
  41. data/views/fields/blob.slim +1 -1
  42. data/views/fields/bs_select.slim +2 -2
  43. data/views/fields/bsselect_picker.slim +4 -4
  44. data/views/fields/bsselect_picker_opt.slim +5 -5
  45. data/views/fields/checkbox.slim +4 -4
  46. data/views/fields/checkbox_button.slim +6 -0
  47. data/views/fields/checkbox_buttons.slim +3 -3
  48. data/views/fields/checkbox_buttons_opt.slim +3 -3
  49. data/views/fields/currency.slim +2 -2
  50. data/views/fields/date.slim +4 -4
  51. data/views/fields/date_range.slim +9 -9
  52. data/views/fields/date_time.slim +9 -9
  53. data/views/fields/datetime.slim +8 -8
  54. data/views/fields/decimal.slim +1 -1
  55. data/views/fields/decimal_date.slim +3 -3
  56. data/views/fields/decimal_time.slim +3 -3
  57. data/views/fields/email.slim +3 -3
  58. data/views/fields/file_store.slim +11 -11
  59. data/views/fields/input_text.slim +4 -4
  60. data/views/fields/integer.slim +1 -1
  61. data/views/fields/list_bsmselect.slim +20 -0
  62. data/views/fields/list_bsselect.slim +5 -5
  63. data/views/fields/list_bsselect_opt.slim +6 -6
  64. data/views/fields/list_buttons.slim +1 -1
  65. data/views/fields/list_buttons_opt.slim +2 -2
  66. data/views/fields/list_mbuttons.slim +9 -0
  67. data/views/fields/list_mbuttons_opt.slim +11 -0
  68. data/views/fields/list_mselect.slim +12 -0
  69. data/views/fields/list_select.slim +4 -4
  70. data/views/fields/list_select_opt.slim +5 -5
  71. data/views/fields/password.slim +4 -4
  72. data/views/fields/radio_checkbox.slim +3 -3
  73. data/views/fields/scaffold.slim +2 -2
  74. data/views/fields/scaffold_picker.slim +5 -5
  75. data/views/fields/select_picker.slim +3 -3
  76. data/views/fields/select_picker_opt.slim +4 -4
  77. data/views/fields/text_area.slim +4 -3
  78. data/views/fields/time.slim +5 -4
  79. data/views/fields/typeahead_picker.slim +12 -9
  80. data/views/index.slim +3 -3
  81. data/views/infra/index.slim +0 -0
  82. data/views/infra/inspect.slim +41 -10
  83. data/views/modals/close_m.slim +0 -0
  84. data/views/modals/confirm_m.slim +0 -0
  85. data/views/modals/empty_m.slim +0 -0
  86. data/views/modals/menu_m.slim +1 -1
  87. data/views/modals/yes_no_m.slim +0 -0
  88. data/views/panels/menu_m.slim +1 -1
  89. data/views/scaffold/confirm.slim +0 -0
  90. data/views/scaffold/fields.slim +6 -4
  91. data/views/scaffold/form.slim +1 -1
  92. data/views/scaffold/form_collapse.slim +4 -3
  93. data/views/scaffold/form_tabs.slim +3 -2
  94. data/views/scaffold/list.slim +0 -0
  95. data/views/scaffold/message.slim +0 -0
  96. data/views/scaffold/search.slim +4 -4
  97. data/views/scaffold/search_collapse.slim +8 -7
  98. data/views/scaffold/search_tabs.slim +6 -5
  99. data/views/scaffold/view.slim +2 -2
  100. data/views/scaffold/view_collapse.slim +5 -4
  101. data/views/scaffold/view_tabs.slim +4 -3
  102. data/views/search_fields/bsmselect_picker.slim +4 -4
  103. data/views/search_fields/bsselect_picker.slim +4 -4
  104. data/views/search_fields/checkbox.slim +3 -3
  105. data/views/search_fields/checkbox2.slim +5 -5
  106. data/views/search_fields/checkbox_buttons.slim +3 -3
  107. data/views/search_fields/date.slim +20 -0
  108. data/views/search_fields/date_range.slim +8 -8
  109. data/views/search_fields/decimal_date_range.slim +5 -5
  110. data/views/search_fields/input_text.slim +2 -2
  111. data/views/search_fields/integer.slim +1 -1
  112. data/views/search_fields/integer_range.slim +2 -2
  113. data/views/search_fields/list_bsmselect.slim +4 -4
  114. data/views/search_fields/list_bsselect.slim +4 -4
  115. data/views/search_fields/list_buttons.slim +2 -2
  116. data/views/search_fields/list_mbuttons.slim +12 -0
  117. data/views/search_fields/list_select.slim +3 -3
  118. data/views/search_fields/scaffold_picker.slim +2 -2
  119. data/views/search_fields/select_picker.slim +3 -3
  120. data/views/search_fields/typeahead_picker.slim +8 -7
  121. metadata +53 -48
  122. data/lib/engine2/meta.rb +0 -1216
  123. data/lib/engine2/meta/array_meta.rb +0 -82
  124. data/lib/engine2/meta/delete_meta.rb +0 -60
  125. data/lib/engine2/meta/form_meta.rb +0 -15
  126. data/lib/engine2/meta/link_meta.rb +0 -134
  127. data/lib/engine2/meta/list_meta.rb +0 -281
  128. data/lib/engine2/meta/save_meta.rb +0 -50
  129. data/lib/engine2/meta/view_meta.rb +0 -7
  130. data/public/__sinatra__/404.png +0 -0
  131. data/public/__sinatra__/500.png +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 6174073612b4abd1d1a7c2fefbbf071874c2c566
4
- data.tar.gz: 081dee914669d77bb0a9e32d13c219612aa79d75
2
+ SHA256:
3
+ metadata.gz: cc5654aaf1573375a69ca96d3d957b07e1970ef777e0deb65c18f28c40666489
4
+ data.tar.gz: aa79d8a00f564a0e23e4520ea1198446a6049c241b5bdb714416aa7ed378de6d
5
5
  SHA512:
6
- metadata.gz: 5631fd58edca95f2c3966ccc240a29b0089c78e7a80b9d3a7e5e239e2a001e0a02f7baa9ed6cdff6a0d98f061022f02d0768282513b457bf6e9fd180c8adf96e
7
- data.tar.gz: bcc3a19a71996a4c2e8f5f2d511354dc1085de8bdd5eacce667cbe1f24a91587dee04892fb22d51276f7a152a4b21c72681e90f379731ad2f1d546f9c5940294
6
+ metadata.gz: 271c8416bd7551d08fa8574319be948aa9b8658fc431e432c65a19deb456deead3fc92e213259e2f9ad0783a77099a8dbce13c460fe45d715abab7bf09efc392
7
+ data.tar.gz: b65bd605e3d622586f433d5900d9da115e880e62a0827913f03c2e18dc9ce744fb6ebaa88f4a31e3582251c8551be9b71a0f03299498d1042082dd3eeb0f3074
data/Gemfile CHANGED
File without changes
data/Rakefile CHANGED
@@ -1,17 +1,17 @@
1
1
  desc "Compile SLIM"
2
2
  task :compile_slim do
3
3
  require 'slim'
4
- view_dirs = ["fields", "scaffold", "search_fields", "modals"]
4
+ view_dirs = ["fields", "scaffold", "search_fields", "modals", "panels"]
5
5
  slims = view_dirs.each.map do |view_dir|
6
6
  Dir["views/#{view_dir}/*.slim"].map do |slim_file|
7
7
  slim = Slim::Template.new(slim_file).render.gsub('"', '\"')
8
8
  tpl_name = slim_file.sub("views/", "").sub(".slim", "")
9
- "$templateCache.put('#{tpl_name}', \"#{slim}\");"
9
+ "c.put('#{tpl_name}', \"#{slim}\");"
10
10
  end
11
11
  end
12
12
 
13
- open("app/engine2templates.js", "wb") << <<-EOF
14
- angular.module('Engine2').run(['$templateCache', function($templateCache) {
13
+ open("app/templates.js", "wb") << <<-EOF
14
+ angular.module('Engine2').run(['$templateCache', function(c) {
15
15
  #{slims.join("\n")}
16
16
  }]);
17
17
  EOF
@@ -8,7 +8,7 @@ angular.module('Engine2')
8
8
  throw "Invalid action path: '#{action_attr}'" unless action_names
9
9
  action_names = action_names.split('/') if _.isString(action_names)
10
10
  create = (action) ->
11
- action.create_action_path(action_names, $scope, $element).then (act) -> act.invoke() if $attrs.invoke
11
+ action.create_action_path(action_names, $scope, $element).then (act) -> act.invoke($parse($attrs.invoke)($scope)) if $attrs.invoke?
12
12
 
13
13
  sc = $scope
14
14
  if sc.action
@@ -22,13 +22,13 @@ angular.module('Engine2')
22
22
  $http.get("api/meta").then (mresponse) -> $scope.$broadcast "bootstrap_action",
23
23
  $scope.action = new E2Actions.root(mresponse.data, $scope, null, $element, action_resource: 'api')
24
24
 
25
- .factory 'E2Actions', (E2, $http, $timeout, $e2Modal, $injector, $compile, $templateCache, $q, localStorageService, $route, $window, $rootScope, $location) ->
25
+ .factory 'E2Actions', (E2, $http, $timeout, $injector, $compile, $templateCache, $q, localStorageService, $rootScope, $location, angularLoad, $websocket, MetaCache, $stateRegistry, $urlRouter) ->
26
26
  globals = E2.globals
27
27
  action: class Action
28
28
  constructor: (response, scope, parent, element, action_info) ->
29
29
  @find_action_info = (name, raise = true) ->
30
30
  act = response.actions[name]
31
- throw "Undefined action '#{name}' for action #{@action_info().name} (under #{@parent()?.action_info().action_resource})" if raise && !act
31
+ throw "Undefined action '#{name}' for action #{@parent()?.action_info().action_resource}/#{@action_info().name}" if raise && !act
32
32
  act
33
33
 
34
34
  _.each response.actions, (act, nm) -> act.name = nm
@@ -51,11 +51,19 @@ angular.module('Engine2')
51
51
  @meta.panel.modal_action = false
52
52
  @meta.panel.footer = true unless @meta.panel.footer == false
53
53
 
54
+
55
+ if scope && @meta.invokable != false
56
+ scope.$on action_info.action_resource, (e, args) => @invoke(args)
57
+
58
+ @websocket_connect() if @meta.websocket
54
59
  @initialize()
55
60
 
61
+ broadcast: (sub_action, args) ->
62
+ @scope().$broadcast(@action_info().action_resource + '/' + sub_action, args)
63
+
56
64
  initialize: ->
57
65
  @process_static_meta()
58
- console.log "CREATE #{@action_info().action_resource}"
66
+ console.info "CREATE #{@action_info().action_resource}"
59
67
 
60
68
  process_static_meta: ->
61
69
  if @meta.menus
@@ -67,45 +75,28 @@ angular.module('Engine2')
67
75
  if action_info.access
68
76
  $rootScope.$broadcast "relogin", element?, create
69
77
  else
70
- $e2Modal.error("#{err.status}: #{err.data.message}", err.data.cause || err.data.message)
71
- $q.reject(err)
72
-
73
- perform_invoke: (params) ->
74
- info = @action_info()
75
- get_invoke = if @meta.invokable == false then $q.when(data: (response: {})) else
76
- (params ?= {}).initial = true if @meta.panel && !@action_invoked && info.method == 'get'
77
- $http[info.method](info.action_resource, if info.method == 'post' then params else (params: params))
78
+ tle = "#{err.status}: #{err.data.message}"
79
+ msg = err.data.cause || err.data.message
80
+ if msg.length > 500
81
+ @globals().modal().error(tle, msg)
82
+ else
83
+ @globals().toastr().error(tle, msg, extendedTimeOut: 5000, closeButton: true)
78
84
 
79
- globals.action_pending = if @meta.panel then @ else @parent()
85
+ $q.reject(err)
80
86
 
81
- get_invoke.then (response) =>
82
- if exec = response.data.$execute
83
- @scope().$eval(exec)
84
- delete response.data.$execute
87
+ save_state: () ->
88
+ _.each @meta.state, (s) => localStorageService.set("#{@globals().application}/#{@action_info().action_resource}/#{s}", @[s])
89
+ load_state: () ->
90
+ _.each @meta.state, (s) => _.merge(@[s], localStorageService.get("#{@globals().application}/#{@action_info().action_resource}/#{s}"))
85
91
 
86
- E2.merge(@, response.data)
87
- @process_meta()
88
- (if @meta.reload_routes then $route.load_routes() else $q.when({})).then =>
89
- @arguments = _.keys(response.data)
90
- unless @meta.panel # persistent action
91
- prnt = @parent()
92
- throw "Attempted parent merge for root action: #{info.name}" unless prnt
93
- E2.merge(prnt, response.data)
94
-
95
- globals.action_pending = false
96
- if @meta.panel && !@action_invoked
97
- @action_invoked = true
98
- @panel_render()
99
- ,
100
- (err) =>
101
- globals.action_pending = false
102
- @handle_error(err, info, @element())
92
+ destroy: (e) ->
93
+ console.log "DESTROY #{@action_info().action_resource}"
103
94
 
104
95
  create_action: (name, sc, el) ->
105
96
  info = @find_action_info(name)
106
97
  info.action_resource = "#{@action_info().action_resource}/#{info.name}"
107
98
  get_meta = if !info.terminal || info.meta
108
- $http.get("#{info.action_resource}/meta", cache: true).then (response) =>
99
+ $http.get("#{info.action_resource}/meta", cache: MetaCache).then (response) =>
109
100
  if info.recheck_access
110
101
  $http.get("#{info.action_resource}/meta", params: (access: true, parent_id: @current_id())).then (aresponse) ->
111
102
  response.data.actions[k].access = v for k, v of aresponse.data
@@ -113,7 +104,7 @@ angular.module('Engine2')
113
104
  else response # $q.when ^
114
105
  else $q.when(data: (meta: {}, actions: []))
115
106
  E2A = $injector.get("E2Actions")
116
- get_meta.then (mresponse) => new (E2A[info.meta_type] ? E2A.default_action)(mresponse.data, sc, @, el, info)
107
+ get_meta.then (mresponse) => new (E2A[info.action_type] ? E2A.default_action)(mresponse.data, sc, @, el, info)
117
108
  ,
118
109
  (err) => @handle_error(err, info, el)
119
110
 
@@ -122,34 +113,61 @@ angular.module('Engine2')
122
113
 
123
114
  create_action_path: (action_names, sc, elem) ->
124
115
  last_name = action_names.pop()
125
- _.reduce(action_names, ((pr, nm) -> pr.then (act) -> act.create_action(nm)), $q.when(@)).then (act) ->
116
+ _.reduce(action_names, ((pr, nm) -> pr.then (act) -> act.create_action(nm)), $q.when(@)).then (act) -> # self = @
126
117
  act.create_action(last_name, sc, elem).then (act) -> sc.action = act
127
118
 
128
119
  globals: -> globals
120
+ _ : -> _
121
+ current: -> @globals().current_action
129
122
  action_pending: -> globals.action_pending == @
130
123
  pre_invoke: ->
131
124
  post_invoke: ->
132
125
 
133
- invoke: (args) ->
134
- @pre_invoke(args)
135
- _.merge(args ?= {}, @meta.arguments) if @meta.arguments
136
- @perform_invoke(args).then =>
137
- @post_invoke(args)
138
- @scope().$eval(@meta.execute) if @meta.execute
139
- if @meta.repeat
140
- unless @meta.destroy_repeat
141
- @scope().$on("$destroy", => delete @meta.repeat)
142
- @meta.destroy_repeat = true
143
- $timeout (=> @invoke(args)), @meta.repeat
144
- @
126
+ invoke: (params) ->
127
+ @globals().current_action = @
128
+ params ?= {}
129
+ @globals().action_pending = if @meta.panel then @ else @parent()
130
+ @pre_invoke(params)
131
+ if @meta.arguments # _.merge(params, @meta.arguments)
132
+ _.each @meta.arguments, (v, k) =>
133
+ if _.endsWith(k, '!') then params[k.slice(0, -1)] = @scope().$eval(v) else params[k] = v
145
134
 
146
- save_state: () ->
147
- _.each @meta.state, (s) => localStorageService.set("#{globals.application}/#{@action_info().action_resource}/#{s}", @[s])
148
- load_state: () ->
149
- _.each @meta.state, (s) => _.merge(@[s], localStorageService.get("#{globals.application}/#{@action_info().action_resource}/#{s}"))
150
135
 
151
- destroy: (e) ->
152
- console.log "DESTROY #{@action_info().action_resource}"
136
+ info = @action_info()
137
+ get_invoke = if @meta.invokable == false then $q.when(data: (response: {})) else
138
+ params.initial = true if @meta.panel && !@action_invoked && info.method == 'get'
139
+ $http[info.method](info.action_resource, if info.method == 'post' then params else (params: params))
140
+
141
+ @execute_commands('pre_execute')
142
+ get_invoke.then (response) =>
143
+ @arguments = _.keys(response.data)
144
+ E2.merge_meta(@, response.data)
145
+ @process_meta()
146
+
147
+ promise = if @meta.panel # persistent action
148
+ if !@action_invoked
149
+ @action_invoked = true
150
+ @panel_render()
151
+ else
152
+ prnt = @parent()
153
+ throw "Attempted parent merge for root action: #{info.name}" unless prnt
154
+ E2.merge_meta(prnt, response.data)
155
+
156
+ @post_invoke(params)
157
+ @execute_commands('execute')
158
+ if @meta.repeat
159
+ @scope().$on "$destroy", => @destroyed = true
160
+ $timeout (=> @invoke(params)), @meta.repeat unless @destroyed
161
+ delete @meta.repeat
162
+
163
+ @globals().action_pending = false
164
+ @globals().current_action = null
165
+ promise
166
+ ,
167
+ (err) =>
168
+ @globals().action_pending = false
169
+ @globals().current_action = null
170
+ @handle_error(err, info, @element())
153
171
 
154
172
  panel_render: ->
155
173
  if @meta.panel.modal_action
@@ -161,13 +179,13 @@ angular.module('Engine2')
161
179
  @panel_shown?()
162
180
 
163
181
  else
164
- $e2Modal.show(@)
182
+ @globals().modal().show(@).then => @
165
183
  else
166
184
  @panel_scope?().$destroy()
167
185
  act = @
168
186
  act = act.parent() until act.element()
169
187
  element = act.element() # @element()
170
- is_modal = $e2Modal.is_modal() && !@element()
188
+ is_modal = @globals().modal().is_modal() && !@element()
171
189
  E2.fetch_panel(@meta.panel, is_modal).then (template) =>
172
190
  @panel_show?()
173
191
  # @panel_scope().$destroy()
@@ -190,7 +208,7 @@ angular.module('Engine2')
190
208
  panel_close: ->
191
209
  if @meta.panel.modal_action
192
210
  @modal_hide()
193
- else
211
+ else if @parent().parent()
194
212
  # @parent().panel_refresh()
195
213
  @panel_hide?()
196
214
  @panel_hidden()
@@ -201,12 +219,47 @@ angular.module('Engine2')
201
219
  panel_menu_cancel: ->
202
220
  @panel_close()
203
221
 
222
+ panel_menu_close: ->
223
+ @panel_close()
224
+
225
+ websocket_connect: ->
226
+ l = $location
227
+ ws_meta = @meta.websocket
228
+ ws = $websocket "ws#{l.protocol().slice(4, 5)}://#{l.host()}:#{l.port()}#{'/'}#{@action_info().action_resource}", undefined, ws_meta.options
229
+ _.each @globals().ws_methods, (method) =>
230
+ ws_method_impl = @["ws_#{method}"]
231
+ ws["on#{_.capitalize(method)}"] (evt) =>
232
+ if method == 'message'
233
+ msg = JSON.parse(evt.data)
234
+ if msg.error then @globals().modal().error("WebSocket [#{evt.origin}] - #{msg.error.method}", msg.error.exception) else
235
+ E2.merge_meta(@, msg)
236
+ @process_meta()
237
+ else msg = evt
238
+ ws_method_impl.bind(@)(msg, ws, evt) if ws_method_impl
239
+ @execute_commands('execute')
240
+
241
+ @web_socket = -> ws
242
+ @scope().$on "$destroy", -> ws.close()
243
+
244
+ execute_commands: (execute) ->
245
+ if @meta[execute]
246
+ scope = @scope()
247
+ _.reduce(@meta[execute], ((pr, cmd) -> pr.then -> scope.$eval(cmd)), $q.when())
248
+ @meta[execute].splice(0, @meta[execute].length)
249
+ delete @meta[execute]
250
+
251
+ console_log: (o) ->
252
+ console.log o
253
+
204
254
  root: class RootAction extends Action
205
255
  initialize: ->
206
256
  super()
207
257
  _.merge(globals, @meta)
208
258
  @meta = {}
209
259
 
260
+ invoke: (args) ->
261
+ console.warn "Root action invoked"
262
+
210
263
  default_action: class DefaultAction extends Action
211
264
  initialize: ->
212
265
  super()
@@ -217,6 +270,8 @@ angular.module('Engine2')
217
270
  super()
218
271
  @tree = actions: [name: 'api', number: 0, access: true]
219
272
  @invoke_action('models')
273
+ @invoke_action('environment')
274
+ @local_storage = localStorageService
220
275
 
221
276
  open: (stack, node, collapsed, expand) ->
222
277
  tree = @tree
@@ -244,7 +299,10 @@ angular.module('Engine2')
244
299
  _.each response.data.actions, (act, nm) -> act.name = nm
245
300
  tree.actions ?= _.toArray(response.data.actions)
246
301
  @meta_json = response.data.meta
247
- @action_state = if @meta_json.state then _.zipObject(@meta_json.state.map (k) -> [k, localStorageService.get("#{path}/#{k}")]) else {}
302
+ if @meta_json.state
303
+ @action_state = {}
304
+ _.each @meta_json.state, (s) => @action_state[s] = localStorageService.get("#{@globals().application}/#{path.join('/')}/#{s}")
305
+ @action_state
248
306
  ,
249
307
  (err) =>
250
308
  delete @meta_json
@@ -253,65 +311,69 @@ angular.module('Engine2')
253
311
  has_assoc: (model) ->
254
312
  _.size(model.assoc) > 0
255
313
 
314
+ ws_message: (msg, ws, evt) ->
315
+ @event = evt
316
+
256
317
  menu: class MenuAction extends Action
257
318
  process_static_meta: ->
258
319
 
259
320
  initialize: ->
260
321
  super()
261
- $route.load_routes = =>
322
+ $stateRegistry.load_routes = (init) =>
262
323
  @invoke().then =>
263
- _.each _.keys($route.routes), (k) -> delete $route.routes[k]
264
324
  menu = @meta.menus.menu
265
- $route.routes[null] = reloadOnSearch: true, redirectTo: '/' + (menu.properties.default ? menu.entries[0].name)
325
+ _.each $stateRegistry.get(), (s) -> $stateRegistry.deregister(s.name) unless _.isEmpty(s.name)
326
+ otherwise = menu.properties.default ? menu.entries[0].name
327
+ $urlRouter.otherwise(otherwise)
266
328
  @register(menu.entries)
267
- $route.reload() # $location.path('')
268
329
  @scope().routes = menu.entries
269
- out = if _.size(menu.entries) == 0 then angular.element("<div></div>") else $compile(@traverse(menu.entries))(@scope())
330
+ out = $compile(@traverse(menu.entries))(@scope())
270
331
  @element().replaceWith(out)
271
332
  @element = -> out
272
- $route.load_routes()
333
+ loc = $location.path().slice(1)
334
+ @globals().state().go(if init && !_.isEmpty($location.path()) && $stateRegistry.get(loc)? then loc else otherwise)
335
+
336
+ $stateRegistry.load_routes(true)
273
337
 
274
338
  register: (routes) ->
275
339
  _.each routes, (route) =>
276
- if route.menu then @register(route.menu.entries) else
277
- name = '/' + route.name
278
- route.href = '#' + route.name
279
- if route.bootstrap?
280
- action = if route.bootstrap == true then '' else route.bootstrap + '/'
281
- $templateCache.put(route.name + '_route_template!', "<div e2-action='' action=\"'#{action}#{route.name}'\" invoke='true'></div>")
282
-
283
- $route.routes[name] =
284
- reloadOnSearch: true
285
- templateUrl: if route.bootstrap? then route.name + '_route_template!' else route.name
286
- originalPath: name
287
- regexp: new RegExp("^#{name}$")
288
- keys: []
289
-
290
- $route.routes[name + '/'] =
291
- redirectTo: name
292
- originalPath: name + '/'
293
- regexp: new RegExp("^#{name}/$")
294
- keys: []
340
+ unless route.divider
341
+ if route.menu then @register(route.menu.entries) else
342
+ route.href = route.name
343
+ if route.bootstrap?
344
+ action = if route.bootstrap == true then '' else route.bootstrap + '/'
345
+ $templateCache.put(route.name + '_route_template!', "<div e2-action='' action=\"'#{action}#{route.name}'\" invoke='true'></div>")
346
+
347
+ $stateRegistry.register
348
+ name: route.name
349
+ templateUrl: if route.bootstrap? then route.name + '_route_template!' else route.name
350
+ url: '/' + route.name
351
+ # reloadOnSearch: true
295
352
 
296
353
  traverse: (routes) ->
297
- menu_tmpl = _.template("<li><a href='{{href}}'>{{icon}}{{aicon}} {{loc}}</a></li>")
298
- menu_sub_tmpl = _.template("<li e2-dropdown='{{dropdown}}' data-animation='{{animation}}'><a href='javascript://'>{{icon}}{{aicon}} {{loc}}<span class='caret'></span></a></li>")
354
+ menu_tmpl = _.template("<li {{show}} {{hide}} ui-sref-active='active'><a {{href}}>{{icon}} {{loc}}</a></li>")
355
+ 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>")
299
356
  animation = @meta.menus.menu.properties.animation
300
357
  out = routes.map (route, i) ->
301
- if route.menu
358
+ if route.render == false
359
+ ''
360
+ else if route.menu
302
361
  menu_sub_tmpl
303
362
  dropdown: "routes[#{i}].menu.entries"
304
363
  animation: animation
305
- icon: route.menu.icon && E2.icon(route.menu.icon) || ""
306
- aicon: route.menu.aicon && E2.aicon(route.menu.aicon) || ""
307
364
  loc: route.menu.loc
365
+ show: route.show && "ng-show=\"#{route.show}\"" || ''
366
+ hide: route.hide && "ng-hide=\"#{route.hide}\"" || ''
367
+ icon: route.menu.icon && E2.icon(route.menu.icon) || ""
308
368
  else
309
369
  menu_tmpl
310
- href: route.href
370
+ href: "ui-sref='#{route.name}'"
311
371
  loc: route.loc
372
+ show: route.show && "ng-show=\"#{route.show}\"" || ''
373
+ hide: route.hide && "ng-hide=\"#{route.hide}\"" || ''
312
374
  icon: route.icon && E2.icon(route.icon) || ''
313
- aicon: route.aicon && E2.aicon(route.aicon) || ''
314
- out.join('')
375
+ out = out.join('')
376
+ if _.size(out) == 0 then "<div></div>" else out
315
377
 
316
378
  list: class ListAction extends Action
317
379
  initialize: ->
@@ -320,9 +382,8 @@ angular.module('Engine2')
320
382
  @ui_state = {}
321
383
  @load_state()
322
384
 
323
- delete @query.order unless @meta.info[@query.order]?.sort # _.includes(@meta.fields, @query.order)
324
- _.each @query.search, ((sv, sn) => delete @query.search[sn] unless _.includes(@meta.search_fields, sn))
325
- # $window.addEventListener 'beforeunload', (e, v) => @save_state()
385
+ delete @query.order unless @meta.fields[@query.order]?.sort # _.includes(@meta.field_list, @query.order)
386
+ _.each @query.search, ((sv, sn) => delete @query.search[sn] unless _.includes(@meta.search_field_list, sn))
326
387
 
327
388
  destroy: ->
328
389
  @save_state()
@@ -331,10 +392,13 @@ angular.module('Engine2')
331
392
  process_meta: ->
332
393
  super()
333
394
  meta = @meta
334
- meta.fields = meta.fields.filter((f) => !meta.info[f].hidden) if meta.fields
395
+ meta.field_list = meta.field_list.filter((f) => !meta.fields[f].hidden) if meta.field_list
335
396
 
336
397
  # confirm_create, view, confirm_modify, confirm_delete, assocs - implicit
337
398
 
399
+ render_table: ->
400
+ @scope().$broadcast 'render_table'
401
+
338
402
  menu_search_toggle: ->
339
403
  @ui_state.search_active = !@ui_state.search_active
340
404
  @save_state() unless @ui_state.search_active
@@ -348,12 +412,13 @@ angular.module('Engine2')
348
412
 
349
413
  menu_select_toggle: ->
350
414
  if @selection then delete @selection else @selection = {}
351
- @scope().$broadcast 'render_table'
415
+ @render_table()
416
+
352
417
 
353
418
  menu_show_meta: ->
354
- $e2Modal.show
419
+ @globals().modal().show
355
420
  the_meta: @meta
356
- meta: panel: (panel_template: "close_m", template_string: "<pre>{{action.the_meta | json}}</pre>", title: "Meta", class: "modal-huge", backdrop: true, footer: true)
421
+ meta: panel: (panel_template: "close_m", template_string: "<pre>{{action.the_meta | yaml}}</pre>", title: "Meta", class: "modal-huge", backdrop: true, footer: true)
357
422
 
358
423
  # show_assoc: (index, assoc) ->
359
424
  # # parent_id = E2.id_for(@entries[index], @meta)
@@ -370,7 +435,7 @@ angular.module('Engine2')
370
435
  E2.id_for(@current_entry(), @meta)
371
436
 
372
437
  list_cell: (e, f) ->
373
- E2.render_field(e, f, @meta)
438
+ E2.render_field(e, f, @meta, "<br>")
374
439
 
375
440
  invoke: (args = {}) ->
376
441
  @save_state()
@@ -380,7 +445,7 @@ angular.module('Engine2')
380
445
  super(query).then =>
381
446
  @ui = _.pick @query, ['order', 'asc', 'page']
382
447
  @ui.pagination_active = @ui.page != 0 || @entries.length >= @meta.config.per_page
383
- @scope().$broadcast 'render_table'
448
+ @render_table()
384
449
 
385
450
  load_new: ->
386
451
  @query.page = 0
@@ -413,14 +478,14 @@ angular.module('Engine2')
413
478
  @load_new()
414
479
 
415
480
  search_field_change: (f) ->
416
- info = @meta.info[f]
481
+ info = @meta.fields[f]
417
482
 
418
- @scope().$eval(info.onchange) if info.onchange
483
+ @scope().$eval(info.onchange.action) if info.onchange
419
484
 
420
485
  if remote_onchange = info.remote_onchange
421
486
  params = value: @query.search[f]
422
- params.record = @query.search if info.remote_onchange_record
423
- @invoke_action(remote_onchange, params).then =>
487
+ params.record = @query.search if remote_onchange.record
488
+ @invoke_action(remote_onchange.action, params).then =>
424
489
  @load_new() if info.search_live
425
490
  else
426
491
  @load_new() if info.search_live
@@ -441,6 +506,22 @@ angular.module('Engine2')
441
506
  selected_info: ->
442
507
  @meta.loc.selected + ": " + @selected_size()
443
508
 
509
+ entry_dropped: (moved_to, render = true) ->
510
+ from = @entries[@moved_from]
511
+ @entries.splice(@moved_from, 1)
512
+ @entries.splice((if moved_to > @moved_from then moved_to - 1 else moved_to), 0, from)
513
+ delete @moved_from
514
+ @render_table() if render
515
+ true
516
+
517
+ entry_moved: (index) ->
518
+ @moved_from = index
519
+
520
+ list_parent_action: ->
521
+ parent = @parent()
522
+ parent = parent.parent() until parent instanceof ListAction
523
+ parent
524
+
444
525
  bulk_delete: class BulkDeleteAction extends Action
445
526
  invoke: ->
446
527
  super(ids: [_.keys(@parent().parent().selection)]).then =>
@@ -448,79 +529,81 @@ angular.module('Engine2')
448
529
 
449
530
  view: class ViewAction extends Action
450
531
  view_cell: (e, f) ->
451
- E2.render_field(e, f, @meta)
532
+ E2.render_field(e, f, @meta, "<br>")
452
533
 
453
534
  form_base_action: class FormBaseAction extends Action
454
535
  initialize: ->
455
536
  super()
456
- _.each @meta.info, (info, name) =>
457
- if info.remote_onchange
458
- @scope().$watch (=> @record?[name]), (n) => if n? #if typeof(n) != "undefined"
459
- params = value: @record[name]
460
- params.record = @record if info.remote_onchange_record
461
- @invoke_action(info.remote_onchange, params)
462
-
463
- if info.onchange
464
- @scope().$watch (=> @record?[name]), (n) => if n?
465
- @scope().$eval(info.onchange)
466
-
467
- if @meta.tabs
537
+ if @meta.tab_list
468
538
  @scope().$watch "action.activeTab", (tab) => if tab? # && tab >= 0
469
539
  @panel_shown()
470
540
 
471
541
  @["panel_menu_#{@default_action_name}"] = -> @panel_menu_default_action()
472
- @scope().$on "return_pressed", (e) => @panel_menu_default_action()
473
542
 
474
543
  post_invoke: (args) ->
475
544
  super()
476
- _.each @meta.info, (info, name) =>
477
- if _.isString(@record[name]) && !info.dont_strip
545
+ _.each @meta.fields, (info, name) =>
546
+ if @record[name] is undefined
547
+ @record[name] = null
548
+ else if _.isString(@record[name]) && !info.dont_strip
478
549
  @record[name] = @record[name].trim()
479
550
 
551
+ if info.onchange || info.remote_onchange
552
+ onchange = => @scope().$eval(info.onchange.action)
553
+ remote_onchange = =>
554
+ params = value: @record[name]
555
+ params.record = @record if info.remote_onchange.record
556
+ @invoke_action(info.remote_onchange.action, params)
557
+
558
+ @scope().$watch (=> @record[name]), (n, o) => if n != o
559
+ onchange() if info.onchange
560
+ remote_onchange() if info.remote_onchange
561
+
562
+ onchange() if info.onchange?.trigger_on_start
563
+ remote_onchange() if info.remote_onchange?.trigger_on_start
564
+
480
565
  panel_menu_default_action: ->
481
- _.each @meta.info, (v, n) =>
482
- @record[n] = null if @record[n] is undefined
483
566
  params = record: @record
484
567
  params.parent_id ?= @parent().query?.parent_id # and StarToManyList ?
485
568
  @invoke_action(@default_action_name, params).then =>
486
569
  dfd = $q.defer()
487
570
  if @errors
488
- if @meta.tabs
571
+ if @meta.tab_list
489
572
  [i, first, curr] = [0, null, false]
490
- for tab in @meta.tabs
491
- if _(tab.fields).find((f) => @errors[f])
573
+ for tab_name in @meta.tab_list
574
+ tab = @meta.tabs[tab_name]
575
+ if _(tab.field_list).find((f) => @errors[f])
492
576
  first = i if not first?
493
577
  act = true if @activeTab == i
494
578
  i++
495
579
  @activeTab = first unless act
496
580
 
497
581
  if @activeTab?
498
- field = _(@meta.tabs[@activeTab].fields).find((f) => @errors[f])
582
+ field = _(@meta.tabs[@meta.tab_list[@activeTab]].field_list).find((f) => @errors[f])
499
583
  # console.log field undefined ?
500
584
  else
501
585
  @activeTab = 0
502
586
  @alert = @errors
503
587
  else
504
- field = _(@meta.fields).find((f) => @errors[f])
505
- @alert = @errors if (!field || !@meta.info[field] || @meta.info[field].hidden) # ?
588
+ field = _(@meta.field_list).find((f) => @errors[f])
589
+ @alert = @errors if (!field || !@meta.fields[field] || @meta.fields[field].hidden) # ?
506
590
  $timeout => @scope().$broadcast("focus_field", field)
507
591
  #e.scope.$eval(meta.execute) if meta.execute # ?
508
- dfd.reject(@errors)
592
+ dfd.resolve()
509
593
  else
510
- @panel_close()
511
594
  dfd.resolve(@record) # $q.when(true) ?
512
595
  dfd.promise
513
596
 
514
597
  panel_shown: ->
515
- field = if @meta.tabs
516
- tab = @meta.tabs[@activeTab]
598
+ field = if @meta.tab_list
599
+ tab = @meta.tabs[@meta.tab_list[@activeTab]]
517
600
  if @errors
518
- _(tab.fields).find((f) => @errors[f]) || _(tab.fields).find((f) => !@meta.info[f].hidden)
601
+ _(tab.field_list).find((f) => @errors[f]) || _(tab.field_list).find((f) => !@meta.fields[f].hidden)
519
602
  else
520
- tab ?= @meta.tabs[0]
521
- _(tab.fields).find((f) => !@meta.info[f].hidden && !@meta.info[f].disabled)
603
+ tab ?= @meta.tabs[@meta.tab_list[0]]
604
+ _(tab.field_list).find((f) => !@meta.fields[f].hidden && !@meta.fields[f].disabled)
522
605
  else
523
- _(@meta.fields).find((f) => !@meta.info[f].hidden && !@meta.info[f].disabled)
606
+ _(@meta.field_list).find((f) => !@meta.fields[f].hidden && !@meta.fields[f].disabled)
524
607
  $timeout (=> @scope().$broadcast("focus_field", field)), 300 # hack, on shown ?
525
608
 
526
609
  infra: class InfraAction extends Action
@@ -530,33 +613,34 @@ angular.module('Engine2')
530
613
  if @user
531
614
  @invoke_action('login_form').then (act) =>
532
615
  act.record = name: @user.name
533
- act.meta.info.name.disabled = true
616
+ act.meta.fields.name.disabled = true
534
617
  act.dont_reload_routes = !reload_routes # true
535
618
  else
536
- @invoke().then => @set_access(true, true)
619
+ @invoke().then => @set_access(true, true, @user)
537
620
 
538
- set_access: (login, load_routes) ->
539
- @find_action_info('logout_form').access = login
540
- @find_action_info('inspect_modal').access = login
541
- @find_action_info('login_form').access = !login
542
- $route.load_routes() if load_routes
621
+ @scope().$on "set_access", (evt, login, load_routes, user) => @set_access(login, load_routes, user)
622
+
623
+ set_access: (login, load_routes, user) ->
624
+ if user || !login
625
+ @user = user
626
+ @find_action_info('logout_form').access = login
627
+ @find_action_info('inspect_modal').access = login
628
+ @find_action_info('login_form').access = !login
629
+ $stateRegistry.load_routes() if load_routes
543
630
 
544
631
  login_form: class LoginFormAction extends FormBaseAction
545
632
  panel_menu_default_action: ->
546
633
  super().then =>
547
- @parent().user = @user
548
- @parent().set_access(true, !@dont_reload_routes)
634
+ $rootScope.$broadcast "set_access", true, !@dont_reload_routes, @user
549
635
 
550
636
  logout_form: class LogoutForm extends Action
551
637
  panel_menu_logout: ->
552
638
  @invoke_action('logout').then =>
553
- @parent().user = null
554
- @parent().set_access(false, true)
639
+ $rootScope.$broadcast "set_access", false, true, null
555
640
  @panel_close()
641
+ MetaCache.removeAll()
556
642
 
557
643
  form: class FormAction extends FormBaseAction
558
- panel_menu_default_action: ->
559
- super().then => @parent().invoke()
560
644
 
561
645
  create: class CreateAction extends FormAction
562
646
  invoke: (args) ->
@@ -568,26 +652,18 @@ angular.module('Engine2')
568
652
  modify: class ModifyAction extends FormAction
569
653
  # invoke: (args) ->
570
654
  # super(args).then =>
571
- # _.each @meta.primary_fields, (f) => @meta.info[f].disabled = true
572
-
573
- on_change: class OnChangeAction extends Action
574
- post_invoke: ->
575
- super()
576
- @parent().scope().$eval(@meta.execute) if @meta.execute
655
+ # _.each @meta.primary_fields, (f) => @meta.fields[f].disabled = true
577
656
 
578
657
  confirm: class ConfirmAction extends Action
579
658
  panel_menu_approve: ->
580
659
  @initial_arguments ?= @arguments
581
- @invoke_action(@default_action_name, _.pick(@, @initial_arguments)).then (act) =>
582
- unless @errors
583
- @parent().invoke()
584
- @panel_close()
660
+ @invoke_action(@default_action_name, _.pick(@, @initial_arguments))
585
661
 
586
662
  decode_action: class DecodeAction extends Action
587
663
  initialize: ->
588
664
  super()
589
665
  @decode_field = @scope().f
590
- @dinfo = @parentp().meta.info[@decode_field]
666
+ @dinfo = @parentp().meta.fields[@decode_field]
591
667
  throw "Primary and foreign key list lengths dont match: [#{@meta.primary_fields}] and [#{@dinfo.fields}]" unless @meta.primary_fields.length == @dinfo.fields.length
592
668
  @scope().$on "search_reset", => @clean()
593
669
 
@@ -606,8 +682,7 @@ angular.module('Engine2')
606
682
  @parentp().search_field_change?(@decode_field)
607
683
 
608
684
  decode_description: (entry) ->
609
- fields = @meta.decode_fields ? @meta.fields
610
- fields.map((f) => E2.render_field(entry, f, @meta)).join(@meta.separator)
685
+ @meta.decode_fields.map((f) => E2.render_field(entry, f, @meta, ', ')).join(@meta.separator)
611
686
 
612
687
  parentp: ->
613
688
  @parent().parent()
@@ -634,11 +709,11 @@ angular.module('Engine2')
634
709
  if @selected.length > 0
635
710
  _.each @dinfo.fields, (fk) -> record[fk] = []
636
711
  _.each @selected, (sel) =>
637
- _(@dinfo.fields).zip(E2.split_keys(sel)).each(([fk, k]) => record[fk].push E2.parse_entry(k, @parentp().meta.info[fk])).value
712
+ _(@dinfo.fields).zip(E2.split_keys(sel)).each(([fk, k]) => record[fk].push E2.parse_entry(k, @parentp().meta.fields[fk])).value
638
713
  else @clear_record()
639
714
  else
640
715
  if @selected
641
- _(@dinfo.fields).zip(E2.split_keys(@selected)).each(([fk, k]) => record[fk] = E2.parse_entry(k, @parentp().meta.info[fk])).value
716
+ _(@dinfo.fields).zip(E2.split_keys(@selected)).each(([fk, k]) => record[fk] = E2.parse_entry(k, @parentp().meta.fields[fk])).value
642
717
  else @clear_record()
643
718
 
644
719
  @parentp().search_field_change?(@decode_field)
@@ -660,12 +735,12 @@ angular.module('Engine2')
660
735
  if @multiple
661
736
  _.each @dinfo.fields, (fk) => record[fk] = []
662
737
  _.each sel, (rec, ids) =>
663
- _(@dinfo.fields).zip(E2.split_keys(ids)).each(([k, v]) => record[k].push E2.parse_entry(v, @parentp().meta.info[k])).value
738
+ _(@dinfo.fields).zip(E2.split_keys(ids)).each(([k, v]) => record[k].push E2.parse_entry(v, @parentp().meta.fields[k])).value
664
739
  @invoke_decode _.values(sel)
665
740
  delete @decode if _.isEmpty(sel)
666
741
  else
667
742
  [ids, rec] = _(sel).toPairs().head()
668
- _(@dinfo.fields).zip(E2.split_keys(ids)).each(([k, v]) => record[k] = E2.parse_entry(v, @parentp().meta.info[k])).value
743
+ _(@dinfo.fields).zip(E2.split_keys(ids)).each(([k, v]) => record[k] = E2.parse_entry(v, @parentp().meta.fields[k])).value
669
744
  @invoke_decode [rec]
670
745
  @parentp().search_field_change?(@decode_field)
671
746
 
@@ -675,7 +750,7 @@ angular.module('Engine2')
675
750
  else
676
751
  decode_descriptions = (recs) => @decode = recs.map((fields) => @decode_description(fields)).join(' | ')
677
752
  recs = recs.map (r) => if _.isArray(r) then E2.from_id(r, @meta) else r
678
- if _(recs).every((r) => _(@meta.fields).every((f) -> r[f]?)) then decode_descriptions(recs) else
753
+ if _(recs).every((r) => _(@meta.field_list).every((f) -> r[f]?)) && !@meta.dynamic_meta then decode_descriptions(recs) else
679
754
  @invoke(ids: [recs.map((r) => @meta.primary_fields.map (k) -> r[k])]).then => decode_descriptions(@entries)
680
755
 
681
756
  open: ->
@@ -694,29 +769,31 @@ angular.module('Engine2')
694
769
  typeahead: class TypeAheadAction extends DecodeAction
695
770
  initialize: ->
696
771
  super()
772
+ @scope().$on "$typeahead.select", (e, v, index) =>
773
+ e.stopPropagation()
774
+ _(@dinfo.fields).zip(E2.split_keys(@values[index].id)).each(([fk, k]) => @record()[fk] = E2.parse_entry(k, @parentp().meta.fields[fk])).value
775
+ @parentp().scope().$digest()
776
+ @parentp().search_field_change?(@decode_field)
777
+
778
+ @scope().$watch "action.decode", (e) => @reset() if e == null
779
+
697
780
  @if_fk_values (fk_values) =>
698
781
  @invoke(id: E2.join_keys(fk_values)).then =>
699
782
  if @entry
700
783
  @decode = id: E2.id_for(@entry, @meta), value: @decode_description(@entry)
701
784
 
702
- @scope().$on "$typeahead.select", (e, v, index) =>
703
- e.stopPropagation()
704
- _(@dinfo.fields).zip(E2.split_keys(@values[index].id)).each(([fk, k]) => @record()[fk] = E2.parse_entry(k, @parentp().meta.info[fk])).value
705
- @parentp().search_field_change?(@decode_field)
706
-
707
- @scope().$watch "action.decode", (e) => if e?
708
- @reset() if e.length == 0
785
+ @decode = '' unless @decode?
786
+ # @dinfo.render.min_length == 0
709
787
 
710
788
  load: (value) ->
711
- if value? && value.length > 0 && @key_pressed # check again after strap updates ?
712
- @invoke(query: value).then =>
713
- if @entries # ?
714
- @values = @entries.map (e) => id: E2.id_for(e, @meta), value: @decode_description(e)
715
- delete @entries
716
- @values
789
+ if _.isString(value)
790
+ @invoke(query: value).then => if @entries # ?
791
+ @values = @entries.map (e) => id: E2.id_for(e, @meta), value: @decode_description(e)
792
+ delete @entries
793
+ @values
717
794
 
718
795
  clean: ->
719
- delete @decode
796
+ @decode = ''
720
797
  @clear_record()
721
798
 
722
799
  many_to_one_list: class ManyToOneListAction extends ListAction
@@ -740,11 +817,12 @@ angular.module('Engine2')
740
817
  star_to_many_list: class StarToManyList extends ListAction
741
818
  initialize: ->
742
819
  super()
743
- @query.parent_id = @parent().current_id()
820
+ @query.parent_id = @list_parent_action().current_id()
744
821
 
745
822
  # link_list: implicit
746
- item_menu_confirm_unlink: (index) ->
747
- @invoke_action('confirm_unlink', id: E2.id_for(@entries[index], @meta), parent_id: @query.parent_id)
823
+ item_menu_confirm_unlink: (args) ->
824
+ args.parent_id = @query.parent_id
825
+ @item_menu_confirm_unlink_super(args)
748
826
 
749
827
  star_to_many_bulk_unlink: class StarToManyBulkUnlinkAction extends Action
750
828
  invoke: ->
@@ -762,26 +840,81 @@ angular.module('Engine2')
762
840
  panel_menu_link: ->
763
841
  selection = _.keys(@selection)
764
842
  if selection.length > 0
765
- @invoke_action('link', parent_id: @query.parent_id, ids: selection).then (act) =>
766
- unless @errors
767
- @parent().invoke()
768
- @panel_close()
843
+ @invoke_action('link', parent_id: @query.parent_id, ids: selection)
769
844
 
770
845
  star_to_many_field: class StarToManyField extends ListAction
771
846
  initialize: ->
772
847
  super()
773
848
  @query.parent_id = E2.id_for(@parent().record, @parent().meta)
774
- links = @parent().record[@scope().$parent.f]
775
- @links = links ? (linked: [], unlinked: [])
849
+ @changes = (@parent().record[@scope().$parent.f] ?= (link: [], unlink: [], create: [], modify: [], delete: []))
776
850
  @invoke()
777
851
 
778
852
  invoke: ->
779
- @query.unlinked = [@links.unlinked]
780
- @query.linked = [@links.linked]
853
+ @query.changes = @changes
781
854
  super()
782
855
 
783
- sync_record: ->
784
- @parent().record[@scope().$parent.f] = @links
856
+ entry_dropped: (moved_to) ->
857
+ pos_field = @meta.draggable.position_field
858
+ positions = @entries.map (e) -> e[pos_field]
859
+ super(moved_to, false)
860
+ _.each positions, (p, i) =>
861
+ if @entries[i][pos_field] != p
862
+ if entry = @current_entry_is('create', @entries[i]) ? @current_entry_is('modify', @entries[i])
863
+ entry[pos_field] = p
864
+ else
865
+ @changes.modify.push(@entries[i])
866
+ @entries[i][pos_field] = p
867
+
868
+ @render_table() # @invoke()
869
+ true
870
+
871
+ current_entry_is: (mode, entry = @current_entry()) ->
872
+ key = E2.id_for(entry, @meta)
873
+ _.find(@changes[mode], (e) => E2.id_for(e, @meta) == key)
874
+
875
+ star_to_many_field_view: class StarToManyFieldView extends ViewAction
876
+ invoke: (args) ->
877
+ if entry = @parent().current_entry_is('create') ? @parent().current_entry_is('modify')
878
+ @meta.invokable = false
879
+ @record = entry
880
+ super(args)
881
+
882
+ star_to_many_field_modify: class StarToManyFieldModifyAction extends ModifyAction
883
+ invoke: (args) ->
884
+ if entry = @parent().current_entry_is('create') ? @parent().current_entry_is('modify')
885
+ @meta.invokable = false
886
+ @record = entry
887
+ super(args)
888
+
889
+ star_to_many_field_approve: class StarToManyFieldApprove extends Action
890
+ post_invoke: (args) ->
891
+ super(args)
892
+ unless @errors
893
+ pparent = @parent().parent()
894
+ if @parent() instanceof StarToManyFieldModifyAction
895
+ if entry = pparent.current_entry_is('create') ? pparent.current_entry_is('modify')
896
+ _.assign(entry, @parent().record)
897
+ else
898
+ pparent.changes.modify.push @parent().record
899
+ else # CreateAction
900
+ _(@parent().meta.primary_fields).each (k) => @parent().record[k] = E2.uuid()
901
+ if draggable = pparent.meta.draggable
902
+ max = _.maxBy(pparent.entries, (e) -> e.position)
903
+ @parent().record[draggable.position_field] = if max then max[draggable.position_field] + 1 else 1
904
+ pparent.changes.create.push @parent().record
905
+
906
+ star_to_many_field_delete: class StarToManyFieldDelete extends Action
907
+ invoke: (args) ->
908
+ pparent = @parent().parent()
909
+ if entry = pparent.current_entry_is('create')
910
+ _.remove(pparent.changes.create, entry)
911
+ else if entry = pparent.current_entry_is('modify')
912
+ _.remove(pparent.changes.modify, entry)
913
+ pparent.changes.delete.push args.id
914
+ else
915
+ pparent.changes.delete.push args.id
916
+ @meta.invokable = false
917
+ super(args)
785
918
 
786
919
  star_to_many_field_link_list: class StarToManyFieldLinkList extends ListAction
787
920
  initialize: ->
@@ -791,57 +924,50 @@ angular.module('Engine2')
791
924
  @selection = {}
792
925
 
793
926
  invoke: ->
794
- @query.unlinked = [@parent().links.unlinked]
795
- @query.linked = [@parent().links.linked]
927
+ @query.changes = @parent().changes
796
928
  super()
797
929
 
798
930
  panel_menu_link: ->
799
931
  if @selected_size() > 0
800
932
  _.each @selection, (v, k) =>
801
933
  id = k
802
- if _.includes(@parent().links.unlinked, id) then _.pull(@parent().links.unlinked, id) else @parent().links.linked.push id
934
+ if _.includes(@parent().changes.unlink, id) then _.pull(@parent().changes.unlink, id) else @parent().changes.link.push id
803
935
  @parent().invoke()
804
- @parent().sync_record()
805
936
  @panel_close()
806
937
 
807
938
  star_to_many_field_unlink: class StarToManyFieldUnlink extends Action
808
939
  invoke: (args) ->
809
940
  id = args.id
810
941
  pparent = @parent().parent()
811
- if _.includes(pparent.links.linked, id) then _.pull(pparent.links.linked, id) else pparent.links.unlinked.push id
812
- pparent.sync_record()
942
+ if _.includes(pparent.changes.link, id) then _.pull(pparent.changes.link, id) else pparent.changes.unlink.push id
943
+ @meta.invokable = false
944
+ super(args)
813
945
 
814
946
  file_store: class FileStoreAction extends Action
815
947
  initialize: ->
816
948
  super()
817
949
  @progress = 0
818
950
  id = E2.id_for(@parent().record, @parent().meta)
819
- if id.length > 0
820
- @invoke(owner: id).then => @sync_record()
821
- else
822
- @files = []
823
- @sync_record()
824
-
825
- sync_record: ->
826
- @parent().record[@scope().f] = @files
951
+ files = @parent().record[@scope().f]
952
+ @invoke(owner: id).then =>
953
+ @parent().record[@scope().f] = if files? then files else @files
954
+ delete @files
827
955
 
828
956
  select: (files) ->
829
957
  _.each files, (file) =>
830
958
  upload = $injector.get('Upload').upload url: "#{@action_info().action_resource}/upload", file: file
831
959
  upload.progress (e) =>
832
- globals.action_pending = false
960
+ @globals().action_pending = false
833
961
  @progress = parseInt(100.0 * e.loaded / e.total)
834
962
  upload.success (data, status, headers, config) =>
835
- @files.push mime: file.type, name: file.name, rackname: data.rackname, id: data.id
963
+ @parent().record[@scope().f].push mime: file.type, name: file.name, rackname: data.rackname, id: data.id, new: true
836
964
  @message = "Wysłano, #{file.name}"
837
- globals.action_pending = false
838
- @sync_record()
965
+ @globals().action_pending = false
839
966
 
840
967
  delete_file: (file) ->
841
968
  @scope().$broadcast 'confirm_delete',
842
969
  confirm: =>
843
- @sync_record()
844
- file.deleted = true
970
+ if file.new then _.pull(@parent().record[@scope().f], file) else file.deleted = true
845
971
  @scope().$broadcast 'confirm_delete_close'
846
972
 
847
973
  show_file: (file) ->