skozlov-netzke-core 0.1.0.2 → 0.4.1

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.
Files changed (48) hide show
  1. data/CHANGELOG +108 -0
  2. data/LICENSE +2 -19
  3. data/Manifest +50 -0
  4. data/README.rdoc +12 -0
  5. data/Rakefile +2 -3
  6. data/TODO +2 -0
  7. data/autotest/discover.rb +3 -0
  8. data/generators/netzke_core/netzke_core_generator.rb +6 -6
  9. data/generators/netzke_core/templates/create_netzke_preferences.rb +2 -2
  10. data/javascripts/core.js +474 -111
  11. data/lib/app/controllers/netzke_controller.rb +76 -0
  12. data/lib/app/models/netzke_preference.rb +128 -39
  13. data/lib/netzke-core.rb +23 -9
  14. data/lib/netzke/action_view_ext.rb +26 -0
  15. data/lib/netzke/base.rb +440 -102
  16. data/lib/netzke/base_js.rb +258 -0
  17. data/lib/netzke/controller_extensions.rb +80 -29
  18. data/lib/netzke/core_ext.rb +72 -21
  19. data/lib/netzke/feedback_ghost.rb +43 -0
  20. data/lib/netzke/routing.rb +9 -0
  21. data/netzke-core.gemspec +10 -11
  22. data/stylesheets/core.css +12 -0
  23. data/test/app_root/app/controllers/{application.rb → application_controller.rb} +0 -0
  24. data/test/app_root/app/models/role.rb +3 -0
  25. data/test/app_root/app/models/user.rb +3 -0
  26. data/test/app_root/config/boot.rb +2 -1
  27. data/test/app_root/config/database.yml +10 -0
  28. data/test/app_root/config/environment.rb +1 -0
  29. data/test/app_root/db/migrate/20081222035855_create_netzke_preferences.rb +18 -0
  30. data/test/app_root/db/migrate/20090423214303_create_roles.rb +11 -0
  31. data/test/app_root/db/migrate/20090423222114_create_users.rb +12 -0
  32. data/test/app_root/lib/console_with_fixtures.rb +4 -0
  33. data/test/app_root/script/console +1 -0
  34. data/test/fixtures/roles.yml +7 -0
  35. data/test/fixtures/users.yml +9 -0
  36. data/test/test_helper.rb +3 -2
  37. data/test/unit/core_ext_test.rb +66 -0
  38. data/test/unit/netzke_core_test.rb +167 -0
  39. data/test/unit/netzke_preference_test.rb +103 -0
  40. metadata +45 -30
  41. data/README.mdown +0 -11
  42. data/generators/netzke_core/templates/create_netzke_layouts.rb +0 -14
  43. data/generators/netzke_core/templates/netzke.html.erb +0 -10
  44. data/lib/app/models/netzke_layout.rb +0 -75
  45. data/lib/netzke/js_class_builder.rb +0 -114
  46. data/lib/vendor/facets/hash/recursive_merge.rb +0 -28
  47. data/test/core_ext_test.rb +0 -35
  48. data/test/netzke_core_test.rb +0 -136
