netzke-core 0.2.8 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,10 @@
1
+ v0.2.9
2
+ Actions, toolbars and tools reworked for easier configuration.
3
+ Menus introduced (based on actions).
4
+ Significant code clean-up.
5
+ Bug fix (nasty one): Ext.widgetMixIn was getting messed up along with dynamic widget loading.
6
+ Must work in IE now.
7
+
1
8
  v0.2.8
2
9
  Support for extra javascripts and stylesheets per widget.
3
10
 
data/Manifest CHANGED
@@ -1,5 +1,4 @@
1
1
  CHANGELOG
2
- css/core.css
3
2
  generators/netzke_core/netzke_core_generator.rb
4
3
  generators/netzke_core/templates/create_netzke_layouts.rb
5
4
  generators/netzke_core/templates/create_netzke_preferences.rb
@@ -25,6 +24,7 @@ Manifest
25
24
  netzke-core.gemspec
26
25
  Rakefile
27
26
  README.mdown
27
+ stylesheets/core.css
28
28
  tasks/netzke_core_tasks.rake
29
29
  test/app_root/app/controllers/application.rb
30
30
  test/app_root/config/boot.rb
@@ -42,4 +42,5 @@ test/core_ext_test.rb
42
42
  test/netzke_core_test.rb
43
43
  test/netzke_preference_test.rb
44
44
  test/test_helper.rb
45
+ TODO
45
46
  uninstall.rb
data/README.mdown CHANGED
@@ -10,4 +10,4 @@ The tutorials: http://blog.writelesscode.com
10
10
 
11
11
  Also see the netzke-basepack project: http://github.com/skozlov/netzke-basepack/tree/master
12
12
 
13
- Copyright (c) 2008-2009 Sergei Kozlov, released under GNU GPL license v3
13
+ Copyright (c) 2008-2009 Sergei Kozlov, released under LGPL 3.0
data/TODO ADDED
@@ -0,0 +1 @@
1
+ * Re-factor JS-level inheritance mechanisms
data/javascripts/core.js CHANGED
@@ -1,8 +1,12 @@
1
+ /*
2
+ This file gets loaded along with the rest of Ext library at the initial load
3
+ */
4
+
1
5
  Ext.BLANK_IMAGE_URL = "/extjs/resources/images/default/s.gif";
2
6
  Ext.namespace('Ext.netzke');
3
7
  Ext.netzke.cache = {};
4
8
 
5
- Ext.QuickTips.init(); // seems obligatory in Ext v2.2.1, otherwise destroy() stops working properly
9
+ Ext.QuickTips.init(); // seems obligatory in Ext v2.2.1, otherwise Ext.Component#destroy() stops working properly
6
10
 
7
11
  // to comply with Rails' forgery protection
