nitro 0.28.0 → 0.29.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +382 -0
- data/ProjectInfo +4 -4
- data/README +1 -1
- data/doc/AUTHORS +15 -15
- data/doc/MIGRATION +13 -0
- data/doc/RELEASES +102 -0
- data/lib/glue/sweeper.rb +1 -1
- data/lib/nitro.rb +38 -9
- data/lib/nitro/adapter/acgi.rb +1 -3
- data/lib/nitro/adapter/cgi.rb +1 -1
- data/lib/nitro/adapter/fastcgi.rb +1 -3
- data/lib/nitro/adapter/mongrel.rb +8 -6
- data/lib/nitro/adapter/webrick.rb +1 -2
- data/lib/nitro/cgi.rb +1 -1
- data/lib/nitro/compiler.rb +21 -40
- data/lib/nitro/compiler/elements.rb +72 -32
- data/lib/nitro/compiler/errors.rb +92 -42
- data/lib/nitro/compiler/include.rb +47 -17
- data/lib/nitro/compiler/morphing.rb +1 -3
- data/lib/nitro/compiler/script.rb +2 -2
- data/lib/nitro/context.rb +36 -0
- data/lib/nitro/controller.rb +140 -31
- data/lib/nitro/dispatcher.rb +27 -28
- data/lib/nitro/element.rb +52 -15
- data/lib/nitro/flash.rb +44 -0
- data/lib/nitro/helper/buffer.rb +0 -2
- data/lib/nitro/helper/form.rb +2 -2
- data/lib/nitro/helper/form/controls.rb +14 -3
- data/lib/nitro/helper/pager.rb +1 -1
- data/lib/nitro/helper/table.rb +4 -3
- data/lib/nitro/helper/xml.rb +1 -1
- data/lib/nitro/part.rb +20 -0
- data/lib/nitro/render.rb +44 -5
- data/lib/nitro/router.rb +81 -0
- data/lib/nitro/scaffolding.rb +24 -23
- data/lib/nitro/server.rb +12 -1
- data/lib/nitro/server/runner.rb +12 -0
- data/lib/nitro/session.rb +3 -12
- data/lib/nitro/session/drb.rb +2 -5
- data/lib/nitro/session/file.rb +2 -2
- data/lib/nitro/session/memcached.rb +14 -0
- data/lib/nitro/session/memory.rb +3 -26
- data/lib/nitro/session/og.rb +1 -1
- data/lib/nitro/test/assertions.rb +1 -1
- data/lib/nitro/test/context.rb +8 -2
- data/lib/nitro/test/testcase.rb +16 -7
- data/proto/public/error.xhtml +58 -21
- data/proto/public/js/controls.js +60 -15
- data/proto/public/js/dragdrop.js +105 -16
- data/proto/public/js/effects.js +19 -12
- data/proto/public/js/scriptaculous.js +1 -1
- data/proto/public/js/slider.js +2 -2
- data/proto/public/js/unittest.js +29 -20
- data/proto/public/scaffold/edit.xhtml +1 -1
- data/proto/public/scaffold/index.xhtml +2 -2
- data/proto/public/scaffold/list.xhtml +2 -2
- data/proto/public/scaffold/new.xhtml +1 -1
- data/proto/public/scaffold/search.xhtml +1 -1
- data/src/part/admin/controller.rb +5 -5
- data/src/part/admin/template/index.xhtml +2 -2
- data/test/nitro/compiler/tc_compiler.rb +23 -0
- data/test/nitro/helper/tc_table.rb +35 -0
- data/test/nitro/tc_cgi.rb +1 -1
- data/test/nitro/tc_controller.rb +3 -3
- data/test/nitro/tc_controller_aspect.rb +2 -0
- data/test/nitro/tc_dispatcher.rb +10 -1
- data/test/nitro/tc_flash.rb +14 -0
- data/test/nitro/tc_router.rb +58 -0
- data/test/nitro/tc_session.rb +26 -9
- metadata +13 -12
- data/lib/nitro/routing.rb +0 -41
- data/test/nitro/caching/tc_stores.rb +0 -17
- data/test/nitro/tc_table.rb +0 -66
data/lib/nitro/server.rb
CHANGED
@@ -77,7 +77,7 @@ class Server
|
|
77
77
|
|
78
78
|
def dispatcher
|
79
79
|
unless @dispatcher
|
80
|
-
@dispatcher = Dispatcher.new(
|
80
|
+
@dispatcher = Dispatcher.new(@map)
|
81
81
|
end
|
82
82
|
@dispatcher
|
83
83
|
end
|
@@ -87,6 +87,17 @@ class Server
|
|
87
87
|
def start(options = {})
|
88
88
|
@map['/'] = options[:controller] if options[:controller]
|
89
89
|
@dispatcher = options[:dispatcher] || Dispatcher.new(@map)
|
90
|
+
|
91
|
+
# Create the actual store. Copy values already inserted
|
92
|
+
# in the temporary cache.
|
93
|
+
#--
|
94
|
+
# FIXME: cleanup this code
|
95
|
+
#++
|
96
|
+
|
97
|
+
temp = $global
|
98
|
+
$global = $application = Context.global_cache_class.new
|
99
|
+
$global.update(temp)
|
100
|
+
|
90
101
|
return self
|
91
102
|
end
|
92
103
|
|
data/lib/nitro/server/runner.rb
CHANGED
@@ -124,6 +124,14 @@ class Runner
|
|
124
124
|
self.class.mode = :live
|
125
125
|
end
|
126
126
|
|
127
|
+
opts.on('--address IP', 'Force the server to run on this address.') do |a|
|
128
|
+
@server_address = a
|
129
|
+
end
|
130
|
+
|
131
|
+
opts.on('--port PORT', 'Force the server to run on this port.') do |p|
|
132
|
+
@server_port = p.to_i
|
133
|
+
end
|
134
|
+
|
127
135
|
opts.on('-w', '--webrick', 'Use a webrick server [default].') do
|
128
136
|
@server = :webrick
|
129
137
|
end
|
@@ -293,6 +301,10 @@ class Runner
|
|
293
301
|
def invoke_server(server)
|
294
302
|
spider_thread = nil
|
295
303
|
|
304
|
+
# FIXME refactor !
|
305
|
+
server.address = @server_address if @server_address
|
306
|
+
server.port = @server_port if @server_port
|
307
|
+
|
296
308
|
case @action
|
297
309
|
when :start
|
298
310
|
|
data/lib/nitro/session.rb
CHANGED
@@ -7,7 +7,6 @@ require 'facet/times'
|
|
7
7
|
require 'glue'
|
8
8
|
require 'glue/attribute'
|
9
9
|
require 'glue/configuration'
|
10
|
-
require 'glue/logger'
|
11
10
|
require 'glue/expirable'
|
12
11
|
|
13
12
|
require 'nitro/cgi/cookie'
|
@@ -31,7 +30,7 @@ module Nitro
|
|
31
30
|
#++
|
32
31
|
|
33
32
|
class Session < Hash
|
34
|
-
include Expirable
|
33
|
+
include Glue::Expirable
|
35
34
|
|
36
35
|
# Session id salt.
|
37
36
|
|
@@ -50,14 +49,6 @@ class Session < Hash
|
|
50
49
|
|
51
50
|
setting :keepalive, :default => 30.minutes, :doc => 'The session keepalive time'
|
52
51
|
|
53
|
-
# The address of the Session cache / store (if distibuted).
|
54
|
-
|
55
|
-
setting :cache_address, :default => '127.0.0.1', :doc => 'The address of the Session cache'
|
56
|
-
|
57
|
-
# The port of the Session DRb cache / store (if distributed).
|
58
|
-
|
59
|
-
setting :cache_port, :default => 9069, :doc => 'The port of the Session cache'
|
60
|
-
|
61
52
|
# The sessions cache (store).
|
62
53
|
|
63
54
|
cattr_accessor :cache
|
@@ -70,8 +61,8 @@ class Session < Hash
|
|
70
61
|
# * :memory [default]
|
71
62
|
# * :drb
|
72
63
|
# * :og
|
73
|
-
# * :file
|
74
|
-
# * :memcached
|
64
|
+
# * :file
|
65
|
+
# * :memcached
|
75
66
|
|
76
67
|
def cache_type=(cache_type)
|
77
68
|
# gmosx: RDoc friendly.
|
data/lib/nitro/session/drb.rb
CHANGED
@@ -3,12 +3,9 @@ require 'nitro/session'
|
|
3
3
|
|
4
4
|
module Nitro
|
5
5
|
|
6
|
-
Logger.debug "Using DRb sessions at #{
|
6
|
+
Logger.debug "Using DRb sessions at #{Glue::DrbCache.address}:#{Glue::DrbCache.port}." if defined?(Logger)
|
7
7
|
|
8
|
-
Session.cache = DrbCache.new
|
9
|
-
:address => Session.cache_address,
|
10
|
-
:port => Session.cache_port
|
11
|
-
)
|
8
|
+
Session.cache = Glue::DrbCache.new
|
12
9
|
|
13
10
|
end
|
14
11
|
|
data/lib/nitro/session/file.rb
CHANGED
@@ -5,9 +5,9 @@ module Nitro
|
|
5
5
|
|
6
6
|
# A Session manager that persists sessions on disk.
|
7
7
|
|
8
|
-
Logger.debug "Using File sessions."
|
8
|
+
Logger.debug "Using File sessions." if defined?(Logger)
|
9
9
|
|
10
|
-
Session.cache = FileCache.new("session_#{Session.cookie_name}", Session.keepalive)
|
10
|
+
Session.cache = Glue::FileCache.new("session_#{Session.cookie_name}", Session.keepalive)
|
11
11
|
|
12
12
|
end
|
13
13
|
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'glue/cache/memcached'
|
2
|
+
require 'nitro/session'
|
3
|
+
|
4
|
+
module Nitro
|
5
|
+
|
6
|
+
# A Session manager that persists sessions on disk.
|
7
|
+
|
8
|
+
Logger.debug "Using MemCached sessions." if defined?(Logger)
|
9
|
+
|
10
|
+
Session.cache = Glue::MemCached.new("session_#{Session.cookie_name}", Session.keepalive)
|
11
|
+
|
12
|
+
end
|
13
|
+
|
14
|
+
# * Guillaume Pierronnet <guillaume.pierronnet@gmail.com>
|
data/lib/nitro/session/memory.rb
CHANGED
@@ -1,36 +1,13 @@
|
|
1
1
|
require 'glue/cache/memory'
|
2
2
|
require 'nitro/session'
|
3
|
+
require 'glue/logger'
|
3
4
|
|
4
5
|
module Nitro
|
5
6
|
|
6
|
-
Logger.debug "Using Memory sessions."
|
7
|
+
Logger.debug "Using Memory sessions." if defined?(Logger)
|
7
8
|
|
8
|
-
Session.cache = MemoryCache.new
|
9
|
+
Session.cache = Glue::MemoryCache.new
|
9
10
|
|
10
11
|
end
|
11
12
|
|
12
13
|
# * George Moschovitis <gm@navel.gr>
|
13
|
-
|
14
|
-
=begin
|
15
|
-
|
16
|
-
module Nitro
|
17
|
-
|
18
|
-
class MemorySessionStore < SyncHash
|
19
|
-
|
20
|
-
# Perform session garbage collection. Typically this method
|
21
|
-
# is called from a cron like mechanism (for example using
|
22
|
-
# script/runner).
|
23
|
-
|
24
|
-
def gc!
|
25
|
-
delete_if { |key, s| s.expired? }
|
26
|
-
end
|
27
|
-
|
28
|
-
alias :all :values
|
29
|
-
end
|
30
|
-
|
31
|
-
Session.store = MemorySessionStore.new
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
# * George Moschovitis <gm@navel.gr>
|
36
|
-
=end
|
data/lib/nitro/session/og.rb
CHANGED
data/lib/nitro/test/context.rb
CHANGED
@@ -40,11 +40,17 @@ end
|
|
40
40
|
# to include methods useful for testing.
|
41
41
|
|
42
42
|
class Context
|
43
|
+
attr_writer :session, :cookies
|
44
|
+
|
43
45
|
def session
|
44
46
|
@session || @session = {}
|
45
|
-
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def cookies
|
50
|
+
@cookies || @cookies = {}
|
51
|
+
end
|
52
|
+
|
46
53
|
end
|
47
54
|
|
48
55
|
end
|
49
|
-
|
50
56
|
# * George Moschovitis <gm@navel.gr>
|
data/lib/nitro/test/testcase.rb
CHANGED
@@ -10,8 +10,11 @@ module Test::Unit
|
|
10
10
|
class TestCase
|
11
11
|
include Nitro
|
12
12
|
|
13
|
-
def
|
14
|
-
@
|
13
|
+
def reset_context
|
14
|
+
@context_config = OpenStruct.new(
|
15
|
+
:dispatcher => Nitro::Dispatcher.new(Nitro::Server.map)
|
16
|
+
)
|
17
|
+
@context = Nitro::Context.new(@context_config)
|
15
18
|
end
|
16
19
|
|
17
20
|
# Send a request to the controller. Alternatively you can use
|
@@ -29,17 +32,23 @@ class TestCase
|
|
29
32
|
uri = options[:uri]
|
30
33
|
uri = "/#{uri}" unless uri =~ /^\//
|
31
34
|
|
32
|
-
|
33
|
-
|
35
|
+
reset_context unless @context
|
36
|
+
context = @context
|
37
|
+
if @last_response_cookies
|
38
|
+
@last_response_cookies.each do |cookie|
|
39
|
+
context.cookies.merge! cookie.name => cookie.value
|
40
|
+
end
|
41
|
+
end
|
34
42
|
context.params = options[:params] || {}
|
35
43
|
context.headers = options[:headers] || options[:env] || {}
|
36
44
|
context.headers['REQUEST_URI'] = uri
|
37
45
|
context.headers['REQUEST_METHOD'] = options[:method].to_s.upcase
|
38
|
-
context.
|
39
|
-
context.
|
46
|
+
context.headers['REMOTE_ADDR'] ||= '127.0.0.1'
|
47
|
+
context.cookies.merge! options[:cookies] if options[:cookies]
|
48
|
+
context.session.merge! options[:session] if options[:session]
|
40
49
|
|
41
50
|
context.render(context.path)
|
42
|
-
|
51
|
+
@last_response_cookies = context.response_cookies
|
43
52
|
return context.body
|
44
53
|
end
|
45
54
|
|
data/proto/public/error.xhtml
CHANGED
@@ -1,5 +1,17 @@
|
|
1
1
|
<html>
|
2
2
|
<head>
|
3
|
+
<script lang="javascript" type="text/javascript">
|
4
|
+
// <!--
|
5
|
+
function toggleVisible(element) {
|
6
|
+
if (element.style.display == 'block') {
|
7
|
+
element.style.display = 'none';
|
8
|
+
} else {
|
9
|
+
element.style.display = 'block';
|
10
|
+
}
|
11
|
+
return false;
|
12
|
+
}
|
13
|
+
// -->
|
14
|
+
</script>
|
3
15
|
<title>Error</title>
|
4
16
|
<style>
|
5
17
|
.path {
|
@@ -38,27 +50,52 @@
|
|
38
50
|
<?r for error, path in @context.rendering_errors ?>
|
39
51
|
<div class="path"><strong>Path:</strong> #{path}</div>
|
40
52
|
<div class="error"><strong>#{CGI.escapeHTML(error.to_s)}</strong></div>
|
41
|
-
<div class="load">
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
<?r
|
60
|
-
|
61
|
-
|
53
|
+
<div class="load">
|
54
|
+
<strong><a href="#{request.uri}">Reload</a></strong> this page.
|
55
|
+
Go to the <strong><a href="#{request.referer}">referer</a></strong> or the <strong><a href="/">home page</a></strong>.
|
56
|
+
</div>
|
57
|
+
<div class="source">
|
58
|
+
<?r
|
59
|
+
extract = error.source_extract.split("\n")
|
60
|
+
?>
|
61
|
+
In file <b>'#{error.hot_file}'</b> #{error.hot_file =~ /\.xhtml$/ ? '(line numbering is aproximate due to template transformation)' : nil}:
|
62
|
+
<br /><br />
|
63
|
+
<?r
|
64
|
+
extract.each_with_index do |line, idx|
|
65
|
+
line = sanitize(line)
|
66
|
+
if 5 == idx
|
67
|
+
?>
|
68
|
+
<div style="background: #eee">#{line}</div>
|
69
|
+
<?r else ?>
|
70
|
+
<div>#{line}</div>
|
71
|
+
<?r
|
72
|
+
end
|
73
|
+
end
|
74
|
+
?>
|
75
|
+
</div>
|
76
|
+
<h2><a href="#" onclick="return toggleVisible(document.getElementById('trace'));">Stack Trace</a></h2>
|
77
|
+
<div id="trace" style="display: none;">
|
78
|
+
<?r error.backtrace.zip(error.source_for_backtrace).each_with_index do |step,step_idx| ?>
|
79
|
+
<div><a href="#" onclick="return toggleVisible(document.getElementById('trace_#{step_idx}'));">#{sanitize(step.first)}</a></div>
|
80
|
+
<div class="source" id="trace_#{step_idx}" style="display: none;">
|
81
|
+
<?r
|
82
|
+
extract = step.last.split("\n")
|
83
|
+
extract.each_with_index do |line, idx|
|
84
|
+
line = sanitize(line)
|
85
|
+
if 5 == idx
|
86
|
+
?>
|
87
|
+
<div style="background: #eee">#{line}</div>
|
88
|
+
<?r else ?>
|
89
|
+
<div>#{line}</div>
|
90
|
+
<?r
|
91
|
+
end
|
92
|
+
end
|
93
|
+
?>
|
94
|
+
</div>
|
95
|
+
|
96
|
+
|
97
|
+
<?r end ?>
|
98
|
+
</div>
|
62
99
|
<?r end ?>
|
63
100
|
|
64
101
|
<h2><a href="#" onclick="document.getElementById('request').style.display = 'block'; return false">Request</a></h2>
|
data/proto/public/js/controls.js
CHANGED
@@ -152,6 +152,12 @@ Autocompleter.Base.prototype = {
|
|
152
152
|
setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
|
153
153
|
},
|
154
154
|
|
155
|
+
activate: function() {
|
156
|
+
this.changed = false;
|
157
|
+
this.hasFocus = true;
|
158
|
+
this.getUpdatedChoices();
|
159
|
+
},
|
160
|
+
|
155
161
|
onHover: function(event) {
|
156
162
|
var element = Event.findElement(event, 'LI');
|
157
163
|
if(this.index != element.autocompleteIndex)
|
@@ -477,9 +483,10 @@ Ajax.InPlaceEditor.prototype = {
|
|
477
483
|
formClassName: 'inplaceeditor-form',
|
478
484
|
highlightcolor: Ajax.InPlaceEditor.defaultHighlightColor,
|
479
485
|
highlightendcolor: "#FFFFFF",
|
480
|
-
externalControl:
|
486
|
+
externalControl: null,
|
481
487
|
submitOnBlur: false,
|
482
|
-
ajaxOptions: {}
|
488
|
+
ajaxOptions: {},
|
489
|
+
evalScripts: false
|
483
490
|
}, options || {});
|
484
491
|
|
485
492
|
if(!this.options.formId && this.element.id) {
|
@@ -548,6 +555,7 @@ Ajax.InPlaceEditor.prototype = {
|
|
548
555
|
okButton = document.createElement("input");
|
549
556
|
okButton.type = "submit";
|
550
557
|
okButton.value = this.options.okText;
|
558
|
+
okButton.className = 'editor_ok_button';
|
551
559
|
this.form.appendChild(okButton);
|
552
560
|
}
|
553
561
|
|
@@ -556,6 +564,7 @@ Ajax.InPlaceEditor.prototype = {
|
|
556
564
|
cancelLink.href = "#";
|
557
565
|
cancelLink.appendChild(document.createTextNode(this.options.cancelText));
|
558
566
|
cancelLink.onclick = this.onclickCancel.bind(this);
|
567
|
+
cancelLink.className = 'editor_cancel';
|
559
568
|
this.form.appendChild(cancelLink);
|
560
569
|
}
|
561
570
|
},
|
@@ -584,6 +593,7 @@ Ajax.InPlaceEditor.prototype = {
|
|
584
593
|
textField.name = "value";
|
585
594
|
textField.value = text;
|
586
595
|
textField.style.backgroundColor = this.options.highlightcolor;
|
596
|
+
textField.className = 'editor_field';
|
587
597
|
var size = this.options.size || this.options.cols || 0;
|
588
598
|
if (size != 0) textField.size = size;
|
589
599
|
if (this.options.submitOnBlur)
|
@@ -597,6 +607,7 @@ Ajax.InPlaceEditor.prototype = {
|
|
597
607
|
textArea.value = this.convertHTMLLineBreaks(text);
|
598
608
|
textArea.rows = this.options.rows;
|
599
609
|
textArea.cols = this.options.cols || 40;
|
610
|
+
textArea.className = 'editor_field';
|
600
611
|
if (this.options.submitOnBlur)
|
601
612
|
textArea.onblur = this.onSubmit.bind(this);
|
602
613
|
this.editField = textArea;
|
@@ -649,19 +660,26 @@ Ajax.InPlaceEditor.prototype = {
|
|
649
660
|
// to be displayed indefinitely
|
650
661
|
this.onLoading();
|
651
662
|
|
652
|
-
|
653
|
-
|
654
|
-
|
655
|
-
|
656
|
-
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
663
|
-
|
664
|
-
|
663
|
+
if (this.options.evalScripts) {
|
664
|
+
new Ajax.Request(
|
665
|
+
this.url, Object.extend({
|
666
|
+
parameters: this.options.callback(form, value),
|
667
|
+
onComplete: this.onComplete.bind(this),
|
668
|
+
onFailure: this.onFailure.bind(this),
|
669
|
+
asynchronous:true,
|
670
|
+
evalScripts:true
|
671
|
+
}, this.options.ajaxOptions));
|
672
|
+
} else {
|
673
|
+
new Ajax.Updater(
|
674
|
+
{ success: this.element,
|
675
|
+
// don't update on failure (this could be an option)
|
676
|
+
failure: null },
|
677
|
+
this.url, Object.extend({
|
678
|
+
parameters: this.options.callback(form, value),
|
679
|
+
onComplete: this.onComplete.bind(this),
|
680
|
+
onFailure: this.onFailure.bind(this)
|
681
|
+
}, this.options.ajaxOptions));
|
682
|
+
}
|
665
683
|
// stop the event to avoid a page refresh in Safari
|
666
684
|
if (arguments.length > 1) {
|
667
685
|
Event.stop(arguments[0]);
|
@@ -743,6 +761,33 @@ Ajax.InPlaceEditor.prototype = {
|
|
743
761
|
}
|
744
762
|
};
|
745
763
|
|
764
|
+
Ajax.InPlaceCollectionEditor = Class.create();
|
765
|
+
Object.extend(Ajax.InPlaceCollectionEditor.prototype, Ajax.InPlaceEditor.prototype);
|
766
|
+
Object.extend(Ajax.InPlaceCollectionEditor.prototype, {
|
767
|
+
createEditField: function() {
|
768
|
+
if (!this.cached_selectTag) {
|
769
|
+
var selectTag = document.createElement("select");
|
770
|
+
var collection = this.options.collection || [];
|
771
|
+
var optionTag;
|
772
|
+
collection.each(function(e,i) {
|
773
|
+
optionTag = document.createElement("option");
|
774
|
+
optionTag.value = (e instanceof Array) ? e[0] : e;
|
775
|
+
if(this.options.value==optionTag.value) optionTag.selected = true;
|
776
|
+
optionTag.appendChild(document.createTextNode((e instanceof Array) ? e[1] : e));
|
777
|
+
selectTag.appendChild(optionTag);
|
778
|
+
}.bind(this));
|
779
|
+
this.cached_selectTag = selectTag;
|
780
|
+
}
|
781
|
+
|
782
|
+
this.editField = this.cached_selectTag;
|
783
|
+
if(this.options.loadTextURL) this.loadExternalText();
|
784
|
+
this.form.appendChild(this.editField);
|
785
|
+
this.options.callback = function(form, value) {
|
786
|
+
return "value=" + encodeURIComponent(value);
|
787
|
+
}
|
788
|
+
}
|
789
|
+
});
|
790
|
+
|
746
791
|
// Delayed observer, like Form.Element.Observer,
|
747
792
|
// but waits for delay after last key input
|
748
793
|
// Ideal for live-search fields
|