netzke-core 0.2.8 → 0.2.9

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