netzke-core 0.6.4 → 0.6.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (76) hide show
  1. data/CHANGELOG.rdoc +19 -0
  2. data/README.markdown +43 -0
  3. data/TODO +1 -5
  4. data/app/controllers/netzke_controller.rb +47 -15
  5. data/config/database.yml +2 -0
  6. data/features/component_loader.feature +6 -1
  7. data/features/composition.feature +2 -0
  8. data/features/js_include.feature +18 -0
  9. data/features/nested_views.feature +9 -0
  10. data/features/persistence.feature +6 -4
  11. data/features/support/paths.rb +3 -0
  12. data/javascripts/core.js +166 -519
  13. data/javascripts/ext.js +355 -0
  14. data/javascripts/touch.js +47 -0
  15. data/lib/netzke/actions.rb +31 -38
  16. data/lib/netzke/base.rb +48 -6
  17. data/lib/netzke/composition.rb +52 -63
  18. data/lib/netzke/configuration.rb +6 -2
  19. data/lib/netzke/core/version.rb +2 -2
  20. data/lib/netzke/core.rb +22 -15
  21. data/lib/netzke/javascript/scopes.rb +39 -0
  22. data/lib/netzke/javascript.rb +145 -114
  23. data/lib/netzke/railz/action_view_ext/ext.rb +59 -0
  24. data/lib/netzke/railz/action_view_ext/touch.rb +50 -0
  25. data/lib/netzke/railz/action_view_ext.rb +86 -0
  26. data/lib/netzke/railz/controller_extensions.rb +33 -0
  27. data/lib/netzke/{rails → railz}/routes.rb +0 -0
  28. data/lib/netzke/railz.rb +3 -0
  29. data/lib/netzke/session.rb +18 -3
  30. data/lib/netzke/state.rb +42 -15
  31. data/lib/netzke/stylesheets.rb +23 -8
  32. data/lib/netzke-core.rb +23 -16
  33. data/netzke-core.gemspec +52 -10
  34. data/spec/component/base_spec.rb +11 -0
  35. data/spec/component/javascript_spec.rb +3 -2
  36. data/spec/component/state_spec.rb +18 -0
  37. data/spec/spec_helper.rb +1 -1
  38. data/test/rails_app/Gemfile +3 -2
  39. data/test/rails_app/Gemfile.lock +73 -71
  40. data/test/rails_app/app/components/component_loader.rb +39 -4
  41. data/test/rails_app/app/components/{custom.css → component_with_custom_css/stylesheets/custom.css} +0 -0
  42. data/test/rails_app/app/components/component_with_custom_css.rb +2 -2
  43. data/test/rails_app/app/components/component_with_js_mixin/javascripts/extra_one.js +2 -0
  44. data/test/rails_app/app/components/component_with_js_mixin/javascripts/extra_two.js +2 -0
  45. data/test/rails_app/app/components/component_with_js_mixin/javascripts/method_set_one.js +6 -0
  46. data/test/rails_app/app/components/component_with_js_mixin/javascripts/method_set_two.js +5 -0
  47. data/test/rails_app/app/components/component_with_js_mixin.rb +8 -0
  48. data/test/rails_app/app/components/component_with_session_persistence.rb +10 -3
  49. data/test/rails_app/app/components/extended_component_with_js_mixin/javascripts/some_method_set.js +5 -0
  50. data/test/rails_app/app/components/extended_component_with_js_mixin.rb +7 -0
  51. data/test/rails_app/app/components/hello_world_component.rb +31 -0
  52. data/test/rails_app/app/components/server_caller.rb +1 -1
  53. data/test/rails_app/app/components/simple_panel.rb +2 -0
  54. data/test/rails_app/app/components/touch/hello_world_component.rb +25 -0
  55. data/test/rails_app/app/components/touch/server_caller.rb +28 -0
  56. data/test/rails_app/app/components/touch/simple_carousel.rb +17 -0
  57. data/test/rails_app/app/controllers/components_controller.rb +6 -1
  58. data/test/rails_app/app/controllers/touch_controller.rb +6 -0
  59. data/test/rails_app/app/helpers/touch_helper.rb +2 -0
  60. data/test/rails_app/app/views/components/panel_with_autoload.html.erb +2 -0
  61. data/test/rails_app/app/views/components/some_tab_panel.html.erb +11 -0
  62. data/test/rails_app/app/views/layouts/nested.html.erb +5 -0
  63. data/test/rails_app/app/views/layouts/touch.html.erb +13 -0
  64. data/test/rails_app/config/initializers/netzke.rb +1 -1
  65. data/test/rails_app/config/locales/en.yml +7 -1
  66. data/test/rails_app/config/routes.rb +10 -1
  67. data/test/rails_app/db/migrate/20110110132720_create_netzke_component_states.rb +20 -0
  68. data/test/rails_app/db/schema.rb +14 -1
  69. data/test/rails_app/spec/controllers/touch_controller_spec.rb +5 -0
  70. data/test/rails_app/spec/helpers/touch_helper_spec.rb +15 -0
  71. data/test/unit/netzke_core_test.rb +2 -6
  72. metadata +53 -11
  73. data/README.rdoc +0 -136
  74. data/lib/netzke/rails/action_view_ext.rb +0 -103
  75. data/lib/netzke/rails/controller_extensions.rb +0 -31
  76. data/test/rails_app/db/migrate/20100905214933_create_netzke_preferences.rb +0 -16
