nitro 0.26.0 → 0.27.0
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 +312 -0
- data/INSTALL +3 -1
- data/ProjectInfo +6 -9
- data/README +32 -5
- data/Rakefile +5 -1
- data/bin/nitrogen +3 -60
- data/doc/MIGRATION +24 -0
- data/doc/RELEASES +141 -0
- data/doc/lhttpd.txt +3 -0
- data/lib/glue/magick.rb +38 -0
- data/lib/glue/thumbnails.rb +3 -0
- data/lib/glue/webfile.rb +137 -0
- data/lib/nitro.rb +1 -1
- data/lib/nitro/adapter/acgi.rb +235 -0
- data/lib/nitro/adapter/cgi.rb +16 -17
- data/lib/nitro/adapter/scgi.rb +4 -4
- data/lib/nitro/adapter/webrick.rb +9 -2
- data/lib/nitro/cgi.rb +49 -49
- data/lib/nitro/cgi/response.rb +4 -0
- data/lib/nitro/cgi/stream.rb +7 -7
- data/lib/nitro/cgi/utils.rb +2 -1
- data/lib/nitro/compiler.rb +47 -4
- data/lib/nitro/compiler/elements.rb +40 -20
- data/lib/nitro/compiler/layout.rb +21 -0
- data/lib/nitro/compiler/localization.rb +3 -1
- data/lib/nitro/compiler/markup.rb +2 -0
- data/lib/nitro/compiler/morphing.rb +16 -4
- data/lib/nitro/compiler/script.rb +109 -0
- data/lib/nitro/context.rb +10 -10
- data/lib/nitro/dispatcher.rb +4 -2
- data/lib/nitro/element.rb +107 -26
- data/lib/nitro/element/{java_script.rb → javascript.rb} +7 -1
- data/lib/nitro/flash.rb +4 -1
- data/lib/nitro/helper.rb +15 -0
- data/lib/nitro/helper/benchmark.rb +8 -2
- data/lib/nitro/helper/form.rb +3 -3
- data/lib/nitro/helper/form/controls.rb +131 -29
- data/lib/nitro/helper/{dojo.rb → form/test.xhtml} +0 -0
- data/lib/nitro/helper/javascript.rb +69 -59
- data/lib/nitro/helper/{scriptaculous.rb → javascript/dojo.rb} +0 -0
- data/lib/nitro/helper/javascript/morphing.rb +163 -0
- data/lib/nitro/helper/javascript/prototype.rb +96 -0
- data/lib/nitro/helper/javascript/scriptaculous.rb +18 -0
- data/lib/nitro/helper/layout.rb +42 -0
- data/lib/nitro/helper/table.rb +190 -27
- data/lib/nitro/{adapter → helper}/wee.rb +9 -3
- data/lib/nitro/render.rb +23 -17
- data/lib/nitro/scaffolding.rb +19 -2
- data/lib/nitro/server.rb +4 -8
- data/lib/nitro/server/runner.rb +28 -6
- data/lib/nitro/session.rb +7 -7
- data/lib/nitro_and_og.rb +2 -0
- data/proto/public/Makefile.acgi +40 -0
- data/proto/public/acgi.c +138 -0
- data/proto/public/js/builder.js +7 -3
- data/proto/public/js/controls.js +32 -12
- data/proto/public/js/dragdrop.js +4 -3
- data/proto/public/js/effects.js +111 -62
- data/proto/public/js/scriptaculous.js +10 -13
- data/proto/public/js/slider.js +88 -31
- data/proto/public/scaffold/new.xhtml +2 -2
- data/setup.rb +1585 -0
- data/src/part/admin.rb +6 -0
- data/src/part/admin/controller.rb +3 -3
- data/src/part/admin/skin.rb +1 -8
- data/test/nitro/adapter/tc_webrick.rb +2 -0
- data/test/nitro/tc_controller_aspect.rb +1 -1
- data/test/nitro/tc_element.rb +5 -6
- data/test/nitro/tc_table.rb +66 -0
- metadata +277 -271
- data/doc/architecture.txt +0 -2
- data/doc/bugs.txt +0 -15
- data/doc/tutorial.txt +0 -26
- data/install.rb +0 -37
- data/lib/nitro/compiler/script_generator.rb +0 -14
- data/lib/nitro/compiler/shaders.rb +0 -206
- data/lib/nitro/helper/prototype.rb +0 -49
- data/lib/nitro/scaffold/relations.rb +0 -54
data/lib/nitro/context.rb
CHANGED
@@ -8,13 +8,13 @@ require 'nitro/session'
|
|
8
8
|
|
9
9
|
module Nitro
|
10
10
|
|
11
|
-
# Encapsulates an HTTP processing cycle context.
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
# The Context object can be accessed by the
|
16
|
-
#
|
17
|
-
#
|
11
|
+
# Encapsulates an HTTP processing cycle context. Integrates the
|
12
|
+
# HTTP Request, the HTTP Response and the Render used to
|
13
|
+
# generate the response body.
|
14
|
+
#
|
15
|
+
# The Context object can be accessed by the context, request or
|
16
|
+
# response aliases. You can use the alias that makes sense
|
17
|
+
# every time.
|
18
18
|
|
19
19
|
class Context
|
20
20
|
include Request
|
@@ -64,9 +64,9 @@ class Context
|
|
64
64
|
#++
|
65
65
|
|
66
66
|
def out
|
67
|
-
return @out if @out ==
|
67
|
+
return @out if @out == ''
|
68
68
|
if @rendering_errors
|
69
|
-
@out =
|
69
|
+
@out = ''
|
70
70
|
render '/error'
|
71
71
|
end
|
72
72
|
@out
|
@@ -88,7 +88,7 @@ class Context
|
|
88
88
|
@dispatcher.controllers[@base || '/']
|
89
89
|
end
|
90
90
|
|
91
|
-
#
|
91
|
+
# Automagically populate an object from request parameters.
|
92
92
|
# This is a truly dangerous method.
|
93
93
|
#
|
94
94
|
# === Options
|
data/lib/nitro/dispatcher.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'nano/kernel/singleton'
|
2
2
|
|
3
3
|
require 'nitro/controller'
|
4
|
+
require 'nitro/compiler'
|
4
5
|
require 'nitro/routing'
|
5
6
|
require 'nitro/helper/default'
|
6
7
|
|
@@ -199,7 +200,7 @@ class Dispatcher
|
|
199
200
|
#--
|
200
201
|
# FIXME: no raise to make testable.
|
201
202
|
#++
|
202
|
-
raise NoActionError, "No action
|
203
|
+
raise NoActionError, "No action for path '#{path}' on '#{klass}'"
|
203
204
|
end
|
204
205
|
|
205
206
|
# push any remaining parts of the url onto the query
|
@@ -223,7 +224,8 @@ private
|
|
223
224
|
def controller_class_for(key)
|
224
225
|
klass = @controllers[key]
|
225
226
|
|
226
|
-
if klass and Compiler.reload
|
227
|
+
if $autoreload_dirty and klass and Compiler.reload
|
228
|
+
Logger.info "Reloading controller '#{klass}'"
|
227
229
|
klass.instance_methods.grep(/(_action$)|(_template$)/).each do |m|
|
228
230
|
klass.send(:remove_method, m) rescue nil
|
229
231
|
end
|
data/lib/nitro/element.rb
CHANGED
@@ -1,16 +1,34 @@
|
|
1
1
|
require 'nano/string/capitalized'
|
2
2
|
require 'nano/string/camelize'
|
3
|
+
require 'nano/string/underscore'
|
4
|
+
require 'nano/dir/self/recurse'
|
3
5
|
require 'mega/annotation'
|
4
6
|
|
5
7
|
require 'glue/flexob'
|
6
8
|
require 'glue/configuration'
|
7
|
-
|
8
|
-
# load the element compiler here for compatibility?
|
9
|
-
#require 'nitro/compiler/elements'
|
9
|
+
require 'glue/template'
|
10
10
|
|
11
11
|
module Nitro
|
12
12
|
|
13
|
-
# A programmatically generated element.
|
13
|
+
# A programmatically generated element. Elements are a form
|
14
|
+
# of macros to allow for cleaner templates. They are evaluated
|
15
|
+
# at compile time, so there is no performance hit when you use
|
16
|
+
# them (at the expense of slightly reduced functionality).
|
17
|
+
#
|
18
|
+
# Nitro provides an additional method of defining elements.
|
19
|
+
# Instead of creating a lot of small classes, you can put
|
20
|
+
# .xhtml templates in the Element template_root. These templates
|
21
|
+
# are automatically converted into Element classes.
|
22
|
+
#
|
23
|
+
# For extra safety, you are advised to place your classes in the
|
24
|
+
# Nitro::Element namespace. If your classes do not extend
|
25
|
+
# Nitro::Element, the Nitro::ElementMixin is automatically
|
26
|
+
# injected in the class.
|
27
|
+
#
|
28
|
+
# An element can have and access a hierarchy of sub-elements.
|
29
|
+
# use #{content :sub_element_name} to access the render output
|
30
|
+
# of the subelement. Additionaly you can access the whole
|
31
|
+
# subelement object: _children[:sub_element_name]
|
14
32
|
#
|
15
33
|
# === Design
|
16
34
|
#
|
@@ -21,11 +39,7 @@ module Nitro
|
|
21
39
|
# * separate 'view' template files.
|
22
40
|
#++
|
23
41
|
|
24
|
-
|
25
|
-
# The prefix for element tags (in xhtml compatibility mode)
|
26
|
-
|
27
|
-
setting :prefix, :default => 'x', :doc => 'The prefix for element tags'
|
28
|
-
|
42
|
+
module ElementMixin
|
29
43
|
# The parent of this element.
|
30
44
|
|
31
45
|
attr_accessor :_parent
|
@@ -33,11 +47,8 @@ class Element
|
|
33
47
|
# The children of this element.
|
34
48
|
|
35
49
|
attr_accessor :_children
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
attr_accessor :_named
|
40
|
-
|
50
|
+
alias_method :children, :_children
|
51
|
+
|
41
52
|
# The text of this element.
|
42
53
|
|
43
54
|
attr_accessor :_text
|
@@ -47,8 +58,7 @@ class Element
|
|
47
58
|
attr_accessor :_view
|
48
59
|
|
49
60
|
def initialize(*args)
|
50
|
-
@_children =
|
51
|
-
@_named = Flexob.new
|
61
|
+
@_children = {}
|
52
62
|
@_text = ''
|
53
63
|
end
|
54
64
|
|
@@ -60,7 +70,7 @@ class Element
|
|
60
70
|
|
61
71
|
def content(cname = nil)
|
62
72
|
if cname
|
63
|
-
@
|
73
|
+
@_children[cname].content
|
64
74
|
else
|
65
75
|
@_text
|
66
76
|
end
|
@@ -69,35 +79,106 @@ class Element
|
|
69
79
|
def close
|
70
80
|
end
|
71
81
|
|
82
|
+
# Renders the actual content of this Element.
|
83
|
+
|
72
84
|
def render
|
73
85
|
"#{open}#{content}#{close}"
|
74
86
|
end
|
75
87
|
|
76
88
|
def render_children
|
77
89
|
str = ''
|
78
|
-
for c in @_children
|
90
|
+
for c in @_children.values
|
79
91
|
str << c.render
|
80
92
|
end
|
81
93
|
|
82
94
|
return str
|
83
95
|
end
|
84
|
-
|
96
|
+
|
85
97
|
def add_child(child)
|
86
98
|
child._parent = self
|
87
|
-
@_children
|
88
|
-
if cname = child.class.ann.self[:name]
|
89
|
-
@_named[cname] = child
|
90
|
-
end
|
99
|
+
@_children[Element.class_to_name(child.class)] = child
|
91
100
|
end
|
92
101
|
|
93
|
-
alias_method :children, :_children
|
102
|
+
alias_method :children, :_children
|
103
|
+
end
|
104
|
+
|
105
|
+
# A programmatically generated element.
|
106
|
+
#
|
107
|
+
# === Design
|
108
|
+
#
|
109
|
+
# An underscore is used for the standard attibutes to
|
110
|
+
# avoid name clashes.
|
111
|
+
#--
|
112
|
+
# TODO:
|
113
|
+
# * separate 'view' template files.
|
114
|
+
#++
|
115
|
+
|
116
|
+
class Element
|
117
|
+
include ElementMixin
|
118
|
+
|
119
|
+
# The prefix for element tags (in xhtml compatibility mode)
|
120
|
+
|
121
|
+
setting :prefix, :default => 'x', :doc => 'The prefix for element tags'
|
122
|
+
|
123
|
+
# Allow auto extension of element classes?
|
124
|
+
|
125
|
+
setting :auto_extend, :default => true, :doc => 'Allow auto extension of element classes?'
|
126
|
+
|
127
|
+
# The directory where element templates reside. The default
|
128
|
+
# dir is #{Template.root}/element
|
129
|
+
|
130
|
+
if File.exist?(File.join(Template.root, 'element'))
|
131
|
+
default_root = File.join(Template.root, 'element')
|
132
|
+
else
|
133
|
+
default_root = 'element'
|
134
|
+
end
|
135
|
+
|
136
|
+
if File.exist?('src/element')
|
137
|
+
default_root = 'src/element'
|
138
|
+
elsif File.exist?('src/template/element')
|
139
|
+
default_root = 'src/template/element'
|
140
|
+
else
|
141
|
+
default_root = 'element'
|
142
|
+
end
|
94
143
|
|
144
|
+
setting :template_root, :default => default_root, :doc => 'The directory where element templates reside'
|
145
|
+
|
95
146
|
class << self
|
96
|
-
|
97
|
-
|
147
|
+
# Return the name for an Element class.
|
148
|
+
|
149
|
+
def class_to_name(klass)
|
150
|
+
klass.to_s.underscore.to_sym
|
98
151
|
end
|
152
|
+
|
153
|
+
# Compile the element templates into element classes.
|
154
|
+
# Typically called at startup.
|
155
|
+
|
156
|
+
def compile_template_elements
|
157
|
+
if File.exist? Element.template_root
|
158
|
+
Dir.recurse(Element.template_root) do |filename|
|
159
|
+
if filename =~ /\.#{Template.extension}$/
|
160
|
+
name = File.basename(filename).split('.').first.camelize
|
161
|
+
Nitro::Element.module_eval %{
|
162
|
+
class #{name} < Nitro::Element
|
163
|
+
def render
|
164
|
+
<<-END_OF_TEMPLATE
|
165
|
+
#{File.read(filename)}
|
166
|
+
END_OF_TEMPLATE
|
167
|
+
end
|
168
|
+
end
|
169
|
+
}
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
99
175
|
end
|
100
176
|
end
|
101
177
|
|
178
|
+
# Compile the element templates into element classes.
|
179
|
+
|
180
|
+
Element.compile_template_elements
|
181
|
+
|
102
182
|
end
|
183
|
+
|
103
184
|
# * George Moschovitis <gm@navel.gr>
|
@@ -1,6 +1,10 @@
|
|
1
1
|
require 'nitro/element'
|
2
2
|
|
3
|
-
class
|
3
|
+
class Nitro::Element
|
4
|
+
|
5
|
+
# A javescript helper.
|
6
|
+
|
7
|
+
class Javascript
|
4
8
|
def render
|
5
9
|
%~
|
6
10
|
<script type="text/javascript" language="javascript">
|
@@ -12,4 +16,6 @@ class JavaScript < Nitro::Element
|
|
12
16
|
end
|
13
17
|
end
|
14
18
|
|
19
|
+
end
|
20
|
+
|
15
21
|
# * George Moschovitis <gm@navel.gr>
|
data/lib/nitro/flash.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module Nitro
|
2
2
|
|
3
|
+
#--
|
3
4
|
# This module adds flashing support to the Controllers.
|
5
|
+
#++
|
4
6
|
|
5
7
|
module Flashing
|
6
8
|
|
@@ -12,7 +14,8 @@ module Nitro
|
|
12
14
|
|
13
15
|
# A Flash is a special hash object that lives in the session.
|
14
16
|
# The values stored in the Flash are typically maintained
|
15
|
-
# for the duration of one request.
|
17
|
+
# for the duration of one request. After the request is over,
|
18
|
+
# the Hash is cleared.
|
16
19
|
#
|
17
20
|
# You may want to use the Flash to pass error messages or
|
18
21
|
# other short lived objects.
|
data/lib/nitro/helper.rb
CHANGED
@@ -10,6 +10,8 @@ module Nitro
|
|
10
10
|
# macro provided by Helpers, the utility methods are
|
11
11
|
# included as private methods.
|
12
12
|
#
|
13
|
+
# The helper also requires the ruby file.
|
14
|
+
#
|
13
15
|
# === Examples
|
14
16
|
#
|
15
17
|
# class MyController < Nitro::Controller
|
@@ -36,6 +38,19 @@ module Helpers
|
|
36
38
|
base.module_eval do
|
37
39
|
def self.helper(*modules)
|
38
40
|
for mod in modules
|
41
|
+
#--
|
42
|
+
# gmosx, FIXME: temp solution, will fix!
|
43
|
+
#++
|
44
|
+
|
45
|
+
if mod.is_a? String
|
46
|
+
begin
|
47
|
+
# gmosx: dont interpolate (RDoc fix).
|
48
|
+
require('src/helper/' + mod)
|
49
|
+
rescue Object
|
50
|
+
require('nitro/helper/' + mod) rescue nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
39
54
|
unless mod.is_a? Module
|
40
55
|
mod = mod.to_s.camelize
|
41
56
|
# gmosx: check xxxHelper before xxx.
|
@@ -2,10 +2,16 @@ require 'benchmark'
|
|
2
2
|
|
3
3
|
module Nitro
|
4
4
|
|
5
|
+
# This helper adds benchmarking support in your Controllers.
|
6
|
+
# Useful for fine tuning and optimizing your actions.
|
7
|
+
|
5
8
|
module BenchmarkHelper
|
6
9
|
|
7
|
-
# Log real time spent on a task.
|
8
|
-
#
|
10
|
+
# Log real time spent on a task.
|
11
|
+
#
|
12
|
+
# === Example
|
13
|
+
#
|
14
|
+
# benchmark "Doing an operation" { operation }
|
9
15
|
|
10
16
|
def benchmark(message = 'Benchmarking')
|
11
17
|
real = Benchmark.realtime { yield }
|
data/lib/nitro/helper/form.rb
CHANGED
@@ -85,7 +85,7 @@ module FormHelper
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
end
|
88
|
-
|
88
|
+
|
89
89
|
action = "#{@base}/#{action}" unless action =~ /\//
|
90
90
|
|
91
91
|
str << %{<form action="#{action}" method="post"#{enctype_attribute}>}
|
@@ -118,7 +118,7 @@ module FormHelper
|
|
118
118
|
unless options[:all]
|
119
119
|
next if prop.symbol == obj.class.primary_key.symbol or prop[:control] == :none or prop[:relation]
|
120
120
|
end
|
121
|
-
control = Control.fetch(obj,prop).render
|
121
|
+
control = Control.fetch(obj, prop, options).render
|
122
122
|
str << FormBuilder.element(prop, control)
|
123
123
|
end
|
124
124
|
end
|
@@ -130,7 +130,7 @@ module FormHelper
|
|
130
130
|
unless options[:all]
|
131
131
|
next if rel[:control] == :none
|
132
132
|
end
|
133
|
-
control = Control.fetch(obj,rel).render
|
133
|
+
control = Control.fetch(obj, rel, options).render
|
134
134
|
str << FormBuilder.element(rel, control)
|
135
135
|
end
|
136
136
|
end
|
@@ -25,10 +25,11 @@ class Control
|
|
25
25
|
|
26
26
|
# setup instance vars to use in methods
|
27
27
|
|
28
|
-
def initialize(obj, key,
|
28
|
+
def initialize(obj, key, options = {})
|
29
29
|
@obj = obj
|
30
30
|
@prop = key
|
31
|
-
@value = value || obj.send(key.name.to_sym)
|
31
|
+
@value = options[:value] || obj.send(key.name.to_sym)
|
32
|
+
@options = options
|
32
33
|
end
|
33
34
|
|
34
35
|
# Main bulk of the control. Overide to customise
|
@@ -55,7 +56,6 @@ class Control
|
|
55
56
|
# to the incoming values
|
56
57
|
|
57
58
|
def on_populate(val)
|
58
|
-
puts "PREPROCESSING !"
|
59
59
|
return val
|
60
60
|
end
|
61
61
|
|
@@ -72,6 +72,18 @@ private
|
|
72
72
|
style ? %{ style="#{style}"} : ''
|
73
73
|
end
|
74
74
|
|
75
|
+
# add support to your controls for being disabled
|
76
|
+
# by including an emit_disabled on form items
|
77
|
+
# or testing for is_disabled? on more complex controls
|
78
|
+
|
79
|
+
def emit_disabled
|
80
|
+
is_disabled? ? %{ disabled="disabled"} : ''
|
81
|
+
end
|
82
|
+
|
83
|
+
def is_disabled?
|
84
|
+
return false if @options[:all]
|
85
|
+
@options[:disable_controls] || @prop.disable_control
|
86
|
+
end
|
75
87
|
end
|
76
88
|
|
77
89
|
# Fixnum
|
@@ -81,18 +93,32 @@ class FixnumControl < Control
|
|
81
93
|
|
82
94
|
def render
|
83
95
|
style = prop.control_style ||self.class.style
|
84
|
-
%{
|
96
|
+
%{
|
97
|
+
<div class="numeric_ctl_container">
|
98
|
+
<span class="numeric_ctl_input">
|
99
|
+
<input type="text" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="#{value}"#{emit_style}#{emit_disabled} />
|
100
|
+
</span>
|
101
|
+
<span class="numeric_ctl_buttons">
|
102
|
+
<a href="#" onclick="el=document.getElementById('#{prop.symbol}_ctl'); el.value=(parseInt(el.value) || 0)+#{step}; return false;">+</a>
|
103
|
+
<a href="#" onclick="el=document.getElementById('#{prop.symbol}_ctl'); el.value=(parseInt(el.value) || 0)-#{step}; return false;">-</a>
|
104
|
+
</span>
|
105
|
+
</div>
|
106
|
+
}
|
107
|
+
end
|
108
|
+
|
109
|
+
def step
|
110
|
+
1
|
85
111
|
end
|
112
|
+
|
86
113
|
end
|
87
114
|
|
88
115
|
# Float
|
89
116
|
|
90
|
-
class FloatControl <
|
117
|
+
class FloatControl < FixnumControl
|
91
118
|
setting :style, :default => 'width: 100px', :doc => 'The default style'
|
92
119
|
|
93
|
-
def
|
94
|
-
|
95
|
-
%{<input type="text" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="#{value}"#{emit_style} /> + -}
|
120
|
+
def step
|
121
|
+
0.1
|
96
122
|
end
|
97
123
|
end
|
98
124
|
|
@@ -102,7 +128,17 @@ class TextControl < Control
|
|
102
128
|
setting :style, :default => 'width: 250px', :doc => 'The default style'
|
103
129
|
|
104
130
|
def render
|
105
|
-
%{<input type="text" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="#{value}"#{emit_style} />}
|
131
|
+
%{<input type="text" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="#{value}"#{emit_style}#{emit_disabled} />}
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
# Password
|
136
|
+
|
137
|
+
class PasswordControl < Control
|
138
|
+
setting :style, :default => 'width: 250px', :doc => 'The default style'
|
139
|
+
|
140
|
+
def render
|
141
|
+
%{<input type="password" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="#{value}"#{emit_style} />}
|
106
142
|
end
|
107
143
|
end
|
108
144
|
|
@@ -112,7 +148,7 @@ class TextareaControl < Control
|
|
112
148
|
setting :style, :default => 'width: 500px; height: 100px', :doc => 'The default style'
|
113
149
|
|
114
150
|
def render
|
115
|
-
%{<textarea id="#{prop.symbol}_ctl" name="#{prop.symbol}"#{emit_style}>#{value}</textarea>}
|
151
|
+
%{<textarea id="#{prop.symbol}_ctl" name="#{prop.symbol}"#{emit_style}#{emit_disabled}>#{value}</textarea>}
|
116
152
|
end
|
117
153
|
end
|
118
154
|
|
@@ -123,7 +159,61 @@ class CheckboxControl < Control
|
|
123
159
|
|
124
160
|
def render
|
125
161
|
checked = value == true ? ' checked="checked"':''
|
126
|
-
%{<input type="checkbox" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="true"#{emit_style}#{checked} />}
|
162
|
+
%{<input type="checkbox" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="true"#{emit_style}#{checked}#{emit_disabled} />}
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# ArrayControl
|
167
|
+
|
168
|
+
class ArrayControl < Control
|
169
|
+
def render
|
170
|
+
str = emit_container_start
|
171
|
+
str << emit_js
|
172
|
+
if values.empty?
|
173
|
+
str << emit_array_element(:removable => false)
|
174
|
+
else
|
175
|
+
removable = values.size != 1 ? true : false
|
176
|
+
values.each do |item|
|
177
|
+
str << emit_array_element(:selected => item.pk)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
str << emit_container_end
|
181
|
+
end
|
182
|
+
|
183
|
+
def emit_array_element(options={})
|
184
|
+
removable = options.fetch(:removable, true)
|
185
|
+
selected = options.fetch(:selected, nil)
|
186
|
+
%{
|
187
|
+
<div>
|
188
|
+
<input type="text" id="#{prop.symbol}_ctl" name="#{prop.symbol}[]" value="#{value}"#{emit_style}#{emit_disabled} />
|
189
|
+
<input type="button" class="#{prop.symbol}_remove_btn" value=" - " onclick="rm_#{prop.symbol}_rel(this);" #{'disabled="disabled"' unless removable} />
|
190
|
+
<input type="button" class="#{prop.symbol}_add_btn" value=" + " onclick="add_#{prop.symbol}_rel(this);"#{emit_disabled} />
|
191
|
+
</div>
|
192
|
+
}
|
193
|
+
end
|
194
|
+
|
195
|
+
def emit_js
|
196
|
+
%{
|
197
|
+
<script type="text/javascript">
|
198
|
+
rm_#{prop.symbol}_rel = function(el){
|
199
|
+
ctl=el.parentNode;
|
200
|
+
container=ctl.parentNode;
|
201
|
+
container.removeChild(ctl);
|
202
|
+
inputTags = container.getElementsByTagName('input');
|
203
|
+
if(inputTags.length==2)
|
204
|
+
inputTags[0].disabled='disabled';
|
205
|
+
}
|
206
|
+
add_#{prop.symbol}_rel = function(el){
|
207
|
+
ctl=el.parentNode;
|
208
|
+
container=ctl.parentNode;
|
209
|
+
node=ctl.cloneNode(true);
|
210
|
+
node.getElementsByTagName('input')[0].removeAttribute('disabled');
|
211
|
+
if(container.lastChild==ctl) container.appendChild(node);
|
212
|
+
else container.insertBefore(node, ctl.nextSibling);
|
213
|
+
if(container.childNodes.length>1) container.getElementsByTagName('input')[0].disabled='';
|
214
|
+
}
|
215
|
+
</script>
|
216
|
+
}
|
127
217
|
end
|
128
218
|
end
|
129
219
|
|
@@ -133,16 +223,20 @@ end
|
|
133
223
|
|
134
224
|
class RefersToControl < Control
|
135
225
|
def render
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
226
|
+
%{
|
227
|
+
<select id="#{rel.name}_ctl" name="#{rel.name}"#{emit_disabled}>
|
228
|
+
#{emit_options}
|
229
|
+
</select>
|
230
|
+
}
|
231
|
+
end
|
232
|
+
|
233
|
+
def emit_options
|
234
|
+
objs = rel.target_class.all
|
235
|
+
selected = selected.pk if selected = value
|
236
|
+
%{
|
237
|
+
<option value="nil">None</option>
|
238
|
+
#{options(:labels => objs.map{|o| o.to_s}, :values => objs.map{|o| o.pk}, :selected => selected)}
|
239
|
+
}
|
146
240
|
end
|
147
241
|
end
|
148
242
|
|
@@ -156,11 +250,11 @@ class HasManyControl < Control
|
|
156
250
|
str = emit_container_start
|
157
251
|
str << emit_js
|
158
252
|
if selected_items.empty?
|
159
|
-
str << emit_selector(
|
253
|
+
str << emit_selector(:removable => false)
|
160
254
|
else
|
161
255
|
removable = selected_items.size != 1 ? true : false
|
162
256
|
selected_items.each do |item|
|
163
|
-
str << emit_selector(
|
257
|
+
str << emit_selector(:selected => item.pk)
|
164
258
|
end
|
165
259
|
end
|
166
260
|
str << emit_container_end
|
@@ -190,16 +284,17 @@ class HasManyControl < Control
|
|
190
284
|
# :removable controls wether the minus button is active
|
191
285
|
# :selected denotes the oid to flag as selected in the list
|
192
286
|
|
193
|
-
def emit_selector(
|
287
|
+
def emit_selector(options={})
|
194
288
|
removable = options.fetch(:removable, true)
|
289
|
+
selected = options.fetch(:selected, nil)
|
195
290
|
%{
|
196
291
|
<div>
|
197
|
-
<select class="has_many_ctl" name="#{rel.name}[]" #{emit_style}>
|
292
|
+
<select class="has_many_ctl" name="#{rel.name}[]" #{emit_style}#{emit_disabled}>
|
198
293
|
<option value="nil">None</option>
|
199
|
-
#{options(:labels =>
|
294
|
+
#{options(:labels => all_items.map{|o| o.to_s}, :values => all_items.map{|o| o.pk}, :selected => selected)}
|
200
295
|
</select>
|
201
296
|
<input type="button" class="#{rel.name}_remove_btn" value=" - " onclick="rm_#{rel.name}_rel(this);" #{'disabled="disabled"' unless removable} />
|
202
|
-
<input type="button" class="#{rel.name}_add_btn" value=" + " onclick="add_#{rel.name}_rel(this);" />
|
297
|
+
<input type="button" class="#{rel.name}_add_btn" value=" + " onclick="add_#{rel.name}_rel(this);"#{emit_disabled} />
|
203
298
|
</div>
|
204
299
|
}
|
205
300
|
end
|
@@ -239,13 +334,17 @@ class Control
|
|
239
334
|
|
240
335
|
setting :map, :doc => 'Mappings of control names => classes', :default => {
|
241
336
|
:fixnum => FixnumControl,
|
337
|
+
:integer => FixnumControl,
|
242
338
|
:float => FloatControl,
|
243
339
|
:boolean => CheckboxControl,
|
244
340
|
:checkbox => CheckboxControl,
|
245
341
|
:string => TextControl,
|
342
|
+
:password => PasswordControl,
|
246
343
|
:textarea => TextareaControl,
|
247
344
|
:true_class => CheckboxControl,
|
345
|
+
:array => ArrayControl,
|
248
346
|
:refers_to => RefersToControl,
|
347
|
+
:has_one => RefersToControl,
|
249
348
|
:belongs_to => RefersToControl,
|
250
349
|
:has_many => HasManyControl,
|
251
350
|
:many_to_many => HasManyControl,
|
@@ -255,7 +354,8 @@ class Control
|
|
255
354
|
# Fetch a control, setup for 'obj' for a property, or relation
|
256
355
|
# or :symbol defaults to Control if not found
|
257
356
|
|
258
|
-
def self.fetch(obj, key,
|
357
|
+
def self.fetch(obj, key, options={})
|
358
|
+
|
259
359
|
if key.kind_of? Og::Relation
|
260
360
|
control_sym = key[:control] || key.class.to_s.demodulize.underscore.to_sym
|
261
361
|
elsif key.kind_of? Property
|
@@ -263,7 +363,9 @@ class Control
|
|
263
363
|
else
|
264
364
|
control_sym = key.to_sym
|
265
365
|
end
|
266
|
-
|
366
|
+
|
367
|
+
default_to = options.fetch(:default, Control)
|
368
|
+
self.map.fetch(control_sym, Control).new(obj, key, options)
|
267
369
|
end
|
268
370
|
|
269
371
|
end
|