engine2 1.0.3 → 1.0.4

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 (133) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +3 -117
  3. data/{views → app}/app.coffee +0 -0
  4. data/{public/css → app}/app.css +203 -196
  5. data/{views → app}/engine2.coffee +111 -60
  6. data/{views → app}/engine2actions.coffee +47 -28
  7. data/bower.json +13 -0
  8. data/conf/message.yaml +2 -1
  9. data/conf/message_pl.yaml +2 -1
  10. data/config.coffee +62 -0
  11. data/engine2.gemspec +2 -5
  12. data/lib/engine2.rb +1 -1
  13. data/lib/engine2/action.rb +9 -24
  14. data/lib/engine2/core.rb +9 -8
  15. data/lib/engine2/handler.rb +1 -5
  16. data/lib/engine2/meta.rb +61 -24
  17. data/lib/engine2/meta/array_meta.rb +82 -0
  18. data/lib/engine2/meta/decode_meta.rb +1 -1
  19. data/lib/engine2/meta/infra_meta.rb +21 -26
  20. data/lib/engine2/meta/list_meta.rb +2 -3
  21. data/lib/engine2/model.rb +5 -5
  22. data/lib/engine2/pre_bootstrap.rb +9 -1
  23. data/lib/engine2/scheme.rb +4 -0
  24. data/lib/engine2/type_info.rb +8 -1
  25. data/lib/engine2/version.rb +1 -1
  26. data/package.json +38 -0
  27. data/public/img/ajax-loader.gif +0 -0
  28. data/views/fields/blob.slim +4 -5
  29. data/views/fields/bs_select.slim +2 -2
  30. data/views/fields/bsselect_picker.slim +1 -2
  31. data/views/fields/bsselect_picker_opt.slim +1 -2
  32. data/views/fields/checkbox.slim +1 -1
  33. data/views/fields/checkbox_buttons.slim +1 -1
  34. data/views/fields/checkbox_buttons_opt.slim +1 -1
  35. data/views/fields/currency.slim +2 -2
  36. data/views/fields/date.slim +0 -1
  37. data/views/fields/date_range.slim +0 -1
  38. data/views/fields/date_time.slim +0 -1
  39. data/views/fields/datetime.slim +0 -2
  40. data/views/fields/decimal.slim +0 -1
  41. data/views/fields/decimal_date.slim +0 -1
  42. data/views/fields/decimal_time.slim +0 -1
  43. data/views/fields/email.slim +1 -2
  44. data/views/fields/file_store.slim +1 -1
  45. data/views/fields/input_text.slim +1 -2
  46. data/views/fields/integer.slim +1 -1
  47. data/views/fields/list_bsselect.slim +1 -2
  48. data/views/fields/list_bsselect_opt.slim +2 -3
  49. data/views/fields/list_buttons.slim +1 -1
  50. data/views/fields/list_buttons_opt.slim +1 -1
  51. data/views/fields/list_select.slim +1 -2
  52. data/views/fields/list_select_opt.slim +2 -3
  53. data/views/fields/password.slim +1 -2
  54. data/views/fields/radio_checkbox.slim +1 -1
  55. data/views/fields/scaffold.slim +1 -1
  56. data/views/fields/scaffold_picker.slim +1 -2
  57. data/views/fields/select_picker.slim +1 -2
  58. data/views/fields/select_picker_opt.slim +1 -2
  59. data/views/fields/text_area.slim +0 -1
  60. data/views/fields/time.slim +0 -1
  61. data/views/fields/typeahead_picker.slim +1 -2
  62. data/views/index.slim +4 -8
  63. data/views/scaffold/fields.slim +4 -4
  64. data/views/scaffold/form.slim +1 -7
  65. data/views/scaffold/form_collapse.slim +19 -0
  66. data/views/scaffold/form_tabs.slim +9 -0
  67. data/views/scaffold/list.slim +1 -12
  68. data/views/scaffold/search.slim +2 -9
  69. data/views/scaffold/search_collapse.slim +26 -0
  70. data/views/scaffold/search_tabs.slim +15 -0
  71. data/views/scaffold/view.slim +5 -15
  72. data/views/scaffold/view_collapse.slim +22 -0
  73. data/views/scaffold/view_tabs.slim +11 -0
  74. data/views/search_fields/bsmselect_picker.slim +1 -2
  75. data/views/search_fields/bsselect_picker.slim +1 -2
  76. data/views/search_fields/checkbox.slim +3 -3
  77. data/views/search_fields/checkbox2.slim +5 -5
  78. data/views/search_fields/checkbox_buttons.slim +2 -2
  79. data/views/search_fields/date_range.slim +0 -2
  80. data/views/search_fields/decimal_date_range.slim +5 -5
  81. data/views/search_fields/input_text.slim +2 -2
  82. data/views/search_fields/integer.slim +1 -1
  83. data/views/search_fields/integer_range.slim +2 -2
  84. data/views/search_fields/list_bsmselect.slim +1 -1
  85. data/views/search_fields/list_bsselect.slim +0 -1
  86. data/views/search_fields/list_buttons.slim +2 -2
  87. data/views/search_fields/list_select.slim +1 -2
  88. data/views/search_fields/scaffold_picker.slim +1 -2
  89. data/views/search_fields/select_picker.slim +1 -2
  90. data/views/search_fields/typeahead_picker.slim +1 -2
  91. metadata +29 -103
  92. data/public/assets/javascripts.js +0 -13
  93. data/public/assets/styles.css +0 -4
  94. data/public/css/angular-motion.css +0 -1022
  95. data/public/css/angular-ui-tree.min.css +0 -1
  96. data/public/css/bootstrap-additions.css +0 -1560
  97. data/public/css/bootstrap.min.css +0 -11
  98. data/public/css/font-awesome.min.css +0 -4
  99. data/public/fonts/FontAwesome.otf +0 -0
  100. data/public/fonts/fontawesome-webfont.eot +0 -0
  101. data/public/fonts/fontawesome-webfont.svg +0 -655
  102. data/public/fonts/fontawesome-webfont.ttf +0 -0
  103. data/public/fonts/fontawesome-webfont.woff +0 -0
  104. data/public/fonts/fontawesome-webfont.woff2 +0 -0
  105. data/public/fonts/glyphicons-halflings-regular.eot +0 -0
  106. data/public/fonts/glyphicons-halflings-regular.svg +0 -288
  107. data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  108. data/public/fonts/glyphicons-halflings-regular.woff +0 -0
  109. data/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
  110. data/public/images/file.png +0 -0
  111. data/public/images/folder-closed.png +0 -0
  112. data/public/images/folder.png +0 -0
  113. data/public/images/node-closed-2.png +0 -0
  114. data/public/images/node-closed-light.png +0 -0
  115. data/public/images/node-closed.png +0 -0
  116. data/public/images/node-opened-2.png +0 -0
  117. data/public/images/node-opened-light.png +0 -0
  118. data/public/images/node-opened.png +0 -0
  119. data/public/img/ajax-loader-light.gif +0 -0
  120. data/public/js/angular-animate.js +0 -4147
  121. data/public/js/angular-cookies.js +0 -322
  122. data/public/js/angular-local-storage.js +0 -455
  123. data/public/js/angular-route.js +0 -1025
  124. data/public/js/angular-sanitize.js +0 -717
  125. data/public/js/angular-strap.js +0 -4339
  126. data/public/js/angular-strap.tpl.js +0 -43
  127. data/public/js/angular-ui-tree.js +0 -1569
  128. data/public/js/angular.js +0 -30868
  129. data/public/js/i18n/angular-locale_pl.js +0 -115
  130. data/public/js/lodash.custom.min.js +0 -99
  131. data/public/js/ng-file-upload-shim.min.js +0 -2
  132. data/public/js/ng-file-upload.min.js +0 -3
  133. data/views/engine2templates.coffee +0 -0