@@ -0,0 +1,355 @@
1
+ // Because of Netzke's double-underscore notation, Ext.TabPanel should have a different id-delimiter (yes, this should be in netzke-core)
2
+ Ext.TabPanel.prototype.idDelimiter = "___";
3
+
4
+ Ext.QuickTips.init();
5
+
6
+ // We don't want no state managment by default, thank you!
7
+ Ext.state.Provider.prototype.set = function(){};
8
+
9
+ // Check Ext JS version
10
+ (function(){
11
+ var requiredExtVersion = "3.3.1";
12
+ var currentExtVersion = Ext.version;
13
+ if (requiredExtVersion !== currentExtVersion) {
14
+ Netzke.deprecationWarning("Need Ext " + requiredExtVersion + ". You have " + currentExtVersion + ".");
15
+ }
16
+ })();
17
+
18
+ Ext.apply(Netzke.classes.Core.Mixin, {
19
+ height: 400,
20
+ border: false,
21
+
22
+ /*
23
+ Mask shown during loading of a component. Set to false to not mask. Pass config for Ext.LoadMask for configuring msg/cls, etc.
24
+ Set msg to null if mask without any msg is desirable.
25
+ */
26
+ componentLoadMask: true,
27
+
28
+ /* initComponent common for all Netzke components */
29
+ initComponentWithNetzke: function(){
30
+ this.normalizeActions();
31
+
32
+ this.detectActions(this);
33
+
34
+ this.detectComponents(this.items);
35
+
36
+ this.normalizeTools();
37
+
38
+ this.processEndpoints();
39
+
40
+ // This is where the references to different callback functions will be stored
41
+ this.callbackHash = {};
42
+
43
+ // This is where we store the information about components that are currently being loaded with this.loadComponent()
44
+ this.componentsBeingLoaded = {};
45
+
46
+ // Set title
47
+ if (this.mode === "config"){
48
+ if (!this.title) {
49
+ this.title = '[' + this.id + ']';
50
+ } else {
51
+ this.title = this.title + ' [' + this.id + ']';
52
+ }
53
+ } else {
54
+ if (!this.title) {
55
+ this.title = this.id.humanize();
56
+ }
57
+ }
58
+
59
+ // From everywhere accessible FeedbackGhost
60
+ this.feedbackGhost = new Netzke.FeedbackGhost();
61
+
62
+ // Call the original initComponent
63
+ this.initComponentWithoutNetzke();
64
+ },
65
+
66
+ normalizeTools: function() {
67
+ if (this.tools) {
68
+ var normTools = [];
69
+ Ext.each(this.tools, function(tool){
70
+ // Create an event for each action (so that higher-level components could interfere)
71
+ this.addEvents(tool.id+'click');
72
+
73
+ var handler = this.toolActionHandler.createDelegate(this, [tool]);
74
+ normTools.push({id : tool, handler : handler, scope : this});
75
+ }, this);
76
+ this.tools = normTools;
77
+ }
78
+ },
79
+
80
+ /*
81
+ Replaces actions configs with Ext.Action instances, assigning default handler to them
82
+ */
83
+ normalizeActions : function(){
84
+ var normActions = {};
85
+ for (var name in this.actions) {
86
+ // Create an event for each action (so that higher-level components could interfere)
87
+ this.addEvents(name+'click');
88
+
89
+ // Configure the action
90
+ var actionConfig = this.actions[name];
91
+ actionConfig.customHandler = actionConfig.handler;
92
+ actionConfig.handler = this.actionHandler.createDelegate(this); // handler common for all actions
93
+ actionConfig.name = name;
94
+ normActions[name] = new Ext.Action(actionConfig);
95
+ }
96
+ delete(this.actions);
97
+ this.actions = normActions;
98
+ },
99
+
100
+ /*
101
+ Detects action configs in the passed object, and replaces them with instances of Ext.Action created by normalizeActions().
102
+ This detects action in arbitrary level of nesting, which means you can put any other components in your toolbar, and inside of them specify menus/items or even toolbars.
103
+ */
104
+ detectActions: function(o){
105
+ if (Ext.isObject(o)) {
106
+ if ((typeof o.handler === 'string') && Ext.isFunction(this[o.handler.camelize(true)])) {
107
+ // This button config has a handler specified as string - replace it with reference to a real function if it exists
108
+ o.handler = this[o.handler.camelize(true)].createDelegate(this);
109
+ }
110
+ // TODO: this should be configurable!
111
+ Ext.each(["bbar", "tbar", "fbar", "menu", "items", "contextMenu", "buttons"], function(key){
112
+ if (o[key]) {
113
+ var items = [].concat(o[key]); // we need to do it in order to esure that this instance has a separate bbar/tbar/etc, NOT shared via class' prototype
114
+ delete(o[key]);
115
+ o[key] = items;
116
+ this.detectActions(o[key]);
117
+ }
118
+ }, this);
119
+ } else if (Ext.isArray(o)) {
120
+ var a = o;
121
+ Ext.each(a, function(el, i){
122
+ if (Ext.isObject(el)) {
123
+ if (el.action) {
124
+ if (!this.actions[el.action.camelize(true)]) throw "Netzke: action '"+el.action+"' not defined";
125
+ a[i] = this.actions[el.action.camelize(true)];
126
+ delete(el);
127
+ } else {
128
+ this.detectActions(el);
129
+ }
130
+ }
131
+ }, this);
132
+ }
133
+ },
134
+
135
+ /*
136
+ Loads a component. Config options:
137
+ 'name' (required) - the name of the child component to load
138
+ 'container' - the id of a panel with the 'fit' layout where the loaded component will be instantiated
139
+ 'callback' - function that gets called after the component is loaded. It receives the component's instance as parameter.
140
+ 'scope' - scope for the callback.
141
+ */
142
+ loadComponent: function(params){
143
+ if (params.id) {
144
+ params.name = params.id;
145
+ Netzke.deprecationWarning("Using 'id' in loadComponent is deprecated. Use 'name' instead.");
146
+ }
147
+
148
+ params.name = params.name.underscore();
149
+
150
+ // params that will be provided for the server API call (deliver_component); all what's passed in params.params is merged in. This way we exclude from sending along such things as :scope, :callback, etc.
151
+ var serverParams = params.params || {};
152
+ serverParams.name = params.name;
153
+
154
+ // coma-separated list of xtypes of already loaded classes
155
+ serverParams.cache = Netzke.cache.join();
156
+
157
+ var storedConfig = this.componentsBeingLoaded[params.name] = {};
158
+
159
+ // Remember where the loaded component should be inserted into
160
+ if (params.container) {
161
+ storedConfig.container = params.container;
162
+ }
163
+
164
+ // remember the passed callback for the future (per loaded component, as there may be simultaneous ongoing calls)
165
+ if (params.callback) {
166
+ storedConfig.callback = params.callback;
167
+ storedConfig.scope = params.scope;
168
+ // this.callbackHash[params.name.underscore()] = params.callback;
169
+ }
170
+
171
+ var container = params.container && Ext.getCmp(params.container);
172
+ if (container) {
173
+ // remove the old component if the container is specified
174
+ container.removeChild();
175
+ }
176
+
177
+ // Show loading mask if possible
178
+ var containerEl = (container || this).getEl();
179
+ if (this.componentLoadMask && containerEl){
180
+ storedConfig.loadMaskCmp = new Ext.LoadMask(containerEl, this.componentLoadMask);
181
+ storedConfig.loadMaskCmp.show();
182
+ }
183
+
184
+ // do the remote API call
185
+ this.deliverComponent(serverParams);
186
+ },
187
+
188
+ /*
189
+ Called by the server after we ask him to load a component
190
+ */
191
+ componentDelivered : function(config){
192
+ // retrieve the loading config for this component
193
+ var storedConfig = this.componentsBeingLoaded[config.name] || {};
194
+ delete this.componentsBeingLoaded[config.name];
195
+
196
+ if (storedConfig.loadMaskCmp) {
197
+ storedConfig.loadMaskCmp.hide();
198
+ storedConfig.loadMaskCmp.destroy();
199
+ }
200
+
201
+ // instantiate and render it
202
+ var componentInstance = this.instantiateAndRenderComponent(config, storedConfig.container);
203
+
204
+ if (storedConfig.callback) {
205
+ storedConfig.callback.call(storedConfig.scope || this, componentInstance);
206
+ }
207
+
208
+ this.fireEvent('componentload', componentInstance);
209
+ },
210
+
211
+ /*
212
+ Instantiates and renders a component with given config and container
213
+ */
214
+ instantiateAndRenderComponent : function(config, containerId){
215
+ var componentInstance;
216
+ if (containerId) {
217
+ var container = Ext.getCmp(containerId);
218
+ componentInstance = container.instantiateChild(config);
219
+ } else {
220
+ componentInstance = this.instantiateChild(config);
221
+ }
222
+ return componentInstance;
223
+ },
224
+
225
+ /*
226
+ Instantiates and inserts a component into a container with layout 'fit'.
227
+ Arg: an JS object with the following keys:
228
+ - id: id of the receiving container
229
+ - config: configuration of the component to be instantiated and inserted into the container
230
+ */
231
+ // renderComponentInContainer : function(params){
232
+ // var cont = Ext.getCmp(params.container);
233
+ // if (cont) {
234
+ // cont.instantiateChild(params.config);
235
+ // } else {
236
+ // this.instantiateChild(params.config);
237
+ // }
238
+ // },
239
+
240
+ /*
241
+ Returns the parent component
242
+ */
243
+ getParent: function(){
244
+ // simply cutting the last part of the id: some_parent__a_kid__a_great_kid => some_parent__a_kid
245
+ var idSplit = this.id.split("__");
246
+ idSplit.pop();
247
+ var parentId = idSplit.join("__");
248
+
249
+ return parentId === "" ? null : Ext.getCmp(parentId);
250
+ },
251
+
252
+ /*
253
+ Reloads current component (calls the parent to reload it as its component)
254
+ */
255
+ reload : function(){
256
+ var parent = this.getParent();
257
+ if (parent) {
258
+ parent.loadComponent({id:this.localId(parent), container:this.ownerCt.id});
259
+ } else {
260
+ window.location.reload();
261
+ }
262
+ },
263
+
264
+ /*
265
+ Reconfigures the component
266
+ */
267
+ reconfigure: function(config){
268
+ this.ownerCt.instantiateChild(config)
269
+ },
270
+
271
+ // Get the child component
272
+ getChildComponent : function(id){
273
+ if (id === "") {return this};
274
+ id = id.underscore();
275
+ var split = id.split("__");
276
+ if (split[0] === 'parent') {
277
+ split.shift();
278
+ var childInParentScope = split.join("__");
279
+ return this.getParent().getChildComponent(childInParentScope);
280
+ } else {
281
+ return Ext.getCmp(this.id+"__"+id);
282
+ }
283
+ },
284
+
285
+ // At this moment component is fully initializied
286
+ commonAfterConstructor : function(config){
287
+
288
+ // Add the menus
289
+ if (this.initialConfig.menu) {this.addMenu(this.initialConfig.menu, this);}
290
+
291
+ // generic events
292
+ this.addEvents(
293
+ 'componentload' // fired when a child is dynamically loaded
294
+ );
295
+
296
+ // Cleaning up on destroy
297
+ this.on('beforedestroy', function(){
298
+ this.cleanUpMenu();
299
+ }, this);
300
+
301
+ this.callbackHash = {};
302
+
303
+ if (this.afterConstructor) this.afterConstructor(config);
304
+ },
305
+
306
+ feedback:function(msg){
307
+ if (this.initialConfig && this.initialConfig.quiet) {
308
+ return false;
309
+ }
310
+
311
+ if (this.feedbackGhost) {
312
+ this.feedbackGhost.showFeedback(msg);
313
+ } else {
314
+ // there's no application to show the feedback - so, we do it ourselves
315
+ if (typeof msg == 'string'){
316
+ alert(msg);
317
+ } else {
318
+ var compoundResponse = "";
319
+ Ext.each(msg, function(m){
320
+ compoundResponse += m.msg + "\n"
321
+ });
322
+ if (compoundResponse != "") {
323
+ alert(compoundResponse);
324
+ }
325
+ }
326
+ }
327
+ },
328
+
329
+ // Common handler for all component's actions. <tt>comp</tt> is the Component that triggered the action (e.g. button or menu item)
330
+ actionHandler : function(comp){
331
+ var actionName = comp.name;
332
+ // If firing corresponding event doesn't return false, call the handler
333
+ if (this.fireEvent(actionName+'click', comp)) {
334
+ var action = this.actions[actionName];
335
+ var customHandler = action.initialConfig.customHandler;
336
+ var methodName = (customHandler && customHandler.camelize(true)) || "on" + actionName.camelize();
337
+ if (!this[methodName]) {throw "Netzke: action handler '" + methodName + "' is undefined"}
338
+
339
+ // call the handler passing it the triggering component
340
+ this[methodName](comp);
341
+ }
342
+ },
343
+
344
+ // Common handler for tools
345
+ toolActionHandler : function(tool){
346
+ // If firing corresponding event doesn't return false, call the handler
347
+ if (this.fireEvent(tool.id+'click')) {
348
+ var methodName = "on"+tool.camelize();
349
+ if (!this[methodName]) {throw "Netzke: handler for tool '"+tool+"' is undefined"}
350
+ this[methodName]();
351
+ }
352
+ },
353
+
354
+ onComponentLoad:Ext.emptyFn // gets overridden
355
+ });
@@ -0,0 +1,47 @@
1
+ Ext.ns("Netzke.classes.Core");
2
+ Ext.apply(Netzke.classes.Core.Mixin, {
3
+ /* initComponent common for all Netzke components */
4
+ initComponentWithNetzke: function(){
5
+ this.detectActions(this);
6
+
7
+ this.detectComponents(this.items);
8
+
9
+ this.processEndpoints();
10
+
11
+ // This is where the references to different callback functions will be stored
12
+ this.callbackHash = {};
13
+
14
+ // Call the original initComponent
15
+ this.initComponentWithoutNetzke();
16
+ },
17
+
18
+ /*
19
+ Detects action configs in the passed object, and replaces them with instances of Ext.Action created by normalizeActions().
20
+ This detects action in arbitrary level of nesting, which means you can put any other components in your toolbar, and inside of them specify menus/items or even toolbars.
21
+ */
22
+ detectActions: function(o){
23
+ if (Ext.isObject(o)) {
24
+ if ((typeof o.handler === 'string') && Ext.isFunction(this[o.handler.camelize(true)])) {
25
+ // This button config has a handler specified as string - replace it with reference to a real function if it exists
26
+ o.handler = this[o.handler.camelize(true)];
27
+ o.scope = this;
28
+ }
29
+ // TODO: this should be configurable!
30
+ Ext.each(["items", "dockedItems"], function(key){
31
+ if (o[key]) {
32
+ var items = [].concat(o[key]); // we need to do it in order to esure that this instance has a separate bbar/tbar/etc, NOT shared via class' prototype
33
+ delete(o[key]);
34
+ o[key] = items;
35
+ this.detectActions(o[key]);
36
+ }
37
+ }, this);
38
+ } else if (Ext.isArray(o)) {
39
+ var a = o;
40
+ Ext.each(a, function(el, i){
41
+ if (Ext.isObject(el)) {
42
+ this.detectActions(el);
43
+ }
44
+ }, this);
45
+ }
46
+ }
47
+ });
@@ -1,23 +1,34 @@
1
1
  module Netzke
