nitro 0.25.0 → 0.26.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 +531 -1
- data/ProjectInfo +29 -5
- data/README +1 -1
- data/doc/AUTHORS +12 -6
- data/doc/RELEASES +114 -0
- data/lib/glue/sweeper.rb +71 -0
- data/lib/nitro.rb +19 -12
- data/lib/nitro/adapter/cgi.rb +4 -0
- data/lib/nitro/adapter/webrick.rb +4 -2
- data/lib/nitro/caching.rb +1 -0
- data/lib/nitro/caching/fragments.rb +7 -1
- data/lib/nitro/caching/output.rb +6 -1
- data/lib/nitro/caching/stores.rb +13 -1
- data/lib/nitro/cgi.rb +9 -1
- data/lib/nitro/cgi/request.rb +11 -3
- data/lib/nitro/cgi/utils.rb +24 -2
- data/lib/nitro/compiler.rb +89 -63
- data/lib/nitro/compiler/cleanup.rb +16 -0
- data/lib/nitro/compiler/elements.rb +117 -0
- data/lib/nitro/compiler/markup.rb +3 -1
- data/lib/nitro/compiler/morphing.rb +203 -73
- data/lib/nitro/compiler/script_generator.rb +14 -0
- data/lib/nitro/compiler/shaders.rb +1 -1
- data/lib/nitro/context.rb +5 -6
- data/lib/nitro/controller.rb +43 -21
- data/lib/nitro/dispatcher.rb +86 -37
- data/lib/nitro/element.rb +3 -105
- data/lib/nitro/helper/benchmark.rb +3 -0
- data/lib/nitro/helper/dojo.rb +0 -0
- data/lib/nitro/helper/form.rb +85 -255
- data/lib/nitro/helper/form/controls.rb +274 -0
- data/lib/nitro/helper/javascript.rb +86 -6
- data/lib/nitro/helper/pager.rb +5 -0
- data/lib/nitro/helper/prototype.rb +49 -0
- data/lib/nitro/helper/scriptaculous.rb +0 -0
- data/lib/nitro/helper/xhtml.rb +11 -8
- data/lib/nitro/helper/xml.rb +1 -1
- data/lib/nitro/routing.rb +8 -1
- data/lib/nitro/scaffolding.rb +344 -0
- data/lib/nitro/server.rb +5 -1
- data/lib/nitro/server/runner.rb +19 -15
- data/lib/nitro/session.rb +32 -56
- data/lib/nitro/session/drbserver.rb +1 -1
- data/lib/nitro/session/file.rb +34 -15
- data/lib/nitro/session/memory.rb +13 -4
- data/lib/nitro/session/og.rb +56 -0
- data/proto/public/js/controls.js +30 -1
- data/proto/public/js/dragdrop.js +211 -146
- data/proto/public/js/effects.js +261 -399
- data/proto/public/js/prototype.js +131 -72
- data/proto/public/scaffold/edit.xhtml +10 -3
- data/proto/public/scaffold/form.xhtml +1 -7
- data/proto/public/scaffold/index.xhtml +20 -0
- data/proto/public/scaffold/list.xhtml +15 -8
- data/proto/public/scaffold/new.xhtml +10 -3
- data/proto/public/scaffold/search.xhtml +28 -0
- data/proto/public/scaffold/view.xhtml +8 -0
- data/proto/run.rb +93 -1
- data/src/part/admin.rb +4 -2
- data/src/part/admin/controller.rb +62 -28
- data/src/part/admin/skin.rb +8 -8
- data/src/part/admin/system.css +135 -0
- data/src/part/admin/template/index.xhtml +8 -12
- data/test/nitro/caching/tc_stores.rb +17 -0
- data/test/nitro/tc_caching.rb +1 -4
- data/test/nitro/tc_dispatcher.rb +22 -10
- data/test/nitro/tc_element.rb +1 -1
- data/test/nitro/tc_session.rb +23 -11
- data/test/public/blog/another/very_litle/index.xhtml +1 -0
- metadata +29 -15
- data/lib/nitro/dispatcher/general.rb +0 -62
- data/lib/nitro/dispatcher/nice.rb +0 -57
- data/lib/nitro/scaffold.rb +0 -171
- data/proto/public/index.xhtml +0 -83
- data/proto/public/js/scaffold.js +0 -74
- data/proto/public/settings.xhtml +0 -66
data/lib/nitro/session.rb
CHANGED
|
@@ -6,6 +6,8 @@ require 'mega/time_in_english'
|
|
|
6
6
|
|
|
7
7
|
require 'glue/attribute'
|
|
8
8
|
require 'glue/configuration'
|
|
9
|
+
require 'glue/logger'
|
|
10
|
+
require 'glue/expirable'
|
|
9
11
|
|
|
10
12
|
require 'nitro/cgi/cookie'
|
|
11
13
|
|
|
@@ -21,8 +23,11 @@ module Nitro
|
|
|
21
23
|
#
|
|
22
24
|
# The session should be persistable to survive server
|
|
23
25
|
# shutdowns.
|
|
26
|
+
#
|
|
27
|
+
# TODO rehash of the session cookie
|
|
24
28
|
|
|
25
29
|
class Session < Hash
|
|
30
|
+
is Expirable
|
|
26
31
|
|
|
27
32
|
# Session id salt.
|
|
28
33
|
|
|
@@ -32,16 +37,19 @@ class Session < Hash
|
|
|
32
37
|
|
|
33
38
|
setting :cookie_name, :default => 'nsid', :doc => 'The name of the cookie that stores the session id'
|
|
34
39
|
|
|
40
|
+
# useful with persistents sessions stores
|
|
41
|
+
|
|
42
|
+
setting :cookie_expires, :default => false, :doc => 'Set expires parameter of session cookie equal to the keepalive setting?'
|
|
43
|
+
|
|
35
44
|
# The session keepalive time. The session is eligable for
|
|
36
45
|
# garbage collection after this time passes.
|
|
37
46
|
|
|
38
47
|
setting :keepalive, :default => 30.minutes, :doc => 'The session keepalive time'
|
|
39
48
|
|
|
40
|
-
# The sessions store.
|
|
41
|
-
# stored in memory.
|
|
42
|
-
|
|
43
|
-
cattr_accessor :store; @@store = SyncHash.new
|
|
49
|
+
# The sessions store.
|
|
44
50
|
|
|
51
|
+
cattr_accessor :store
|
|
52
|
+
|
|
45
53
|
class << self
|
|
46
54
|
|
|
47
55
|
# Set the session store. The following options are
|
|
@@ -49,16 +57,19 @@ class Session < Hash
|
|
|
49
57
|
#
|
|
50
58
|
# * :memory [default]
|
|
51
59
|
# * :drb
|
|
60
|
+
# * :og
|
|
61
|
+
# * :file (not safe yet with multiple process as in fastcgi)
|
|
52
62
|
# * :memcached (not available yet)
|
|
53
|
-
# * :og (not available yet)
|
|
54
|
-
# * :file (not available yet)
|
|
55
63
|
|
|
56
64
|
def store_type=(store_type)
|
|
57
65
|
# gmosx: RDoc complains about this, so lets use an
|
|
58
66
|
# eval, AAAAAAAARGH!
|
|
59
67
|
# require "nitro/session/#{store_type}"
|
|
68
|
+
Logger.debug "Using #{store_type} sessions."
|
|
69
|
+
|
|
60
70
|
eval %{ require 'nitro/session/#{store_type}' }
|
|
61
71
|
end
|
|
72
|
+
alias_method :set_store_type, :store_type=
|
|
62
73
|
|
|
63
74
|
# Lookup the session in the store by using the session
|
|
64
75
|
# cookie value as a key. If the session does not exist
|
|
@@ -74,6 +85,9 @@ class Session < Hash
|
|
|
74
85
|
# Create new session.
|
|
75
86
|
session = Session.new(context)
|
|
76
87
|
cookie = Cookie.new(Session.cookie_name, session.session_id)
|
|
88
|
+
if Session.cookie_expires
|
|
89
|
+
cookie.expires = Time.now + Session.keepalive
|
|
90
|
+
end
|
|
77
91
|
context.add_cookie(cookie)
|
|
78
92
|
Session.store[session.session_id] = session
|
|
79
93
|
else
|
|
@@ -84,54 +98,24 @@ class Session < Hash
|
|
|
84
98
|
return session
|
|
85
99
|
end
|
|
86
100
|
|
|
87
|
-
# Perform session garbage collection. Typically this method
|
|
88
|
-
# is called from a cron like mechanism (for example using
|
|
89
|
-
# script/runner).
|
|
90
|
-
|
|
91
|
-
def garbage_collection!
|
|
92
|
-
Session.store.delete_if { |key, s| s.invalid? }
|
|
93
|
-
end
|
|
94
|
-
alias_method :gc!, :garbage_collection!
|
|
95
|
-
|
|
96
|
-
# Helper method that returns all sessions.
|
|
97
|
-
|
|
98
|
-
def all
|
|
99
|
-
Session.store.values
|
|
100
|
-
end
|
|
101
|
-
|
|
102
101
|
end
|
|
102
|
+
|
|
103
|
+
# By default sessions are stored in memory.
|
|
104
|
+
#--
|
|
105
|
+
# gmosx: should be placed here.
|
|
106
|
+
#++
|
|
103
107
|
|
|
108
|
+
set_store_type(:memory)
|
|
109
|
+
|
|
104
110
|
# The unique id of this session.
|
|
105
111
|
|
|
106
112
|
attr_reader :session_id
|
|
107
113
|
|
|
108
|
-
# The time this session was created.
|
|
109
|
-
|
|
110
|
-
attr_reader :ctime
|
|
111
|
-
alias_method :create_time, :ctime
|
|
112
|
-
|
|
113
|
-
# The time this session was last modified.
|
|
114
|
-
|
|
115
|
-
attr_accessor :mtime
|
|
116
|
-
alias_method :modify_time, :mtime
|
|
117
|
-
|
|
118
|
-
# The time this session was last accessed.
|
|
119
|
-
|
|
120
|
-
attr_accessor :atime
|
|
121
|
-
alias_method :access_time, :mtime
|
|
122
|
-
|
|
123
114
|
# Create the session for the given context.
|
|
124
115
|
|
|
125
116
|
def initialize(context = nil)
|
|
126
117
|
@session_id = create_id
|
|
127
|
-
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
# Like the unix touch command, updates
|
|
131
|
-
# the atime to now.
|
|
132
|
-
|
|
133
|
-
def touch!
|
|
134
|
-
@atime = Time.now
|
|
118
|
+
expires_after(Session.keepalive)
|
|
135
119
|
end
|
|
136
120
|
|
|
137
121
|
# Synchronize the session store, by
|
|
@@ -143,18 +127,9 @@ class Session < Hash
|
|
|
143
127
|
end
|
|
144
128
|
alias_method :restore, :sync
|
|
145
129
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
Time.now < @atime + Session.keepalive
|
|
150
|
-
end
|
|
151
|
-
alias_method :alive?, :valid?
|
|
152
|
-
|
|
153
|
-
# The reverse of keepalive?
|
|
154
|
-
|
|
155
|
-
def invalid?
|
|
156
|
-
Time.now > @atime + Session.keepalive
|
|
157
|
-
end
|
|
130
|
+
def touch!
|
|
131
|
+
expires_after(Session.keepalive)
|
|
132
|
+
end
|
|
158
133
|
|
|
159
134
|
protected
|
|
160
135
|
|
|
@@ -180,3 +155,4 @@ end
|
|
|
180
155
|
end
|
|
181
156
|
|
|
182
157
|
# * George Moschovitis <gm@navel.gr>
|
|
158
|
+
# * Guillaume Pierronnet <guillaume.pierronnet@gmail.com>
|
data/lib/nitro/session/file.rb
CHANGED
|
@@ -1,40 +1,59 @@
|
|
|
1
1
|
#!/usr/bin/env ruby
|
|
2
2
|
|
|
3
3
|
require 'nitro/session'
|
|
4
|
+
require 'fileutils'
|
|
5
|
+
require 'tmpdir'
|
|
4
6
|
|
|
5
7
|
module Nitro
|
|
6
|
-
class FileSessionStore
|
|
7
|
-
# The session keepalive time. The session is eligable for
|
|
8
|
-
# garbage collection after this time passes.
|
|
9
8
|
|
|
10
|
-
|
|
9
|
+
# A Session manager that persists sessions on disk.
|
|
10
|
+
#
|
|
11
|
+
# TODO: safe locking of files, because Nitro can be multiprocess
|
|
12
|
+
|
|
13
|
+
class FileSessionStore
|
|
14
|
+
setting :path, :default => "#{Dir.tmpdir}/nitro_session_#{Session.cookie_name}", :doc => 'The directory to store file session'
|
|
11
15
|
|
|
12
16
|
def initialize
|
|
13
17
|
@path = FileSessionStore.path
|
|
14
|
-
|
|
18
|
+
FileUtils.mkdir_p(@path)
|
|
15
19
|
end
|
|
16
20
|
|
|
17
21
|
def []=(k,v)
|
|
18
|
-
File.
|
|
22
|
+
fn = File.join(@path, k.to_s)
|
|
23
|
+
encode_file(fn, v)
|
|
19
24
|
end
|
|
20
25
|
|
|
21
26
|
def [](k)
|
|
22
|
-
fn = File.join(@path, k)
|
|
27
|
+
fn = File.join(@path, k.to_s)
|
|
23
28
|
return nil unless File.exists?(fn)
|
|
29
|
+
decode_file(fn)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def gc!
|
|
33
|
+
now = Time.now
|
|
34
|
+
all.each do |fn|
|
|
35
|
+
expire_time = File.stat(fn).atime + Session.keepalive
|
|
36
|
+
File.delete(fn) if now > expire_time
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def all
|
|
41
|
+
Dir.glob( File.join(@path, '*' ) )
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
def decode_file(fn)
|
|
24
47
|
Marshal.load( File.read(fn) )
|
|
25
48
|
end
|
|
26
49
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
#
|
|
31
|
-
# def each
|
|
32
|
-
# #yield
|
|
33
|
-
# end
|
|
50
|
+
def encode_file(fn, value)
|
|
51
|
+
File.open(fn, "w") { |f| f.write(Marshal.dump(value)) }
|
|
52
|
+
end
|
|
34
53
|
|
|
35
54
|
end
|
|
36
55
|
|
|
37
56
|
Session.store = FileSessionStore.new
|
|
38
57
|
end
|
|
39
58
|
|
|
40
|
-
# * Guillaume Pierronnet <guillaume.pierronnet@gmail.com>
|
|
59
|
+
# * Guillaume Pierronnet <guillaume.pierronnet@gmail.com>
|
data/lib/nitro/session/memory.rb
CHANGED
|
@@ -1,10 +1,19 @@
|
|
|
1
|
-
|
|
1
|
+
module Nitro
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
class MemorySessionStore < SyncHash
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
# Perform session garbage collection. Typically this method
|
|
6
|
+
# is called from a cron like mechanism (for example using
|
|
7
|
+
# script/runner).
|
|
8
|
+
|
|
9
|
+
def gc!
|
|
10
|
+
delete_if { |key, s| s.expired? }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
alias :all :values
|
|
14
|
+
end
|
|
6
15
|
|
|
7
|
-
Session.store =
|
|
16
|
+
Session.store = MemorySessionStore.new
|
|
8
17
|
|
|
9
18
|
end
|
|
10
19
|
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
require 'og'
|
|
2
|
+
require 'nitro/session'
|
|
3
|
+
require 'base64'
|
|
4
|
+
|
|
5
|
+
module Nitro
|
|
6
|
+
|
|
7
|
+
class OgSession < Session
|
|
8
|
+
prop_accessor :sessionid, String
|
|
9
|
+
prop_accessor :expires, Time
|
|
10
|
+
prop_accessor :content
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# A Session manager that persists sessions on an Og store.
|
|
14
|
+
|
|
15
|
+
class OgSessionStore
|
|
16
|
+
|
|
17
|
+
def []=(k,v)
|
|
18
|
+
unless s = OgSession.find_by_sessionid(k.to_s)
|
|
19
|
+
s = OgSession.new
|
|
20
|
+
s.sessionid = k.to_s
|
|
21
|
+
end
|
|
22
|
+
#s.content = v.to_yaml
|
|
23
|
+
s.content = encode(v)
|
|
24
|
+
s.save
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def [](k)
|
|
28
|
+
s = OgSession.find_by_sessionid(k.to_s) and decode(s.content)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def gc!
|
|
32
|
+
now = Og.manager.store.quote(Time.now)
|
|
33
|
+
OgSession.find(:condition => "expires < #{now}").each {|s| s.delete }
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def all
|
|
37
|
+
OgSession.all
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def encode(c)
|
|
43
|
+
Base64.encode64(Marshal.dump(c))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def decode(c)
|
|
47
|
+
Marshal::load(Base64.decode64(c))
|
|
48
|
+
#s.content = YAML::load(s.content)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
Session.store = OgSessionStore.new
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# * Guillaume Pierronnet <guillaume.pierronnet@gmail.com>
|
data/proto/public/js/controls.js
CHANGED
|
@@ -80,7 +80,10 @@ Autocompleter.Base.prototype = {
|
|
|
80
80
|
|
|
81
81
|
show: function() {
|
|
82
82
|
if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
|
|
83
|
-
if(!this.iefix &&
|
|
83
|
+
if(!this.iefix &&
|
|
84
|
+
(navigator.appVersion.indexOf('MSIE')>0) &&
|
|
85
|
+
(navigator.userAgent.indexOf('Opera')<0) &&
|
|
86
|
+
(Element.getStyle(this.update, 'position')=='absolute')) {
|
|
84
87
|
new Insertion.After(this.update,
|
|
85
88
|
'<iframe id="' + this.update.id + '_iefix" '+
|
|
86
89
|
'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
|
|
@@ -718,4 +721,30 @@ Ajax.InPlaceEditor.prototype = {
|
|
|
718
721
|
Event.stopObserving(this.options.externalControl, 'mouseout', this.mouseoutListener);
|
|
719
722
|
}
|
|
720
723
|
}
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
// Delayed observer, like Form.Element.Observer,
|
|
727
|
+
// but waits for delay after last key input
|
|
728
|
+
// Ideal for live-search fields
|
|
729
|
+
|
|
730
|
+
Form.Element.DelayedObserver = Class.create();
|
|
731
|
+
Form.Element.DelayedObserver.prototype = {
|
|
732
|
+
initialize: function(element, delay, callback) {
|
|
733
|
+
this.delay = delay || 0.5;
|
|
734
|
+
this.element = $(element);
|
|
735
|
+
this.callback = callback;
|
|
736
|
+
this.timer = null;
|
|
737
|
+
this.lastValue = $F(this.element);
|
|
738
|
+
Event.observe(this.element,'keyup',this.delayedListener.bindAsEventListener(this));
|
|
739
|
+
},
|
|
740
|
+
delayedListener: function(event) {
|
|
741
|
+
if(this.lastValue == $F(this.element)) return;
|
|
742
|
+
if(this.timer) clearTimeout(this.timer);
|
|
743
|
+
this.timer = setTimeout(this.onTimerEvent.bind(this), this.delay * 1000);
|
|
744
|
+
this.lastValue = $F(this.element);
|
|
745
|
+
},
|
|
746
|
+
onTimerEvent: function() {
|
|
747
|
+
this.timer = null;
|
|
748
|
+
this.callback(this.element, $F(this.element));
|
|
749
|
+
}
|
|
721
750
|
};
|
data/proto/public/js/dragdrop.js
CHANGED
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
|
2
2
|
//
|
|
3
|
-
// Element.Class part Copyright (c) 2005 by Rick Olson
|
|
4
|
-
//
|
|
5
3
|
// See scriptaculous.js for full license.
|
|
6
4
|
|
|
7
5
|
/*--------------------------------------------------------------------------*/
|
|
@@ -10,7 +8,7 @@ var Droppables = {
|
|
|
10
8
|
drops: [],
|
|
11
9
|
|
|
12
10
|
remove: function(element) {
|
|
13
|
-
this.drops = this.drops.reject(function(d) { return d.element
|
|
11
|
+
this.drops = this.drops.reject(function(d) { return d.element==$(element) });
|
|
14
12
|
},
|
|
15
13
|
|
|
16
14
|
add: function(element) {
|
|
@@ -31,6 +29,8 @@ var Droppables = {
|
|
|
31
29
|
options._containers.push($(containment));
|
|
32
30
|
}
|
|
33
31
|
}
|
|
32
|
+
|
|
33
|
+
if(options.accept) options.accept = [options.accept].flatten();
|
|
34
34
|
|
|
35
35
|
Element.makePositioned(element); // fix IE
|
|
36
36
|
options.element = element;
|
|
@@ -43,55 +43,50 @@ var Droppables = {
|
|
|
43
43
|
return drop._containers.detect(function(c) { return parentNode == c });
|
|
44
44
|
},
|
|
45
45
|
|
|
46
|
-
isAffected: function(
|
|
46
|
+
isAffected: function(point, element, drop) {
|
|
47
47
|
return (
|
|
48
48
|
(drop.element!=element) &&
|
|
49
49
|
((!drop._containers) ||
|
|
50
50
|
this.isContained(element, drop)) &&
|
|
51
51
|
((!drop.accept) ||
|
|
52
|
-
(Element.
|
|
53
|
-
|
|
52
|
+
(Element.classNames(element).detect(
|
|
53
|
+
function(v) { return drop.accept.include(v) } ) )) &&
|
|
54
|
+
Position.within(drop.element, point[0], point[1]) );
|
|
54
55
|
},
|
|
55
56
|
|
|
56
57
|
deactivate: function(drop) {
|
|
57
58
|
if(drop.hoverclass)
|
|
58
|
-
Element.
|
|
59
|
+
Element.removeClassName(drop.element, drop.hoverclass);
|
|
59
60
|
this.last_active = null;
|
|
60
61
|
},
|
|
61
62
|
|
|
62
63
|
activate: function(drop) {
|
|
63
|
-
if(this.last_active) this.deactivate(this.last_active);
|
|
64
64
|
if(drop.hoverclass)
|
|
65
|
-
Element.
|
|
65
|
+
Element.addClassName(drop.element, drop.hoverclass);
|
|
66
66
|
this.last_active = drop;
|
|
67
67
|
},
|
|
68
68
|
|
|
69
|
-
show: function(
|
|
69
|
+
show: function(point, element) {
|
|
70
70
|
if(!this.drops.length) return;
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
var i = this.drops.length-1; do {
|
|
76
|
-
var drop = this.drops[i];
|
|
77
|
-
if(this.isAffected(pX, pY, element, drop)) {
|
|
71
|
+
|
|
72
|
+
if(this.last_active) this.deactivate(this.last_active);
|
|
73
|
+
this.drops.each( function(drop) {
|
|
74
|
+
if(Droppables.isAffected(point, element, drop)) {
|
|
78
75
|
if(drop.onHover)
|
|
79
76
|
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
|
|
80
77
|
if(drop.greedy) {
|
|
81
|
-
|
|
82
|
-
|
|
78
|
+
Droppables.activate(drop);
|
|
79
|
+
throw $break;
|
|
83
80
|
}
|
|
84
81
|
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if(this.last_active) this.deactivate(this.last_active);
|
|
82
|
+
});
|
|
88
83
|
},
|
|
89
84
|
|
|
90
85
|
fire: function(event, element) {
|
|
91
86
|
if(!this.last_active) return;
|
|
92
87
|
Position.prepare();
|
|
93
88
|
|
|
94
|
-
if (this.isAffected(Event.pointerX(event), Event.pointerY(event), element, this.last_active))
|
|
89
|
+
if (this.isAffected([Event.pointerX(event), Event.pointerY(event)], element, this.last_active))
|
|
95
90
|
if (this.last_active.onDrop)
|
|
96
91
|
this.last_active.onDrop(element, this.last_active.element, event);
|
|
97
92
|
},
|
|
@@ -103,15 +98,84 @@ var Droppables = {
|
|
|
103
98
|
}
|
|
104
99
|
|
|
105
100
|
var Draggables = {
|
|
101
|
+
drags: [],
|
|
106
102
|
observers: [],
|
|
103
|
+
|
|
104
|
+
register: function(draggable) {
|
|
105
|
+
if(this.drags.length == 0) {
|
|
106
|
+
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
|
|
107
|
+
this.eventMouseMove = this.updateDrag.bindAsEventListener(this);
|
|
108
|
+
this.eventKeypress = this.keyPress.bindAsEventListener(this);
|
|
109
|
+
|
|
110
|
+
Event.observe(document, "mouseup", this.eventMouseUp);
|
|
111
|
+
Event.observe(document, "mousemove", this.eventMouseMove);
|
|
112
|
+
Event.observe(document, "keypress", this.eventKeypress);
|
|
113
|
+
}
|
|
114
|
+
this.drags.push(draggable);
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
unregister: function(draggable) {
|
|
118
|
+
this.drags = this.drags.reject(function(d) { return d==draggable });
|
|
119
|
+
if(this.drags.length == 0) {
|
|
120
|
+
Event.stopObserving(document, "mouseup", this.eventMouseUp);
|
|
121
|
+
Event.stopObserving(document, "mousemove", this.eventMouseMove);
|
|
122
|
+
Event.stopObserving(document, "keypress", this.eventKeypress);
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
activate: function(draggable) {
|
|
127
|
+
window.focus(); // allows keypress events if window isn't currently focused, fails for Safari
|
|
128
|
+
this.activeDraggable = draggable;
|
|
129
|
+
},
|
|
130
|
+
|
|
131
|
+
deactivate: function(draggbale) {
|
|
132
|
+
this.activeDraggable = null;
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
updateDrag: function(event) {
|
|
136
|
+
if(!this.activeDraggable) return;
|
|
137
|
+
var pointer = [Event.pointerX(event), Event.pointerY(event)];
|
|
138
|
+
// Mozilla-based browsers fire successive mousemove events with
|
|
139
|
+
// the same coordinates, prevent needless redrawing (moz bug?)
|
|
140
|
+
if(this._lastPointer && (this._lastPointer.inspect() == pointer.inspect())) return;
|
|
141
|
+
this._lastPointer = pointer;
|
|
142
|
+
this.activeDraggable.updateDrag(event, pointer);
|
|
143
|
+
},
|
|
144
|
+
|
|
145
|
+
endDrag: function(event) {
|
|
146
|
+
if(!this.activeDraggable) return;
|
|
147
|
+
this._lastPointer = null;
|
|
148
|
+
this.activeDraggable.endDrag(event);
|
|
149
|
+
},
|
|
150
|
+
|
|
151
|
+
keyPress: function(event) {
|
|
152
|
+
if(this.activeDraggable)
|
|
153
|
+
this.activeDraggable.keyPress(event);
|
|
154
|
+
},
|
|
155
|
+
|
|
107
156
|
addObserver: function(observer) {
|
|
108
|
-
this.observers.push(observer);
|
|
157
|
+
this.observers.push(observer);
|
|
158
|
+
this._cacheObserverCallbacks();
|
|
109
159
|
},
|
|
110
|
-
|
|
160
|
+
|
|
161
|
+
removeObserver: function(element) { // element instead of observer fixes mem leaks
|
|
111
162
|
this.observers = this.observers.reject( function(o) { return o.element==element });
|
|
163
|
+
this._cacheObserverCallbacks();
|
|
112
164
|
},
|
|
113
|
-
|
|
114
|
-
|
|
165
|
+
|
|
166
|
+
notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
|
|
167
|
+
if(this[eventName+'Count'] > 0)
|
|
168
|
+
this.observers.each( function(o) {
|
|
169
|
+
if(o[eventName]) o[eventName](eventName, draggable, event);
|
|
170
|
+
});
|
|
171
|
+
},
|
|
172
|
+
|
|
173
|
+
_cacheObserverCallbacks: function() {
|
|
174
|
+
['onStart','onEnd','onDrag'].each( function(eventName) {
|
|
175
|
+
Draggables[eventName+'Count'] = Draggables.observers.select(
|
|
176
|
+
function(o) { return o[eventName]; }
|
|
177
|
+
).length;
|
|
178
|
+
});
|
|
115
179
|
}
|
|
116
180
|
}
|
|
117
181
|
|
|
@@ -127,68 +191,48 @@ Draggable.prototype = {
|
|
|
127
191
|
},
|
|
128
192
|
reverteffect: function(element, top_offset, left_offset) {
|
|
129
193
|
var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
|
|
130
|
-
new Effect.MoveBy(element, -top_offset, -left_offset, {duration:dur});
|
|
194
|
+
element._revert = new Effect.MoveBy(element, -top_offset, -left_offset, {duration:dur});
|
|
131
195
|
},
|
|
132
196
|
endeffect: function(element) {
|
|
133
|
-
|
|
197
|
+
new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0});
|
|
134
198
|
},
|
|
135
199
|
zindex: 1000,
|
|
136
|
-
revert: false
|
|
200
|
+
revert: false,
|
|
201
|
+
snap: false // false, or xy or [x,y] or function(x,y){ return [x,y] }
|
|
137
202
|
}, arguments[1] || {});
|
|
138
203
|
|
|
139
|
-
this.element
|
|
204
|
+
this.element = $(element);
|
|
205
|
+
|
|
140
206
|
if(options.handle && (typeof options.handle == 'string'))
|
|
141
|
-
this.handle = Element.
|
|
142
|
-
|
|
207
|
+
this.handle = Element.childrenWithClassName(this.element, options.handle)[0];
|
|
143
208
|
if(!this.handle) this.handle = $(options.handle);
|
|
144
209
|
if(!this.handle) this.handle = this.element;
|
|
145
210
|
|
|
146
211
|
Element.makePositioned(this.element); // fix IE
|
|
147
212
|
|
|
148
|
-
this.
|
|
149
|
-
this.
|
|
150
|
-
this.
|
|
151
|
-
this.originalTop = this.currentTop();
|
|
152
|
-
this.originalX = this.element.offsetLeft;
|
|
153
|
-
this.originalY = this.element.offsetTop;
|
|
154
|
-
|
|
155
|
-
this.options = options;
|
|
213
|
+
this.delta = this.currentDelta();
|
|
214
|
+
this.options = options;
|
|
215
|
+
this.dragging = false;
|
|
156
216
|
|
|
157
|
-
this.
|
|
158
|
-
this.
|
|
159
|
-
|
|
160
|
-
this.eventMouseDown = this.startDrag.bindAsEventListener(this);
|
|
161
|
-
this.eventMouseUp = this.endDrag.bindAsEventListener(this);
|
|
162
|
-
this.eventMouseMove = this.update.bindAsEventListener(this);
|
|
163
|
-
this.eventKeypress = this.keyPress.bindAsEventListener(this);
|
|
217
|
+
this.eventMouseDown = this.initDrag.bindAsEventListener(this);
|
|
218
|
+
Event.observe(this.handle, "mousedown", this.eventMouseDown);
|
|
164
219
|
|
|
165
|
-
|
|
220
|
+
Draggables.register(this);
|
|
166
221
|
},
|
|
222
|
+
|
|
167
223
|
destroy: function() {
|
|
168
224
|
Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
|
|
169
|
-
|
|
225
|
+
Draggables.unregister(this);
|
|
170
226
|
},
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
},
|
|
177
|
-
unregisterEvents: function() {
|
|
178
|
-
//if(!this.active) return;
|
|
179
|
-
//Event.stopObserving(document, "mouseup", this.eventMouseUp);
|
|
180
|
-
//Event.stopObserving(document, "mousemove", this.eventMouseMove);
|
|
181
|
-
//Event.stopObserving(document, "keypress", this.eventKeypress);
|
|
182
|
-
},
|
|
183
|
-
currentLeft: function() {
|
|
184
|
-
return parseInt(this.element.style.left || '0');
|
|
227
|
+
|
|
228
|
+
currentDelta: function() {
|
|
229
|
+
return([
|
|
230
|
+
parseInt(this.element.style.left || '0'),
|
|
231
|
+
parseInt(this.element.style.top || '0')]);
|
|
185
232
|
},
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
startDrag: function(event) {
|
|
190
|
-
if(Event.isLeftClick(event)) {
|
|
191
|
-
|
|
233
|
+
|
|
234
|
+
initDrag: function(event) {
|
|
235
|
+
if(Event.isLeftClick(event)) {
|
|
192
236
|
// abort on form elements, fixes a Firefox issue
|
|
193
237
|
var src = Event.element(event);
|
|
194
238
|
if(src.tagName && (
|
|
@@ -196,20 +240,53 @@ Draggable.prototype = {
|
|
|
196
240
|
src.tagName=='SELECT' ||
|
|
197
241
|
src.tagName=='BUTTON' ||
|
|
198
242
|
src.tagName=='TEXTAREA')) return;
|
|
243
|
+
|
|
244
|
+
if(this.element._revert) {
|
|
245
|
+
this.element._revert.cancel();
|
|
246
|
+
this.element._revert = null;
|
|
247
|
+
}
|
|
199
248
|
|
|
200
|
-
// this.registerEvents();
|
|
201
|
-
this.active = true;
|
|
202
249
|
var pointer = [Event.pointerX(event), Event.pointerY(event)];
|
|
203
|
-
var
|
|
204
|
-
this.
|
|
205
|
-
|
|
250
|
+
var pos = Position.cumulativeOffset(this.element);
|
|
251
|
+
this.offset = [0,1].map( function(i) { return (pointer[i] - pos[i]) });
|
|
252
|
+
|
|
253
|
+
Draggables.activate(this);
|
|
206
254
|
Event.stop(event);
|
|
207
255
|
}
|
|
208
256
|
},
|
|
257
|
+
|
|
258
|
+
startDrag: function(event) {
|
|
259
|
+
this.dragging = true;
|
|
260
|
+
|
|
261
|
+
if(this.options.zindex) {
|
|
262
|
+
this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
|
|
263
|
+
this.element.style.zIndex = this.options.zindex;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if(this.options.ghosting) {
|
|
267
|
+
this._clone = this.element.cloneNode(true);
|
|
268
|
+
Position.absolutize(this.element);
|
|
269
|
+
this.element.parentNode.insertBefore(this._clone, this.element);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
Draggables.notify('onStart', this, event);
|
|
273
|
+
if(this.options.starteffect) this.options.starteffect(this.element);
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
updateDrag: function(event, pointer) {
|
|
277
|
+
if(!this.dragging) this.startDrag(event);
|
|
278
|
+
Position.prepare();
|
|
279
|
+
Droppables.show(pointer, this.element);
|
|
280
|
+
Draggables.notify('onDrag', this, event);
|
|
281
|
+
this.draw(pointer);
|
|
282
|
+
if(this.options.change) this.options.change(this);
|
|
283
|
+
|
|
284
|
+
// fix AppleWebKit rendering
|
|
285
|
+
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
|
|
286
|
+
Event.stop(event);
|
|
287
|
+
},
|
|
288
|
+
|
|
209
289
|
finishDrag: function(event, success) {
|
|
210
|
-
// this.unregisterEvents();
|
|
211
|
-
|
|
212
|
-
this.active = false;
|
|
213
290
|
this.dragging = false;
|
|
214
291
|
|
|
215
292
|
if(this.options.ghosting) {
|
|
@@ -219,18 +296,17 @@ Draggable.prototype = {
|
|
|
219
296
|
}
|
|
220
297
|
|
|
221
298
|
if(success) Droppables.fire(event, this.element);
|
|
222
|
-
Draggables.notify('onEnd', this);
|
|
299
|
+
Draggables.notify('onEnd', this, event);
|
|
223
300
|
|
|
224
301
|
var revert = this.options.revert;
|
|
225
302
|
if(revert && typeof revert == 'function') revert = revert(this.element);
|
|
226
|
-
|
|
303
|
+
|
|
304
|
+
var d = this.currentDelta();
|
|
227
305
|
if(revert && this.options.reverteffect) {
|
|
228
306
|
this.options.reverteffect(this.element,
|
|
229
|
-
|
|
230
|
-
this.currentLeft()-this.originalLeft);
|
|
307
|
+
d[1]-this.delta[1], d[0]-this.delta[0]);
|
|
231
308
|
} else {
|
|
232
|
-
this.
|
|
233
|
-
this.originalTop = this.currentTop();
|
|
309
|
+
this.delta = d;
|
|
234
310
|
}
|
|
235
311
|
|
|
236
312
|
if(this.options.zindex)
|
|
@@ -239,70 +315,48 @@ Draggable.prototype = {
|
|
|
239
315
|
if(this.options.endeffect)
|
|
240
316
|
this.options.endeffect(this.element);
|
|
241
317
|
|
|
242
|
-
|
|
318
|
+
Draggables.deactivate(this);
|
|
243
319
|
Droppables.reset();
|
|
244
320
|
},
|
|
321
|
+
|
|
245
322
|
keyPress: function(event) {
|
|
246
|
-
if(
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
Event.stop(event);
|
|
250
|
-
}
|
|
251
|
-
}
|
|
323
|
+
if(!event.keyCode==Event.KEY_ESC) return;
|
|
324
|
+
this.finishDrag(event, false);
|
|
325
|
+
Event.stop(event);
|
|
252
326
|
},
|
|
327
|
+
|
|
253
328
|
endDrag: function(event) {
|
|
254
|
-
if(this.
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
this.active = false;
|
|
259
|
-
this.dragging = false;
|
|
329
|
+
if(!this.dragging) return;
|
|
330
|
+
this.finishDrag(event, true);
|
|
331
|
+
Event.stop(event);
|
|
260
332
|
},
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
var
|
|
264
|
-
|
|
265
|
-
|
|
333
|
+
|
|
334
|
+
draw: function(point) {
|
|
335
|
+
var pos = Position.cumulativeOffset(this.element);
|
|
336
|
+
var d = this.currentDelta();
|
|
337
|
+
pos[0] -= d[0]; pos[1] -= d[1];
|
|
338
|
+
|
|
339
|
+
var p = [0,1].map(function(i){ return (point[i]-pos[i]-this.offset[i]) }.bind(this));
|
|
340
|
+
|
|
341
|
+
if(this.options.snap) {
|
|
342
|
+
if(typeof this.options.snap == 'function') {
|
|
343
|
+
p = this.options.snap(p[0],p[1]);
|
|
344
|
+
} else {
|
|
345
|
+
if(this.options.snap instanceof Array) {
|
|
346
|
+
p = p.map( function(v, i) {
|
|
347
|
+
return Math.round(v/this.options.snap[i])*this.options.snap[i] }.bind(this))
|
|
348
|
+
} else {
|
|
349
|
+
p = p.map( function(v) {
|
|
350
|
+
return Math.round(v/this.options.snap)*this.options.snap }.bind(this))
|
|
351
|
+
}
|
|
352
|
+
}}
|
|
353
|
+
|
|
266
354
|
var style = this.element.style;
|
|
267
355
|
if((!this.options.constraint) || (this.options.constraint=='horizontal'))
|
|
268
|
-
style.left =
|
|
356
|
+
style.left = p[0] + "px";
|
|
269
357
|
if((!this.options.constraint) || (this.options.constraint=='vertical'))
|
|
270
|
-
style.top =
|
|
358
|
+
style.top = p[1] + "px";
|
|
271
359
|
if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
|
|
272
|
-
},
|
|
273
|
-
update: function(event) {
|
|
274
|
-
if(this.active) {
|
|
275
|
-
if(!this.dragging) {
|
|
276
|
-
var style = this.element.style;
|
|
277
|
-
this.dragging = true;
|
|
278
|
-
|
|
279
|
-
if(Element.getStyle(this.element,'position')=='')
|
|
280
|
-
style.position = "relative";
|
|
281
|
-
|
|
282
|
-
if(this.options.zindex) {
|
|
283
|
-
this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
|
|
284
|
-
style.zIndex = this.options.zindex;
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if(this.options.ghosting) {
|
|
288
|
-
this._clone = this.element.cloneNode(true);
|
|
289
|
-
Position.absolutize(this.element);
|
|
290
|
-
this.element.parentNode.insertBefore(this._clone, this.element);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
Draggables.notify('onStart', this);
|
|
294
|
-
if(this.options.starteffect) this.options.starteffect(this.element);
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
Droppables.show(event, this.element);
|
|
298
|
-
this.draw(event);
|
|
299
|
-
if(this.options.change) this.options.change(this);
|
|
300
|
-
|
|
301
|
-
// fix AppleWebKit rendering
|
|
302
|
-
if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
|
|
303
|
-
|
|
304
|
-
Event.stop(event);
|
|
305
|
-
}
|
|
306
360
|
}
|
|
307
361
|
}
|
|
308
362
|
|
|
@@ -315,9 +369,11 @@ SortableObserver.prototype = {
|
|
|
315
369
|
this.observer = observer;
|
|
316
370
|
this.lastValue = Sortable.serialize(this.element);
|
|
317
371
|
},
|
|
372
|
+
|
|
318
373
|
onStart: function() {
|
|
319
374
|
this.lastValue = Sortable.serialize(this.element);
|
|
320
375
|
},
|
|
376
|
+
|
|
321
377
|
onEnd: function() {
|
|
322
378
|
Sortable.unmark();
|
|
323
379
|
if(this.lastValue != Sortable.serialize(this.element))
|
|
@@ -327,10 +383,12 @@ SortableObserver.prototype = {
|
|
|
327
383
|
|
|
328
384
|
var Sortable = {
|
|
329
385
|
sortables: new Array(),
|
|
386
|
+
|
|
330
387
|
options: function(element){
|
|
331
388
|
element = $(element);
|
|
332
389
|
return this.sortables.detect(function(s) { return s.element == element });
|
|
333
390
|
},
|
|
391
|
+
|
|
334
392
|
destroy: function(element){
|
|
335
393
|
element = $(element);
|
|
336
394
|
this.sortables.findAll(function(s) { return s.element == element }).each(function(s){
|
|
@@ -340,6 +398,7 @@ var Sortable = {
|
|
|
340
398
|
});
|
|
341
399
|
this.sortables = this.sortables.reject(function(s) { return s.element == element });
|
|
342
400
|
},
|
|
401
|
+
|
|
343
402
|
create: function(element) {
|
|
344
403
|
element = $(element);
|
|
345
404
|
var options = Object.extend({
|
|
@@ -413,7 +472,7 @@ var Sortable = {
|
|
|
413
472
|
(this.findElements(element, options) || []).each( function(e) {
|
|
414
473
|
// handles are per-draggable
|
|
415
474
|
var handle = options.handle ?
|
|
416
|
-
Element.
|
|
475
|
+
Element.childrenWithClassName(e, options.handle)[0] : e;
|
|
417
476
|
options.draggables.push(
|
|
418
477
|
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
|
|
419
478
|
Droppables.add(e, options_for_droppable);
|
|
@@ -433,8 +492,8 @@ var Sortable = {
|
|
|
433
492
|
if(!element.hasChildNodes()) return null;
|
|
434
493
|
var elements = [];
|
|
435
494
|
$A(element.childNodes).each( function(e) {
|
|
436
|
-
if(e.tagName && e.tagName==options.tag.toUpperCase() &&
|
|
437
|
-
(!options.only || (Element.
|
|
495
|
+
if(e.tagName && e.tagName.toUpperCase()==options.tag.toUpperCase() &&
|
|
496
|
+
(!options.only || (Element.hasClassName(e, options.only))))
|
|
438
497
|
elements.push(e);
|
|
439
498
|
if(options.tree) {
|
|
440
499
|
var grandchildren = this.findElements(e, options);
|
|
@@ -491,14 +550,20 @@ var Sortable = {
|
|
|
491
550
|
if(!Sortable._marker) {
|
|
492
551
|
Sortable._marker = $('dropmarker') || document.createElement('DIV');
|
|
493
552
|
Element.hide(Sortable._marker);
|
|
494
|
-
Element.
|
|
553
|
+
Element.addClassName(Sortable._marker, 'dropmarker');
|
|
495
554
|
Sortable._marker.style.position = 'absolute';
|
|
496
555
|
document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
|
|
497
556
|
}
|
|
498
557
|
var offsets = Position.cumulativeOffset(dropon);
|
|
499
|
-
Sortable._marker.style.top = offsets[1] + 'px';
|
|
500
|
-
if(position=='after') Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
|
|
501
558
|
Sortable._marker.style.left = offsets[0] + 'px';
|
|
559
|
+
Sortable._marker.style.top = offsets[1] + 'px';
|
|
560
|
+
|
|
561
|
+
if(position=='after')
|
|
562
|
+
if(sortable.overlap == 'horizontal')
|
|
563
|
+
Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
|
|
564
|
+
else
|
|
565
|
+
Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
|
|
566
|
+
|
|
502
567
|
Element.show(Sortable._marker);
|
|
503
568
|
},
|
|
504
569
|
|
|
@@ -511,9 +576,9 @@ var Sortable = {
|
|
|
511
576
|
name: element.id,
|
|
512
577
|
format: sortableOptions.format || /^[^_]*_(.*)$/
|
|
513
578
|
}, arguments[1] || {});
|
|
514
|
-
return $(this.findElements(element, options) || []).
|
|
579
|
+
return $(this.findElements(element, options) || []).map( function(item) {
|
|
515
580
|
return (encodeURIComponent(options.name) + "[]=" +
|
|
516
581
|
encodeURIComponent(item.id.match(options.format) ? item.id.match(options.format)[1] : ''));
|
|
517
582
|
}).join("&");
|
|
518
583
|
}
|
|
519
|
-
}
|
|
584
|
+
}
|