nitro 0.29.0 → 0.30.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 +410 -0
- data/ProjectInfo +36 -44
- data/README +5 -5
- data/doc/AUTHORS +6 -0
- data/doc/RELEASES +159 -2
- data/lib/glue/sweeper.rb +2 -2
- data/lib/glue/webfile.rb +14 -1
- data/lib/nitro.rb +6 -9
- data/lib/nitro/adapter/mongrel.rb +36 -43
- data/lib/nitro/adapter/scgi.rb +1 -1
- data/lib/nitro/adapter/webrick.rb +96 -24
- data/lib/nitro/caching/actions.rb +2 -1
- data/lib/nitro/caching/fragments.rb +1 -8
- data/lib/nitro/caching/output.rb +14 -4
- data/lib/nitro/cgi.rb +19 -21
- data/lib/nitro/cgi/cookie.rb +5 -1
- data/lib/nitro/cgi/request.rb +20 -4
- data/lib/nitro/compiler.rb +74 -28
- data/lib/nitro/compiler/cleanup.rb +1 -1
- data/lib/nitro/compiler/elements.rb +1 -2
- data/lib/nitro/compiler/localization.rb +1 -1
- data/lib/nitro/compiler/markup.rb +1 -1
- data/lib/nitro/compiler/script.rb +52 -44
- data/lib/nitro/compiler/squeeze.rb +4 -3
- data/lib/nitro/compiler/xslt.rb +7 -6
- data/lib/nitro/context.rb +39 -20
- data/lib/nitro/controller.rb +24 -5
- data/lib/nitro/dispatcher.rb +13 -5
- data/lib/nitro/global.rb +63 -0
- data/lib/nitro/helper/feed.rb +432 -0
- data/lib/nitro/helper/form.rb +11 -3
- data/lib/nitro/helper/form/builder.rb +140 -0
- data/lib/nitro/helper/form/controls.rb +2 -1
- data/lib/nitro/helper/javascript.rb +6 -0
- data/lib/nitro/helper/javascript/morphing.rb +13 -6
- data/lib/nitro/helper/xhtml.rb +42 -6
- data/lib/nitro/helper/xml.rb +3 -0
- data/lib/nitro/part.rb +2 -2
- data/lib/nitro/render.rb +7 -2
- data/lib/nitro/router.rb +57 -16
- data/lib/nitro/scaffolding.rb +29 -20
- data/lib/nitro/server.rb +4 -10
- data/lib/nitro/server/drb.rb +1 -1
- data/lib/nitro/server/runner.rb +10 -0
- data/lib/nitro/session.rb +31 -12
- data/lib/nitro/session/drb.rb +13 -1
- data/lib/nitro/session/file.rb +1 -1
- data/lib/nitro/session/memcached.rb +1 -1
- data/lib/nitro/session/memory.rb +1 -1
- data/lib/nitro/session/og.rb +1 -1
- data/lib/nitro/test/testcase.rb +3 -0
- data/proto/public/error.xhtml +5 -5
- data/proto/public/js/controls.js +2 -2
- data/proto/public/js/dragdrop.js +320 -79
- data/proto/public/js/effects.js +200 -152
- data/proto/public/js/prototype.js +284 -63
- data/proto/public/js/scriptaculous.js +7 -5
- data/proto/public/js/unittest.js +11 -0
- data/proto/public/scaffold/advanced_search.xhtml +30 -0
- data/proto/public/scaffold/list.xhtml +8 -1
- data/proto/public/scaffold/search.xhtml +2 -1
- data/proto/script/scgi_service +1 -1
- data/src/part/admin/controller.rb +1 -1
- data/src/part/admin/skin.rb +1 -1
- data/test/nitro/CONFIG.rb +3 -0
- data/test/nitro/adapter/tc_webrick.rb +1 -1
- data/test/nitro/cgi/tc_cookie.rb +1 -1
- data/test/nitro/cgi/tc_request.rb +5 -5
- data/test/nitro/compiler/tc_client_morpher.rb +47 -0
- data/test/nitro/compiler/tc_compiler.rb +2 -0
- data/test/nitro/helper/tc_feed.rb +138 -0
- data/test/nitro/helper/tc_pager.rb +1 -1
- data/test/nitro/helper/tc_rss.rb +1 -1
- data/test/nitro/helper/tc_table.rb +1 -1
- data/test/nitro/helper/tc_xhtml.rb +1 -1
- data/test/nitro/tc_caching.rb +1 -1
- data/test/nitro/tc_cgi.rb +1 -1
- data/test/nitro/tc_context.rb +1 -1
- data/test/nitro/tc_controller.rb +31 -3
- data/test/nitro/tc_controller_aspect.rb +1 -1
- data/test/nitro/tc_dispatcher.rb +1 -1
- data/test/nitro/tc_element.rb +1 -1
- data/test/nitro/tc_flash.rb +1 -1
- data/test/nitro/tc_helper.rb +1 -1
- data/test/nitro/tc_render.rb +6 -6
- data/test/nitro/tc_router.rb +8 -4
- data/test/nitro/tc_server.rb +1 -3
- data/test/nitro/tc_session.rb +1 -3
- metadata +107 -104
- data/Rakefile +0 -232
- data/lib/nitro/adapter/acgi.rb +0 -237
- data/proto/public/Makefile.acgi +0 -40
- data/proto/public/acgi.c +0 -138
data/lib/nitro/server.rb
CHANGED
@@ -88,16 +88,6 @@ class Server
|
|
88
88
|
@map['/'] = options[:controller] if options[:controller]
|
89
89
|
@dispatcher = options[:dispatcher] || Dispatcher.new(@map)
|
90
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
|
-
|
101
91
|
return self
|
102
92
|
end
|
103
93
|
|
@@ -128,6 +118,10 @@ class Server
|
|
128
118
|
runner.setup_mode
|
129
119
|
runner.daemonize if runner.daemon
|
130
120
|
|
121
|
+
unless Session.cache
|
122
|
+
require 'nitro/session/memory'
|
123
|
+
end
|
124
|
+
|
131
125
|
server = Server.new
|
132
126
|
server.start(options)
|
133
127
|
|
data/lib/nitro/server/drb.rb
CHANGED
@@ -68,7 +68,7 @@ class DrbServer
|
|
68
68
|
# The default implementation only creates a session store.
|
69
69
|
|
70
70
|
def setup_drb_objects
|
71
|
-
require 'nitro/session'
|
71
|
+
require 'nitro/session/drb'
|
72
72
|
@session_cache = SyncHash.new
|
73
73
|
DRb.start_service("druby://#{Session.cache_address}:#{Session.cache_port}", @session_cache)
|
74
74
|
puts "Drb session cache at druby://#{Session.cache_address}:#{Session.cache_port}."
|
data/lib/nitro/server/runner.rb
CHANGED
@@ -189,6 +189,16 @@ class Runner
|
|
189
189
|
@spider = :render
|
190
190
|
end
|
191
191
|
|
192
|
+
opts.on('--record FILENAME', 'Record the application server session to the given file.') do |filename|
|
193
|
+
@server = :webrick
|
194
|
+
$record_session_filename = filename || 'vcrsession.yaml'
|
195
|
+
end
|
196
|
+
|
197
|
+
opts.on('--playback FILENAME', 'Playback a previously recorded session from the given file.') do |filename|
|
198
|
+
@server = :webrick
|
199
|
+
$playback_session_filename = filename || 'vcrsession.yaml'
|
200
|
+
end
|
201
|
+
|
192
202
|
opts.on_tail('-v', '--version', 'Show version.') do
|
193
203
|
puts "Nitro #{Nitro::Version}"
|
194
204
|
exit
|
data/lib/nitro/session.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
require 'md5'
|
2
2
|
require 'webrick'
|
3
3
|
|
4
|
-
require '
|
5
|
-
require '
|
4
|
+
require 'facets/more/synchash'
|
5
|
+
require 'facets/more/times'
|
6
|
+
require 'facets/more/expirable'
|
6
7
|
|
7
8
|
require 'glue'
|
8
9
|
require 'glue/attribute'
|
9
10
|
require 'glue/configuration'
|
10
|
-
require 'glue/expirable'
|
11
11
|
|
12
12
|
require 'nitro/cgi/cookie'
|
13
13
|
|
@@ -24,13 +24,19 @@ module Nitro
|
|
24
24
|
# The session should be persistable to survive server
|
25
25
|
# shutdowns.
|
26
26
|
#
|
27
|
+
# The session can be considered as a Hash where key-value
|
28
|
+
# pairs are stored. Typically symbols are used as keys. By
|
29
|
+
# convention uppercase symbols are used for internal Nitro
|
30
|
+
# session variables (ie :FLASH, :USER, etc). User applications
|
31
|
+
# typically use lowercase symbols (ie :cart, :history, etc).
|
32
|
+
#
|
27
33
|
#--
|
28
34
|
# TODO: rehash of the session cookie
|
29
35
|
# TODO: store -> cache, reimplement helpers.
|
30
36
|
#++
|
31
37
|
|
32
38
|
class Session < Hash
|
33
|
-
include
|
39
|
+
include Expirable
|
34
40
|
|
35
41
|
# Session id salt.
|
36
42
|
|
@@ -99,15 +105,28 @@ class Session < Hash
|
|
99
105
|
return session
|
100
106
|
end
|
101
107
|
|
108
|
+
# The number of active (online) sessions.
|
109
|
+
# DON'T use yet!
|
110
|
+
|
111
|
+
def count
|
112
|
+
Session.cache.size
|
113
|
+
end
|
114
|
+
|
115
|
+
# Perform Session garbage collection. You may call this
|
116
|
+
# method from a cron job.
|
117
|
+
|
118
|
+
def garbage_collect
|
119
|
+
expired = []
|
120
|
+
for s in Session.cache.all
|
121
|
+
expired << s.session_id if s.expired?
|
122
|
+
end
|
123
|
+
for sid in expired
|
124
|
+
Session.cache.delete(sid)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
alias_method :gc!, :garbage_collect
|
102
128
|
end
|
103
129
|
|
104
|
-
# By default sessions are stored in memory.
|
105
|
-
#--
|
106
|
-
# gmosx: should be placed here.
|
107
|
-
#++
|
108
|
-
|
109
|
-
set_cache_type(:memory)
|
110
|
-
|
111
130
|
# The unique id of this session.
|
112
131
|
|
113
132
|
attr_reader :session_id
|
@@ -145,7 +164,7 @@ protected
|
|
145
164
|
# Random may produce equal ids? add a prefix
|
146
165
|
# (SALT) to stop hackers from creating session_ids.
|
147
166
|
#--
|
148
|
-
# THINK: Is MD5 slow???
|
167
|
+
# THINK: Is MD5 slow??? Allow for pluggable hashes.
|
149
168
|
#++
|
150
169
|
|
151
170
|
def create_id
|
data/lib/nitro/session/drb.rb
CHANGED
@@ -3,7 +3,19 @@ require 'nitro/session'
|
|
3
3
|
|
4
4
|
module Nitro
|
5
5
|
|
6
|
-
Logger.
|
6
|
+
Logger.info "Using DRb sessions at #{Glue::DrbCache.address}:#{Glue::DrbCache.port}." if defined?(Logger) && $DBG
|
7
|
+
|
8
|
+
class Session < Hash
|
9
|
+
|
10
|
+
# The address of the Drb store.
|
11
|
+
|
12
|
+
setting :cache_address, :default => '127.0.0.1', :doc => 'The address of the Drb store'
|
13
|
+
|
14
|
+
# The port of the Drb store.
|
15
|
+
|
16
|
+
setting :cache_port, :default => 9069, :doc => 'The port of the Drb store'
|
17
|
+
|
18
|
+
end
|
7
19
|
|
8
20
|
Session.cache = Glue::DrbCache.new
|
9
21
|
|
data/lib/nitro/session/file.rb
CHANGED
@@ -5,7 +5,7 @@ module Nitro
|
|
5
5
|
|
6
6
|
# A Session manager that persists sessions on disk.
|
7
7
|
|
8
|
-
Logger.
|
8
|
+
Logger.info "Using File sessions." if defined?(Logger) && $DBG
|
9
9
|
|
10
10
|
Session.cache = Glue::FileCache.new("session_#{Session.cookie_name}", Session.keepalive)
|
11
11
|
|
@@ -5,7 +5,7 @@ module Nitro
|
|
5
5
|
|
6
6
|
# A Session manager that persists sessions on disk.
|
7
7
|
|
8
|
-
Logger.debug "Using MemCached sessions." if defined?(Logger)
|
8
|
+
Logger.debug "Using MemCached sessions." if defined?(Logger) && $DBG
|
9
9
|
|
10
10
|
Session.cache = Glue::MemCached.new("session_#{Session.cookie_name}", Session.keepalive)
|
11
11
|
|
data/lib/nitro/session/memory.rb
CHANGED
data/lib/nitro/session/og.rb
CHANGED
@@ -5,7 +5,7 @@ module Nitro
|
|
5
5
|
|
6
6
|
# A Session manager that persists sessions on an Og store.
|
7
7
|
|
8
|
-
Logger.debug "Using Og sessions." if defined?(Logger)
|
8
|
+
Logger.debug "Using Og sessions." if defined?(Logger) && $DBG
|
9
9
|
|
10
10
|
Session.cache = OgCache.new("session_#{Session.cookie_name}", Session.keepalive)
|
11
11
|
|
data/lib/nitro/test/testcase.rb
CHANGED
@@ -44,6 +44,9 @@ class TestCase
|
|
44
44
|
context.headers['REQUEST_URI'] = uri
|
45
45
|
context.headers['REQUEST_METHOD'] = options[:method].to_s.upcase
|
46
46
|
context.headers['REMOTE_ADDR'] ||= '127.0.0.1'
|
47
|
+
if ((:get == options[:method]) and (options[:params]))
|
48
|
+
context.headers['QUERY_STRING'] = options[:params].collect {|k,v| "#{k}=#{v}"}.join('&')
|
49
|
+
end
|
47
50
|
context.cookies.merge! options[:cookies] if options[:cookies]
|
48
51
|
context.session.merge! options[:session] if options[:session]
|
49
52
|
|
data/proto/public/error.xhtml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
<html>
|
2
2
|
<head>
|
3
3
|
<script lang="javascript" type="text/javascript">
|
4
|
-
|
4
|
+
// <!--
|
5
5
|
function toggleVisible(element) {
|
6
6
|
if (element.style.display == 'block') {
|
7
7
|
element.style.display = 'none';
|
@@ -98,26 +98,26 @@
|
|
98
98
|
</div>
|
99
99
|
<?r end ?>
|
100
100
|
|
101
|
-
<h2><a href="#" onclick="document.getElementById('request')
|
101
|
+
<h2><a href="#" onclick="return toggleVisible(document.getElementById('request'));">Request</a></h2>
|
102
102
|
<div id="request" style="display: none">
|
103
103
|
<p><strong>Parameters:</strong> #{request.params.reject{ |k,v| k == :__RELOADED__ }.inspect}</p>
|
104
104
|
<p><strong>Cookies:</strong> #{request.cookies.inspect}</p>
|
105
105
|
<p><strong>Headers:</strong><br />#{request.headers.collect { |k, v| "#{k} => #{v}" }.join('<br />')}</p>
|
106
106
|
</div>
|
107
107
|
|
108
|
-
<h2><a href="#" onclick="document.getElementById('response')
|
108
|
+
<h2><a href="#" onclick="return toggleVisible(document.getElementById('response'));">Response</a></h2>
|
109
109
|
<div id="response" style="display: none">
|
110
110
|
<p><strong>Headers:</strong> #{request.response_headers.inspect}</p>
|
111
111
|
<p><strong>Cookies:</strong> #{request.response_cookies.inspect}</p>
|
112
112
|
</div>
|
113
113
|
|
114
|
-
<h2><a href="#" onclick="document.getElementById('session')
|
114
|
+
<h2><a href="#" onclick="return toggleVisible(document.getElementById('session'));">Session</a></h2>
|
115
115
|
<div id="session" style="display: none">
|
116
116
|
<p><strong>Values:</strong> #{session.inspect}</p>
|
117
117
|
</div>
|
118
118
|
|
119
119
|
<br /><br />
|
120
|
-
Powered by <a href="http://www.
|
120
|
+
Powered by <a href="http://www.nitroproject.org">Nitro</a> version #{Nitro::Version}
|
121
121
|
<?r end ?>
|
122
122
|
</body>
|
123
123
|
</html>
|
data/proto/public/js/controls.js
CHANGED
@@ -141,8 +141,8 @@ Autocompleter.Base.prototype = {
|
|
141
141
|
return;
|
142
142
|
}
|
143
143
|
else
|
144
|
-
|
145
|
-
|
144
|
+
if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN ||
|
145
|
+
(navigator.appVersion.indexOf('AppleWebKit') > 0 && event.keyCode == 0)) return;
|
146
146
|
|
147
147
|
this.changed = true;
|
148
148
|
this.hasFocus = true;
|
data/proto/public/js/dragdrop.js
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
|
2
|
+
// (c) 2005 Sammi Williams (http://www.oriontransfer.co.nz, sammi@oriontransfer.co.nz)
|
2
3
|
//
|
3
4
|
// See scriptaculous.js for full license.
|
4
5
|
|
@@ -15,7 +16,8 @@ var Droppables = {
|
|
15
16
|
element = $(element);
|
16
17
|
var options = Object.extend({
|
17
18
|
greedy: true,
|
18
|
-
hoverclass: null
|
19
|
+
hoverclass: null,
|
20
|
+
tree: false
|
19
21
|
}, arguments[1] || {});
|
20
22
|
|
21
23
|
// cache containers
|
@@ -37,12 +39,27 @@ var Droppables = {
|
|
37
39
|
|
38
40
|
this.drops.push(options);
|
39
41
|
},
|
42
|
+
|
43
|
+
findDeepestChild: function(drops) {
|
44
|
+
deepest = drops[0];
|
45
|
+
|
46
|
+
for (i = 1; i < drops.length; ++i)
|
47
|
+
if (Element.isParent(drops[i].element, deepest.element))
|
48
|
+
deepest = drops[i];
|
49
|
+
|
50
|
+
return deepest;
|
51
|
+
},
|
40
52
|
|
41
53
|
isContained: function(element, drop) {
|
42
|
-
var
|
43
|
-
|
54
|
+
var containmentNode;
|
55
|
+
if(drop.tree) {
|
56
|
+
containmentNode = element.treeNode;
|
57
|
+
} else {
|
58
|
+
containmentNode = element.parentNode;
|
59
|
+
}
|
60
|
+
return drop._containers.detect(function(c) { return containmentNode == c });
|
44
61
|
},
|
45
|
-
|
62
|
+
|
46
63
|
isAffected: function(point, element, drop) {
|
47
64
|
return (
|
48
65
|
(drop.element!=element) &&
|
@@ -68,18 +85,22 @@ var Droppables = {
|
|
68
85
|
|
69
86
|
show: function(point, element) {
|
70
87
|
if(!this.drops.length) return;
|
88
|
+
var affected = [];
|
71
89
|
|
72
90
|
if(this.last_active) this.deactivate(this.last_active);
|
73
91
|
this.drops.each( function(drop) {
|
74
|
-
if(Droppables.isAffected(point, element, drop))
|
75
|
-
|
76
|
-
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
|
77
|
-
if(drop.greedy) {
|
78
|
-
Droppables.activate(drop);
|
79
|
-
throw $break;
|
80
|
-
}
|
81
|
-
}
|
92
|
+
if(Droppables.isAffected(point, element, drop))
|
93
|
+
affected.push(drop);
|
82
94
|
});
|
95
|
+
|
96
|
+
if(affected.length>0) {
|
97
|
+
drop = Droppables.findDeepestChild(affected);
|
98
|
+
Position.within(drop.element, point[0], point[1]);
|
99
|
+
if(drop.onHover)
|
100
|
+
drop.onHover(element, drop.element, Position.overlap(drop.overlap, drop.element));
|
101
|
+
|
102
|
+
Droppables.activate(drop);
|
103
|
+
}
|
83
104
|
},
|
84
105
|
|
85
106
|
fire: function(event, element) {
|
@@ -187,15 +208,17 @@ Draggable.prototype = {
|
|
187
208
|
initialize: function(element) {
|
188
209
|
var options = Object.extend({
|
189
210
|
handle: false,
|
190
|
-
starteffect: function(element) {
|
191
|
-
|
211
|
+
starteffect: function(element) {
|
212
|
+
element._opacity = Element.getOpacity(element);
|
213
|
+
new Effect.Opacity(element, {duration:0.2, from:element._opacity, to:0.7});
|
192
214
|
},
|
193
215
|
reverteffect: function(element, top_offset, left_offset) {
|
194
216
|
var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
|
195
217
|
element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur});
|
196
218
|
},
|
197
|
-
endeffect: function(element) {
|
198
|
-
|
219
|
+
endeffect: function(element) {
|
220
|
+
var toOpacity = typeof element._opacity == 'number' ? element._opacity : 1.0
|
221
|
+
new Effect.Opacity(element, {duration:0.2, from:0.7, to:toOpacity});
|
199
222
|
},
|
200
223
|
zindex: 1000,
|
201
224
|
revert: false,
|
@@ -207,12 +230,15 @@ Draggable.prototype = {
|
|
207
230
|
|
208
231
|
this.element = $(element);
|
209
232
|
|
210
|
-
if(options.handle && (typeof options.handle == 'string'))
|
211
|
-
|
233
|
+
if(options.handle && (typeof options.handle == 'string')) {
|
234
|
+
var h = Element.childrenWithClassName(this.element, options.handle, true);
|
235
|
+
if(h.length>0) this.handle = h[0];
|
236
|
+
}
|
212
237
|
if(!this.handle) this.handle = $(options.handle);
|
213
238
|
if(!this.handle) this.handle = this.element;
|
214
239
|
|
215
|
-
if(options.scroll
|
240
|
+
if(options.scroll && !options.scroll.scrollTo && !options.scroll.outerHTML)
|
241
|
+
options.scroll = $(options.scroll);
|
216
242
|
|
217
243
|
Element.makePositioned(this.element); // fix IE
|
218
244
|
|
@@ -277,8 +303,14 @@ Draggable.prototype = {
|
|
277
303
|
}
|
278
304
|
|
279
305
|
if(this.options.scroll) {
|
280
|
-
|
281
|
-
|
306
|
+
if (this.options.scroll == window) {
|
307
|
+
var where = this._getWindowScroll(this.options.scroll);
|
308
|
+
this.originalScrollLeft = where.left;
|
309
|
+
this.originalScrollTop = where.top;
|
310
|
+
} else {
|
311
|
+
this.originalScrollLeft = this.options.scroll.scrollLeft;
|
312
|
+
this.originalScrollTop = this.options.scroll.scrollTop;
|
313
|
+
}
|
282
314
|
}
|
283
315
|
|
284
316
|
Draggables.notify('onStart', this, event);
|
@@ -294,13 +326,18 @@ Draggable.prototype = {
|
|
294
326
|
if(this.options.change) this.options.change(this);
|
295
327
|
|
296
328
|
if(this.options.scroll) {
|
297
|
-
//if(this.scrollInterval) this.scroll();
|
298
329
|
this.stopScrolling();
|
299
|
-
|
300
|
-
p
|
301
|
-
|
302
|
-
|
303
|
-
|
330
|
+
|
331
|
+
var p;
|
332
|
+
if (this.options.scroll == window) {
|
333
|
+
with(this._getWindowScroll(this.options.scroll)) { p = [ left, top, left+width, top+height ]; }
|
334
|
+
} else {
|
335
|
+
p = Position.page(this.options.scroll);
|
336
|
+
p[0] += this.options.scroll.scrollLeft;
|
337
|
+
p[1] += this.options.scroll.scrollTop;
|
338
|
+
p.push(p[0]+this.options.scroll.offsetWidth);
|
339
|
+
p.push(p[1]+this.options.scroll.offsetHeight);
|
340
|
+
}
|
304
341
|
var speed = [0,0];
|
305
342
|
if(pointer[0] < (p[0]+this.options.scrollSensitivity)) speed[0] = pointer[0]-(p[0]+this.options.scrollSensitivity);
|
306
343
|
if(pointer[1] < (p[1]+this.options.scrollSensitivity)) speed[1] = pointer[1]-(p[1]+this.options.scrollSensitivity);
|
@@ -366,7 +403,7 @@ Draggable.prototype = {
|
|
366
403
|
var d = this.currentDelta();
|
367
404
|
pos[0] -= d[0]; pos[1] -= d[1];
|
368
405
|
|
369
|
-
if(this.options.scroll) {
|
406
|
+
if(this.options.scroll && (this.options.scroll != window)) {
|
370
407
|
pos[0] -= this.options.scroll.scrollLeft-this.originalScrollLeft;
|
371
408
|
pos[1] -= this.options.scroll.scrollTop-this.originalScrollTop;
|
372
409
|
}
|
@@ -377,7 +414,7 @@ Draggable.prototype = {
|
|
377
414
|
|
378
415
|
if(this.options.snap) {
|
379
416
|
if(typeof this.options.snap == 'function') {
|
380
|
-
p = this.options.snap(p[0],p[1]);
|
417
|
+
p = this.options.snap(p[0],p[1],this);
|
381
418
|
} else {
|
382
419
|
if(this.options.snap instanceof Array) {
|
383
420
|
p = p.map( function(v, i) {
|
@@ -400,6 +437,7 @@ Draggable.prototype = {
|
|
400
437
|
if(this.scrollInterval) {
|
401
438
|
clearInterval(this.scrollInterval);
|
402
439
|
this.scrollInterval = null;
|
440
|
+
Draggables._lastScrollPointer = null;
|
403
441
|
}
|
404
442
|
},
|
405
443
|
|
@@ -413,15 +451,55 @@ Draggable.prototype = {
|
|
413
451
|
var current = new Date();
|
414
452
|
var delta = current - this.lastScrolled;
|
415
453
|
this.lastScrolled = current;
|
416
|
-
this.options.scroll
|
417
|
-
|
454
|
+
if(this.options.scroll == window) {
|
455
|
+
with (this._getWindowScroll(this.options.scroll)) {
|
456
|
+
if (this.scrollSpeed[0] || this.scrollSpeed[1]) {
|
457
|
+
var d = delta / 1000;
|
458
|
+
this.options.scroll.scrollTo( left + d*this.scrollSpeed[0], top + d*this.scrollSpeed[1] );
|
459
|
+
}
|
460
|
+
}
|
461
|
+
} else {
|
462
|
+
this.options.scroll.scrollLeft += this.scrollSpeed[0] * delta / 1000;
|
463
|
+
this.options.scroll.scrollTop += this.scrollSpeed[1] * delta / 1000;
|
464
|
+
}
|
418
465
|
|
419
466
|
Position.prepare();
|
420
467
|
Droppables.show(Draggables._lastPointer, this.element);
|
421
468
|
Draggables.notify('onDrag', this);
|
422
|
-
|
469
|
+
Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
|
470
|
+
Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
|
471
|
+
Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
|
472
|
+
if (Draggables._lastScrollPointer[0] < 0)
|
473
|
+
Draggables._lastScrollPointer[0] = 0;
|
474
|
+
if (Draggables._lastScrollPointer[1] < 0)
|
475
|
+
Draggables._lastScrollPointer[1] = 0;
|
476
|
+
this.draw(Draggables._lastScrollPointer);
|
423
477
|
|
424
478
|
if(this.options.change) this.options.change(this);
|
479
|
+
},
|
480
|
+
|
481
|
+
_getWindowScroll: function(w) {
|
482
|
+
var T, L, W, H;
|
483
|
+
with (w.document) {
|
484
|
+
if (w.document.documentElement && documentElement.scrollTop) {
|
485
|
+
T = documentElement.scrollTop;
|
486
|
+
L = documentElement.scrollLeft;
|
487
|
+
} else if (w.document.body) {
|
488
|
+
T = body.scrollTop;
|
489
|
+
L = body.scrollLeft;
|
490
|
+
}
|
491
|
+
if (w.innerWidth) {
|
492
|
+
W = w.innerWidth;
|
493
|
+
H = w.innerHeight;
|
494
|
+
} else if (w.document.documentElement && documentElement.clientWidth) {
|
495
|
+
W = documentElement.clientWidth;
|
496
|
+
H = documentElement.clientHeight;
|
497
|
+
} else {
|
498
|
+
W = body.offsetWidth;
|
499
|
+
H = body.offsetHeight
|
500
|
+
}
|
501
|
+
}
|
502
|
+
return { top: T, left: L, width: W, height: H };
|
425
503
|
}
|
426
504
|
}
|
427
505
|
|
@@ -447,30 +525,41 @@ SortableObserver.prototype = {
|
|
447
525
|
}
|
448
526
|
|
449
527
|
var Sortable = {
|
450
|
-
sortables:
|
528
|
+
sortables: {},
|
451
529
|
|
452
|
-
|
453
|
-
element
|
454
|
-
|
530
|
+
_findRootElement: function(element) {
|
531
|
+
while (element.tagName != "BODY") {
|
532
|
+
if(element.id && Sortable.sortables[element.id]) return element;
|
533
|
+
element = element.parentNode;
|
534
|
+
}
|
535
|
+
},
|
536
|
+
|
537
|
+
options: function(element) {
|
538
|
+
element = Sortable._findRootElement($(element));
|
539
|
+
if(!element) return;
|
540
|
+
return Sortable.sortables[element.id];
|
455
541
|
},
|
456
542
|
|
457
543
|
destroy: function(element){
|
458
|
-
|
459
|
-
|
544
|
+
var s = Sortable.options(element);
|
545
|
+
|
546
|
+
if(s) {
|
460
547
|
Draggables.removeObserver(s.element);
|
461
548
|
s.droppables.each(function(d){ Droppables.remove(d) });
|
462
549
|
s.draggables.invoke('destroy');
|
463
|
-
|
464
|
-
|
550
|
+
|
551
|
+
delete Sortable.sortables[s.element.id];
|
552
|
+
}
|
465
553
|
},
|
466
|
-
|
554
|
+
|
467
555
|
create: function(element) {
|
468
556
|
element = $(element);
|
469
557
|
var options = Object.extend({
|
470
558
|
element: element,
|
471
559
|
tag: 'li', // assumes li children, override with tag: 'tagname'
|
472
560
|
dropOnEmpty: false,
|
473
|
-
tree: false,
|
561
|
+
tree: false,
|
562
|
+
treeTag: 'ul',
|
474
563
|
overlap: 'vertical', // one of 'vertical', 'horizontal'
|
475
564
|
constraint: 'vertical', // one of 'vertical', 'horizontal', false
|
476
565
|
containment: element, // also takes array of elements (or id's); or false
|
@@ -479,6 +568,8 @@ var Sortable = {
|
|
479
568
|
hoverclass: null,
|
480
569
|
ghosting: false,
|
481
570
|
scroll: false,
|
571
|
+
scrollSensitivity: 20,
|
572
|
+
scrollSpeed: 15,
|
482
573
|
format: /^[^_]*_(.*)$/,
|
483
574
|
onChange: Prototype.emptyFunction,
|
484
575
|
onUpdate: Prototype.emptyFunction
|
@@ -491,6 +582,8 @@ var Sortable = {
|
|
491
582
|
var options_for_draggable = {
|
492
583
|
revert: true,
|
493
584
|
scroll: options.scroll,
|
585
|
+
scrollSpeed: options.scrollSpeed,
|
586
|
+
scrollSensitivity: options.scrollSensitivity,
|
494
587
|
ghosting: options.ghosting,
|
495
588
|
constraint: options.constraint,
|
496
589
|
handle: options.handle };
|
@@ -516,9 +609,17 @@ var Sortable = {
|
|
516
609
|
var options_for_droppable = {
|
517
610
|
overlap: options.overlap,
|
518
611
|
containment: options.containment,
|
612
|
+
tree: options.tree,
|
519
613
|
hoverclass: options.hoverclass,
|
520
|
-
onHover: Sortable.onHover
|
521
|
-
greedy: !options.dropOnEmpty
|
614
|
+
onHover: Sortable.onHover
|
615
|
+
//greedy: !options.dropOnEmpty
|
616
|
+
}
|
617
|
+
|
618
|
+
var options_for_tree = {
|
619
|
+
onHover: Sortable.onEmptyHover,
|
620
|
+
overlap: options.overlap,
|
621
|
+
containment: options.containment,
|
622
|
+
hoverclass: options.hoverclass
|
522
623
|
}
|
523
624
|
|
524
625
|
// fix for gecko engine
|
@@ -527,12 +628,9 @@ var Sortable = {
|
|
527
628
|
options.draggables = [];
|
528
629
|
options.droppables = [];
|
529
630
|
|
530
|
-
// make it so
|
531
|
-
|
532
631
|
// drop on empty handling
|
533
|
-
if(options.dropOnEmpty) {
|
534
|
-
Droppables.add(element,
|
535
|
-
{containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false});
|
632
|
+
if(options.dropOnEmpty || options.tree) {
|
633
|
+
Droppables.add(element, options_for_tree);
|
536
634
|
options.droppables.push(element);
|
537
635
|
}
|
538
636
|
|
@@ -543,11 +641,20 @@ var Sortable = {
|
|
543
641
|
options.draggables.push(
|
544
642
|
new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
|
545
643
|
Droppables.add(e, options_for_droppable);
|
644
|
+
if(options.tree) e.treeNode = element;
|
546
645
|
options.droppables.push(e);
|
547
646
|
});
|
647
|
+
|
648
|
+
if(options.tree) {
|
649
|
+
(Sortable.findTreeElements(element, options) || []).each( function(e) {
|
650
|
+
Droppables.add(e, options_for_tree);
|
651
|
+
e.treeNode = element;
|
652
|
+
options.droppables.push(e);
|
653
|
+
});
|
654
|
+
}
|
548
655
|
|
549
656
|
// keep reference
|
550
|
-
this.sortables.
|
657
|
+
this.sortables[element.id] = options;
|
551
658
|
|
552
659
|
// for onupdate
|
553
660
|
Draggables.addObserver(new SortableObserver(element, options.onUpdate));
|
@@ -556,23 +663,21 @@ var Sortable = {
|
|
556
663
|
|
557
664
|
// return all suitable-for-sortable elements in a guaranteed order
|
558
665
|
findElements: function(element, options) {
|
559
|
-
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
var grandchildren = this.findElements(e, options);
|
567
|
-
if(grandchildren) elements.push(grandchildren);
|
568
|
-
}
|
569
|
-
});
|
570
|
-
|
571
|
-
return (elements.length>0 ? elements.flatten() : null);
|
666
|
+
return Element.findChildren(
|
667
|
+
element, options.only, options.tree ? true : false, options.tag);
|
668
|
+
},
|
669
|
+
|
670
|
+
findTreeElements: function(element, options) {
|
671
|
+
return Element.findChildren(
|
672
|
+
element, options.only, options.tree ? true : false, options.treeTag);
|
572
673
|
},
|
573
674
|
|
574
675
|
onHover: function(element, dropon, overlap) {
|
575
|
-
if(
|
676
|
+
if(Element.isParent(dropon, element)) return;
|
677
|
+
|
678
|
+
if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
|
679
|
+
return;
|
680
|
+
} else if(overlap>0.5) {
|
576
681
|
Sortable.mark(dropon, 'before');
|
577
682
|
if(dropon.previousSibling != element) {
|
578
683
|
var oldParentNode = element.parentNode;
|
@@ -595,13 +700,37 @@ var Sortable = {
|
|
595
700
|
}
|
596
701
|
}
|
597
702
|
},
|
598
|
-
|
599
|
-
onEmptyHover: function(element, dropon) {
|
600
|
-
|
601
|
-
|
602
|
-
|
703
|
+
|
704
|
+
onEmptyHover: function(element, dropon, overlap) {
|
705
|
+
var oldParentNode = element.parentNode;
|
706
|
+
var droponOptions = Sortable.options(dropon);
|
707
|
+
|
708
|
+
if(!Element.isParent(dropon, element)) {
|
709
|
+
var index;
|
710
|
+
|
711
|
+
var children = Sortable.findElements(dropon, {tag: droponOptions.tag});
|
712
|
+
var child = null;
|
713
|
+
|
714
|
+
if(children) {
|
715
|
+
var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
|
716
|
+
|
717
|
+
for (index = 0; index < children.length; index += 1) {
|
718
|
+
if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
|
719
|
+
offset -= Element.offsetSize (children[index], droponOptions.overlap);
|
720
|
+
} else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
|
721
|
+
child = index + 1 < children.length ? children[index + 1] : null;
|
722
|
+
break;
|
723
|
+
} else {
|
724
|
+
child = children[index];
|
725
|
+
break;
|
726
|
+
}
|
727
|
+
}
|
728
|
+
}
|
729
|
+
|
730
|
+
dropon.insertBefore(element, child);
|
731
|
+
|
603
732
|
Sortable.options(oldParentNode).onChange(element);
|
604
|
-
|
733
|
+
droponOptions.onChange(element);
|
605
734
|
}
|
606
735
|
},
|
607
736
|
|
@@ -633,6 +762,75 @@ var Sortable = {
|
|
633
762
|
|
634
763
|
Element.show(Sortable._marker);
|
635
764
|
},
|
765
|
+
|
766
|
+
_tree: function(element, options, parent) {
|
767
|
+
var children = Sortable.findElements(element, options) || [];
|
768
|
+
|
769
|
+
for (var i = 0; i < children.length; ++i) {
|
770
|
+
var match = children[i].id.match(options.format);
|
771
|
+
|
772
|
+
if (!match) continue;
|
773
|
+
|
774
|
+
var child = {
|
775
|
+
id: encodeURIComponent(match ? match[1] : null),
|
776
|
+
element: element,
|
777
|
+
parent: parent,
|
778
|
+
children: new Array,
|
779
|
+
position: parent.children.length,
|
780
|
+
container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase())
|
781
|
+
}
|
782
|
+
|
783
|
+
/* Get the element containing the children and recurse over it */
|
784
|
+
if (child.container)
|
785
|
+
this._tree(child.container, options, child)
|
786
|
+
|
787
|
+
parent.children.push (child);
|
788
|
+
}
|
789
|
+
|
790
|
+
return parent;
|
791
|
+
},
|
792
|
+
|
793
|
+
/* Finds the first element of the given tag type within a parent element.
|
794
|
+
Used for finding the first LI[ST] within a L[IST]I[TEM].*/
|
795
|
+
_findChildrenElement: function (element, containerTag) {
|
796
|
+
if (element && element.hasChildNodes)
|
797
|
+
for (var i = 0; i < element.childNodes.length; ++i)
|
798
|
+
if (element.childNodes[i].tagName == containerTag)
|
799
|
+
return element.childNodes[i];
|
800
|
+
|
801
|
+
return null;
|
802
|
+
},
|
803
|
+
|
804
|
+
tree: function(element) {
|
805
|
+
element = $(element);
|
806
|
+
var sortableOptions = this.options(element);
|
807
|
+
var options = Object.extend({
|
808
|
+
tag: sortableOptions.tag,
|
809
|
+
treeTag: sortableOptions.treeTag,
|
810
|
+
only: sortableOptions.only,
|
811
|
+
name: element.id,
|
812
|
+
format: sortableOptions.format
|
813
|
+
}, arguments[1] || {});
|
814
|
+
|
815
|
+
var root = {
|
816
|
+
id: null,
|
817
|
+
parent: null,
|
818
|
+
children: new Array,
|
819
|
+
container: element,
|
820
|
+
position: 0
|
821
|
+
}
|
822
|
+
|
823
|
+
return Sortable._tree (element, options, root);
|
824
|
+
},
|
825
|
+
|
826
|
+
/* Construct a [i] index for a particular node */
|
827
|
+
_constructIndex: function(node) {
|
828
|
+
var index = '';
|
829
|
+
do {
|
830
|
+
if (node.id) index = '[' + node.position + ']' + index;
|
831
|
+
} while ((node = node.parent) != null);
|
832
|
+
return index;
|
833
|
+
},
|
636
834
|
|
637
835
|
sequence: function(element) {
|
638
836
|
element = $(element);
|
@@ -655,20 +853,63 @@ var Sortable = {
|
|
655
853
|
});
|
656
854
|
|
657
855
|
new_sequence.each(function(ident) {
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
|
662
|
-
|
856
|
+
var n = nodeMap[ident];
|
857
|
+
if (n) {
|
858
|
+
n[1].appendChild(n[0]);
|
859
|
+
delete nodeMap[ident];
|
860
|
+
}
|
663
861
|
});
|
664
862
|
},
|
665
|
-
|
863
|
+
|
666
864
|
serialize: function(element) {
|
667
865
|
element = $(element);
|
866
|
+
var options = Object.extend(Sortable.options(element), arguments[1] || {});
|
668
867
|
var name = encodeURIComponent(
|
669
868
|
(arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
|
670
|
-
|
671
|
-
|
672
|
-
|
869
|
+
|
870
|
+
if (options.tree) {
|
871
|
+
return Sortable.tree(element, arguments[1]).children.map( function (item) {
|
872
|
+
return [name + Sortable._constructIndex(item) + "=" +
|
873
|
+
encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
|
874
|
+
}).flatten().join('&');
|
875
|
+
} else {
|
876
|
+
return Sortable.sequence(element, arguments[1]).map( function(item) {
|
877
|
+
return name + "[]=" + encodeURIComponent(item);
|
878
|
+
}).join('&');
|
879
|
+
}
|
673
880
|
}
|
674
881
|
}
|
882
|
+
|
883
|
+
/* Returns true if child is contained within element */
|
884
|
+
Element.isParent = function(child, element) {
|
885
|
+
if (!child.parentNode || child == element) return false;
|
886
|
+
|
887
|
+
if (child.parentNode == element) return true;
|
888
|
+
|
889
|
+
return Element.isParent(child.parentNode, element);
|
890
|
+
}
|
891
|
+
|
892
|
+
Element.findChildren = function(element, only, recursive, tagName) {
|
893
|
+
if(!element.hasChildNodes()) return null;
|
894
|
+
tagName = tagName.toUpperCase();
|
895
|
+
if(only) only = [only].flatten();
|
896
|
+
var elements = [];
|
897
|
+
$A(element.childNodes).each( function(e) {
|
898
|
+
if(e.tagName && e.tagName.toUpperCase()==tagName &&
|
899
|
+
(!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
|
900
|
+
elements.push(e);
|
901
|
+
if(recursive) {
|
902
|
+
var grandchildren = Element.findChildren(e, only, recursive, tagName);
|
903
|
+
if(grandchildren) elements.push(grandchildren);
|
904
|
+
}
|
905
|
+
});
|
906
|
+
|
907
|
+
return (elements.length>0 ? elements.flatten() : []);
|
908
|
+
}
|
909
|
+
|
910
|
+
Element.offsetSize = function (element, type) {
|
911
|
+
if (type == 'vertical' || type == 'height')
|
912
|
+
return element.offsetHeight;
|
913
|
+
else
|
914
|
+
return element.offsetWidth;
|
915
|
+
}
|