2
- # Netzke component allows specifying Ext actions.
3
- # An action can be defined in 2 different ways, both of which result in a method definition like this
4
- # def _<some_action>_action
5
- # ...
6
- # end
2
+ # Netzke components allow specifying Ext actions (see http://dev.sencha.com/deploy/dev/docs/?class=Ext.Action)
7
3
  #
4
+ # == Defining actions in a component
8
5
  # The 2 ways to define an action are:
9
- # * as a hash:
10
- # action :bug_server, :text => "Call server", :icon => "/images/icons/phone.png"
6
+ # * as a hash, e.g:
7
+ #
8
+ # action :bug_server, :text => "Call server", :icon => :phone
9
+ #
10
+ # (if the same action was defined in the super class, the superclass's definition get merged with the current definition)
11
11
  #
12
- # * as a block:
12
+ # * as a block, in case you need access to the component's instance, e.g.:
13
13
  # action :bug_server do
14
14
  # {:text => config[:text], :disabled => true}
15
15
  # end
16
16
  #
17
- # The block can optionally receive the configuration of an action being overridden:
18
- # action :bug_server do |orig|
19
- # {:text => config[:text] + orig[:text], :disabled => orig[:disabled]}
17
+ # Both of the ways result in a definition of an instance method named {action_name}_action. So, overriding an action in the child class is done be redefining the method, e.g.:
18
+ #
19
+ # def bug_server_action
20
+ # # super will have the superclass's action definition
20
21
  # end