@@ -1,20 +1,15 @@
1
1
  'use strict'
2
+ require 'angular-route'
3
+ require 'angular-sanitize'
4
+ require 'angular-animate'
5
+ require 'angular-cookies'
6
+ require 'angular-local-storage'
7
+ require 'angular-ui-tree'
8
+ require 'ng-file-upload'
9
+
2
10
  _.templateSettings.interpolate = /{{([\s\S]+?)}}/g;
3
11
 
4
12
  angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'mgcrea.ngStrap', 'ngFileUpload', 'ui.tree', 'LocalStorageModule']) # 'draggabilly'
5
- .factory 'E2Snippets', ->
6
- icon = (name) -> "<span class='glyphicon glyphicon-#{name}'></span>"
7
- aicon = (name) -> "<i class='fa fa-#{name}'></i>"
8
- ng_class_names = ['active', 'enabled', 'disabled']
9
- icon: icon
10
- aicon: aicon
11
- boolean_true_value: icon('check')
12
- boolean_false_value: icon('unchecked')
13
- make_ng_class: (o) ->
14
- out = []
15
- _.each ng_class_names, (e) -> out.push(if e == 'enabled' then "'disabled': !(#{o[e]})" else "'#{e}': #{o[e]}") if o[e]?
16
- if out.length > 0 then "ng-class=\"{#{out.join(',')}}\"" else ""
17
-
18
13
  .config ($httpProvider, $routeProvider, $compileProvider, localStorageServiceProvider, $logProvider) ->