data/CHANGELOG CHANGED
@@ -1,3 +1,111 @@
1
+ v0.4.1
2
+ 2009-09-06
3
+ Version bumb to force github rebuild the gem (Manifest is now included)
4
+
5
+ v0.4.0
6
+ 2009-09-05
7
+ Major refactoring.
8
+
9
+ v0.3.2
10
+ 2009-06-05
11
+ Netzke doesn't overwrite session[:user] anymore to not cause authentication-related problems.
12
+
13
+ v0.3.1
14
+ 2009-05-07
15
+ Fix: persistent_config_manager can now be set to nil, and it will work fine
16
+
17
+ v0.3.0
18
+ 2009-05-07
19
+ Refactor: got rid of NetzkeLayout model, now all layouts are stored in netzke_preferences
20
+ New: persistent_config now has a method for_widget that accepts a block
21
+ autotest compatibility
22
+ New: String#to_b converts a string to true/false
23
+ New: Netzke::Base.session introduced for session data
24
+ New: weak_children_config and strong_children_config can now be declared by a widget, which specifies weak and strong configuration that every child of this widget will receive (e.g. display/hide configuration tool)
25
+ Fix: (degradation) flash message is now shown again in case of erroneous attempt to load a widget
26
+ New: widgets now can check session[:netzke_just_logged_in] and session[:netzke_just_logged_out] automatically set by Netzke after login/logout
27
+
28
+ v0.2.11
29
+ Introduction of getOwnerWidget()-method to Ext.Component. It provides the Netzke widget this Component belongs to.
30
+
31
+ v0.2.10
32
+ Removed dependency on 'json' gem.
33
+ Rails v2.3.2 compatibility.
34
+
35
+ v0.2.9
36
+ Actions, toolbars and tools reworked for easier configuration.
37
+ Menus introduced (based on actions).
38
+ Significant code clean-up.
39
+ Bug fix (nasty one): Ext.widgetMixIn was getting messed up along with dynamic widget loading.
40
+ Must work in IE now.
41
+
42
+ v0.2.8
43
+ Support for extra javascripts and stylesheets per widget.
44
+
45
+ v0.2.7
46
+ QuickTips get initialized now, as otherwise Ext 2.2.1 doesn't properly destroy() BoxComponents for me.
47
+
48
+ v0.2.6
49
+ FeedackGhost is now capable of displaying multiple flash messages.
50
+ Dependencies slightly refactored.
51
+ An informative exception added to Base#aggregatee_instance.
52
+ JS-level inheritance enabled.
53
+ Work-around for the problem with Ext 2.2.1 in loadWidget.
54
+ Events "<action_id>click" added to the widgets along with the actions.
55
+ aggregatee_missing method added to Netzke::Base - called when a non-existing aggregate of a widget is tried to be invoked
56
+ Code readability improvements.
57
+
58
+ v0.2.5
59
+ Minor code restructuring.
60
+
61
+ v0.2.4
62
+ Some minor improvements.
63
+
64
+ v0.2.3
65
+ FeedbackGhost will show the feedback on the top of the screen independent of the page scrolling.
66
+ Ext.Panel#loadWidget will accept null as url to delete the currently loaded widget
67
+ Bug fix: persistent_config works again
68
+
69
+ v0.2.2
70
+ js_ext_config instance method added for overwriting
71
+ Multiuser support
72
+ Using Rails.logger for logging
73
+ "config"-class method for every class inheriting Netzke::Base - for class-level configurations
74
+
75
+ v0.2.1
76
+ Fixed the path to ext-base-min.js for production mode.
77
+ Also works in Safari now.
78
+
79
+ v0.2.0
80
+ * Some re-factoring and redesign. Now simple compound widgets can be created on the fly in the controller
81
+ * Added ext_widget[:quiet] configuration option to suppress widget's feedback
82
+ * Support for extra CSS sources, similar to JS
83
+ * NETZKE_BOOT_CONFIG introduced to specify which Netzke functionality should be disabled to reduce the size of /netzke/netzke.[js|css]
84
+ * FeedbackGhost widget added - invisible widget providing feedback to the user
85
+ * netzke_widget controller class-method renamed into netzke
86
+ * JS-comments now get stripped also from the extra files that get included in the netzke-* gems.
87
+ * Permissions joined js_config
88
+ * Bug fixes
89
+
90
+ v0.1.4
91
+ Helpers added to facilitate ExtJS/netzke.js inclusion
92
+ The route defined for netzke_controller
93
+ netzke.html.erb-layout is not needed anymore, so not produced by the generator
94
+ Now compliant with Rails' forgery protection
95
+
96
+ v0.1.3
97
+ Generators fixed
98
+
99
+ v0.1.2
100
+ Fixed the bug with <widget>_class_definition returning empty string on sequential loading.
101
+
102
+ v0.1.1.1
103
+ Meta: moving from GitHub to RubyForge
104
+
105
+ v0.1.1
106
+ Inter-widget dependencies code reworked
107
+ JS-class code generation code slightly reworked
108
+
1
109
  v0.1.0.2
2
110
  Meta: fix outdated Manifest
3
111
 
