netzke-core 0.1.1.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.
- data/CHANGELOG +13 -0
- data/LICENSE +20 -0
- data/Manifest +39 -0
- data/README.mdown +11 -0
- data/Rakefile +14 -0
- data/generators/netzke_core/USAGE +8 -0
- data/generators/netzke_core/netzke_core_generator.rb +13 -0
- data/generators/netzke_core/templates/create_netzke_layouts.rb +14 -0
- data/generators/netzke_core/templates/create_netzke_preferences.rb +18 -0
- data/generators/netzke_core/templates/netzke.html.erb +10 -0
- data/init.rb +1 -0
- data/install.rb +1 -0
- data/javascripts/core.js +124 -0
- data/lib/app/controllers/netzke_controller.rb +16 -0
- data/lib/app/models/netzke_layout.rb +75 -0
- data/lib/app/models/netzke_preference.rb +66 -0
- data/lib/netzke-core.rb +28 -0
- data/lib/netzke/base.rb +210 -0
- data/lib/netzke/controller_extensions.rb +95 -0
- data/lib/netzke/core_ext.rb +77 -0
- data/lib/netzke/js_class_builder.rb +114 -0
- data/lib/vendor/facets/hash/recursive_merge.rb +28 -0
- data/netzke-core.gemspec +32 -0
- data/tasks/netzke_core_tasks.rake +4 -0
- data/test/app_root/app/controllers/application.rb +2 -0
- data/test/app_root/config/boot.rb +114 -0
- data/test/app_root/config/database.yml +21 -0
- data/test/app_root/config/environment.rb +13 -0
- data/test/app_root/config/environments/in_memory.rb +0 -0
- data/test/app_root/config/environments/mysql.rb +0 -0
- data/test/app_root/config/environments/postgresql.rb +0 -0
- data/test/app_root/config/environments/sqlite.rb +0 -0
- data/test/app_root/config/environments/sqlite3.rb +0 -0
- data/test/app_root/config/routes.rb +4 -0
- data/test/app_root/script/console +6 -0
- data/test/core_ext_test.rb +35 -0
- data/test/netzke_core_test.rb +133 -0
- data/test/test_helper.rb +20 -0
- data/uninstall.rb +1 -0
- metadata +109 -0
data/lib/netzke-core.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# NetzkeCore
|
2
|
+
require 'netzke/js_class_builder'
|
3
|
+
require 'netzke/base'
|
4
|
+
require 'netzke/core_ext'
|
5
|
+
require 'netzke/controller_extensions'
|
6
|
+
|
7
|
+
# Vendor
|
8
|
+
require 'vendor/facets/hash/recursive_merge'
|
9
|
+
|
10
|
+
|
11
|
+
%w{ models controllers }.each do |dir|
|
12
|
+
path = File.join(File.dirname(__FILE__), 'app', dir)
|
13
|
+
$LOAD_PATH << path
|
14
|
+
ActiveSupport::Dependencies.load_paths << path
|
15
|
+
ActiveSupport::Dependencies.load_once_paths.delete(path)
|
16
|
+
end
|
17
|
+
|
18
|
+
# raise 'test'
|
19
|
+
|
20
|
+
ActionController::Base.class_eval do
|
21
|
+
include Netzke::ControllerExtensions
|
22
|
+
end
|
23
|
+
|
24
|
+
# Make this plugin reloadable for easier development
|
25
|
+
ActiveSupport::Dependencies.load_once_paths.delete(File.join(File.dirname(__FILE__)))
|
26
|
+
|
27
|
+
# Include the javascript
|
28
|
+
Netzke::Base.config[:javascripts] << "#{File.dirname(__FILE__)}/../javascripts/core.js"
|
data/lib/netzke/base.rb
ADDED
@@ -0,0 +1,210 @@
|
|
1
|
+
require 'json'
|
2
|
+
module Netzke
|
3
|
+
class Base
|
4
|
+
# Global Netzke configuration
|
5
|
+
def self.config
|
6
|
+
@@config ||= {
|
7
|
+
:javascripts => ["#{File.dirname(__FILE__)}/../../javascripts/core.js"] # locations of javascript files (which automatically will be collected into one file and sent as netzke.js)
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.js_class_code(cached_dependencies = [])
|
12
|
+
self.new(:js_class => true).js_missing_code(cached_dependencies)
|
13
|
+
end
|
14
|
+
|
15
|
+
# Helper class to read/write from/to widget's persistent preferences. TODO: rework it.
|
16
|
+
class Config
|
17
|
+
def initialize(widget_name)
|
18
|
+
@widget_name = widget_name
|
19
|
+
end
|
20
|
+
def []=(k,v)
|
21
|
+
NetzkePreference.custom_field = @widget_name
|
22
|
+
NetzkePreference[k] = v
|
23
|
+
end
|
24
|
+
def [](k)
|
25
|
+
NetzkePreference.custom_field = @widget_name
|
26
|
+
NetzkePreference[k]
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# client-side code (generates JS-classes of the widgets)
|
31
|
+
include Netzke::JsClassBuilder
|
32
|
+
|
33
|
+
attr_accessor :config, :server_confg, :parent, :logger, :id_name, :permissions
|
34
|
+
attr_reader :pref
|
35
|
+
|
36
|
+
def initialize(config = {}, parent = nil)
|
37
|
+
@logger = Logger.new("debug.log")
|
38
|
+
@config = initial_config.recursive_merge(config)
|
39
|
+
@parent = parent
|
40
|
+
@id_name = parent.nil? ? config[:name].to_s : "#{parent.id_name}__#{config[:name]}"
|
41
|
+
|
42
|
+
@flash = []
|
43
|
+
@pref = Config.new(@id_name)
|
44
|
+
|
45
|
+
@config[:ext_config] ||= {} # configuration used to instantiate JS class
|
46
|
+
|
47
|
+
process_permissions_config
|
48
|
+
end
|
49
|
+
|
50
|
+
def initial_config
|
51
|
+
{}
|
52
|
+
end
|
53
|
+
|
54
|
+
# 'Netzke::Grid' => 'Grid'
|
55
|
+
def short_widget_class_name
|
56
|
+
self.class.short_widget_class_name
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.short_widget_class_name
|
60
|
+
name.split("::").last
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Use this class-method to declare connection points between client side of a widget and its server side. A method in a widget class with the same name will be (magically) called by the client-side of the widget. See Grid widget for an example
|
65
|
+
#
|
66
|
+
def self.interface(*interface_points)
|
67
|
+
interfacep = read_inheritable_attribute(:interface_points) || []
|
68
|
+
interface_points.each{|p| interfacep << p}
|
69
|
+
write_inheritable_attribute(:interface_points, interfacep)
|
70
|
+
|
71
|
+
interface_points.each do |interfacep|
|
72
|
+
module_eval <<-END, __FILE__, __LINE__
|
73
|
+
def interface_#{interfacep}(*args)
|
74
|
+
#{interfacep}(*args).to_js
|
75
|
+
end
|
76
|
+
# FIXME: commented out because otherwise ColumnOperations stop working
|
77
|
+
# def #{interfacep}(*args)
|
78
|
+
# flash :warning => "API point '#{interfacep}' is not implemented for widget '#{short_widget_class_name}'"
|
79
|
+
# {:flash => @flash}
|
80
|
+
# end
|
81
|
+
END
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.interface_points
|
86
|
+
read_inheritable_attribute(:interface_points)
|
87
|
+
end
|
88
|
+
|
89
|
+
def interface_points
|
90
|
+
self.class.interface_points
|
91
|
+
end
|
92
|
+
|
93
|
+
interface :get_widget # default
|
94
|
+
|
95
|
+
## Dependencies
|
96
|
+
def dependencies
|
97
|
+
@dependencies ||= begin
|
98
|
+
non_late_aggregatees_widget_classes = aggregatees.values.map{|v| v[:widget_class_name]}
|
99
|
+
(initial_dependencies + non_late_aggregatees_widget_classes).uniq
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# override this method if you need some extra dependencies, which are not the aggregatees
|
104
|
+
def initial_dependencies
|
105
|
+
[]
|
106
|
+
end
|
107
|
+
|
108
|
+
### Aggregation
|
109
|
+
def initial_aggregatees
|
110
|
+
{}
|
111
|
+
end
|
112
|
+
|
113
|
+
def aggregatees
|
114
|
+
@aggregatees ||= initial_aggregatees.merge(initial_late_aggregatees.each_pair{|k,v| v.merge!(:late_aggregation => true)})
|
115
|
+
end
|
116
|
+
|
117
|
+
def non_late_aggregatees
|
118
|
+
aggregatees.reject{|k,v| v[:late_aggregation]}
|
119
|
+
end
|
120
|
+
|
121
|
+
def add_aggregatee(aggr)
|
122
|
+
aggregatees.merge!(aggr)
|
123
|
+
end
|
124
|
+
|
125
|
+
# The difference between aggregatees and late aggregatees is the following: the former gets instantiated together with its aggregator and is normally instantly visible as a part of it. While a late aggregatee doesn't get instantiated along with its aggregator. Until it gets requested, it doesn't take any part in its aggregator's lifecycle. An example of late aggregatee could be a widget that is loaded by an application widget on user's request, or a preferences window that only gets instantiated when user wants to edit widget's preferences. An example of a normal aggregatee is any widget (like a grid) within a BorderLayout-based widget (i.e. aggregator) - it should get instantiated and shown along with its aggregator.
|
126
|
+
def initial_late_aggregatees
|
127
|
+
{}
|
128
|
+
end
|
129
|
+
|
130
|
+
def add_late_aggregatee(aggr)
|
131
|
+
aggregatees.merge!(aggr.merge(:late_aggregation => true))
|
132
|
+
end
|
133
|
+
|
134
|
+
# recursively instantiates an aggregatee based on its "path": e.g. if we have an aggregatee :aggr1 which in its turn has an aggregatee :aggr10, the path to the latter would be "aggr1__aggr10"
|
135
|
+
def aggregatee_instance(name)
|
136
|
+
aggregator = self
|
137
|
+
name.to_s.split('__').each do |aggr|
|
138
|
+
aggr = aggr.to_sym
|
139
|
+
# TODO: should we put all the classes under Netzke::-scope?
|
140
|
+
# widget_class = full_widget_class_name(aggregator.aggregatees[aggr][:widget_class_name]).constantize
|
141
|
+
widget_class = "Netzke::#{aggregator.aggregatees[aggr][:widget_class_name]}".constantize
|
142
|
+
aggregator = widget_class.new(aggregator.aggregatees[aggr].merge(:name => aggr), aggregator)
|
143
|
+
end
|
144
|
+
aggregator
|
145
|
+
end
|
146
|
+
|
147
|
+
|
148
|
+
def full_widget_class_name(short_name)
|
149
|
+
"Netzke::#{short_name}"
|
150
|
+
end
|
151
|
+
|
152
|
+
def flash(flash_hash)
|
153
|
+
level = flash_hash.keys.first
|
154
|
+
raise "Unknown message level for flash" unless %(notice warning error).include?(level.to_s)
|
155
|
+
@flash << {:level => level, :msg => flash_hash[level]}
|
156
|
+
end
|
157
|
+
|
158
|
+
def widget_action(action_name)
|
159
|
+
"#{@id_name}__#{action_name}"
|
160
|
+
end
|
161
|
+
|
162
|
+
# permissions
|
163
|
+
def available_permissions
|
164
|
+
[]
|
165
|
+
end
|
166
|
+
|
167
|
+
def process_permissions_config
|
168
|
+
if !available_permissions.empty?
|
169
|
+
# First, process permissions from the config
|
170
|
+
@permissions = available_permissions.inject({}){|h,p| h.merge(p.to_sym => true)} # by default anything is allowed
|
171
|
+
|
172
|
+
config[:prohibit] = available_permissions if config[:prohibit] == :all # short-cut for all permissions
|
173
|
+
config[:prohibit] = [config[:prohibit]] if config[:prohibit].is_a?(Symbol) # so that config[:prohibit] => :write works
|
174
|
+
config[:prohibit] && config[:prohibit].each{|p| @permissions.merge!(p.to_sym => false)} # prohibit
|
175
|
+
|
176
|
+
config[:allow] = [config[:allow]] if config[:allow].is_a?(Symbol) # so that config[:allow] => :write works
|
177
|
+
config[:allow] && config[:allow].each{|p| @permissions.merge!(p.to_sym => true)} # allow
|
178
|
+
|
179
|
+
# ... and then merge it with NetzkePreferences (if not instantiated to only generate JS-class code)
|
180
|
+
!config[:js_class] && available_permissions.each do |p|
|
181
|
+
@permissions[p.to_sym] = @pref["permissions.#{p}"] if !@pref["permissions.#{p}"].nil?
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
## method dispatcher - sends method to the proper aggregatee
|
187
|
+
def method_missing(method_name, params = {})
|
188
|
+
widget, *action = method_name.to_s.split('__')
|
189
|
+
widget = widget.to_sym
|
190
|
+
action = !action.empty? && action.join("__").to_sym
|
191
|
+
|
192
|
+
if action && aggregatees[widget]
|
193
|
+
# only actions starting with "interface_" are accessible
|
194
|
+
interface_action = action.to_s.index('__') ? action : "interface_#{action}"
|
195
|
+
aggregatee_instance(widget).send(interface_action, params)
|
196
|
+
else
|
197
|
+
super
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
#### API section
|
202
|
+
def get_widget(params = {})
|
203
|
+
# if browser does not have our component class cached (and all dependencies), send it to him
|
204
|
+
components_cache = (JSON.parse(params[:components_cache]) if params[:components_cache]) || []
|
205
|
+
|
206
|
+
{:config => js_config, :class_definition => js_missing_code(components_cache)}
|
207
|
+
end
|
208
|
+
|
209
|
+
end
|
210
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
module Netzke
|
2
|
+
module ControllerExtensions
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ControllerClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
def method_missing(method_name)
|
8
|
+
if self.class.widget_config_storage == {}
|
9
|
+
super
|
10
|
+
else
|
11
|
+
widget, *action = method_name.to_s.split('__')
|
12
|
+
widget = widget.to_sym
|
13
|
+
action = !action.empty? && action.join("__").to_sym
|
14
|
+
|
15
|
+
# only widget's actions starting with "interface_" are accessible from outside (security)
|
16
|
+
if action
|
17
|
+
interface_action = action.to_s.index('__') ? action : "interface_#{action}"
|
18
|
+
|
19
|
+
# widget module
|
20
|
+
widget_class = "Netzke::#{self.class.widget_config_storage[widget][:widget_class_name]}".constantize
|
21
|
+
|
22
|
+
# instantiate the server part of the widget
|
23
|
+
widget_instance = widget_class.new(self.class.widget_config_storage[widget].merge(:controller => self)) # OPTIMIZE: top-level widgets have access to the controller - can we avoid that?
|
24
|
+
render :text => widget_instance.send(interface_action, params)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
module ControllerClassMethods
|
30
|
+
|
31
|
+
# widget_config_storage for all widgets
|
32
|
+
def widget_config_storage
|
33
|
+
@@widget_config_storage ||= {}
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# Use this method to declare a widget in the controller
|
38
|
+
#
|
39
|
+
def netzke_widget(name, config={})
|
40
|
+
# which module is the widget?
|
41
|
+
config[:widget_class_name] ||= name.to_s.classify
|
42
|
+
config[:name] ||= name
|
43
|
+
|
44
|
+
# register the widget config in the storage
|
45
|
+
widget_config_storage[name] = config
|
46
|
+
|
47
|
+
# provide widget helpers
|
48
|
+
ActionView::Base.module_eval <<-END_EVAL, __FILE__, __LINE__
|
49
|
+
def #{name}_widget_instance(config = {})
|
50
|
+
# get the global config from the controller's singleton class
|
51
|
+
global_config = controller.class.widget_config_storage[:#{name}]
|
52
|
+
|
53
|
+
# when instantiating a client side instance, the configuration may be overwritten
|
54
|
+
# (but the server side will know nothing about it!)
|
55
|
+
local_config = global_config.merge(config)
|
56
|
+
|
57
|
+
# instantiate it
|
58
|
+
widget_instance = Netzke::#{config[:widget_class_name]}.new(local_config)
|
59
|
+
|
60
|
+
# return javascript code for instantiating on the javascript level
|
61
|
+
widget_instance.js_widget_instance
|
62
|
+
end
|
63
|
+
|
64
|
+
def #{name}_class_definition
|
65
|
+
result = ""
|
66
|
+
config = controller.class.widget_config_storage[:#{name}]
|
67
|
+
@@generated_widget_classes ||= []
|
68
|
+
# do not duplicate javascript code on the same page
|
69
|
+
unless @@generated_widget_classes.include?("#{config[:widget_class_name]}")
|
70
|
+
@@generated_widget_classes << "#{config[:widget_class_name]}"
|
71
|
+
result = Netzke::#{config[:widget_class_name]}.js_class_code
|
72
|
+
end
|
73
|
+
result
|
74
|
+
end
|
75
|
+
END_EVAL
|
76
|
+
|
77
|
+
# add controller action which will render a simple HTML page containing the widget
|
78
|
+
define_method("#{name}_test") do
|
79
|
+
@widget_name = name
|
80
|
+
render :inline => %Q(
|
81
|
+
<script type="text/javascript" charset="utf-8">
|
82
|
+
<%= #{name}_class_definition %>
|
83
|
+
Ext.onReady(function(){
|
84
|
+
<%= #{name}_widget_instance %>
|
85
|
+
#{name.to_js}.render("#{name.to_s.split('_').join('-')}");
|
86
|
+
})
|
87
|
+
</script>
|
88
|
+
<div id="#{name.to_s.split('_').join('-')}"></div>
|
89
|
+
), :layout => "netzke"
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
class Hash
|
2
|
+
|
3
|
+
# Recursively convert the keys. Example:
|
4
|
+
# irb> {:bla_bla => 1, "wow_now" => {:look_ma => true}}.convert_keys{|k| k.camelize}
|
5
|
+
# irb> => {:BlaBla => 1, "WowNow" => {:LookMa => true}}
|
6
|
+
def convert_keys(&block)
|
7
|
+
block_given? ? self.inject({}) do |h,(k,v)|
|
8
|
+
h[k.is_a?(Symbol) ? yield(k.to_s).to_sym : yield(k.to_s)] = v.respond_to?('convert_keys') ? v.convert_keys(&block) : v
|
9
|
+
h
|
10
|
+
end : self
|
11
|
+
end
|
12
|
+
|
13
|
+
# First camelizes the keys, then convert the whole hash to JSON
|
14
|
+
def to_js
|
15
|
+
# self.delete_if{ |k,v| v.nil? } # we don't need to explicitely pass null values to javascript
|
16
|
+
self.recursive_delete_if_nil.convert_keys{|k| k.camelize(:lower)}.to_json
|
17
|
+
end
|
18
|
+
|
19
|
+
# Converts values to strings
|
20
|
+
def stringify_values!
|
21
|
+
self.each_pair{|k,v| self[k] = v.to_s if v.is_a?(Symbol)}
|
22
|
+
end
|
23
|
+
|
24
|
+
def recursive_delete_if_nil
|
25
|
+
self.inject({}) do |h,(k,v)|
|
26
|
+
if !v.nil?
|
27
|
+
h[k] = v.respond_to?('recursive_delete_if_nil') ? v.recursive_delete_if_nil : v
|
28
|
+
end
|
29
|
+
h
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
class Array
|
36
|
+
# Camelizes the keys of hashes and converts them to JSON
|
37
|
+
def to_js
|
38
|
+
self.recursive_delete_if_nil.map{|el| el.is_a?(Hash) ? el.convert_keys{|k| k.camelize(:lower)} : el}.to_json
|
39
|
+
end
|
40
|
+
|
41
|
+
# Applies convert_keys to each element which responds to convert_keys
|
42
|
+
def convert_keys(&block)
|
43
|
+
block_given? ? self.map do |i|
|
44
|
+
i.respond_to?('convert_keys') ? i.convert_keys(&block) : i
|
45
|
+
end : self
|
46
|
+
end
|
47
|
+
|
48
|
+
def recursive_delete_if_nil
|
49
|
+
self.map{|el| el.respond_to?('recursive_delete_if_nil') ? el.recursive_delete_if_nil : el}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class String
|
54
|
+
# Converts self to "literal JSON"-string - one that doesn't get quotes appended when being sent "to_json" method
|
55
|
+
def l
|
56
|
+
def self.to_json
|
57
|
+
self
|
58
|
+
end
|
59
|
+
self
|
60
|
+
end
|
61
|
+
|
62
|
+
def to_js
|
63
|
+
self.camelize(:lower)
|
64
|
+
end
|
65
|
+
|
66
|
+
# removes JS-comments (both single-line and multiple-line) from the string
|
67
|
+
def strip_js_comments
|
68
|
+
regexp = /\/\/.*$|(?m:\/\*.*?\*\/)/
|
69
|
+
self.gsub(regexp, '')
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class Symbol
|
74
|
+
def to_js
|
75
|
+
self.to_s.camelize(:lower).to_sym
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Netzke
|
2
|
+
#
|
3
|
+
# Module which provides JS-class generation functionality for the widgets ("client-side"). This code is executed only once per widget class, and the results are cached at the server (unless widget specifies config[:no_caching] => true).
|
4
|
+
# Included into Netzke::Base class
|
5
|
+
# Most of the methods below are meant to be overwritten by a concrete widget class.
|
6
|
+
#
|
7
|
+
module JsClassBuilder
|
8
|
+
# the JS (Ext) class that we inherit from
|
9
|
+
def js_base_class; "Ext.Panel"; end
|
10
|
+
|
11
|
+
# widget's actions that are loaded at the moment of instantiating a widget
|
12
|
+
def actions; null; end
|
13
|
+
|
14
|
+
# widget's tools that are loaded at the moment of instantiating a widget see (js_config method)
|
15
|
+
def tools; []; end
|
16
|
+
|
17
|
+
def tbar; null; end
|
18
|
+
|
19
|
+
def bbar; null; end
|
20
|
+
|
21
|
+
# functions and properties that will be used to extend the functionality of (Ext) JS-class specified in js_base_class
|
22
|
+
def js_extend_properties; {}; end
|
23
|
+
|
24
|
+
# code executed before and after the constructor
|
25
|
+
def js_before_constructor; ""; end
|
26
|
+
def js_after_constructor; ""; end
|
27
|
+
|
28
|
+
# widget's listeners
|
29
|
+
def js_listeners; {}; end
|
30
|
+
|
31
|
+
# widget's menus
|
32
|
+
def js_menus; []; end
|
33
|
+
|
34
|
+
# items
|
35
|
+
def js_items; null; end
|
36
|
+
|
37
|
+
# default config that is passed into the constructor
|
38
|
+
def js_default_config
|
39
|
+
{
|
40
|
+
:title => short_widget_class_name,
|
41
|
+
:listeners => js_listeners,
|
42
|
+
:tools => "config.tools".l,
|
43
|
+
:actions => "config.actions".l,
|
44
|
+
:tbar => "config.tbar".l,
|
45
|
+
:bbar => "config.bbar".l,
|
46
|
+
:items => js_items,
|
47
|
+
:height => 400,
|
48
|
+
:width => 800,
|
49
|
+
:border => false
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
# declaration of widget's class (stored directly in the cache storage at the client side to be reused at the moment of widget instantiation)
|
54
|
+
def js_class
|
55
|
+
<<-JS
|
56
|
+
Ext.componentCache['#{short_widget_class_name}'] = Ext.extend(#{js_base_class}, Ext.chainApply([Ext.widgetMixIn, {
|
57
|
+
constructor: function(config){
|
58
|
+
this.widgetInit(config);
|
59
|
+
#{js_before_constructor}
|
60
|
+
Ext.componentCache['#{short_widget_class_name}'].superclass.constructor.call(this, Ext.apply(#{js_default_config.to_js}, config));
|
61
|
+
#{js_after_constructor}
|
62
|
+
this.setEvents();
|
63
|
+
this.addMenus(#{js_menus.to_js});
|
64
|
+
}
|
65
|
+
}, #{js_extend_properties.to_js}]))
|
66
|
+
JS
|
67
|
+
end
|
68
|
+
|
69
|
+
# generate instantiating - used when a widget is generated stand-alone (as a part of a HTML page)
|
70
|
+
def js_widget_instance
|
71
|
+
%Q(var #{config[:name].to_js} = new Ext.componentCache['#{short_widget_class_name}'](#{js_config.to_js});)
|
72
|
+
end
|
73
|
+
|
74
|
+
# the config that is send from the server and is used for instantiating a widget
|
75
|
+
def js_config
|
76
|
+
res = {}
|
77
|
+
|
78
|
+
# recursively include configs of all (non-late) aggregatees, so that the widget can instantiate them, too
|
79
|
+
aggregatees.each_pair do |aggr_name, aggr_config|
|
80
|
+
next if aggr_config[:late_aggregation]
|
81
|
+
res["#{aggr_name}_config".to_sym] = aggregatee_instance(aggr_name.to_sym).js_config
|
82
|
+
end
|
83
|
+
|
84
|
+
# interface
|
85
|
+
interface = interface_points.inject({}){|h,interfacep| h.merge(interfacep => widget_action(interfacep))}
|
86
|
+
res.merge!(:interface => interface)
|
87
|
+
|
88
|
+
res.merge!(:widget_class_name => short_widget_class_name)
|
89
|
+
|
90
|
+
res.merge!(config[:ext_config])
|
91
|
+
res.merge!(:id => @id_name)
|
92
|
+
|
93
|
+
# include tools and actions
|
94
|
+
res.merge!(:tools => tools)
|
95
|
+
res.merge!(:actions => actions)
|
96
|
+
res
|
97
|
+
end
|
98
|
+
|
99
|
+
# class definition of the widget plus that of all the dependencies, minus those that are specified as dependencies_to_exclude
|
100
|
+
def js_missing_code(cached_dependencies = [])
|
101
|
+
result = ""
|
102
|
+
dependencies.each do |dep_name|
|
103
|
+
dependency_class = "Netzke::#{dep_name}".constantize
|
104
|
+
result << dependency_class.js_class_code(cached_dependencies)
|
105
|
+
end
|
106
|
+
result << js_class.strip_js_comments unless cached_dependencies.include?(short_widget_class_name) && !config[:no_caching]
|
107
|
+
result
|
108
|
+
end
|
109
|
+
|
110
|
+
# little helpers
|
111
|
+
def this; "this".l; end
|
112
|
+
def null; "null".l; end
|
113
|
+
end
|
114
|
+
end
|