19
14
  loaderOn = -> angular.element(document.querySelectorAll('.loader')).eq(-1).css("visibility", 'visible')
20
15
  $httpProvider.interceptors.push 'e2HttpInterceptor'
@@ -51,7 +46,23 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
51
46
  $injector.get('$e2Modal').error("#{E2Snippets.icon('bell')} #{response.status}: #{message}", cause)
52
47
  $q.reject(response)
53
48
 
54
- .factory 'E2', ($templateCache, $http, E2Snippets, $e2Modal, $q, $injector, e2HttpInterceptor, $route, $dateFormatter) ->
49
+ .factory 'E2Snippets', ->
50
+ icon = (name) -> "<span class='glyphicon glyphicon-#{name}'></span>"
51
+ aicon = (name) -> "<i class='fa fa-#{name}'></i>"
52
+ ng_class_names = ['active', 'enabled', 'disabled']
53
+ icon: icon
54
+ aicon: aicon
55
+ boolean_true_value: icon('check')
56
+ boolean_false_value: icon('unchecked')
57
+ make_ng_class: (o) ->
58
+ out = []
59
+ _.each ng_class_names, (e) -> out.push(if e == 'enabled' then "'disabled': !(#{o[e]})" else "'#{e}': #{o[e]}") if o[e]?
60
+ _.each(o.class, (v, k) -> out.push "#{k}: #{v}") if o.class?
61
+ if out.length > 0 then "ng-class=\"{#{out.join(',')}}\"" else ""
62
+
63
+ .factory 'E2', ($templateCache, $http, E2Snippets, $e2Modal, $q, $injector, e2HttpInterceptor, $route, $dateFormatter, $parse) ->
64
+ globals: {}
65
+
55
66
  compact: (o) ->
56
67
  _.each o, (v, k) =>
57
68
  if (v == null || (_.isString(v) && !v)) || (!_.isDate(v) && _.isObject(v) && @compact(v) && _.isEmpty(v))
@@ -66,6 +77,7 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
66
77
  else
67
78
  # delete o[k]
68
79
  o[k] = null
80
+
69
81
  merge: (dst, src) ->
70
82
  for k, v of src
71
83
  if _.isObject(v) && !_.isArray(v)
@@ -80,8 +92,8 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
80
92
  transpose: (a) ->
81
93
  _.keys(a[0]).map((c) -> a.map (r) -> r[c])
82
94
 
83
- join_keys: (keys) -> keys.join('|')
84
- split_keys: (key) -> key.split('|')
95
+ join_keys: (keys) -> keys.join(@globals.key_separator)
96
+ split_keys: (key) -> key.split(@globals.key_separator)
85
97
  id_for: (rec, meta) -> @join_keys(meta.primary_fields.map((e) -> rec[e]))
86
98
  from_id: (id, meta) -> _.zipObject(meta.primary_fields, id) # _.zip(meta.primary_fields, id).reduce(((rec, [k, v]) -> rec[k] = v; rec), {})
87
99
 
@@ -121,13 +133,15 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
121
133
  m.click = fun_invoke
122
134
 
123
135
  if click
124
- action[fun_name] = (args...) ->
125
- processor.arg_fun(action, args...)
136
+ action[fun_name] = (arg) ->
137
+ processor.arg_fun(action, arg)
126
138
  action.scope().$eval(click)
127
139
  else
128
- action[fun_name] ?= (args...) ->
129
- processor.arg_fun(action, args...)
130
- action.invoke_action(m.name, processor.arg_ret(action))
140
+ action[fun_name] ?= (arg) ->
141
+ processor.arg_fun(action, arg)
142
+ args = processor.arg_ret(action)
143
+ _.merge(args, $parse(m.arguments)(action.scope())) if m.arguments?
144
+ action.invoke_action(m.name, args)
131
145
 
132
146
  if action.find_action_info(m.name, false)?
133
147
  show = if m.show then " && " + m.show else ""
@@ -136,17 +150,16 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
136
150
  menu_processors:
137
151
  menu:
138
152
  arg_name: ''
139
- arg_fun: (action) -> undefined
140
- arg_ret: (action) -> undefined
153
+ arg_fun: (action) ->
154
+ arg_ret: (action) -> {}
141
155
  panel_menu:
