rails 1.1.0 → 1.1.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rails might be problematic. Click here for more details.

data/CHANGELOG CHANGED
@@ -1,3 +1,42 @@
1
+ *1.1.1* (April 6th, 2005)
2
+
3
+ * Enhances plugin#discover allowing it to discover svn:// like URIs (closes #4565) [ruben.nine@gmail.com]
4
+
5
+ * Update to Prototype 1.5.0_rc0 [Sam Stephenson]
6
+
7
+ * Fixed that the -r/--ruby path option of the rails command was not being respected #4549 [ryan.raaum@gmail.com]
8
+
9
+ * Added that Dispatcher exceptions should not be shown to the user unless a default log has not been configured. Instead show public/500.html [DHH]
10
+
11
+ * Fixed that rake clone_structure_to_test should quit on pgsql if the dump is unsuccesful #4585 [augustz@augustz.com]
12
+
13
+ * Fixed that rails --version should have the return code of 0 (success) #4560 [blair@orcaware.com]
14
+
15
+ * Install alias so Rails::InfoController is accessible at /rails_info. Closes #4546. [Nicholas Seckar]
16
+
17
+ * Fixed that spawner should daemonize if running in repeat mode [DHH]
18
+
19
+ * Added TAG option for rake rails:freeze:edge, so you can say rake rails:freeze:edge TAG=rel_1-1-0 to lock to the 1.1.0 release [DHH]
20
+
21
+ * Applied Prototype $() performance patches (#4465, #4477) and updated script.aculo.us [Sam Stephenson, Thomas Fuchs]
22
+
23
+ * Use --simple-prompt instead of --prompt-mode simple for console compatibility with Windows/Ruby 1.8.2 #4532 [starr@starrnhorne.com]
24
+
25
+ * Make Rails::VERSION implicitly loadable #4491. [Nicholas Seckar]
26
+
27
+ * Fixed rake rails:freeze:gems #4518 [benji@silverinsanity.com]
28
+
29
+ * Added -f/--freeze option to rails command for freezing the application to the Rails version it was generated with [DHH]
30
+
31
+ * Added gem binding of apps generated through the rails command to the gems of they were generated with [Nicholas Seckar]
32
+
33
+ * Added expiration settings for JavaScript, CSS, HTML, and images to default lighttpd.conf [DHH]
34
+
35
+ * Added gzip compression for JavaScript, CSS, and HTML to default lighttpd.conf [DHH]
36
+
37
+ * Avoid passing escapeHTML non-string in Rails' info controller [Nicholas Seckar]
38
+
39
+
1
40
  *1.1.0* (March 27th, 2005)
2
41
 
3
42
  * Allow db:fixtures:load to load a subset of the applications fixtures. [Chad Fowler]
data/README CHANGED
@@ -48,8 +48,10 @@ Mongrel. It's a Ruby-based web server with a C-component (so it requires compila
48
48
  also works very well with Windows. See more at http://mongrel.rubyforge.org/.
49
49
 
50
50
  But of course its also possible to run Rails with the premiere open source web server Apache.
51
- To get decent performance, though, you'll need to install FastCGI. See
52
- http://wiki.rubyonrails.com/rails/pages/FastCGI for more information on FastCGI.
51
+ To get decent performance, though, you'll need to install FastCGI. For Apache 1.3, you want
52
+ to use mod_fastcgi. For Apache 2.0+, you want to use mod_fcgid.
53
+
54
+ See http://wiki.rubyonrails.com/rails/pages/FastCGI for more information on FastCGI.
53
55
 
54
56
  == Example for Apache conf
55
57
 
data/Rakefile CHANGED
@@ -7,7 +7,7 @@ require 'rake/contrib/rubyforgepublisher'
7
7
  require 'date'
8
8
  require 'rbconfig'
9
9
 
10
- require File.join(File.dirname(__FILE__), 'lib', 'rails_version')
10
+ require File.join(File.dirname(__FILE__), 'lib/rails', 'version')
11
11
 
12
12
  PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
13
13
  PKG_NAME = 'rails'
@@ -277,12 +277,12 @@ spec = Gem::Specification.new do |s|
277
277
  on top of either MySQL, PostgreSQL, SQLite, DB2, SQL Server, or Oracle with eRuby- or Builder-based templates.
278
278
  EOF
279
279
 
280
- s.add_dependency('rake', '>= 0.7.0')
281
- s.add_dependency('activesupport', '= 1.3.0' + PKG_BUILD)
282
- s.add_dependency('activerecord', '= 1.14.0' + PKG_BUILD)
283
- s.add_dependency('actionpack', '= 1.12.0' + PKG_BUILD)
284
- s.add_dependency('actionmailer', '= 1.2.0' + PKG_BUILD)
285
- s.add_dependency('actionwebservice', '= 1.1.0' + PKG_BUILD)
280
+ s.add_dependency('rake', '>= 0.7.1')
281
+ s.add_dependency('activesupport', '= 1.3.1' + PKG_BUILD)
282
+ s.add_dependency('activerecord', '= 1.14.1' + PKG_BUILD)
283
+ s.add_dependency('actionpack', '= 1.12.1' + PKG_BUILD)
284
+ s.add_dependency('actionmailer', '= 1.2.1' + PKG_BUILD)
285
+ s.add_dependency('actionwebservice', '= 1.1.1' + PKG_BUILD)
286
286
 
287
287
  s.rdoc_options << '--exclude' << '.'
288
288
  s.has_rdoc = false
data/bin/rails CHANGED
@@ -1,11 +1,19 @@
1
1
  require File.dirname(__FILE__) + '/../lib/ruby_version_check'
2
2
  Signal.trap("INT") { puts; exit }
3
3
 
4
- require File.dirname(__FILE__) + '/../lib/rails_version'
5
- abort "Rails #{Rails::VERSION::STRING}" if %w(--version -v).include? ARGV.first
4
+ require File.dirname(__FILE__) + '/../lib/rails/version'
5
+ if %w(--version -v).include? ARGV.first
6
+ puts "Rails #{Rails::VERSION::STRING}"
7
+ exit(0)
8
+ end
9
+
10
+ freeze = ARGV.any? { |option| %w(--freeze -f).include?(option) }
11
+ app_path = ARGV.first
6
12
 
7
13
  require File.dirname(__FILE__) + '/../lib/rails_generator'
8
14
 
9
15
  require 'rails_generator/scripts/generate'
10
16
  Rails::Generator::Base.use_application_sources!
11
17
  Rails::Generator::Scripts::Generate.new.run(ARGV, :generator => 'app')
18
+
19
+ Dir.chdir(app_path) { `rake rails:freeze:gems`; puts "froze" } if freeze
@@ -1,5 +1,3 @@
1
- require 'rails_version'
2
-
3
1
  module Rails
4
2
  module Info
5
3
  mattr_accessor :properties
@@ -35,7 +33,11 @@ module Rails
35
33
 
36
34
  def freeze_edge_version
37
35
  if File.exists?(rails_vendor_root)
38
- Dir[File.join(rails_vendor_root, 'REVISION_*')].first.scan(/_(\d+)$/).first.first rescue 'unknown'
36
+ begin
37
+ Dir[File.join(rails_vendor_root, 'REVISION_*')].first.scan(/_(\d+)$/).first.first
38
+ rescue
39
+ Dir[File.join(rails_vendor_root, 'TAG_*')].first.scan(/_(.+)$/).first.first rescue 'unknown'
40
+ end
39
41
  end
40
42
  end
41
43
 
@@ -51,8 +53,8 @@ module Rails
51
53
  def to_html
52
54
  returning table = '<table>' do
53
55
  properties.each do |(name, value)|
54
- table << %(<tr><td class="name">#{CGI.escapeHTML(name)}</td>)
55
- table << %(<td class="value">#{CGI.escapeHTML(value)}</td></tr>)
56
+ table << %(<tr><td class="name">#{CGI.escapeHTML(name.to_s)}</td>)
57
+ table << %(<td class="value">#{CGI.escapeHTML(value.to_s)}</td></tr>)
56
58
  end
57
59
  table << '</table>'
58
60
  end
@@ -1,11 +1,9 @@
1
- module Rails
2
- class InfoController < ActionController::Base
3
- def properties
4
- if local_request?
5
- render :inline => Rails::Info.to_html
6
- else
7
- render :text => '<p>For security purposes, this information is only available to local requests.</p>', :status => 500
8
- end
1
+ class Rails::InfoController < ActionController::Base
2
+ def properties
3
+ if local_request?
4
+ render :inline => Rails::Info.to_html
5
+ else
6
+ render :text => '<p>For security purposes, this information is only available to local requests.</p>', :status => 500
9
7
  end
10
8
  end
11
- end
9
+ end
@@ -0,0 +1,2 @@
1
+ module Rails::InfoHelper
2
+ end
@@ -0,0 +1,2 @@
1
+ # Alias to ensure old public.html still works.
2
+ RailsInfoController = Rails::InfoController
@@ -1,9 +1,11 @@
1
1
  # Default configuration file for the lighttpd web server
2
2
  # Start using ./script/server lighttpd
3
3
 
4
+ server.bind = "0.0.0.0"
4
5
  server.port = 3000
5
6
 
6
- server.modules = ( "mod_rewrite", "mod_accesslog", "mod_fastcgi" )
7
+ server.modules = ( "mod_rewrite", "mod_accesslog", "mod_fastcgi", "mod_compress", "mod_expire" )
8
+
7
9
  server.error-handler-404 = "/dispatch.fcgi"
8
10
  server.document-root = CWD + "/public/"
9
11
 
@@ -12,19 +14,24 @@ accesslog.filename = CWD + "/log/lighttpd.access.log"
12
14
 
13
15
  url.rewrite = ( "^/$" => "index.html", "^([^.]+)$" => "$1.html" )
14
16
 
17
+ compress.filetype = ( "text/plain", "text/html", "text/css", "text/javascript" )
18
+ compress.cache-dir = CWD + "/tmp/cache"
19
+
20
+ expire.url = ( "/favicon.ico" => "access 3 days",
21
+ "/images/" => "access 3 days",
22
+ "/stylesheets/" => "access 3 days",
23
+ "/javascripts/" => "access 3 days" )
24
+
25
+
15
26
  # Change *-procs to 2 if you need to use Upload Progress or other tasks that
16
27
  # *need* to execute a second request while the first is still pending.
17
- fastcgi.server = ( ".fcgi" =>
18
- ( "localhost" =>
19
- (
20
- "min-procs" => 1,
21
- "max-procs" => 1,
22
- "socket" => CWD + "/tmp/sockets/fcgi.socket",
23
- "bin-path" => CWD + "/public/dispatch.fcgi",
24
- "bin-environment" => ( "RAILS_ENV" => "development" )
25
- )
26
- )
27
- )
28
+ fastcgi.server = ( ".fcgi" => ( "localhost" => (
29
+ "min-procs" => 1,
30
+ "max-procs" => 1,
31
+ "socket" => CWD + "/tmp/sockets/fcgi.socket",
32
+ "bin-path" => CWD + "/public/dispatch.fcgi",
33
+ "bin-environment" => ( "RAILS_ENV" => "development" )
34
+ ) ) )
28
35
 
29
36
  mimetype.assign = (
30
37
  ".css" => "text/css",
@@ -13,7 +13,26 @@ if File.directory?("#{RAILS_ROOT}/vendor/rails")
13
13
  require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
14
14
  else
15
15
  require 'rubygems'
16
- require 'initializer'
16
+
17
+ if !defined?(RAILS_GEM_VERSION) && File.read(File.dirname(__FILE__) + '/environment.rb') =~ /RAILS_GEM_VERSION = '([\d.]+)'/
18
+ RAILS_GEM_VERSION = $1
19
+ end
20
+
21
+ if defined?(RAILS_GEM_VERSION)
22
+ rails_gem = Gem.cache.search('rails', "=#{RAILS_GEM_VERSION}").first
23
+
24
+ if rails_gem
25
+ require rails_gem.full_gem_path + '/lib/initializer'
26
+ else
27
+ STDERR.puts %(Cannot find gem for Rails =#{RAILS_GEM_VERSION}:
28
+ Install the missing gem with 'gem install -v=#{RAILS_GEM_VERSION} rails', or
29
+ change environment.rb to define RAILS_GEM_VERSION with your desired version.
30
+ )
31
+ exit 1
32
+ end
33
+ else
34
+ require 'initializer'
35
+ end
17
36
  end
18
37
 
19
- Rails::Initializer.run(:set_load_path)
38
+ Rails::Initializer.run(:set_load_path)
@@ -14,6 +14,7 @@ config.breakpoint_server = true
14
14
  # Show full error reports and disable caching
15
15
  config.action_controller.consider_all_requests_local = true
16
16
  config.action_controller.perform_caching = false
17
+ config.action_view.cache_template_extensions = false
17
18
  config.action_view.debug_rjs = true
18
19
 
19
20
  # Don't care if the mailer can't send
@@ -4,6 +4,9 @@
4
4
  # you don't control web/app server and can't set it the proper way
5
5
  # ENV['RAILS_ENV'] ||= 'production'
6
6
 
7
+ # Specifies gem version of Rails to use when vendor/rails is not present
8
+ <%= '# ' if freeze %>RAILS_GEM_VERSION = '<%= Rails::VERSION::STRING %>'
9
+
7
10
  # Bootstrap the Rails environment, frameworks, and default configuration
8
11
  require File.join(File.dirname(__FILE__), 'boot')
9
12
 
@@ -244,7 +244,7 @@
244
244
  </div>
245
245
 
246
246
  <div id="about">
247
- <h3><a href="rails_info/properties" onclick="about(); return false">About your application&rsquo;s environment</a></h3>
247
+ <h3><a href="rails/info/properties" onclick="about(); return false">About your application&rsquo;s environment</a></h3>
248
248
  <div id="about-content" style="display: none"></div>
249
249
  </div>
250
250
 
@@ -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 parentNode = element.parentNode;
43
- return drop._containers.detect(function(c) { return parentNode == c });
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
- if(drop.onHover)
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) {
@@ -207,8 +228,10 @@ Draggable.prototype = {
207
228
 
208
229
  this.element = $(element);
209
230
 
210
- if(options.handle && (typeof options.handle == 'string'))
211
- this.handle = Element.childrenWithClassName(this.element, options.handle, true)[0];
231
+ if(options.handle && (typeof options.handle == 'string')) {
232
+ var h = Element.childrenWithClassName(this.element, options.handle, true);
233
+ if(h.length>0) this.handle = h[0];
234
+ }
212
235
  if(!this.handle) this.handle = $(options.handle);
213
236
  if(!this.handle) this.handle = this.element;
214
237
 
@@ -412,6 +435,7 @@ Draggable.prototype = {
412
435
  if(this.scrollInterval) {
413
436
  clearInterval(this.scrollInterval);
414
437
  this.scrollInterval = null;
438
+ Draggables._lastScrollPointer = null;
415
439
  }
416
440
  },
417
441
 
@@ -440,7 +464,14 @@ Draggable.prototype = {
440
464
  Position.prepare();
441
465
  Droppables.show(Draggables._lastPointer, this.element);
442
466
  Draggables.notify('onDrag', this);
443
- this.draw(Draggables._lastPointer);
467
+ Draggables._lastScrollPointer = Draggables._lastScrollPointer || $A(Draggables._lastPointer);
468
+ Draggables._lastScrollPointer[0] += this.scrollSpeed[0] * delta / 1000;
469
+ Draggables._lastScrollPointer[1] += this.scrollSpeed[1] * delta / 1000;
470
+ if (Draggables._lastScrollPointer[0] < 0)
471
+ Draggables._lastScrollPointer[0] = 0;
472
+ if (Draggables._lastScrollPointer[1] < 0)
473
+ Draggables._lastScrollPointer[1] = 0;
474
+ this.draw(Draggables._lastScrollPointer);
444
475
 
445
476
  if(this.options.change) this.options.change(this);
446
477
  },
@@ -492,30 +523,41 @@ SortableObserver.prototype = {
492
523
  }
493
524
 
494
525
  var Sortable = {
495
- sortables: new Array(),
526
+ sortables: {},
496
527
 
497
- options: function(element){
498
- element = $(element);
499
- return this.sortables.detect(function(s) { return s.element == element });
528
+ _findRootElement: function(element) {
529
+ while (element.tagName != "BODY") {
530
+ if(element.id && Sortable.sortables[element.id]) return element;
531
+ element = element.parentNode;
532
+ }
533
+ },
534
+
535
+ options: function(element) {
536
+ element = Sortable._findRootElement($(element));
537
+ if(!element) return;
538
+ return Sortable.sortables[element.id];
500
539
  },
501
540
 
502
541
  destroy: function(element){
503
- element = $(element);
504
- this.sortables.findAll(function(s) { return s.element == element }).each(function(s){
542
+ var s = Sortable.options(element);
543
+
544
+ if(s) {
505
545
  Draggables.removeObserver(s.element);
506
546
  s.droppables.each(function(d){ Droppables.remove(d) });
507
547
  s.draggables.invoke('destroy');
508
- });
509
- this.sortables = this.sortables.reject(function(s) { return s.element == element });
548
+
549
+ delete Sortable.sortables[s.element.id];
550
+ }
510
551
  },
511
-
552
+
512
553
  create: function(element) {
513
554
  element = $(element);
514
555
  var options = Object.extend({
515
556
  element: element,
516
557
  tag: 'li', // assumes li children, override with tag: 'tagname'
517
558
  dropOnEmpty: false,
518
- tree: false, // fixme: unimplemented
559
+ tree: false,
560
+ treeTag: 'ul',
519
561
  overlap: 'vertical', // one of 'vertical', 'horizontal'
520
562
  constraint: 'vertical', // one of 'vertical', 'horizontal', false
521
563
  containment: element, // also takes array of elements (or id's); or false
@@ -565,9 +607,17 @@ var Sortable = {
565
607
  var options_for_droppable = {
566
608
  overlap: options.overlap,
567
609
  containment: options.containment,
610
+ tree: options.tree,
568
611
  hoverclass: options.hoverclass,
569
- onHover: Sortable.onHover,
570
- greedy: !options.dropOnEmpty
612
+ onHover: Sortable.onHover
613
+ //greedy: !options.dropOnEmpty
614
+ }
615
+
616
+ var options_for_tree = {
617
+ onHover: Sortable.onEmptyHover,
618
+ overlap: options.overlap,
619
+ containment: options.containment,
620
+ hoverclass: options.hoverclass
571
621
  }
572
622
 
573
623
  // fix for gecko engine
@@ -576,12 +626,9 @@ var Sortable = {
576
626
  options.draggables = [];
577
627
  options.droppables = [];
578
628
 
579
- // make it so
580
-
581
629
  // drop on empty handling
582
- if(options.dropOnEmpty) {
583
- Droppables.add(element,
584
- {containment: options.containment, onHover: Sortable.onEmptyHover, greedy: false});
630
+ if(options.dropOnEmpty || options.tree) {
631
+ Droppables.add(element, options_for_tree);
585
632
  options.droppables.push(element);
586
633
  }
587
634
 
@@ -592,11 +639,20 @@ var Sortable = {
592
639
  options.draggables.push(
593
640
  new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
594
641
  Droppables.add(e, options_for_droppable);
642
+ if(options.tree) e.treeNode = element;
595
643
  options.droppables.push(e);
596
644
  });
645
+
646
+ if(options.tree) {
647
+ (Sortable.findTreeElements(element, options) || []).each( function(e) {
648
+ Droppables.add(e, options_for_tree);
649
+ e.treeNode = element;
650
+ options.droppables.push(e);
651
+ });
652
+ }
597
653
 
598
654
  // keep reference
599
- this.sortables.push(options);
655
+ this.sortables[element.id] = options;
600
656
 
601
657
  // for onupdate
602
658
  Draggables.addObserver(new SortableObserver(element, options.onUpdate));
@@ -605,24 +661,21 @@ var Sortable = {
605
661
 
606
662
  // return all suitable-for-sortable elements in a guaranteed order
607
663
  findElements: function(element, options) {
608
- if(!element.hasChildNodes()) return null;
609
- var elements = [];
610
- var only = options.only ? [options.only].flatten() : null;
611
- $A(element.childNodes).each( function(e) {
612
- if(e.tagName && e.tagName.toUpperCase()==options.tag.toUpperCase() &&
613
- (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
614
- elements.push(e);
615
- if(options.tree) {
616
- var grandchildren = this.findElements(e, options);
617
- if(grandchildren) elements.push(grandchildren);
618
- }
619
- });
620
-
621
- return (elements.length>0 ? elements.flatten() : null);
664
+ return Element.findChildren(
665
+ element, options.only, options.tree ? true : false, options.tag);
666
+ },
667
+
668
+ findTreeElements: function(element, options) {
669
+ return Element.findChildren(
670
+ element, options.only, options.tree ? true : false, options.treeTag);
622
671
  },
623
672
 
624
673
  onHover: function(element, dropon, overlap) {
625
- if(overlap>0.5) {
674
+ if(Element.isParent(dropon, element)) return;
675
+
676
+ if(overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
677
+ return;
678
+ } else if(overlap>0.5) {
626
679
  Sortable.mark(dropon, 'before');
627
680
  if(dropon.previousSibling != element) {
628
681
  var oldParentNode = element.parentNode;
@@ -645,13 +698,37 @@ var Sortable = {
645
698
  }
646
699
  }
647
700
  },
648
-
649
- onEmptyHover: function(element, dropon) {
650
- if(element.parentNode!=dropon) {
651
- var oldParentNode = element.parentNode;
652
- dropon.appendChild(element);
701
+
702
+ onEmptyHover: function(element, dropon, overlap) {
703
+ var oldParentNode = element.parentNode;
704
+ var droponOptions = Sortable.options(dropon);
705
+
706
+ if(!Element.isParent(dropon, element)) {
707
+ var index;
708
+
709
+ var children = Sortable.findElements(dropon, {tag: droponOptions.tag});
710
+ var child = null;
711
+
712
+ if(children) {
713
+ var offset = Element.offsetSize(dropon, droponOptions.overlap) * (1.0 - overlap);
714
+
715
+ for (index = 0; index < children.length; index += 1) {
716
+ if (offset - Element.offsetSize (children[index], droponOptions.overlap) >= 0) {
717
+ offset -= Element.offsetSize (children[index], droponOptions.overlap);
718
+ } else if (offset - (Element.offsetSize (children[index], droponOptions.overlap) / 2) >= 0) {
719
+ child = index + 1 < children.length ? children[index + 1] : null;
720
+ break;
721
+ } else {
722
+ child = children[index];
723
+ break;
724
+ }
725
+ }
726
+ }
727
+
728
+ dropon.insertBefore(element, child);
729
+
653
730
  Sortable.options(oldParentNode).onChange(element);
654
- Sortable.options(dropon).onChange(element);
731
+ droponOptions.onChange(element);
655
732
  }
656
733
  },
657
734
 
@@ -683,6 +760,75 @@ var Sortable = {
683
760
 
684
761
  Element.show(Sortable._marker);
685
762
  },
763
+
764
+ _tree: function(element, options, parent) {
765
+ var children = Sortable.findElements(element, options) || [];
766
+
767
+ for (var i = 0; i < children.length; ++i) {
768
+ var match = children[i].id.match(options.format);
769
+
770
+ if (!match) continue;
771
+
772
+ var child = {
773
+ id: encodeURIComponent(match ? match[1] : null),
774
+ element: element,
775
+ parent: parent,
776
+ children: new Array,
777
+ position: parent.children.length,
778
+ container: Sortable._findChildrenElement(children[i], options.treeTag.toUpperCase())
779
+ }
780
+
781
+ /* Get the element containing the children and recurse over it */
782
+ if (child.container)
783
+ this._tree(child.container, options, child)
784
+
785
+ parent.children.push (child);
786
+ }
787
+
788
+ return parent;
789
+ },
790
+
791
+ /* Finds the first element of the given tag type within a parent element.
792
+ Used for finding the first LI[ST] within a L[IST]I[TEM].*/
793
+ _findChildrenElement: function (element, containerTag) {
794
+ if (element && element.hasChildNodes)
795
+ for (var i = 0; i < element.childNodes.length; ++i)
796
+ if (element.childNodes[i].tagName == containerTag)
797
+ return element.childNodes[i];
798
+
799
+ return null;
800
+ },
801
+
802
+ tree: function(element) {
803
+ element = $(element);
804
+ var sortableOptions = this.options(element);
805
+ var options = Object.extend({
806
+ tag: sortableOptions.tag,
807
+ treeTag: sortableOptions.treeTag,
808
+ only: sortableOptions.only,
809
+ name: element.id,
810
+ format: sortableOptions.format
811
+ }, arguments[1] || {});
812
+
813
+ var root = {
814
+ id: null,
815
+ parent: null,
816
+ children: new Array,
817
+ container: element,
818
+ position: 0
819
+ }
820
+
821
+ return Sortable._tree (element, options, root);
822
+ },
823
+
824
+ /* Construct a [i] index for a particular node */
825
+ _constructIndex: function(node) {
826
+ var index = '';
827
+ do {
828
+ if (node.id) index = '[' + node.position + ']' + index;
829
+ } while ((node = node.parent) != null);
830
+ return index;
831
+ },
686
832
 
687
833
  sequence: function(element) {
688
834
  element = $(element);
@@ -705,20 +851,63 @@ var Sortable = {
705
851
  });
706
852
 
707
853
  new_sequence.each(function(ident) {
708
- var n = nodeMap[ident];
709
- if (n) {
710
- n[1].appendChild(n[0]);
711
- delete nodeMap[ident];
712
- }
854
+ var n = nodeMap[ident];
855
+ if (n) {
856
+ n[1].appendChild(n[0]);
857
+ delete nodeMap[ident];
858
+ }
713
859
  });
714
860
  },
715
-
861
+
716
862
  serialize: function(element) {
717
863
  element = $(element);
864
+ var options = Object.extend(Sortable.options(element), arguments[1] || {});
718
865
  var name = encodeURIComponent(
719
866
  (arguments[1] && arguments[1].name) ? arguments[1].name : element.id);
720
- return Sortable.sequence(element, arguments[1]).map( function(item) {
721
- return name + "[]=" + encodeURIComponent(item);
722
- }).join('&');
867
+
868
+ if (options.tree) {
869
+ return Sortable.tree(element, arguments[1]).children.map( function (item) {
870
+ return [name + Sortable._constructIndex(item) + "=" +
871
+ encodeURIComponent(item.id)].concat(item.children.map(arguments.callee));
872
+ }).flatten().join('&');
873
+ } else {
874
+ return Sortable.sequence(element, arguments[1]).map( function(item) {
875
+ return name + "[]=" + encodeURIComponent(item);
876
+ }).join('&');
877
+ }
723
878
  }
724
879
  }
880
+
881
+ /* Returns true if child is contained within element */
882
+ Element.isParent = function(child, element) {
883
+ if (!child.parentNode || child == element) return false;
884
+
885
+ if (child.parentNode == element) return true;
886
+
887
+ return Element.isParent(child.parentNode, element);
888
+ }
889
+
890
+ Element.findChildren = function(element, only, recursive, tagName) {
891
+ if(!element.hasChildNodes()) return null;
892
+ tagName = tagName.toUpperCase();
893
+ if(only) only = [only].flatten();
894
+ var elements = [];
895
+ $A(element.childNodes).each( function(e) {
896
+ if(e.tagName && e.tagName.toUpperCase()==tagName &&
897
+ (!only || (Element.classNames(e).detect(function(v) { return only.include(v) }))))
898
+ elements.push(e);
899
+ if(recursive) {
900
+ var grandchildren = Element.findChildren(e, only, recursive, tagName);
901
+ if(grandchildren) elements.push(grandchildren);
902
+ }
903
+ });
904
+
905
+ return (elements.length>0 ? elements.flatten() : []);
906
+ }
907
+
908
+ Element.offsetSize = function (element, type) {
909
+ if (type == 'vertical' || type == 'height')
910
+ return element.offsetHeight;
911
+ else
912
+ return element.offsetWidth;
913
+ }