data/LICENSE CHANGED
@@ -1,20 +1,3 @@
1
- Copyright (c) 2008 Sergei Kozlov
1
+ Copyright (c) 2008-2009 Sergei Kozlov
2
2
 
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
10
-
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
13
-
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3
+ GNU GPL license v3
@@ -0,0 +1,50 @@
1
+ CHANGELOG
2
+ LICENSE
3
+ Manifest
4
+ README.rdoc
5
+ Rakefile
6
+ TODO
7
+ autotest/discover.rb
8
+ generators/netzke_core/USAGE
9
+ generators/netzke_core/netzke_core_generator.rb
10
+ generators/netzke_core/templates/create_netzke_preferences.rb
11
+ init.rb
12
+ install.rb
13
+ javascripts/core.js
14
+ lib/app/controllers/netzke_controller.rb
15
+ lib/app/models/netzke_preference.rb
16
+ lib/netzke-core.rb
17
+ lib/netzke/action_view_ext.rb
18
+ lib/netzke/base.rb
19
+ lib/netzke/base_js.rb
20
+ lib/netzke/controller_extensions.rb
21
+ lib/netzke/core_ext.rb
22
+ lib/netzke/feedback_ghost.rb
23
+ lib/netzke/routing.rb
24
+ netzke-core.gemspec
25
+ stylesheets/core.css
26
+ tasks/netzke_core_tasks.rake
27
+ test/app_root/app/controllers/application_controller.rb
28
+ test/app_root/app/models/role.rb
29
+ test/app_root/app/models/user.rb
30
+ test/app_root/config/boot.rb
31
+ test/app_root/config/database.yml
32
+ test/app_root/config/environment.rb
33
+ test/app_root/config/environments/in_memory.rb
34
+ test/app_root/config/environments/mysql.rb
35
+ test/app_root/config/environments/postgresql.rb
36
+ test/app_root/config/environments/sqlite.rb
37
+ test/app_root/config/environments/sqlite3.rb
38
+ test/app_root/config/routes.rb
39
+ test/app_root/db/migrate/20081222035855_create_netzke_preferences.rb
40
+ test/app_root/db/migrate/20090423214303_create_roles.rb
41
+ test/app_root/db/migrate/20090423222114_create_users.rb
42
+ test/app_root/lib/console_with_fixtures.rb
43
+ test/app_root/script/console
44
+ test/fixtures/roles.yml
45
+ test/fixtures/users.yml
46
+ test/test_helper.rb
47
+ test/unit/core_ext_test.rb
48
+ test/unit/netzke_core_test.rb
49
+ test/unit/netzke_preference_test.rb
50
+ uninstall.rb
@@ -0,0 +1,12 @@
1
+ = netzke-core
2
+ Create Ext JS + Rails reusable components (widgets) with minimum effort.
3
+
4
+ Introduction to Netzke framework: http://github.com/skozlov/netzke/tree/master
5
+
6
+ Tutorials: http://blog.writelesscode.com
7
+
8
+ Live-demo: http://netzke-demo.writelesscode.com
9
+
10
+ Also see netzke-basepack (pre-programmed widgets) project: http://github.com/skozlov/netzke-basepack/tree/master
11
+
12
+ Copyright (c) 2009 Sergei Kozlov, released under the MIT license
data/Rakefile CHANGED
@@ -2,10 +2,9 @@ require 'echoe'
2
2
 
3
3
  Echoe.new("netzke-core") do |p|
4
4
  p.author = "Sergei Kozlov"
5
- p.email = "sergei@writelesscode.com"
5
+ p.email = "sergei@playcode.nl"
6
6
  p.summary = "Build ExtJS/Rails widgets with minimum effort"
7
- p.url = "http://writelesscode.com"
8
- # p.runtime_dependencies = ["searchlogic >=1.6.2"]
7
+ p.url = "http://playcode.nl"
9
8
  p.development_dependencies = []
10
9
  p.test_pattern = 'test/**/*_test.rb'
11
10
  p.retain_gemspec = true
data/TODO ADDED
@@ -0,0 +1,2 @@
1
+ * Re-factor JS-level inheritance mechanisms (2009-07-5 why?)
2
+ * Get rid of the default_config method, because the same functionality maybe achieved by overwriting the initialize method
@@ -0,0 +1,3 @@
1
+ Autotest.add_discovery do
2
+ "rails"
3
+ end
@@ -2,12 +2,12 @@
2
2
  class NetzkeCoreGenerator < Rails::Generator::Base
3
3
  def manifest
4
4
  record do |m|
5
- m.directory "public/javascripts/netzke"
6
- # m.file 'netzke.js', "public/javascripts/netzke/netzke.js"
7
- m.file 'netzke.html.erb', "app/views/layouts/netzke.html.erb"
8
- m.migration_template 'create_netzke_preferences.rb', "db/migrate", {:migration_file_name => "create_netzke_preferences"}
9
- # FIXME: how do we avoid getting the same migration IDs?
10
- m.migration_template 'create_netzke_layouts.rb', "db/migrate", {:migration_file_name => "create_netzke_layouts"}
5
+ # FIXME: how do we avoid getting the same migration timestamps?
6
+ # Work-around
7
+ time = Time.now.utc.strftime("%Y%m%d%H%M%S")
8
+ m.directory 'db/migrate'
9
+ # m.file 'create_netzke_layouts.rb', "db/migrate/#{time}_create_netzke_layouts.rb"
10
+ m.file 'create_netzke_preferences.rb', "db/migrate/#{time.to_i+1}_create_netzke_preferences.rb"
11
11
  end
12
12
  end