142
156
  arg_name: ''
143
- arg_fun: (action) -> undefined
144
- arg_ret: (action) -> undefined
157
+ arg_fun: (action) ->
158
+ arg_ret: (action) -> {}
145
159
  item_menu:
146
160
  arg_name: '$index'
147
- arg_ret: (action) -> id: action.current_id
148
- arg_fun: (action, index) ->
149
- action.current_id = $injector.get('E2').id_for(action.entries[index], action.meta)
161
+ arg_fun: (action, index) -> action.current_index = index
162
+ arg_ret: (action) -> id: action.current_id()
150
163
 
151
164
  renderers:
152
165
  boolean: (value, render) =>
@@ -180,7 +193,8 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
180
193
  if f_info? && type = f_info.type
181
194
  @renderers[type](value, f_info.render)
182
195
  else
183
- (value + "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
196
+ if f_info?.escape == false then value else
197
+ (value + "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;")
184
198
  else
185
199
  value
186
200
 
@@ -200,7 +214,7 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
200
214
  constructor: () ->
201
215
  backdrop_z_index: (num, index) -> angular.element(document.querySelectorAll('.modal-backdrop')).eq(num).css('z-index', index)
202
216
  modal_num: (num) -> angular.element(document.querySelectorAll('.modal')).eq(num)
203
- backdrop: -> 'static'
217
+ backdrop: (bdr) -> bdr ? 'static'
204
218
  show_before: ->
205
219
  @z_index = MManager.Z_INDEX + MManager.index * 2
206
220
  @modal.css('z-index', @z_index + 1)
@@ -218,7 +232,7 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
218
232
  constructor: () ->
219
233
  super()
220
234
  @threshold = 2
221
- backdrop: -> if MManager.index > @threshold then false else super()
235
+ backdrop: (bdr) -> if MManager.index > @threshold then false else super(bdr)
222
236
  show_before: ->
223
237
  super()
224
238
  if MManager.index > @threshold
@@ -234,7 +248,7 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
234
248
  @modal_num((MManager.index - 1) - @threshold).css('display', 'block')
235
249
 
236
250
  class SingleBackdropMManager extends MManager
237
- backdrop: -> if MManager.index > 0 then false else super()
251
+ backdrop: (bdr) -> if MManager.index > 0 then false else super(bdr)
238
252
  show_before: ->
239
253
  super()
240
254
  @backdrop_z_index(0, @z_index)
@@ -253,33 +267,38 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
253
267
  e.stopPropagation()
254
268
  manager.modal = m.$element
255
269
  manager.backdrop = m.$backdrop
256
- throw "Modal has element" if scope.action.element?()
257
- scope.action.element = -> m.$element
258
- scope.action.panel_show?()
270
+ throw "Modal has element" if action.element?()
271
+ action.element = -> m.$element
272
+ action.panel_show?()
259
273
  manager.show_before()
260
274
  MManager.index++
261
275
 
262
276
  scope.$on 'modal.show', (e, m) ->
263
277
  e.stopPropagation()
264
278
  manager.show()
265
- scope.action.panel_shown?()
279
+ action.panel_shown?()
266
280
 
267
281
  scope.$on 'modal.hide.before', (e) ->
268
282
  e.stopPropagation()
269
283
  MManager.index--
270
284
  manager.hide_before()
271
- scope.action.panel_hide?()
285
+ action.panel_hide?()
272
286
 
273
287
  scope.$on 'modal.hide', (e) ->
274
288
  e.stopPropagation()
275
289
  manager.hide()
276
- scope.action.panel_hidden?()
290
+ action.panel_hidden?()
277
291
  scope.$destroy()
278
292
 
279
- $injector.get('E2').fetch_panel(scope.action.meta.panel, true).then (template) ->
280
- # template = "<div class='modal' ng-class='action.meta.panel.class'>#{template}</div>"
281
- modal = $modal(scope: scope, template: template, backdrop: manager.backdrop(), animation: 'am-fade', show: false)
282
- scope.action.modal_hide = -> modal.$scope.$hide()
293
+ $injector.get('E2').fetch_panel(action.meta.panel, true).then (template) ->
294
+ modal = $modal
295
+ scope: scope
296
+ show: false
297
+ template: template
298
+ backdrop: manager.backdrop(action.meta.panel.backdrop)
299
+ animation: action.meta.panel.animation ? 'am-fade'
300
+
301
+ action.modal_hide = -> modal.$scope.$hide()
283
302
  modal.$promise.then ->
284
303
  modal.show()
285
304
  modal
@@ -385,31 +404,54 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
385
404
  elem.after($compile(body)(scope))
386
405
 
387
406
  .directive 'e2TableBody', ($parse, $compile) ->
407
+ table_tmpl = _.template("<thead><tr>{{thead}}</tr></thead><tbody>{{tbody}}</tbody>")
388
408
  scope: false
389
409
  restrict: 'A'
390
410
  link: (scope, elem, attrs) ->
391
411
  scope.$on 'render_table', (a, ev) ->
392
412
  # ev.stopPropagation()
393
- meta = scope.action.meta
394
- selection = scope.action.selection
395
- out = ''
396
- _.each scope.action.entries, (e, i) ->
397
- out += if selection then "<tr ng-class='action.selected_class(#{i})' class='tr_hover' ng-click='action.select(#{i}, $event)'>" else
413
+ action = scope.action
414
+ meta = action.meta
415
+ position = meta.menus.item_menu.properties.position ? 0
416
+ right_style = if position >= meta.fields.length then "style=\"text-align: right\"" else ""
417
+
418
+ thead = ""
419
+ fields = meta.fields.slice()
420
+ fields.splice(position, 0, null)
421
+ _.each fields, (f) ->
422
+ if f
423
+ info = meta.info[f]
424
+ thead += "<th>"
425
+ title = if info.title then "title=\"#{info.title}\"" else ""
426
+ if info.sort
427
+ thead += "<a ng-click=\"action.order('#{f}')\" #{title}><strong>#{info.loc}</strong></a>"
428
+ if action.ui.order == f
429
+ cls = if action.ui.asc then "glyphicon-chevron-up" else "glyphicon-chevron-down"
430
+ thead += " <span class=\"glyphicon #{cls}\"></span>"
431
+ else
432
+ thead += "<span #{title}>#{info.loc}</span>"
433
+ thead += "</th>"
434
+ else
435
+ thead += "<th class=\"#{meta.menus.menu.properties.class || ''}\" #{right_style}><div e2-button-set=\"action.meta.menus.menu\"></div></th>"
436
+
437
+ tbody = ""
438
+ _.each action.entries, (e, i) ->
439
+ tbody += if action.selection then "<tr ng-class=\"action.selected_class(#{i})\" class=\"tr_hover\" ng-click=\"action.select(#{i}, $event)\">" else
398
440
  row_cls = e.$row_info?.class
399
441
  if row_cls then "<tr class=\"#{row_cls}\">" else "<tr>"
400
- out += "<td>"
401
- out += "<div e2-button-set='action.meta.menus.item_menu' index='#{i}'></div>" if meta.config.show_item_menu
402
- out += "</td>"
403
- _.each meta.fields, (f) ->
404
- out += if col_cls = meta.info[f].column_class then "<td class='#{col_cls}'>" else "<td>"
405
- out += scope.action.list_cell(e, f) ? ''
406
- out += "</td>"
407
- out += "</tr>"
442
+ _.each fields, (f) ->
443
+ if f
444
+ tbody += if col_cls = meta.info[f].column_class then "<td class=\"#{col_cls}\">" else "<td>"
445
+ tbody += action.list_cell(e, f) ? ''
446
+ tbody += "</td>"
447
+ else
448
+ tbody += "<td #{right_style}><div e2-button-set=\"action.meta.menus.item_menu\" index=\"#{i}\"></div></td>"
449
+ tbody += "</tr>"
408
450
 
409
451
  elem.empty()
410
- elem.append($compile(out)(scope)) unless out.length == 0 # leak ?
452
+ elem.append($compile(table_tmpl thead: thead, tbody: tbody)(scope))
411
453
 
412
- .directive 'e2DropDown', ($parse, $dropdown, $timeout, E2Snippets) ->
454
+ .directive 'e2Dropdown', ($parse, $dropdown, $timeout, E2Snippets) ->
413
455
  event_num = 0
414
456
  dropdown_sub_tmpl = _.template("<li class='dropdown-submenu'><a href=''> {{icon}}{{aicon}} {{loc}}</a>{{sub}}</li>")
415
457
  dropdown_tmpl = _.template("<li {{clazz}} {{show}} {{hide}}> <a href='{{href}}' {{click}}> {{icon}}{{aicon}} {{loc}}</a></li>")
@@ -446,7 +488,8 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
446
488
  elem.off "mousedown"
447
489
  # event.preventDefault()
448
490
  # event.stopPropagation()
449
- dropdown = $dropdown(elem, (scope: scope, template: render($parse(attrs.e2DropDown)(scope), 0), animation: 'am-flip-x', prefixEvent: "#{event_num}.tooltip")) # , delay: 1
491
+ menu = $parse(attrs.e2Dropdown)(scope)
492
+ dropdown = $dropdown(elem, (scope: scope, template: render(menu, 0), animation: attrs.animation || 'am-flip-x', prefixEvent: "#{event_num}.tooltip")) # , delay: 1
450
493
  dropdown.$promise.then ->
451
494
  event_hide = scope.$on "#{event_num}.tooltip.hide", (e) ->
452
495
  e.stopPropagation()
@@ -463,13 +506,14 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
463
506
 
464
507
  .directive 'e2ButtonSet', ($parse, $compile, E2Snippets) ->
465
508
  button_set_tmpl = _.template("<div class='btn btn-default' {{clazz}} {{click}} {{show}} {{hide}} {{title}}> {{icon}}{{aicon}} {{loc}}</div>")
466
- button_set_arr_tmpl = _.template("<div class='btn btn-default' e2-drop-down='{{dropdown}}'>{{icon}}{{aicon}}<span class='caret'></span></div>")
509
+ button_set_arr_tmpl = _.template("<div class='btn btn-default' e2-dropdown='{{dropdown}}' data-animation='{{animation}}'>{{icon}}{{aicon}}<span class='caret'></span></div>")
467
510
  scope: true # because $index
468
511
  link: (scope, elem, attrs) ->
469
512
  menu = $parse(attrs.e2ButtonSet)(scope)
470
513
  if menu && menu.entries.length > 0
471
514
  group_class = menu.properties.group_class || ''
472
515
  brk = menu.properties.break
516
+ animation = menu.properties.animation
473
517
  out = ""
474
518
  for m, i in menu.entries
475
519
  if i >= brk
@@ -477,6 +521,7 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
477
521
  else if m.menu
478
522
  out += button_set_arr_tmpl
479
523
  dropdown: "#{attrs.e2ButtonSet}.entries[#{i}].menu.entries"
524
+ animation: animation
480
525
  icon: m.menu.icon && "#{E2Snippets.icon(m.menu.icon)}&nbsp;" || ''
481
526
  aicon: m.menu.aicon && "#{E2Snippets.aicon(m.menu.aicon)}&nbsp;" || ''
482
527
  else if m.divider
@@ -491,7 +536,13 @@ angular.module('Engine2', ['ngRoute', 'ngSanitize', 'ngAnimate', 'ngCookies', 'm
491
536
  loc: !(m.button_loc == false) && m.loc || ''
492
537
  title: (m.button_loc == false) && "title=\"#{m.loc}\"" || ''
493
538
 
494
- out += button_set_arr_tmpl dropdown: "#{attrs.e2ButtonSet}.entries.slice(#{brk})", icon: '', aicon: '' if menu.entries.length > brk
539
+ if menu.entries.length > brk
540
+ out += button_set_arr_tmpl
541
+ dropdown: "#{attrs.e2ButtonSet}.entries.slice(#{brk})"
542
+ animation: animation
543
+ icon: ''
544
+ aicon: ''
545
+
495
546
  out = "<div class='btn-group #{group_class}'>#{out}</div>"
496
547
  out = $compile(angular.element(out))(scope)
497
548
  if attrs.index && !scope.$index?
@@ -1,3 +1,4 @@
1
+ 'use strict'
1
2
  angular.module('Engine2')
2
3
  .directive 'e2Action', (E2Actions) ->
3
4
  scope: true
@@ -22,7 +23,7 @@ angular.module('Engine2')
22
23
  $scope.action = new E2Actions.root(mresponse.data, $scope, null, $element, action_resource: 'api')
23
24
 
24
25
  .factory 'E2Actions', (E2, $http, $timeout, $e2Modal, $injector, $compile, $templateCache, $q, localStorageService, $route, $window, $rootScope, $location) ->
25
- globals = {}
26
+ globals = E2.globals
26
27
  action: class Action
27
28
  constructor: (response, scope, parent, element, action_info) ->
28
29
  @find_action_info = (name, raise = true) ->
@@ -71,14 +72,17 @@ angular.module('Engine2')
71
72
 
72
73
  perform_invoke: (params) ->
73
74
  info = @action_info()
74
- get_invoke = if info.invokable
75
- params.initial = true if !@action_invoked && params && info.method == 'get'
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'
76
77
  $http[info.method](info.action_resource, if info.method == 'post' then params else (params: params))
77
- else $q.when(data: (response: {}))
78
78
 
79
79
  globals.action_pending = if @meta.panel then @ else @parent()
80
80
 
81
81
  get_invoke.then (response) =>
82
+ if exec = response.data.$execute
83
+ @scope().$eval(exec)
84
+ delete response.data.$execute
85
+
82
86
  E2.merge(@, response.data)
83
87
  @process_meta()
84
88
  (if @meta.reload_routes then $route.load_routes() else $q.when({})).then =>
@@ -103,7 +107,7 @@ angular.module('Engine2')
103
107
  get_meta = if !info.terminal || info.meta
104
108
  $http.get("#{info.action_resource}/meta", cache: true).then (response) =>
105
109
  if info.recheck_access
106
- $http.get("#{info.action_resource}/meta", params: (access: true, parent_id: @current_id)).then (aresponse) ->
110
+ $http.get("#{info.action_resource}/meta", params: (access: true, parent_id: @current_id())).then (aresponse) ->
107
111
  response.data.actions[k].access = v for k, v of aresponse.data
108
112
  response
109
113
  else response # $q.when ^
@@ -113,8 +117,8 @@ angular.module('Engine2')
113
117
  ,
114
118
  (err) => @handle_error(err, info, el)
115
119
 
116
- invoke_action: (name, arg) ->
117
- @create_action(name, @scope()).then (act) -> act.invoke(arg)
120
+ invoke_action: (name, args) ->
121
+ @create_action(name, @scope()).then (act) -> act.invoke(args)
118
122
 
119
123
  create_action_path: (action_names, sc, elem) ->
120
124
  last_name = action_names.pop()
@@ -126,17 +130,23 @@ angular.module('Engine2')
126
130
  pre_invoke: ->
127
131
  post_invoke: ->
128
132
 
129
- invoke: ->
130
- args = arguments
131
- @pre_invoke(args...)
132
- @perform_invoke(args...).then (response) =>
133
- @post_invoke(args...)
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
134
144
  @
135
145
 
136
146
  save_state: () ->
137
147
  _.each @meta.state, (s) => localStorageService.set("#{globals.application}/#{@action_info().action_resource}/#{s}", @[s])
138
148
  load_state: () ->
139
- _.each @meta.state, (s) => E2.merge(@[s], localStorageService.get("#{globals.application}/#{@action_info().action_resource}/#{s}"))
149
+ _.each @meta.state, (s) => _.merge(@[s], localStorageService.get("#{globals.application}/#{@action_info().action_resource}/#{s}"))
140
150
 
141
151
  destroy: (e) ->
142
152
  console.log "DESTROY #{@action_info().action_resource}"
@@ -194,7 +204,7 @@ angular.module('Engine2')
194
204
  root: class RootAction extends Action
195
205
  initialize: ->
196
206
  super()
197
- E2.merge(globals, @meta)
207
+ _.merge(globals, @meta)
198
208
  @meta = {}
199
209
 
200
210
  default_action: class DefaultAction extends Action
@@ -266,10 +276,13 @@ angular.module('Engine2')
266
276
  if route.menu then @register(route.menu.entries) else
267
277
  name = '/' + route.name
268
278
  route.href = '#' + route.name
269
- $templateCache.put(route.name, "<div e2-action='' action=\"'#{route.bootstrap}/#{route.name}'\" invoke='true'></div>") if route.bootstrap
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
+
270
283
  $route.routes[name] =
271
284
  reloadOnSearch: true
272
- templateUrl: route.name
285
+ templateUrl: if route.bootstrap? then route.name + '_route_template!' else route.name
273
286
  originalPath: name
274
287
  regexp: new RegExp("^#{name}$")
275
288
  keys: []
@@ -282,14 +295,16 @@ angular.module('Engine2')
282
295
 
283
296
  traverse: (routes) ->
284
297
  menu_tmpl = _.template("<li><a href='{{href}}'>{{icon}}{{aicon}} {{loc}}</a></li>")
285
- menu_sub_tmpl = _.template("<li e2-drop-down='{{dropdown}}'><a href='javascript://'>{{icon}}{{aicon}} {{loc}}<span class='caret'></span></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>")
299
+ animation = @meta.menus.menu.properties.animation
286
300
  out = routes.map (route, i) ->
287
301
  if route.menu
288
302
  menu_sub_tmpl
303
+ dropdown: "routes[#{i}].menu.entries"
304
+ animation: animation
289
305
  icon: route.menu.icon && E2.icon(route.menu.icon) || ""
290
306
  aicon: route.menu.aicon && E2.aicon(route.menu.aicon) || ""
291
307
  loc: route.menu.loc
292
- dropdown: "routes[#{i}].menu.entries"
293
308
  else
294
309
  menu_tmpl
295
310
  href: route.href
@@ -298,8 +313,6 @@ angular.module('Engine2')
298
313
  aicon: route.aicon && E2.aicon(route.aicon) || ''
299
314
  out.join('')
300
315
 
301
- # dummy: class DummyAction extends Action
302
-
303
316
  list: class ListAction extends Action
304
317
  initialize: ->
305
318
  super()
@@ -340,7 +353,7 @@ angular.module('Engine2')
340
353
  menu_show_meta: ->
341
354
  $e2Modal.show
342
355
  the_meta: @meta
343
- meta: panel: (panel_template: "close_m", template_string: "<pre>{{action.the_meta | json}}</pre>", title: "Meta", class: "modal-huge")
356
+ meta: panel: (panel_template: "close_m", template_string: "<pre>{{action.the_meta | json}}</pre>", title: "Meta", class: "modal-huge", backdrop: true, footer: true)
344
357
 
345
358
  # show_assoc: (index, assoc) ->
346
359
  # # parent_id = E2.id_for(@entries[index], @meta)
@@ -350,14 +363,20 @@ angular.module('Engine2')
350
363
  # @current_id = E2.id_for(@entries[index], @meta)
351
364
  # @invoke_action(assoc)
352
365
 
366
+ current_entry: ->
367
+ @entries[@current_index]
368
+
369
+ current_id: ->
370
+ E2.id_for(@current_entry(), @meta)
371
+
353
372
  list_cell: (e, f) ->
354
373
  E2.render_field(e, f, @meta)
355
374
 
356
- invoke: (arg = {}) ->
375
+ invoke: (args = {}) ->
357
376
  @save_state()
358
377
  query = _.cloneDeep(@query)
359
378
  delete query.search if _.isEmpty(E2.compact(query.search))
360
- _.merge(query, arg)
379
+ _.merge(query, args)
361
380
  super(query).then =>
362
381
  @ui = _.pick @query, ['order', 'asc', 'page']
363
382
  @ui.pagination_active = @ui.page != 0 || @entries.length >= @meta.config.per_page
@@ -599,7 +618,7 @@ angular.module('Engine2')
599
618
  @multiple = @dinfo.render.multiple
600
619
  @clear_selected()
601
620
  @if_fk_values (fk_values) =>
602
- @selected = if @multiple then E2.transpose(fk_values).map(E2.join_keys) else E2.join_keys(fk_values)
621
+ @selected = if @multiple then E2.transpose(fk_values).map((e) -> E2.join_keys(e)) else E2.join_keys(fk_values)
603
622
  @invoke()
604
623
 
605
624
  clear_selected: -> @selected = if @multiple then [] else null # no need to null complex keys
@@ -652,7 +671,7 @@ angular.module('Engine2')
652
671
 
653
672
  invoke_decode: (recs, f) ->
654
673
  if @multiple && _.size(recs) > @meta.show_max_selected
655
- @decode = "#{_.size(recs)} #{@meta.decode_selected}"
674
+ @decode = "#{_.size(recs)} #{@meta.loc.decode_selected}"
656
675
  else
657
676
  decode_descriptions = (recs) => @decode = recs.map((fields) => @decode_description(fields)).join(' | ')
658
677
  recs = recs.map (r) => if _.isArray(r) then E2.from_id(r, @meta) else r
@@ -721,7 +740,7 @@ angular.module('Engine2')
721
740
  star_to_many_list: class StarToManyList extends ListAction
722
741
  initialize: ->
723
742
  super()
724
- @query.parent_id = @parent().current_id
743
+ @query.parent_id = @parent().current_id()
725
744
 
726
745
  # link_list: implicit
727
746
  item_menu_confirm_unlink: (index) ->
@@ -786,8 +805,8 @@ angular.module('Engine2')
786
805
  @panel_close()
787
806
 
788
807
  star_to_many_field_unlink: class StarToManyFieldUnlink extends Action
789
- invoke: (arg) ->
790
- id = arg.id
808
+ invoke: (args) ->
809
+ id = args.id
791
810
  pparent = @parent().parent()
792
811
  if _.includes(pparent.links.linked, id) then _.pull(pparent.links.linked, id) else pparent.links.unlinked.push id
793
812
  pparent.sync_record()