8
12
  Ext.Ajax.extraParams = {
@@ -16,7 +20,50 @@ Ext.chainApply = function(objectArray){
16
20
  return res;
17
21
  };
18
22
 
19
- // implementation of totalProperty, successProperty and root configuration options for ArrayReader
23
+ // Type detection functions
24
+ function isArray(o) {
25
+ return (o != null && typeof o == "object" && o.constructor.toString() == Array.toString());
26
+ }
27
+
28
+ function isObject(o) {
29
+ return (o != null && typeof o == "object" && o.constructor.toString() == Object.toString());
30
+ }
31
+
32
+ // Some Rubyish String extensions
33
+ // from http://code.google.com/p/inflection-js/
34
+ String.prototype.camelize=function(lowFirstLetter)
35
+ {
36
+ var str=this.toLowerCase();
37
+ var str_path=str.split('/');
38
+ for(var i=0;i<str_path.length;i++)
39
+ {
40
+ var str_arr=str_path[i].split('_');
41
+ var initX=((lowFirstLetter&&i+1==str_path.length)?(1):(0));
42
+ for(var x=initX;x<str_arr.length;x++)
43
+ str_arr[x]=str_arr[x].charAt(0).toUpperCase()+str_arr[x].substring(1);
44
+ str_path[i]=str_arr.join('');
45
+ }
46
+ str=str_path.join('::');
47
+ return str;
48
+ };
49
+
50
+ String.prototype.capitalize=function()
51
+ {
52
+ var str=this.toLowerCase();
53
+ str=str.substring(0,1).toUpperCase()+str.substring(1);
54
+ return str;
55
+ };
56
+
57
+ String.prototype.humanize=function(lowFirstLetter)
58
+ {
59
+ var str=this.toLowerCase();
60
+ str=str.replace(new RegExp('_id','g'),'');
61
+ str=str.replace(new RegExp('_','g'),' ');
62
+ if(!lowFirstLetter)str=str.capitalize();
63
+ return str;
64
+ };
65
+
66
+ // Implementation of totalProperty, successProperty and root configuration options for ArrayReader
20
67
  Ext.data.ArrayReader = Ext.extend(Ext.data.JsonReader, {
21
68
  readRecords : function(o){
22
69
  var sid = this.meta ? this.meta.id : null;
@@ -49,68 +96,151 @@ Ext.data.ArrayReader = Ext.extend(Ext.data.JsonReader, {
49
96
 
50
97
  // Methods common to all widget classes
51
98
  Ext.widgetMixIn = {
99
+ // Common handler for actions
52
100
  actionHandler : function(action){
53
- if (this.fireEvent(action.handlerName+'click', action)) this[action.handlerName](action);
101
+ // If firing corresponding event doesn't return false, call the handler
102
+ if (this.fireEvent(action.name+'click', action)) {
103
+ this[(action.fn || action.name)](action);
104
+ }
54
105
  },
55
106
 
56
- widgetInit : function(config){
57
- this.app = Ext.getCmp('feedback_ghost');
107
+ // Common handler for tools
108
+ toolActionHandler : function(tool){
109
+ // If firing corresponding event doesn't return false, call the handler
110
+ if (this.fireEvent(tool.id+'click')) {
111
+ this[tool]();
112
+ }
113
+ },
58
114
 
59
- if (config.tools) Ext.each(config.tools, function(i){
60
- i.on.click = this[i.on.click].createDelegate(this);
61
- }, this);
115
+ beforeConstructor : function(config){
116
+ this.actions = {};
62
117
 
63
- if (config.actions) Ext.each(config.actions, function(i){
64
- this.addEvents(i.handlerName + 'click');
65
- i.handler = this.actionHandler.createDelegate(this);
66
- }, this);
118
+ // Create Ext.Actions based on config.actions
119
+ if (config.actions) {
120
+ for (var name in config.actions) {
121
+ // Create an event for each action (so that higher-level widgets could interfere)
122
+ this.addEvents(name+'click');
67
123
 
68
- if (config.menu) Ext.each(config.menu, function(i){
69
- this.addMenuItem(i)
70
- }, this);
124
+ // Configure the action
125
+ var actionConfig = config.actions[name];
126
+ actionConfig.handler = this.actionHandler.createDelegate(this);
127
+ actionConfig.name = name;
128
+ this.actions[name] = new Ext.Action(actionConfig);
129
+ }
71
130
 
72
- // set events
73
- this.on('beforedestroy', function(){
74
- // clean-up menus
75
- if (this.app && !!this.app.unhostMenus) {
76
- this.app.unhostMenus(this)
131
+ /* Parse the bbar and tbar (both Arrays), replacing the strings with the corresponding methods. For example:
132
+ replaceStringsWithActions( ['add', {text:'Menu', menu:['edit', 'delete']}] )
133
+ => [scope.actions['add'], {text:'Menu', menu:[scope.actions['edit'], scope.actions['delete']]}]
134
+ */
135
+ var replaceStringsWithActions = function(arry, scope){
136
+ var res = []; // new array
137
+ Ext.each(arry, function(o){
138
+ if (typeof o === "string") {
139
+ var camelized = o.camelize(true);
140
+ if (scope.actions[camelized]){
141
+ res.push(scope.actions[camelized]);
142
+ } else {
143
+ // if there's no action with this name, maybe it's a separator or something
144
+ res.push(o);
145
+ }
146
+ } else if (isObject(o)) {
147
+ // look inside the objects...
148
+ for (var key in o) {
149
+ if (isArray(o[key])) {
150
+ // ... and recursively process inner arrays found
151
+ o[key] = replaceStringsWithActions(o[key], scope);
152
+ }
153
+ }
154
+ res.push(o);
155
+ }
156
+ });
157
+ return res;
77
158
  }
159
+ config.bbar = config.bbar && replaceStringsWithActions(config.bbar, this);
160
+ config.tbar = config.tbar && replaceStringsWithActions(config.tbar, this);
161
+ config.menu = config.menu && replaceStringsWithActions(config.menu, this);
162
+
163
+ }
164
+
165
+ // Normalize tools
166
+ if (config.tools) {
167
+ var normTools = [];
168
+ Ext.each(config.tools, function(tool){
169
+ // Create an event for each action (so that higher-level widgets could interfere)
170
+ this.addEvents(tool.id+'click');
171
+
172
+ var handler = this.toolActionHandler.createDelegate(this, [tool]);
173
+ normTools.push({id : tool, handler : handler, scope : this});
174
+ }, this);
175
+ config.tools = normTools;
176
+ }
177
+
178
+ },
179
+
180
+ afterConstructor : function(config){
181
+ this.feedbackGhost = Ext.getCmp('feedback_ghost');
182
+
183
+ // cleaning up
184
+ this.on('beforedestroy', function(){
185
+ this.cleanUpMenu(this);
78
186
  }, this);
79
187
 
188
+ // After render, add the menus
189
+ this.on('render', function(){
190
+ if (this.initialConfig.menu) {this.addMenu(this.initialConfig.menu);}
191
+ }, this);
192
+
80
193
  this.on('render', this.onWidgetLoad, this);
81
194
  },
82
195
 
83
196
  feedback:function(msg){
84
- if (this.initialConfig && this.initialConfig.quiet) return false;
85
- if (this.app && !!this.app.showFeedback) {
86
- this.app.showFeedback(msg)
197
+ if (this.initialConfig && this.initialConfig.quiet) {
198
+ return false;
199
+ }
200
+
201
+ if (this.feedbackGhost) {
202
+ this.feedbackGhost.showFeedback(msg);
87
203
  } else {
88
204
  // there's no application to show the feedback - so, we do it ourselves
89
205
  if (typeof msg == 'string'){
90
- alert(msg)
206
+ alert(msg);
91
207
  } else {
92
- var compoundResponse = ""
208
+ var compoundResponse = "";
93
209
  Ext.each(msg, function(m){
94
210
  compoundResponse += m.msg + "\n"
95
- })
96
- if (compoundResponse != "") alert(compoundResponse);
211
+ });
212
+ if (compoundResponse != "") {
213
+ alert(compoundResponse);
214
+ }
97
215
  }
98
- };
216
+ }
99
217
  },
100
218
 
101
- addMenuItem : function(item){
102
- if (this.hostMenuItem) {
103
- this.hostMenuItem(item, this);
219
+ addMenu : function(menu, owner){
220
+ if (!owner) {
221
+ owner = this;
222
+ }
223
+
224
+ if (!!this.hostMenu) {
225
+ this.hostMenu(menu, owner);
104
226
  } else {
105
- if (this.ownerCt && this.ownerCt.ownerCt) {
106
- this.ownerCt.ownerCt.addMenuItem(item)
227
+ if (this.parent) {
228
+ this.parent.addMenu(menu, owner);
107
229
  }
108
230
  }
109
231
  },
110
-
111
- addMenus:function(menus){
112
- if (this.app && !!this.app.hostMenu) {
113
- Ext.each(menus, function(menu){this.app.hostMenu(menu, this)}, this)
232
+
233
+ cleanUpMenu : function(owner){
234
+ if (!owner) {
235
+ owner = this;
236
+ }
237
+
238
+ if (!!this.unhostMenu) {
239
+ this.unhostMenu(owner);
240
+ } else {
241
+ if (this.parent) {
242
+ this.parent.cleanUpMenu(owner);
243
+ }
114
244
  }
115
245
  },
116
246
 
@@ -120,11 +250,13 @@ Ext.widgetMixIn = {
120
250
  // Make Panel with layout 'fit' capable to dynamically load widgets
121
251
  Ext.override(Ext.Panel, {
122
252
  getWidget: function(){
123
- return this.items.get(0)
253
+ return this.items.get(0);
124
254
  },
125
255
 
126
256
  loadWidget: function(url, params){
127
- if (!params) params = {}
257
+ if (!params) {
258
+ params = {};
259
+ }
128
260
 
129
261
  this.remove(this.getWidget()); // first delete previous widget
130
262
 
@@ -149,7 +281,7 @@ Ext.override(Ext.Panel, {
149
281
 
150
282
  // evaluate widget's stylesheets
151
283
  if (responseObj.css){
152
- var linkTag = document.createElement('style')
284
+ var linkTag = document.createElement('style');
153
285
  linkTag.type = 'text/css';
154
286
  linkTag.innerHTML = responseObj.css;
155
287
  document.body.appendChild(linkTag);
@@ -160,14 +292,14 @@ Ext.override(Ext.Panel, {
160
292
  eval(responseObj.js);
161
293
  }
162
294
 
163
- responseObj.config.parent = this // we might want to know the parent panel in advance (e.g. to know its size)
295
+ responseObj.config.parent = this.ownerCt; // we might want to know the parent panel in advance (e.g. to know its size)
164
296
  var instance = new Ext.netzke.cache[responseObj.config.widgetClassName](responseObj.config)
165
297
 
166
298
  this.add(instance);
167
299
  this.doLayout();
168
300
  } else {
169
301
  // we didn't get normal response - desplay the flash with eventual errors
170
- this.ownerCt.feedback(responseObj.flash)
302
+ this.ownerCt.feedback(responseObj.flash);
171
303
  }
172
304
 
173
305
  // reenable the panel
@@ -178,20 +310,3 @@ Ext.override(Ext.Panel, {
178
310
  }
179
311
  });
180
312
 
181
- // Some Rubyish String extensions
182
- // from http://code.google.com/p/inflection-js/
183
- String.prototype.capitalize=function()
184
- {
185
- var str=this.toLowerCase();
186
- str=str.substring(0,1).toUpperCase()+str.substring(1);
187
- return str;
188
- };
189
-
190
- String.prototype.humanize=function(lowFirstLetter)
191
- {
192
- var str=this.toLowerCase();
193
- str=str.replace(new RegExp('_id','g'),'');
194
- str=str.replace(new RegExp('_','g'),' ');
195
- if(!lowFirstLetter)str=str.capitalize();
196
- return str;
197
- };
@@ -14,7 +14,7 @@ class NetzkeController < ActionController::Base
14
14
 
15
15
  format.css {
16
16
  res = ""
17
- Netzke::Base.config[:css].each do |path|
17
+ Netzke::Base.config[:stylesheets].each do |path|
18
18
  f = File.new(path)
19
19
  res << f.read
20
20
  end
@@ -1,16 +1,9 @@
1
1
  class NetzkeLayout < ActiveRecord::Base
2
2
  EXT_UNRELATED_ATTRIBUTES = %w{ id layout_id position created_at updated_at }
3
+
3
4
  # Multi user support
4
- def self.user
5
- @@user ||= nil
6
- end
7
-
8
- def self.user=(user)
9
- @@user = user
10
- end
11
-
12
5
  def self.user_id
13
- user && user.id
6
+ Netzke::Base.user && Netzke::Base.user.id
14
7
  end
15
8
 
16
9
  # normal create, but with a user_id merged-in
@@ -6,19 +6,12 @@
6
6
  # etc
7
7
  #
8
8
  class NetzkePreference < ActiveRecord::Base
9
- ELEMENTARY_CONVERTION_METHODS= {'Fixnum' => 'to_i', 'String' => 'to_s', 'Float' => 'to_f', 'Symbol' => 'to_sym'}
10
-
11
- # Multi user support
12
- def self.user
13
- @@user ||= nil
14
- end
9
+ named_scope :for_current_user, lambda { {:conditions => {:user_id => user_id}} }
15
10
 
16
- def self.user=(user)
17
- @@user = user
18
- end
11
+ ELEMENTARY_CONVERTION_METHODS= {'Fixnum' => 'to_i', 'String' => 'to_s', 'Float' => 'to_f', 'Symbol' => 'to_sym'}
19
12
 
20
13
  def self.user_id
21
- user && user.id
14
+ Netzke::Base.user && Netzke::Base.user.id
22
15
  end
23
16
 
24
17
  def self.widget_name=(value)
@@ -54,14 +47,14 @@ class NetzkePreference < ActiveRecord::Base
54
47
  end
55
48
 
56
49
  def self.[](pref_name)
57
- pref_name = pref_name.to_s
50
+ pref_name = normalize_preference_name(pref_name)
58
51
  conditions = {:name => pref_name, :user_id => user_id, :widget_name => self.widget_name}
59
52
  pref = self.find(:first, :conditions => conditions)
60
53
  pref && pref.normalized_value
61
54
  end
62
55
 
63
56
  def self.[]=(pref_name, new_value)
64
- pref_name = pref_name.to_s
57
+ pref_name = normalize_preference_name(pref_name)
65
58
  conditions = {:name => pref_name, :user_id => user_id, :widget_name => self.widget_name}
66
59
  pref = self.find(:first, :conditions => conditions)
67
60
 
@@ -75,4 +68,9 @@ class NetzkePreference < ActiveRecord::Base
75
68
  end
76
69
  end
77
70
 
71
+ private
72
+ def self.normalize_preference_name(name)
73
+ name.to_s.gsub(".", "__").gsub("/", "__")
74
+ end
75
+
78
76
  end
data/lib/netzke/base.rb CHANGED
@@ -1,4 +1,5 @@
1
- require 'json'
1
+ require 'netzke/base_extras/js_builder'
2
+ require 'netzke/base_extras/interface'
2
3
 
3
4
  module Netzke
4
5
  #
@@ -8,48 +9,38 @@ module Netzke
8
9
  # should be aware of this constant.
9
10
  #
10
11
  class Base
11
- module ClassMethods
12
-
13
- # Used to get access to the location of the source file of a Widget, e.g. to automatically include
14
- # <widget_name>_extras/*.rb files
15
- def widget_file=(file)
16
- write_inheritable_attribute(:widget_file, file)
17
- end
18
-
19
- def widget_file
20
- read_inheritable_attribute(:widget_file) || __FILE__
21
- end
22
- #
23
- #
12
+
13
+ # Class-level Netzke::Base configuration. The defaults also get specified here.
14
+ def self.config
15
+ set_default_config({
16
+ # which javascripts and stylesheets must get included at the initial load (see netzke-core.rb)
17
+ :javascripts => [],
18
+ :stylesheets => [],
19
+
20
+ :layout_manager => "NetzkeLayout",
21
+ :persistent_config_manager => "NetzkePreference",
22
+
23
+ :ext_location => defined?(RAILS_ROOT) && "#{RAILS_ROOT}/public/extjs"
24
+ })
25
+ end
24
26
 
25
- # Global Netzke::Base configuration
26
- def config
27
- set_default_config({
28
- :javascripts => [],
29
- :css => [],
30
- :layout_manager => "NetzkeLayout",
31
- :persistent_config_manager => "NetzkePreference"
32
- })
33
- end
27
+ include Netzke::BaseExtras::JsBuilder
28
+ include Netzke::BaseExtras::Interface
29
+
30
+ module ClassMethods
34
31
 
32
+ # "Netzke::SomeWidget" => "SomeWidget"
35
33
  def short_widget_class_name
36
- name.split("::").last
34
+ self.name.split("::").last
37
35
  end
38
36
 
37
+ # Multi-user support
39
38
  def user
40
39
  @@user ||= nil
41
40
  end
42
41
 
43
42
  def user=(user)
44
43
  @@user = user
45
-
46
- # also set up the managers
47
- persistent_config_manager_class && persistent_config_manager_class.user = user
48
- layout_manager_class && layout_manager_class.user = user
49
- end
50
-
51
- def user_has_role?(role)
52
- user.login == 'sergei' && role.to_s == 'configurator'
53
44
  end
54
45
 
55
46
  #
@@ -78,32 +69,6 @@ module Netzke
78
69
  read_inheritable_attribute(:interface_points)
79
70
  end
80
71
 
81
- #
82
- # Include extra code from Ext js library (e.g. examples)
83
- #
84
- def ext_js_include(*args)
85
- included_ext_js = read_inheritable_attribute(:included_ext_js) || []
86
- args.each {|f| included_ext_js << f}
87
- write_inheritable_attribute(:included_ext_js, included_ext_js)
88
- end
89
-
90
- # def js_include(*args)
91
- # included_js = read_inheritable_attribute(:included_js) || []
92
- # args.each {|f| included_js << f}
93
- # write_inheritable_attribute(:included_js, included_js)
94
- # end
95
-
96
- # include eventual extra modules
97
- def include_extras
98
- extras_dir = File.join(File.dirname(widget_file), short_widget_class_name.underscore + "_extras")
99
- file_list = Dir.glob("#{extras_dir}/*.rb")
100
- for file_name in file_list
101
- require file_name
102
- module_name = "#{self.name}Extras::#{File.basename(file_name, ".rb").classify}"
103
- include module_name.constantize
104
- end
105
- end
106
-
107
72
  # returns an instance of a widget defined in the config
108
73
  def instance_by_config(config)
109
74
  widget_class = "Netzke::#{config[:widget_class_name]}".constantize
@@ -123,7 +88,6 @@ module Netzke
123
88
  nil
124
89
  end
125
90
 
126
-
127
91
  private
128
92
  def set_default_config(default_config)
129
93
  @@config ||= {}
@@ -134,9 +98,6 @@ module Netzke
134
98
  end
135
99
  extend ClassMethods
136
100
 
137
- # include extra modules
138
- include_extras
139
-
140
101
  attr_accessor :config, :server_confg, :parent, :logger, :id_name, :permissions
141
102
  attr_reader :pref
142
103
 
@@ -171,9 +132,14 @@ module Netzke
171
132
  # persistent_config["window.size"] => 100
172
133
  # This method is user-aware
173
134
  def persistent_config
174
- config_klass = self.class.persistent_config_manager_class
175
- config_klass && config_klass.widget_name = id_name # pass to the config class our unique name
176
- config_klass || {} # if we don't have the presistent config manager, all the calls to it will always return nil, and the "="-operation will be ignored
135
+ config_klass = config[:persistent_config] && self.class.persistent_config_manager_class
136
+ if config_klass
137
+ config_klass.widget_name = id_name # pass to the config class our unique name
138
+ config_klass
139
+ else
140
+ # if we can't use presistent config, all the calls to it will always return nil, and the "="-operation will be ignored
141
+ {}
142
+ end
177
143
  end
178
144
 
179
145
  def initial_config
@@ -272,7 +238,11 @@ module Netzke
272
238
 
273
239
  # ... and then merge it with NetzkePreferences
274
240
  available_permissions.each do |p|
275
- persistent_permisson = persistent_config["permissions.#{p}"]
241
+ # if nothing is stored in persistent_config, store the permission from the config; otherwise leave what's there
242
+ persistent_config["permissions/#{p}"].nil? && persistent_config["permissions/#{p}"] = @permissions[p.to_sym]
243
+
244
+ # what's stored in persistent_config has higher priority, so, if there's something there, use that
245
+ persistent_permisson = persistent_config["permissions/#{p}"]
276
246
  @permissions[p.to_sym] = persistent_permisson unless persistent_permisson.nil?
277
247
  end
278
248
  end
@@ -303,5 +273,30 @@ module Netzke
303
273
  {:success => false, :flash => @flash}.to_json
304
274
  end
305
275
 
276
+ def tools
277
+ persistent_config[:tools] ||= config[:tools] == false ? nil : config[:tools]
278
+ end
279
+
280
+ def bbar
281
+ persistent_config[:bottom_bar] ||= config[:bbar] == false ? nil : config[:bbar]
282
+ end
283
+
284
+ def tbar
285
+ persistent_config[:top_bar] ||= config[:tbar] == false ? nil : config[:tbar]
286
+ end
287
+
288
+ def menu
289
+ persistent_config[:menu] ||= config[:menu] == false ? nil : config[:menu]
290
+ end
291
+
292
+ # some convenience for instances
293
+ def layout_manager_class
294
+ self.class.layout_manager_class
295
+ end
296
+
297
+ def persistent_config_manager_class
298
+ self.class.persistent_config_manager_class
299
+ end
300
+
306
301
  end
307
302
  end
@@ -1,3 +1,5 @@
1
+ require 'json'
2
+
1
3
  module Netzke
2
4
  module BaseExtras
3
5
  module Interface
@@ -17,29 +17,35 @@ module Netzke
17
17
  # The config that is sent from the server and is used for instantiating a widget
18
18
  def js_config
19
19
  res = {}
20
+
21
+ # Unique id of the widget
22
+ res.merge!(:id => @id_name)
20
23
 
21
- # recursively include configs of all (non-late) aggregatees, so that the widget can instantiate them
24
+ # Recursively include configs of all non-late aggregatees, so that the widget can instantiate them in
25
+ # in the browser immediately.
22
26
  aggregatees.each_pair do |aggr_name, aggr_config|
23
27
  next if aggr_config[:late_aggregation]
24
28
  res["#{aggr_name}_config".to_sym] = aggregatee_instance(aggr_name.to_sym).js_config
25
29
  end
26
30
 
27
- # interface
31
+ # Interface
28
32
  interface = self.class.interface_points.inject({}){|h,interfacep| h.merge(interfacep => widget_action(interfacep))}
29
33
  res.merge!(:interface => interface)
30
34
 
35
+ # Widget class name
31
36
  res.merge!(:widget_class_name => short_widget_class_name)
32
-
37
+
38
+ # Include
33
39
  res.merge!(js_ext_config)
34
- res.merge!(:id => @id_name)
35
40
 
36
- # include tools and actions
37
- res.merge!(:tools => tools) if tools
38
- res.merge!(:actions => actions) if actions
39
- res.merge!(:bbar => tbar) if tbar
40
- res.merge!(:tbar => bbar) if bbar
41
+ # Actions, toolbars and menus
42
+ tools && res.merge!(:tools => tools)
43
+ actions && res.merge!(:actions => actions)
44
+ tbar && res.merge!(:tbar => tbar)
45
+ bbar && res.merge!(:bbar => bbar)
46
+ menu && res.merge!(:menu => menu)
41
47
 
42
- # include permissions
48
+ # Permissions
43
49
  res.merge!(:permissions => permissions) unless available_permissions.empty?
44
50
 
45
51
  res
@@ -69,7 +75,7 @@ module Netzke
69
75
 
70
76
  # instantiating
71
77
  def js_widget_instance
72
- %Q{var #{config[:name].to_js} = new Ext.netzke.cache['#{short_widget_class_name}'](#{js_config.to_js});}
78
+ %Q{var #{config[:name].to_js} = new Ext.netzke.cache.#{short_widget_class_name}(#{js_config.to_js});}
73
79
  end
74
80
 
75
81
  # rendering
@@ -88,9 +94,10 @@ module Netzke
88
94
 
89
95
  # widget's actions, tools and toolbars that are loaded at the moment of instantiating a widget
90
96
  def actions; nil; end
91
- def tools; nil; end
92
97
  def tbar; nil; end
93
98
  def bbar; nil; end
99
+ def menu; nil; end
100
+ def tools; nil; end
94
101
 
95
102
  # little helpers
96
103
  def this; "this".l; end
@@ -150,50 +157,97 @@ module Netzke
150
157
  def js_class
151
158
  if js_inheritance
152
159
  <<-JS
153
- Ext.netzke.cache['#{short_widget_class_name}'] = Ext.extend(Ext.netzke.cache.#{js_base_class.short_widget_class_name}, Ext.chainApply([Ext.widgetMixIn, {
154
- constructor: function(config){
155
- Ext.netzke.cache['#{short_widget_class_name}'].superclass.constructor.call(this, config);
156
- }
157
- }, #{js_extend_properties.to_js}]))
160
+ Ext.netzke.cache.#{short_widget_class_name} = function(config){
161
+ Ext.netzke.cache.#{short_widget_class_name}.superclass.constructor.call(this, config);
162
+ };
163
+ Ext.extend(Ext.netzke.cache.#{short_widget_class_name}, Ext.netzke.cache.#{js_base_class.short_widget_class_name}, Ext.applyIf(#{js_extend_properties.to_js}, Ext.widgetMixIn));
158
164
 
159
165
  JS
160
166
  else
161
167
  js_add_menus = "this.addMenus(#{js_menus.to_js});" unless js_menus.empty?
162
168
  <<-JS
163
- Ext.netzke.cache['#{short_widget_class_name}'] = Ext.extend(#{js_base_class}, Ext.chainApply([Ext.widgetMixIn, {
164
- constructor: function(config){
165
- // comment
169
+ Ext.netzke.cache.#{short_widget_class_name} = function(config){
170
+ this.beforeConstructor(config);
166
171
  #{js_before_constructor}
167
- Ext.netzke.cache['#{short_widget_class_name}'].superclass.constructor.call(this, Ext.apply(#{js_default_config.to_js}, config));
168
- this.widgetInit(config);
172
+ Ext.netzke.cache.#{short_widget_class_name}.superclass.constructor.call(this, Ext.apply(#{js_default_config.to_js}, config));
173
+ this.afterConstructor(config);
169
174
  #{js_after_constructor}
170
175
  #{js_add_menus}
171
- }
172
- }, #{js_extend_properties.to_js}]))
176
+ };
177
+ Ext.extend(Ext.netzke.cache.#{short_widget_class_name}, #{js_base_class}, Ext.applyIf(#{js_extend_properties.to_js}, Ext.widgetMixIn));
173
178
  JS
174
179
  end
175
180
  end
176
181
 
182
+ #
183
+ # Include extra code from Ext js library (e.g. examples)
184
+ #
185
+ def ext_js_include(*args)
186
+ included_ext_js = read_inheritable_attribute(:included_ext_js) || []
187
+ args.each {|f| included_ext_js << f}
188
+ write_inheritable_attribute(:included_ext_js, included_ext_js)
189
+ end
190
+
191
+ #
192
+ # Include extra Javascript code. This code will be loaded along with the widget's class and in front of it.
193
+ #
194
+ # Example usage:
195
+ # js_include "File.dirname(__FILE__)/form_panel_extras/javascripts/xdatetime.js",
196
+ # :ext_examples => ["grid-filtering/menu/EditableItem.js", "grid-filtering/menu/RangeMenu.js"],
197
+ # "File.dirname(__FILE__)/form_panel_extras/javascripts/xcheckbox.js"
198
+ #
199
+ def js_include(*args)
200
+ included_js = read_inheritable_attribute(:included_js) || []
201
+ args.each do |inclusion|
202
+ if inclusion.is_a?(Hash)
203
+ # we are signalized a non-default file location (e.g. Ext examples)
204
+ case inclusion.keys.first
205
+ when :ext_examples
206
+ location = Netzke::Base.config[:ext_location] + "/examples/"
207
+ end
208
+ files = inclusion.values.first
209
+ else
210
+ location = ""
211
+ files = inclusion
212
+ end
213
+
214
+ files = [files] if files.is_a?(String)
215
+
216
+ for f in files
217
+ included_js << location + f
218
+ end
219
+ end
220
+ write_inheritable_attribute(:included_js, included_js)
221
+ end
222
+
177
223
  # returns all extra js-code (as string) required by this widget's class
178
224
  def js_included
179
225
  # from extjs - defined in the widget class with ext_js_include
180
- extjs_dir = "#{RAILS_ROOT}/public/extjs" # TODO: make extjs location configurable
181
- included_ext_js = read_inheritable_attribute(:included_ext_js) || []
182
- res = included_ext_js.inject("") do |r, path|
183
- f = File.new("#{extjs_dir}/#{path}")
184
- r << f.read
185
- end
226
+ # extjs_dir = "#{RAILS_ROOT}/public/extjs" # TODO: make extjs location configurable
227
+ # begin
228
+ # res = super
229
+ # rescue
230
+ # raise self.superclass.to_s
231
+ # end
186
232
 
187
- res << "\n"
233
+ res = ""
188
234
 
189
- # from <widget_name>_extras/javascripts - all *.js files found there
190
- js_dir = File.join(File.dirname(widget_file), short_widget_class_name.underscore + "_extras", "javascripts")
191
- file_list = Dir.glob("#{js_dir}/*.js")
192
-
193
- for file_name in file_list
194
- f = File.new(file_name)
195
- res << f.read
235
+ included_js = read_inheritable_attribute(:included_js) || []
236
+ res << included_js.inject("") do |r, path|
237
+ f = File.new(path)
238
+ r << f.read
196
239
  end
240
+
241
+ # res << "\n"
242
+ #
243
+ # # from <widget_name>_extras/javascripts - all *.js files found there
244
+ # js_dir = File.join(File.dirname(widget_file), short_widget_class_name.underscore + "_extras", "javascripts")
245
+ # file_list = Dir.glob("#{js_dir}/*.js")
246
+ #
247
+ # for file_name in file_list
248
+ # f = File.new(file_name)
249
+ # res << f.read
250
+ # end
197
251
 
198
252
  res
199
253
  end
@@ -214,19 +268,19 @@ JS
214
268
  end
215
269
 
216
270
  # returns all css code require by this widget's class
217
- def css_included
218
- res = ""
219
- # from <widget_name>_extras/stylesheets - all *.css files found there
220
- js_dir = File.join(File.dirname(widget_file), short_widget_class_name.underscore + "_extras", "stylesheets")
221
- file_list = Dir.glob("#{js_dir}/*.css")
222
-
223
- for file_name in file_list
224
- f = File.new(file_name)
225
- res << f.read
226
- end
227
-
228
- res
229
- end
271
+ # def css_included
272
+ # res = ""
273
+ # # from <widget_name>_extras/stylesheets - all *.css files found there
274
+ # js_dir = File.join(File.dirname(widget_file), short_widget_class_name.underscore + "_extras", "stylesheets")
275
+ # file_list = Dir.glob("#{js_dir}/*.css")
276
+ #
277
+ # for file_name in file_list
278
+ # f = File.new(file_name)
279
+ # res << f.read
280
+ # end
281
+ #
282
+ # res
283
+ # end
230
284
 
231
285
  # all JS code needed for this class including the one from the ancestor widget
232
286
  def css_code(cached_dependencies = [])
@@ -235,7 +289,7 @@ JS
235
289
  # include the base-class javascript if doing JS inheritance
236
290
  res << js_base_class.css_code << "\n" if js_inheritance && !cached_dependencies.include?(js_base_class.short_widget_class_name)
237
291
 
238
- res << css_included << "\n"
292
+ # res << css_included << "\n"
239
293
 
240
294
  res
241
295
  end
@@ -117,7 +117,7 @@ module Netzke
117
117
  Ext.onReady(function(){
118
118
  <%= #{name}_widget_instance %>
119
119
  <%= #{name}_widget_render %>
120
- })
120
+ });
121
121
  </script>
122
122
  </head>
123
123
  <body>
@@ -12,7 +12,6 @@ class Hash
12
12
 
13
13
  # First camelizes the keys, then convert the whole hash to JSON
14
14
  def to_js
15
- # self.delete_if{ |k,v| v.nil? } # we don't need to explicitely pass null values to javascript
16
15
  self.recursive_delete_if_nil.convert_keys{|k| k.camelize(:lower)}.to_json
17
16
  end
18
17
 
@@ -21,6 +20,7 @@ class Hash
21
20
  self.each_pair{|k,v| self[k] = v.to_s if v.is_a?(Symbol)}
22
21
  end
23
22
 
23
+ # We don't need to pass null values in JSON, they are null by simply being absent
24
24
  def recursive_delete_if_nil
25
25
  self.inject({}) do |h,(k,v)|
26
26
  if !v.nil?
data/lib/netzke-core.rb CHANGED
@@ -1,5 +1,8 @@
1
+ require 'active_support'
2
+
1
3
  # NetzkeCore
2
4
  require 'netzke/base'
5
+
3
6
  require 'netzke/action_view_ext'
4
7
  require 'netzke/controller_extensions'
5
8
  require 'netzke/core_ext'
@@ -34,6 +37,7 @@ end
34
37
  # Make this plugin auto-reloadable for easier development
35
38
  ActiveSupport::Dependencies.load_once_paths.delete(File.join(File.dirname(__FILE__)))
36
39
 
37
- # Include javascript & styles
40
+ # Include javascript & styles required by all Netzke widgets.
41
+ # These files will get loaded at the initial load of the framework (along with Ext).
38
42
  Netzke::Base.config[:javascripts] << "#{File.dirname(__FILE__)}/../javascripts/core.js"
39
- Netzke::Base.config[:css] << "#{File.dirname(__FILE__)}/../css/core.css"
43
+ Netzke::Base.config[:stylesheets] << "#{File.dirname(__FILE__)}/../stylesheets/core.css"
data/netzke-core.gemspec CHANGED
@@ -2,15 +2,15 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{netzke-core}
5
- s.version = "0.2.8"
5
+ s.version = "0.2.9"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Sergei Kozlov"]
9
- s.date = %q{2009-03-03}
9
+ s.date = %q{2009-03-19}
10
10
  s.description = %q{Build ExtJS/Rails widgets with minimum effort}
11
11
  s.email = %q{sergei@writelesscode.com}
12
- s.extra_rdoc_files = ["CHANGELOG", "lib/app/controllers/netzke_controller.rb", "lib/app/models/netzke_layout.rb", "lib/app/models/netzke_preference.rb", "lib/netzke/action_view_ext.rb", "lib/netzke/base.rb", "lib/netzke/base_extras/interface.rb", "lib/netzke/base_extras/js_builder.rb", "lib/netzke/controller_extensions.rb", "lib/netzke/core_ext.rb", "lib/netzke/feedback_ghost.rb", "lib/netzke/routing.rb", "lib/netzke-core.rb", "lib/vendor/facets/hash/recursive_merge.rb", "LICENSE", "README.mdown", "tasks/netzke_core_tasks.rake"]
13
- s.files = ["CHANGELOG", "css/core.css", "generators/netzke_core/netzke_core_generator.rb", "generators/netzke_core/templates/create_netzke_layouts.rb", "generators/netzke_core/templates/create_netzke_preferences.rb", "generators/netzke_core/USAGE", "init.rb", "install.rb", "javascripts/core.js", "lib/app/controllers/netzke_controller.rb", "lib/app/models/netzke_layout.rb", "lib/app/models/netzke_preference.rb", "lib/netzke/action_view_ext.rb", "lib/netzke/base.rb", "lib/netzke/base_extras/interface.rb", "lib/netzke/base_extras/js_builder.rb", "lib/netzke/controller_extensions.rb", "lib/netzke/core_ext.rb", "lib/netzke/feedback_ghost.rb", "lib/netzke/routing.rb", "lib/netzke-core.rb", "lib/vendor/facets/hash/recursive_merge.rb", "LICENSE", "Manifest", "netzke-core.gemspec", "Rakefile", "README.mdown", "tasks/netzke_core_tasks.rake", "test/app_root/app/controllers/application.rb", "test/app_root/config/boot.rb", "test/app_root/config/database.yml", "test/app_root/config/environment.rb", "test/app_root/config/environments/in_memory.rb", "test/app_root/config/environments/mysql.rb", "test/app_root/config/environments/postgresql.rb", "test/app_root/config/environments/sqlite.rb", "test/app_root/config/environments/sqlite3.rb", "test/app_root/config/routes.rb", "test/app_root/db/migrate/20081222035855_create_netzke_preferences.rb", "test/app_root/script/console", "test/core_ext_test.rb", "test/netzke_core_test.rb", "test/netzke_preference_test.rb", "test/test_helper.rb", "uninstall.rb"]
12
+ s.extra_rdoc_files = ["CHANGELOG", "lib/app/controllers/netzke_controller.rb", "lib/app/models/netzke_layout.rb", "lib/app/models/netzke_preference.rb", "lib/netzke/action_view_ext.rb", "lib/netzke/base.rb", "lib/netzke/base_extras/interface.rb", "lib/netzke/base_extras/js_builder.rb", "lib/netzke/controller_extensions.rb", "lib/netzke/core_ext.rb", "lib/netzke/feedback_ghost.rb", "lib/netzke/routing.rb", "lib/netzke-core.rb", "lib/vendor/facets/hash/recursive_merge.rb", "LICENSE", "README.mdown", "tasks/netzke_core_tasks.rake", "TODO"]
13
+ s.files = ["CHANGELOG", "generators/netzke_core/netzke_core_generator.rb", "generators/netzke_core/templates/create_netzke_layouts.rb", "generators/netzke_core/templates/create_netzke_preferences.rb", "generators/netzke_core/USAGE", "init.rb", "install.rb", "javascripts/core.js", "lib/app/controllers/netzke_controller.rb", "lib/app/models/netzke_layout.rb", "lib/app/models/netzke_preference.rb", "lib/netzke/action_view_ext.rb", "lib/netzke/base.rb", "lib/netzke/base_extras/interface.rb", "lib/netzke/base_extras/js_builder.rb", "lib/netzke/controller_extensions.rb", "lib/netzke/core_ext.rb", "lib/netzke/feedback_ghost.rb", "lib/netzke/routing.rb", "lib/netzke-core.rb", "lib/vendor/facets/hash/recursive_merge.rb", "LICENSE", "Manifest", "netzke-core.gemspec", "Rakefile", "README.mdown", "stylesheets/core.css", "tasks/netzke_core_tasks.rake", "test/app_root/app/controllers/application.rb", "test/app_root/config/boot.rb", "test/app_root/config/database.yml", "test/app_root/config/environment.rb", "test/app_root/config/environments/in_memory.rb", "test/app_root/config/environments/mysql.rb", "test/app_root/config/environments/postgresql.rb", "test/app_root/config/environments/sqlite.rb", "test/app_root/config/environments/sqlite3.rb", "test/app_root/config/routes.rb", "test/app_root/db/migrate/20081222035855_create_netzke_preferences.rb", "test/app_root/script/console", "test/core_ext_test.rb", "test/netzke_core_test.rb", "test/netzke_preference_test.rb", "test/test_helper.rb", "TODO", "uninstall.rb"]
14
14
  s.has_rdoc = true
15
15
  s.homepage = %q{http://writelesscode.com}
16
16
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Netzke-core", "--main", "README.mdown"]
@@ -1,3 +1,7 @@
1
+ /*
2
+ This file gets loaded along with the rest of Ext library at the initial load
3
+ */
4
+
1
5
  /* feedback */
2
6
  .netzke-feedback {
3
7
  position:fixed;
@@ -160,8 +160,8 @@ class NetzkeCoreTest < ActiveSupport::TestCase
160
160
 
161
161
  test "js inheritance" do
162
162
  widget = JsInheritanceWidget.new
163
- assert(widget.js_missing_code.index("Ext.netzke.cache['JsInheritanceWidget']"))
164
- assert(widget.js_missing_code.index("Ext.netzke.cache['Widget']"))
163
+ assert(widget.js_missing_code.index("Ext.netzke.cache.JsInheritanceWidget"))
164
+ assert(widget.js_missing_code.index("Ext.netzke.cache.Widget"))
165
165
  end
166
166
  # test "multiuser" do
167
167
  # Netzke::Base.current_user = User.new(1)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: netzke-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ version: 0.2.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergei Kozlov
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-03-03 00:00:00 +01:00
12
+ date: 2009-03-19 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -37,9 +37,9 @@ extra_rdoc_files:
37
37
  - LICENSE
38
38
  - README.mdown
39
39
  - tasks/netzke_core_tasks.rake
40
+ - TODO
40
41
  files:
41
42
  - CHANGELOG
42
- - css/core.css
43
43
  - generators/netzke_core/netzke_core_generator.rb
44
44
  - generators/netzke_core/templates/create_netzke_layouts.rb
45
45
  - generators/netzke_core/templates/create_netzke_preferences.rb
@@ -65,6 +65,7 @@ files:
65
65
  - netzke-core.gemspec
66
66
  - Rakefile
67
67
  - README.mdown
68
+ - stylesheets/core.css
68
69
  - tasks/netzke_core_tasks.rake
69
70
  - test/app_root/app/controllers/application.rb
70
71
  - test/app_root/config/boot.rb
@@ -82,6 +83,7 @@ files:
82
83
  - test/netzke_core_test.rb
83
84
  - test/netzke_preference_test.rb
84
85
  - test/test_helper.rb
86
+ - TODO
85
87
  - uninstall.rb
86
88
  has_rdoc: true
87
89
  homepage: http://writelesscode.com