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