netzke-core 0.6.4 → 0.6.5
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.
- data/CHANGELOG.rdoc +19 -0
- data/README.markdown +43 -0
- data/TODO +1 -5
- data/app/controllers/netzke_controller.rb +47 -15
- data/config/database.yml +2 -0
- data/features/component_loader.feature +6 -1
- data/features/composition.feature +2 -0
- data/features/js_include.feature +18 -0
- data/features/nested_views.feature +9 -0
- data/features/persistence.feature +6 -4
- data/features/support/paths.rb +3 -0
- data/javascripts/core.js +166 -519
- data/javascripts/ext.js +355 -0
- data/javascripts/touch.js +47 -0
- data/lib/netzke/actions.rb +31 -38
- data/lib/netzke/base.rb +48 -6
- data/lib/netzke/composition.rb +52 -63
- data/lib/netzke/configuration.rb +6 -2
- data/lib/netzke/core/version.rb +2 -2
- data/lib/netzke/core.rb +22 -15
- data/lib/netzke/javascript/scopes.rb +39 -0
- data/lib/netzke/javascript.rb +145 -114
- data/lib/netzke/railz/action_view_ext/ext.rb +59 -0
- data/lib/netzke/railz/action_view_ext/touch.rb +50 -0
- data/lib/netzke/railz/action_view_ext.rb +86 -0
- data/lib/netzke/railz/controller_extensions.rb +33 -0
- data/lib/netzke/{rails → railz}/routes.rb +0 -0
- data/lib/netzke/railz.rb +3 -0
- data/lib/netzke/session.rb +18 -3
- data/lib/netzke/state.rb +42 -15
- data/lib/netzke/stylesheets.rb +23 -8
- data/lib/netzke-core.rb +23 -16
- data/netzke-core.gemspec +52 -10
- data/spec/component/base_spec.rb +11 -0
- data/spec/component/javascript_spec.rb +3 -2
- data/spec/component/state_spec.rb +18 -0
- data/spec/spec_helper.rb +1 -1
- data/test/rails_app/Gemfile +3 -2
- data/test/rails_app/Gemfile.lock +73 -71
- data/test/rails_app/app/components/component_loader.rb +39 -4
- data/test/rails_app/app/components/{custom.css → component_with_custom_css/stylesheets/custom.css} +0 -0
- data/test/rails_app/app/components/component_with_custom_css.rb +2 -2
- data/test/rails_app/app/components/component_with_js_mixin/javascripts/extra_one.js +2 -0
- data/test/rails_app/app/components/component_with_js_mixin/javascripts/extra_two.js +2 -0
- data/test/rails_app/app/components/component_with_js_mixin/javascripts/method_set_one.js +6 -0
- data/test/rails_app/app/components/component_with_js_mixin/javascripts/method_set_two.js +5 -0
- data/test/rails_app/app/components/component_with_js_mixin.rb +8 -0
- data/test/rails_app/app/components/component_with_session_persistence.rb +10 -3
- data/test/rails_app/app/components/extended_component_with_js_mixin/javascripts/some_method_set.js +5 -0
- data/test/rails_app/app/components/extended_component_with_js_mixin.rb +7 -0
- data/test/rails_app/app/components/hello_world_component.rb +31 -0
- data/test/rails_app/app/components/server_caller.rb +1 -1
- data/test/rails_app/app/components/simple_panel.rb +2 -0
- data/test/rails_app/app/components/touch/hello_world_component.rb +25 -0
- data/test/rails_app/app/components/touch/server_caller.rb +28 -0
- data/test/rails_app/app/components/touch/simple_carousel.rb +17 -0
- data/test/rails_app/app/controllers/components_controller.rb +6 -1
- data/test/rails_app/app/controllers/touch_controller.rb +6 -0
- data/test/rails_app/app/helpers/touch_helper.rb +2 -0
- data/test/rails_app/app/views/components/panel_with_autoload.html.erb +2 -0
- data/test/rails_app/app/views/components/some_tab_panel.html.erb +11 -0
- data/test/rails_app/app/views/layouts/nested.html.erb +5 -0
- data/test/rails_app/app/views/layouts/touch.html.erb +13 -0
- data/test/rails_app/config/initializers/netzke.rb +1 -1
- data/test/rails_app/config/locales/en.yml +7 -1
- data/test/rails_app/config/routes.rb +10 -1
- data/test/rails_app/db/migrate/20110110132720_create_netzke_component_states.rb +20 -0
- data/test/rails_app/db/schema.rb +14 -1
- data/test/rails_app/spec/controllers/touch_controller_spec.rb +5 -0
- data/test/rails_app/spec/helpers/touch_helper_spec.rb +15 -0
- data/test/unit/netzke_core_test.rb +2 -6
- metadata +53 -11
- data/README.rdoc +0 -136
- data/lib/netzke/rails/action_view_ext.rb +0 -103
- data/lib/netzke/rails/controller_extensions.rb +0 -31
- data/test/rails_app/db/migrate/20100905214933_create_netzke_preferences.rb +0 -16
data/javascripts/ext.js
ADDED
@@ -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
|
+
});
|
data/lib/netzke/actions.rb
CHANGED
@@ -1,23 +1,34 @@
|
|
1
1
|
module Netzke
|
2
|
-
# Netzke
|
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
|
-
#
|
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
|
-
#
|
18
|
-
#
|
19
|
-
#
|
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] =
|
89
|
+
config[:icon] = uri_to_icon(config[:icon])
|
78
90
|
end
|
79
91
|
|
80
|
-
|
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
|
86
|
-
|
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
|