13
13
  end
@@ -3,10 +3,10 @@ class CreateNetzkePreferences < ActiveRecord::Migration
3
3
  create_table :netzke_preferences do |t|
4
4
  t.string :name
5
5
  t.string :pref_type
6
- t.string :value
6
+ t.string :value, :limit => 65535
7
7
  t.integer :user_id
8
8
  t.integer :role_id
9
- t.string :custom_field
9
+ t.string :widget_name
10
10
 
11
11
  t.timestamps
12
12
  end
@@ -1,124 +1,487 @@
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
- Ext.componentCache = {};
6
+ Ext.namespace('Ext.netzke'); // namespace for extensions that depend on Ext
7
+ Ext.namespace('Netzke'); // namespace for extensions that do not depend on Ext
8
+ Ext.netzke.cache = {};
9
+
10
+ Ext.QuickTips.init(); // seems obligatory in Ext v2.2.1, otherwise Ext.Component#destroy() stops working properly
11
+
12
+ // To comply with Rails' forgery protection
13
+ Ext.Ajax.extraParams = {
14
+ authenticity_token : Ext.authenticityToken
15
+ };
16
+
17
+ // Type detection functions
18
+ Netzke.isObject = function(o) {
19
+ return (o != null && typeof o == "object" && o.constructor.toString() == Object.toString());
20
+ }
21
+
22
+ // Some Ruby-ish String extensions
23
+ // from http://code.google.com/p/inflection-js/
24
+ String.prototype.camelize=function(lowFirstLetter)
25
+ {
26
+ var str=this.toLowerCase();
27
+ var str_path=str.split('/');
28
+ for(var i=0;i<str_path.length;i++)
29
+ {
30
+ var str_arr=str_path[i].split('_');
31
+ var initX=((lowFirstLetter&&i+1==str_path.length)?(1):(0));
32
+ for(var x=initX;x<str_arr.length;x++)
33
+ str_arr[x]=str_arr[x].charAt(0).toUpperCase()+str_arr[x].substring(1);
34
+ str_path[i]=str_arr.join('');
35
+ }
36
+ str=str_path.join('::');
37
+ return str;
38
+ };
3
39
 
4
- Ext.namespace('Ext.netzke');
40
+ String.prototype.capitalize=function()
41
+ {
42
+ var str=this.toLowerCase();
43
+ str=str.substring(0,1).toUpperCase()+str.substring(1);
44
+ return str;
45
+ };
5
46
 