22
+ #
23
+ # == I18n of actions
24
+ # The text and tooltip for an action will be automatically picked up from a locale file when possible.
25
+ # E.g., an action named "some_action" and defined in the component +MyComponents::CoolComponent+, will look for its text in:
26
+ #
27
+ # I18n.t('my_components.cool_component.actions.some_action')
28
+ #
29
+ # and for its tooltip in:
30
+ #
31
+ # I18n.t('my_components.cool_component.actions.some_action_tooltip')
21
32
  module Actions
22
33
  extend ActiveSupport::Concern
23
34
 
@@ -64,7 +75,7 @@ module Netzke
64
75
 
65
76
  # All actions for this instance
66
77
  def actions
67
- @actions ||= self.class.registered_actions.inject({}){ |res, name| res.merge(name.to_sym => normalize_action_config(send(ACTION_METHOD_NAME % name))) }
78
+ @actions ||= self.class.registered_actions.inject({}){ |res, name| res.merge(name.to_sym => normalize_action_config(send(ACTION_METHOD_NAME % name).merge(:name => name.to_s))) }
68
79
  end
69
80
 
70
81
  def js_config_with_actions #:nodoc
@@ -72,40 +83,22 @@ module Netzke
72
83
  end
73
84
 
74
85
  private
86
+
75
87
  def normalize_action_config(config)