6
- // helper method to do multiple Ext.apply's
7
- Ext.chainApply = function(objectArray){
8
- var res = {};
9
- Ext.each(objectArray, function(obj){Ext.apply(res, obj)});
10
- return res;
47
+ String.prototype.humanize=function(lowFirstLetter)
48
+ {
49
+ var str=this.toLowerCase();
50
+ str=str.replace(new RegExp('_id','g'),'');
51
+ str=str.replace(new RegExp('_','g'),' ');
52
+ if(!lowFirstLetter)str=str.capitalize();
53
+ return str;
11
54
  };
12
55
 
13
- // implementation of totalProperty, successProperty and root configuration options for ArrayReader
56
+ // Implementation of totalProperty, successProperty and root configuration options for ArrayReader
14
57
  Ext.data.ArrayReader = Ext.extend(Ext.data.JsonReader, {
15
- readRecords : function(o){
16
- var sid = this.meta ? this.meta.id : null;
17
- var recordType = this.recordType, fields = recordType.prototype.fields;
18
- var records = [];
19
- // console.info(this.meta);
20
- var root = o[this.meta.root] || o, totalRecords = o[this.meta.totalProperty], success = o[this.meta.successProperty];
21
- for(var i = 0; i < root.length; i++){
22
- var n = root[i];
23
- var values = {};
24
- var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25
- for(var j = 0, jlen = fields.length; j < jlen; j++){
26
- var f = fields.items[j];
27
- var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
28
- var v = n[k] !== undefined ? n[k] : f.defaultValue;
29
- v = f.convert(v, n);
30
- values[f.name] = v;
31
- }
32
- var record = new recordType(values, id);
33
- record.json = n;
34
- records[records.length] = record;
35
- }
36
- return {
37
- records : records,
38
- totalRecords : totalRecords,
39
- success : success
40
- };
58
+ readRecords : function(o){
59
+ var sid = this.meta ? this.meta.id : null;
60
+ var recordType = this.recordType, fields = recordType.prototype.fields;
61
+ var records = [];
62
+ var root = o[this.meta.root] || o, totalRecords = o[this.meta.totalProperty], success = o[this.meta.successProperty];
63
+ for(var i = 0; i < root.length; i++){
64
+ var n = root[i];
65
+ var values = {};
66
+ var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
67
+ for(var j = 0, jlen = fields.length; j < jlen; j++){
68
+ var f = fields.items[j];
69
+ var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
70
+ var v = n[k] !== undefined ? n[k] : f.defaultValue;
71
+ v = f.convert(v, n);
72
+ values[f.name] = v;
73
+ }
74
+ var record = new recordType(values, id);
75
+ record.json = n;
76
+ records[records.length] = record;
41
77
  }
78
+ return {
79
+ records : records,
80
+ totalRecords : totalRecords,
81
+ success : success
82
+ };
83
+ }
42
84
  });
43
85
 
44
- // Methods common to all widget classes
86
+ // Properties/methods common to all widget classes
45
87
  Ext.widgetMixIn = {
46
- widgetInit:function(config){
47
- this.app = Ext.getCmp('application');
48
- if (config.tools) Ext.each(config.tools, function(i){i.on.click = this[i.on.click].createDelegate(this)}, this);
49
- if (config.actions) Ext.each(config.actions, function(i){i.handler = this[i.handler].createDelegate(this);}, this);
50
- },
51
-
52
- setEvents: function(){
53
- this.on('beforedestroy', function(){
54
- // clean-up menus
55
- if (this.app && !!this.app.unhostMenus) {
56
- // alert('beforedestroy');
57
- this.app.unhostMenus(this)
58
- }
59
- }, this);
60
-
61
- this.on('render', this.onWidgetLoad, this);
62
- },
63
-
64
- feedback:function(msg){
65
- if (this.app && !!this.app.showFeedback) {
66
- this.app.showFeedback(msg)
67
- } else {
68
- // there's no application to show the feedback - so, we do it ourselves
69
- if (typeof msg == 'string'){
70
- alert(msg)
71
- } else {
72
- var compoundResponse = ""
73
- Ext.each(msg, function(m){
88
+ height: 400,
89
+ width: 800,
90
+ border: false,
91
+ is_netzke: true, // to distinguish Netzke components from regular Ext components
92
+
93
+ /*
94
+ Loads aggregatee into a container. Sends the widgets cache info to the server.
95
+ */
96
+ loadAggregatee: function(params){
97
+ // build the cached widget list
98
+ var cachedWidgetNames = [];
99
+ for (name in Ext.netzke.cache) {
100
+ cachedWidgetNames.push(name);
101
+ }
102
+
103
+ params.cache = Ext.encode(cachedWidgetNames);
104
+
105
+ // remember the passed callback
106
+ if (params.callback) {
107
+ this.callbackHash[params.id] = params.callback;
108
+ delete params.callback;
109
+ delete params.scope;
110
+ }
111
+
112
+ // visually disable the container while the widget is being loaded
113
+ // Ext.getCmp(params.container).disable();
114
+ Ext.getCmp(params.container).removeChild(); // simply cleanup the area, which speaks for itself
115
+
116
+ // remote api call
117
+ this.loadAggregateeWithCache(params);
118
+ },
119
+
120
+ /*
121
+ Called by the server as callback about loaded widget
122
+ */
123
+ widgetLoaded : function(params){
124
+ if (this.fireEvent('widgetload')) {
125
+ // Enable the container after the widget is succesfully loaded
126
+ // this.getChildWidget(params.id).ownerCt.enable();
127
+
128
+ // provide the callback to that widget that was loading the child, passing the child itself
129
+ if (this.callbackHash[params.id]) {
130
+ this.callbackHash[params.id].call(params.scope || this, this.getChildWidget(params.id));
131
+ delete this.callbackHash[params.id];
132
+ }
133
+ }
134
+ },
135
+
136
+ /*
137
+ Returns the parent widget
138
+ */
139
+ getParent: function(){
140
+ // simply cutting the last part of the id: some_parent__a_kid__a_great_kid => some_parent__a_kid
141
+ var idSplit = this.id.split("__");
142
+ idSplit.pop();
143
+ var parentId = idSplit.join("__");
144
+
145
+ return parentId === "" ? null : Ext.getCmp(parentId);
146
+ },
147
+
148
+ /*
149
+ Reloads current widget (calls the parent to reload it as its aggregatee)
150
+ */
151
+ reload : function(){
152
+ var parent = this.getParent();
153
+ if (parent) {
154
+ parent.loadAggregatee({id:this.localId(parent), container:this.ownerCt.id});
155
+ } else {
156
+ window.location.reload();
157
+ }
158
+ },
159
+
160
+ /*
161
+ Gets id in the context of provided parent.
162
+ For example, the widgets "properties", being a child of "books" has global id "books__properties",
163
+ which *is* its widegt's real id. This methods, with the instance of "books" passed as parameter,
164
+ returns "properties".
165
+ */
166
+ localId : function(parent){
167
+ return this.id.replace(parent.id + "__", "");
168
+ },
169
+
170
+ /*
171
+ Instantiates and inserts a widget into a container with layout 'fit'.
172
+ Arg: an JS object with the following keys:
173
+ - id: id of the receiving container
174
+ - config: configuration of the widget to be instantiated and inserted into the container
175
+ */
176
+ renderWidgetInContainer : function(params){
177
+ var cont = Ext.getCmp(params.container);
178
+ cont.instantiateChild(params.config);
179
+ },
180
+
181
+ /*
182
+ Reconfigures the widget
183
+ */
184
+ reconfigure: function(config){
185
+ this.ownerCt.instantiateChild(config)
186
+ },
187
+
188
+ /*
189
+ Evaluates CSS
190
+ */
191
+ css : function(code){
192
+ var linkTag = document.createElement('style');
193
+ linkTag.type = 'text/css';
194
+ linkTag.innerHTML = code;
195
+ document.body.appendChild(linkTag);
196
+ },
197
+
198
+ /*
199
+ Evaluates JS
200
+ */
201
+ js : function(code){
202
+ eval(code);
203
+ },
204
+
205
+ /*
206
+ Executes a bunch of methods. This method is called almost every time a communication to the server takes place.
207
+ Thus the server side of a widget can provide any set of commands to its client side.
208
+ Args:
209
+ - instructions: array of methods, in the order of execution.
210
+ Each item is an object in one of the following 2 formats:
211
+ 1) {method1:args1, method2:args2}, where methodN is a name of a public method of this widget; these methods are called in no particular order
212
+ 2) {widget:widget_id, methods:arrayOfMethods}, used for recursive call to bulkExecute on some child widget
213
+
214
+ Example:
215
+ - [
216
+ // the same as this.feedback("Your order is accepted")
217
+ {feedback: "You order is accepted"},
218
+
219
+ // the same as this.getChildWidget('users').bulkExecute([{setTitle:'Suprise!'}, {setDisabled:true}])
220
+ {widget:'users', methods:[{setTitle:'Suprise!'}, {setDisabled:true}] },
221
+
222
+ // ... etc:
223
+ {updateStore:{records:[[1, 'Name1'],[2, 'Name2']], total:10}},
224
+ {setColums:[{},{}]},
225
+ {setMenus:[{},{}]},
226
+ ...
227
+ ]
228
+ */
229
+ bulkExecute : function(instructions){
230
+ if (Ext.isArray(instructions)) {
231
+ Ext.each(instructions, function(instruction){ this.bulkExecute(instruction)}, this);
232
+ } else {
233
+ for (var instr in instructions) {
234
+ if (this[instr]) {
235
+ this[instr].apply(this, [instructions[instr]]);
236
+ } else {
237
+ var childWidget = this.getChildWidget(instr);
238
+ if (childWidget) {
239
+ childWidget.bulkExecute(instructions[instr]);
240
+ } else {
241
+ throw "Unknown method or child widget '" + instr +"' in widget '" + this.id + "'"
242
+ }
243
+ }
244
+ }
245
+ }
246
+ },
247
+
248
+ // Get the child widget
249
+ getChildWidget : function(id){
250
+ return id === 'parent' ? this.getParent() : Ext.getCmp(this.id+"__"+id);
251
+ },
252
+
253
+ // Common handler for actions
254
+ actionHandler : function(action){
255
+ // If firing corresponding event doesn't return false, call the handler
256
+ if (this.fireEvent(action.name+'click', action)) {
257
+ this[(action.fn || action.name)](action);
258
+ }
259
+ },
260
+
261
+ // Common handler for tools
262
+ toolActionHandler : function(tool){
263
+ // If firing corresponding event doesn't return false, call the handler
264
+ if (this.fireEvent(tool.id+'click')) {
265
+ this[tool]();
266
+ }
267
+ },
268
+
269
+ // Does the call to the server and processes the response
270
+ callServer : function(intp, params, callback, scope){
271
+ if (!params) params = {};
272
+ Ext.Ajax.request({
273
+ params : params,
274
+ url : this.id + "__" + intp,
275
+ callback : function(options, success, response){
276
+ if (success) {
277
+ // execute commands from server
278
+ this.bulkExecute(Ext.decode(response.responseText));
279
+
280
+ // provade callback if needed
281
+ if (typeof callback == 'function') {
282
+ if (!scope) scope = this;
283
+ callback.apply(scope);
284
+ }
285
+ }
286
+ },
287
+ scope : this
288
+ });
289
+ },
290
+
291
+ /* Parse the bbar and tbar (both Arrays), replacing the strings with the corresponding methods. For example:
292
+ replaceStringsWithActions( ['add', {text:'Menu', menu:['edit', 'delete']}] )
293
+ => [scope.actions['add'], {text:'Menu', menu:[scope.actions['edit'], scope.actions['delete']]}]
294
+ */
295
+ normalizeMenuItems: function(arry, scope){
296
+ var res = []; // new array
297
+ Ext.each(arry, function(o){
298
+ if (typeof o === "string") {
299
+ var camelized = o.camelize(true);
300
+ if (scope.actions[camelized]){
301
+ res.push(scope.actions[camelized]);
302
+ } else {
303
+ // if there's no action with this name, maybe it's a separator or something
304
+ res.push(o);
305
+ }
306
+ } else if (Netzke.isObject(o)) {
307
+ // look inside the objects...
308
+ for (var key in o) {
309
+ if (Ext.isArray(o[key])) {
310
+ // ... and recursively process inner arrays found
311
+ o[key] = this.normalizeMenuItems(o[key], scope);
312
+ }
313
+ }
314
+ res.push(o);
315
+ }
316
+ }, this);
317
+ return res;
318
+ },
319
+
320
+
321
+ // Every Netzke widget
322
+ commonBeforeConstructor : function(config){
323
+ this.actions = {};
324
+
325
+ // Generate methods for api points
326
+ if (!config.api) { config.api = []; }
327
+ config.api.push('load_aggregatee_with_cache'); // all netzke widgets get this API
328
+ Ext.each(config.api, function(intp){
329
+ this[intp.camelize(true)] = function(args, callback, scope){ this.callServer(intp, args, callback, scope); }
330
+ }, this);
331
+
332
+ // Create Ext.Actions based on config.actions
333
+ if (config.actions) {
334
+ this.testActions = {};
335
+ for (var name in config.actions) {
336
+ // Create an event for each action (so that higher-level widgets could interfere)
337
+ this.addEvents(name+'click');
338
+
339
+ // Configure the action
340
+ var actionConfig = config.actions[name];
341
+ actionConfig.handler = this.actionHandler.createDelegate(this);
342
+ actionConfig.name = name;
343
+ this.actions[name] = new Ext.Action(actionConfig);
344
+ }
345
+
346
+ config.bbar = config.bbar && this.normalizeMenuItems(config.bbar, this);
347
+ config.tbar = config.tbar && this.normalizeMenuItems(config.tbar, this);
348
+ config.menu = config.menu && this.normalizeMenuItems(config.menu, this);
349
+ config.contextMenu = config.contextMenu && this.normalizeMenuItems(config.contextMenu, this);
350
+
351
+ // TODO: need to rethink this action related stuff
352
+ config.actions = this.actions;
353
+
354
+ }
355
+
356
+ // Normalize tools
357
+ if (config.tools) {
358
+ var normTools = [];
359
+ Ext.each(config.tools, function(tool){
360
+ // Create an event for each action (so that higher-level widgets could interfere)
361
+ this.addEvents(tool.id+'click');
362
+
363
+ var handler = this.toolActionHandler.createDelegate(this, [tool]);
364
+ normTools.push({id : tool, handler : handler, scope : this});
365
+ }, this);
366
+ config.tools = normTools;
367
+ }
368
+
369
+ // Set title
370
+ if (!config.title) config.title = config.id.humanize();
371
+ },
372
+
373
+ // At this moment component is fully initializied
374
+ commonAfterConstructor : function(config){
375
+ // From everywhere accessible FeedbackGhost
376
+ this.feedbackGhost = Ext.getCmp('feedback_ghost');
377
+
378
+ // Add the menus
379
+ if (this.initialConfig.menu) {this.addMenu(this.initialConfig.menu, this);}
380
+
381
+ // generic events
382
+ this.addEvents(
383
+ 'widgetload' // fired when a child is dynamically loaded
384
+ );
385
+
386
+ // Cleaning up on destroy
387
+ this.on('beforedestroy', function(){
388
+ this.cleanUpMenu();
389
+ }, this);
390
+
391
+ this.callbackHash = {};
392
+
393
+ if (this.afterConstructor) this.afterConstructor(config);
394
+ },
395
+
396
+ feedback:function(msg){
397
+ if (this.initialConfig && this.initialConfig.quiet) {
398
+ return false;
399
+ }
400
+
401
+ if (this.feedbackGhost) {
402
+ this.feedbackGhost.showFeedback(msg);
403
+ } else {
404
+ // there's no application to show the feedback - so, we do it ourselves
405
+ if (typeof msg == 'string'){
406
+ alert(msg);
407
+ } else {
408
+ var compoundResponse = "";
409
+ Ext.each(msg, function(m){
74
410
  compoundResponse += m.msg + "\n"
75
- })
76
- if (compoundResponse != "") alert(compoundResponse);
77
- }
78
- };
79
- },
80
-
81
- addMenus:function(menus){
82
- if (this.app && !!this.app.hostMenu) {
83
- Ext.each(menus, function(menu){this.app.hostMenu(menu, this)}, this)
84
- }
85
- },
86
-
87
- onWidgetLoad:Ext.emptyFn // gets overridden
411
+ });
412
+ if (compoundResponse != "") {
413
+ alert(compoundResponse);
414
+ }
415
+ }
416
+ }
417
+ },
418
+
419
+ addMenu : function(menu, owner){
420
+ if (!owner) {
421
+ owner = this;
422
+ }
423
+
424
+ if (!!this.hostMenu) {
425
+ this.hostMenu(menu, owner);
426
+ } else {
427
+ if (this.ownerWidget) {
428
+ this.ownerWidget.addMenu(menu, owner);
429
+ }
430
+ }
431
+ },
432
+
433
+ cleanUpMenu : function(owner){
434
+ if (!owner) {
435
+ owner = this;
436
+ }
437
+
438
+ if (!!this.unhostMenu) {
439
+ this.unhostMenu(owner);
440
+ } else {
441
+ if (this.ownerWidget) {
442
+ this.ownerWidget.cleanUpMenu(owner);
443
+ }
444
+ }
445
+ },
446
+
447
+ onWidgetLoad:Ext.emptyFn // gets overridden
88
448
  };
89
449
 
90
- // Make Panel with layout 'fit' capable to dynamically load widgets
91
- Ext.override(Ext.Panel, {
92
- getWidget: function(){
93
- return this.items.get(0)
94
- },
95
-
96
- loadWidget: function(url, params){
97
- if (!params) params = {}
98
-
99
- this.remove(this.getWidget()); // first delete previous widget
100
-
101
- // we will let the server know which components we have cached
102
- var cachedComponentNames = [];
103
- for (name in Ext.componentCache) {
104
- cachedComponentNames.push(name);
105
- }
106
-
107
- this.disable(); // to visually emphasize loading
108
-
109
- Ext.Ajax.request(
110
- {url:url, params:Ext.apply(params, {components_cache:Ext.encode(cachedComponentNames)}), script:false, callback:function(panel, success, response){
111
- var response = Ext.decode(response.responseText);
112
- if (response['classDefinition']) eval(response['classDefinition']); // evaluate widget's class if it was sent
113
-
114
- response.config.parent = this // we might want to know the parent panel in advance (e.g. to know its size)
115
- var instance = new Ext.componentCache[response.config.widgetClassName](response.config)
116
-
117
- this.add(instance);
118
- this.doLayout();
119
- this.enable();
120
- }, scope:this}
121
- )
122
-
123
- }
124
- });
450
+ // Netzke extensions for Ext.Container
451
+ Ext.override(Ext.Container, {
452
+ /**
453
+ Get Netzke widget that this Ext.Container is part of (*not* the parent widget, for which call getParent)
454
+ It searches up the Ext.Container hierarchy until it finds a Container that has isNetzke property set to true
455
+ (or until it reaches the top).
456
+ */
457
+ getOwnerWidget : function(){
458
+ if (this.initialConfig.isNetzke) {
459
+ return this;
460
+ } else {
461
+ if (this.ownerCt){
462
+ return this.ownerCt.getOwnerWidget()
463
+ } else {
464
+ return null
465
+ }
466
+ }
467
+ },
468
+
469
+ // Get the widget that we are hosting
470
+ getWidget: function(){
471
+ return this.items ? this.items.get(0) : null; // need this check in case when the container is not yet rendered, like an inactive tab in the TabPanel
472
+ },
473
+
474
+ removeChild : function(){
475
+ this.remove(this.getWidget());
476
+ },
477
+
478
+ instantiateChild : function(config){
479
+ this.remove(this.getWidget()); // first delete previous widget
480
+
481
+ if (!config) return false; // simply remove current widget if null is passed
482
+
483
+ var instance = new Ext.netzke.cache[config.widgetClassName](config);
484
+ this.add(instance);
485
+ this.doLayout();
486
+ }
487
+ });