76
88
  if config[:icon].is_a?(Symbol)
77
- config[:icon] = Netzke::Core.with_icons ? Netzke::Core.icons_uri + "/" + config[:icon].to_s + ".png" : nil
89
+ config[:icon] = uri_to_icon(config[:icon])
78
90
  end
79
91
 
80
- config[:text] ||= config[:name].humanize
92
+ # Default text and tooltip
93
+ default_text = I18n.t(i18n_id + ".actions." + config[:name], :default => config[:name].humanize)
94
+ config[:text] ||= default_text
95
+ config[:tooltip] ||= I18n.t(i18n_id + ".actions." + config[:name] + "_tooltip", :default => default_text)
81
96
 
82
97
  config
83
98
  end
84
99
 
85
- def auto_collect_actions_from_config_and_js_properties
86
- # res = extract_actions(js_properties)
87
- # puts %Q(!!! res: #{res.inspect}\n)
100
+ def uri_to_icon(icon)
101
+ Netzke::Core.with_icons ? Netzke::Core.icons_uri + "/" + icon.to_s + ".png" : nil
88
102
  end
89
-
90
- # def extract_actions(hsh)
91
- # hsh.each_pair.inject({}) do |r,(k,v)|
92
- # v.is_a?(Array) ? r.merge(extract_actions_from_array(v)) : r
93
- # end
94
- # end
95
- #
96
- # def extract_actions_from_array(arry)
97
- # arry.inject({}) do |r, el|
98
- # if el.is_a?(Hash)
99
- # el[:action] ? r.merge(el[:action] => default_action_config(el[:action])) : r.merge(extract_actions(el))
100
- # else
101
- # r
102
- # end
103
- # end
104
- # end
105
- #
106
- # def default_action_config(action_name)
107
- # {:text => action_name.to_s.humanize}
108
- # end
109
-
110
103
  end
111
104
  end