actionpack 1.11.0 → 1.11.1

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

Potentially problematic release.


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

Files changed (40) hide show
  1. data/CHANGELOG +45 -0
  2. data/lib/action_controller/assertions.rb +1 -1
  3. data/lib/action_controller/base.rb +24 -12
  4. data/lib/action_controller/caching.rb +1 -0
  5. data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +1 -0
  6. data/lib/action_controller/cgi_process.rb +44 -27
  7. data/lib/action_controller/components.rb +2 -2
  8. data/lib/action_controller/flash.rb +16 -7
  9. data/lib/action_controller/helpers.rb +12 -1
  10. data/lib/action_controller/layout.rb +3 -1
  11. data/lib/action_controller/macros/in_place_editing.rb +1 -1
  12. data/lib/action_controller/request.rb +20 -16
  13. data/lib/action_controller/session/active_record_store.rb +57 -51
  14. data/lib/action_controller/templates/rescues/_request_and_response.rhtml +0 -2
  15. data/lib/action_controller/vendor/html-scanner/html/node.rb +1 -1
  16. data/lib/action_controller/vendor/html-scanner/html/node.rb.rej +17 -0
  17. data/lib/action_pack/version.rb +2 -2
  18. data/lib/action_view/base.rb +4 -5
  19. data/lib/action_view/helpers/asset_tag_helper.rb +2 -2
  20. data/lib/action_view/helpers/form_options_helper.rb +1 -1
  21. data/lib/action_view/helpers/form_tag_helper.rb +1 -1
  22. data/lib/action_view/helpers/java_script_macros_helper.rb +11 -9
  23. data/lib/action_view/helpers/javascripts/controls.js +30 -1
  24. data/lib/action_view/helpers/javascripts/dragdrop.js +37 -17
  25. data/lib/action_view/helpers/javascripts/effects.js +3 -91
  26. data/lib/action_view/helpers/javascripts/prototype.js +109 -67
  27. data/lib/action_view/helpers/text_helper.rb +30 -9
  28. data/rakefile +2 -2
  29. data/test/controller/active_record_assertions_test.rb +1 -0
  30. data/test/controller/active_record_store_test.rb +12 -6
  31. data/test/controller/flash_test.rb +1 -1
  32. data/test/controller/helper_test.rb +21 -5
  33. data/test/controller/new_render_test.rb +31 -0
  34. data/test/controller/request_test.rb +5 -0
  35. data/test/fixtures/helpers/fun/pdf_helper.rb +3 -0
  36. data/test/fixtures/test/render_file_with_ivar.rhtml +1 -0
  37. data/test/fixtures/test/render_file_with_locals.rhtml +1 -0
  38. data/test/template/form_options_helper_test.rb +16 -6
  39. data/test/template/text_helper_test.rb +7 -0
  40. metadata +7 -3
@@ -5,33 +5,42 @@ require 'base64'
5
5
 
6
6
  class CGI
7
7
  class Session
8
- # Return this session's underlying Session model. Useful for the DB-backed session stores.
8
+ # Return this session's underlying Session instance. Useful for the DB-backed session stores.
9
9
  def model
10
- @dbman.model rescue nil
10
+ @dbman.model if @dbman
11
11
  end
12
12
 
13
- # Proxy missing methods to the underlying Session model.
14
- def method_missing(method, *args, &block)
15
- if model then model.send(method, *args, &block) else super end
16
- end
17
13
 
18
- # A session store backed by an Active Record class.
14
+ # A session store backed by an Active Record class. A default class is
15
+ # provided, but any object duck-typing to an Active Record +Session+ class
16
+ # with text +session_id+ and +data+ attributes is sufficient.
19
17
  #
20
- # A default class is provided, but any object duck-typing to an Active
21
- # Record +Session+ class with text +session_id+ and +data+ attributes
22
- # may be used as the backing store.
18
+ # The default assumes a +sessions+ tables with columns:
19
+ # +id+ (numeric primary key),
20
+ # +session_id+ (text, or longtext if your session data exceeds 65K), and
21
+ # +data+ (text or longtext; careful if your session data exceeds 65KB).
22
+ # The +session_id+ column should always be indexed for speedy lookups.
23
+ # Session data is marshaled to the +data+ column in Base64 format.
24
+ # If the data you write is larger than the column's size limit,
25
+ # ActionController::SessionOverflowError will be raised.
23
26
  #
24
- # The default assumes a +sessions+ tables with columns +id+ (numeric
25
- # primary key), +session_id+ (text, or longtext if your session data exceeds 65K),
26
- # and +data+ (text). Session data is marshaled to +data+. +session_id+ should be
27
- # indexed for speedy lookups.
27
+ # You may configure the table name, primary key, and data column.
28
+ # For example, at the end of config/environment.rb:
29
+ # CGI::Session::ActiveRecordStore::Session.table_name = 'legacy_session_table'
30
+ # CGI::Session::ActiveRecordStore::Session.primary_key = 'session_id'
31
+ # CGI::Session::ActiveRecordStore::Session.data_column_name = 'legacy_session_data'
32
+ # Note that setting the primary key to the session_id frees you from
33
+ # having a separate id column if you don't want it. However, you must
34
+ # set session.model.id = session.session_id by hand! A before_filter
35
+ # on ApplicationController is a good place.
28
36
  #
29
37
  # Since the default class is a simple Active Record, you get timestamps
30
38
  # for free if you add +created_at+ and +updated_at+ datetime columns to
31
39
  # the +sessions+ table, making periodic session expiration a snap.
32
40
  #
33
- # You may provide your own session class, whether a feature-packed
34
- # Active Record or a bare-metal high-performance SQL store, by setting
41
+ # You may provide your own session class implementation, whether a
42
+ # feature-packed Active Record or a bare-metal high-performance SQL
43
+ # store, by setting
35
44
  # +CGI::Session::ActiveRecordStore.session_class = MySessionClass+
36
45
  # You must implement these methods:
37
46
  # self.find_by_session_id(session_id)
@@ -41,28 +50,28 @@ class CGI
41
50
  # save
42
51
  # destroy
43
52
  #
44
- # The fast SqlBypass class is a generic SQL session store. You may
53
+ # The example SqlBypass class is a generic SQL session store. You may
45
54
  # use it as a basis for high-performance database-specific stores.
46
- #
47
- # If the data you are attempting to write to the +data+ column is larger
48
- # than the column's size limit, ActionController::SessionOverflowError
49
- # will be raised.
50
55
  class ActiveRecordStore
51
56
  # The default Active Record class.
52
57
  class Session < ActiveRecord::Base
53
- before_update :loaded? # Don't try to save if we haven't loaded the session
58
+ # Customizable data column name. Defaults to 'data'.
59
+ cattr_accessor :data_column_name
60
+ self.data_column_name = 'data'
61
+
62
+ # Don't try to save if we haven't loaded the session.
63
+ before_update :loaded?
54
64
  before_save :marshal_data!
55
- before_save :ensure_data_not_too_big
56
-
57
- class << self
65
+ before_save :raise_on_session_data_overflow!
58
66
 
67
+ class << self
59
68
  # Don't try to reload ARStore::Session in dev mode.
60
69
  def reloadable? #:nodoc:
61
70
  false
62
71
  end
63
-
72
+
64
73
  def data_column_size_limit
65
- columns_hash['data'].limit
74
+ @data_column_size_limit ||= columns_hash[@@data_column_name].limit
66
75
  end
67
76
 
68
77
  # Hook to set up sessid compatibility.
@@ -71,15 +80,15 @@ class CGI
71
80
  find_by_session_id(session_id)
72
81
  end
73
82
 
74
- def marshal(data) Base64.encode64(Marshal.dump(data)) end
75
- def unmarshal(data) Marshal.load(Base64.decode64(data)) end
83
+ def marshal(data) Base64.encode64(Marshal.dump(data)) if data end
84
+ def unmarshal(data) Marshal.load(Base64.decode64(data)) if data end
76
85
 
77
86
  def create_table!
78
87
  connection.execute <<-end_sql
79
88
  CREATE TABLE #{table_name} (
80
89
  id INTEGER PRIMARY KEY,
81
90
  #{connection.quote_column_name('session_id')} TEXT UNIQUE,
82
- #{connection.quote_column_name('data')} TEXT(255)
91
+ #{connection.quote_column_name(@@data_column_name)} TEXT(255)
83
92
  )
84
93
  end_sql
85
94
  end
@@ -110,22 +119,16 @@ class CGI
110
119
 
111
120
  # Lazy-unmarshal session state.
112
121
  def data
113
- unless @data
114
- case data = read_attribute('data')
115
- when String
116
- @data = self.class.unmarshal(data)
117
- else
118
- @data = data || {}
119
- end
120
- end
121
- @data
122
+ @data ||= self.class.unmarshal(read_attribute(@@data_column_name)) || {}
122
123
  end
123
124
 
124
125
  private
126
+ attr_writer :data
127
+
125
128
  def marshal_data!
126
- write_attribute('data', self.class.marshal(self.data))
129
+ write_attribute(@@data_column_name, self.class.marshal(self.data))
127
130
  end
128
-
131
+
129
132
  # Has the session been loaded yet?
130
133
  def loaded?
131
134
  !! @data
@@ -134,15 +137,17 @@ class CGI
134
137
  # Ensures that the data about to be stored in the database is not
135
138
  # larger than the data storage column. Raises
136
139
  # ActionController::SessionOverflowError.
137
- def ensure_data_not_too_big
138
- return unless limit = self.class.data_column_size_limit
139
- raise ActionController::SessionOverflowError, ActionController::SessionOverflowError::DEFAULT_MESSAGE if read_attribute('data').size > limit
140
+ def raise_on_session_data_overflow!
141
+ limit = self.class.data_column_size_limit
142
+ if loaded? and limit and read_attribute(@@data_column_name).size > limit
143
+ raise ActionController::SessionOverflowError
144
+ end
140
145
  end
141
-
142
146
  end
143
147
 
144
148
  # A barebones session store which duck-types with the default session
145
- # store but bypasses Active Record and issues SQL directly.
149
+ # store but bypasses Active Record and issues SQL directly. This is
150
+ # an example session model class meant as a basis for your own classes.
146
151
  #
147
152
  # The database connection, table name, and session id and data columns
148
153
  # are configurable class attributes. Marshaling and unmarshaling
@@ -182,8 +187,8 @@ class CGI
182
187
  end
183
188
  end
184
189
 
185
- def marshal(data) Base64.encode64(Marshal.dump(data)) end
186
- def unmarshal(data) Marshal.load(Base64.decode64(data)) end
190
+ def marshal(data) Base64.encode64(Marshal.dump(data)) if data end
191
+ def unmarshal(data) Marshal.load(Base64.decode64(data)) if data end
187
192
 
188
193
  def create_table!
189
194
  @@connection.execute <<-end_sql
@@ -219,7 +224,7 @@ class CGI
219
224
  def data
220
225
  unless @data
221
226
  if @marshaled_data
222
- @data, @marshaled_data = self.class.unmarshal(@marshaled_data), nil
227
+ @data, @marshaled_data = self.class.unmarshal(@marshaled_data) || {}, nil
223
228
  else
224
229
  @data = {}
225
230
  end
@@ -257,13 +262,13 @@ class CGI
257
262
  end_sql
258
263
  end
259
264
  end
260
-
261
265
  end
262
266
 
267
+
263
268
  # The class used for session storage. Defaults to
264
269
  # CGI::Session::ActiveRecordStore::Session.
265
270
  cattr_accessor :session_class
266
- @@session_class = Session
271
+ self.session_class = Session
267
272
 
268
273
  # Find or instantiate a session given a CGI::Session.
269
274
  def initialize(session, option = nil)
@@ -273,6 +278,7 @@ class CGI
273
278
  raise CGI::Session::NoSession, 'uninitialized session'
274
279
  end
275
280
  @session = @@session_class.new(:session_id => session_id, :data => {})
281
+ @session.save
276
282
  end
277
283
  end
278
284
 
@@ -31,8 +31,6 @@
31
31
  request_parameters_without_action.delete("controller")
32
32
 
33
33
  request_dump = request_parameters_without_action.inspect.gsub(/,/, ",\n")
34
- session_dump = @request.session.instance_variable_get("@data").inspect.gsub(/,/, ",\n")
35
- response_dump = @response.inspect.gsub(/,/, ",\n")
36
34
  %>
37
35
 
38
36
  <h2 style="margin-top: 30px">Request</h2>
@@ -497,7 +497,7 @@ module HTML #:nodoc:
497
497
  def match_condition(value, condition)
498
498
  case condition
499
499
  when String
500
- value && value.index(condition)
500
+ value && value == condition
501
501
  when Regexp
502
502
  value && value.match(condition)
503
503
  when Numeric
@@ -0,0 +1,17 @@
1
+ ***************
2
+ *** 264,270 ****
3
+
4
+ # A CDATA node is simply a text node with a specialized way of displaying
5
+ # itself.
6
+ - class CDATA < Text
7
+ def to_s
8
+ "<![CDATA[#{super}]>"
9
+ end
10
+ --- 264,270 ----
11
+
12
+ # A CDATA node is simply a text node with a specialized way of displaying
13
+ # itself.
14
+ + class CDATA < Text #:nodoc:
15
+ def to_s
16
+ "<![CDATA[#{super}]>"
17
+ end
@@ -1,8 +1,8 @@
1
1
  module ActionPack
2
- module Version #:nodoc:
2
+ module VERSION #:nodoc:
3
3
  MAJOR = 1
4
4
  MINOR = 11
5
- TINY = 0
5
+ TINY = 1
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -157,13 +157,12 @@ module ActionView #:nodoc:
157
157
 
158
158
  class ObjectWrapper < Struct.new(:value) #:nodoc:
159
159
  end
160
-
160
+
161
161
  def self.load_helpers(helper_dir)#:nodoc:
162
162
  Dir.foreach(helper_dir) do |helper_file|
163
- next unless helper_file =~ /_helper.rb$/
164
- require helper_dir + helper_file
165
- helper_module_name = helper_file.capitalize.gsub(/_([a-z])/) { |m| $1.capitalize }[0..-4]
166
-
163
+ next unless helper_file =~ /^([a-z][a-z_]*_helper).rb$/
164
+ require File.join(helper_dir, $1)
165
+ helper_module_name = $1.camelize
167
166
  class_eval("include ActionView::Helpers::#{helper_module_name}") if Helpers.const_defined?(helper_module_name)
168
167
  end
169
168
  end
@@ -35,7 +35,7 @@ module ActionView
35
35
  compute_public_path(source, 'javascripts', 'js')
36
36
  end
37
37
 
38
- JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls']
38
+ JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls'] unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES)
39
39
  @@javascript_default_sources = JAVASCRIPT_DEFAULT_SOURCES.dup
40
40
 
41
41
  # Returns a script include tag per source given as argument. Examples:
@@ -60,7 +60,7 @@ module ActionView
60
60
  def javascript_include_tag(*sources)
61
61
  options = sources.last.is_a?(Hash) ? sources.pop.stringify_keys : { }
62
62
  if sources.first == :defaults
63
- sources = @@javascript_default_sources
63
+ sources = @@javascript_default_sources.dup
64
64
  if defined?(RAILS_ROOT) and File.exists?("#{RAILS_ROOT}/public/javascripts/application.js")
65
65
  sources << 'application'
66
66
  end
@@ -109,7 +109,7 @@ module ActionView
109
109
  container = container.to_a if Hash === container
110
110
 
111
111
  options_for_select = container.inject([]) do |options, element|
112
- if element.is_a?(Array) || element.is_a?(Range)
112
+ if !element.is_a?(String) and element.respond_to?(:first) and element.respond_to?(:last)
113
113
  is_selected = ( (selected.respond_to?(:include?) ? selected.include?(element.last) : element.last == selected) )
114
114
  is_selected = ( (selected.respond_to?(:include?) && !selected.is_a?(String) ? selected.include?(element.last) : element.last == selected) )
115
115
  if is_selected
@@ -95,7 +95,7 @@ module ActionView
95
95
  # Options:
96
96
  # * <tt>:size</tt> - A string specifying the dimensions of the textarea.
97
97
  # # Outputs <textarea name="body" id="body" cols="25" rows="10"></textarea>
98
- # <%= text_area_tag "body", nil, :size => 25x10 %>
98
+ # <%= text_area_tag "body", nil, :size => "25x10" %>
99
99
  def text_area_tag(name, content = nil, options = {})
100
100
  options = options.stringify_keys
101
101
  if options["size"]
@@ -28,21 +28,23 @@ module ActionView
28
28
  # be sent after the user presses "ok".
29
29
  #
30
30
  # Addtional +options+ are:
31
- # <tt>:rows</tt>:: Number of rows (more than 1 will use a TEXTAREA)
32
- # <tt>:cancel_text</tt>:: The text on the cancel link. (default: "cancel")
33
- # <tt>:ok_text</tt>:: The text on the save link. (default: "ok")
34
- # <tt>:options</tt>:: Pass through options to the AJAX call (see prototype's Ajax.Updater)
35
- # <tt>:with</tt>:: JavaScript snippet that should return what is to be sent
36
- # in the AJAX call, +form+ is an implicit parameter
31
+ # <tt>:rows</tt>:: Number of rows (more than 1 will use a TEXTAREA)
32
+ # <tt>:cancel_text</tt>:: The text on the cancel link. (default: "cancel")
33
+ # <tt>:save_text</tt>:: The text on the save link. (default: "ok")
34
+ # <tt>:external_control</tt>:: The id of an external control used to enter edit mode.
35
+ # <tt>:options</tt>:: Pass through options to the AJAX call (see prototype's Ajax.Updater)
36
+ # <tt>:with</tt>:: JavaScript snippet that should return what is to be sent
37
+ # in the AJAX call, +form+ is an implicit parameter
37
38
  def in_place_editor(field_id, options = {})
38
39
  function = "new Ajax.InPlaceEditor("
39
40
  function << "'#{field_id}', "
40
41
  function << "'#{url_for(options[:url])}'"
41
42
 
42
43
  js_options = {}
43
- js_options['cancelText'] = options[:cancel_text] if options[:cancel_text]
44
- js_options['okText'] = options[:save_text] if options[:save_text]
44
+ js_options['cancelText'] = %('#{options[:cancel_text]}') if options[:cancel_text]
45
+ js_options['okText'] = %('#{options[:save_text]}') if options[:save_text]
45
46
  js_options['rows'] = options[:rows] if options[:rows]
47
+ js_options['externalControl'] = options[:external_control] if options[:external_control]
46
48
  js_options['ajaxOptions'] = options[:options] if options[:options]
47
49
  js_options['callback'] = "function(form) { return #{options[:with]} }" if options[:with]
48
50
  function << (', ' + options_for_javascript(js_options)) unless js_options.empty?
@@ -59,7 +61,7 @@ module ActionView
59
61
  tag = ::ActionView::Helpers::InstanceTag.new(object, method, self)
60
62
  tag_options = {:tag => "span", :id => "#{object}_#{method}_#{tag.object.id}_in_place_editor", :class => "in_place_editor_field"}.merge!(tag_options)
61
63
  in_place_editor_options[:url] = in_place_editor_options[:url] || url_for({ :action => "set_#{object}_#{method}", :id => tag.object.id })
62
- tag.to_content_tag(tag_options[:tag], tag_options) +
64
+ tag.to_content_tag(tag_options.delete(:tag), tag_options) +
63
65
  in_place_editor(tag_options[:id], in_place_editor_options)
64
66
  end
65
67
 
@@ -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 && (navigator.appVersion.indexOf('MSIE')>0) && (Element.getStyle(this.update, 'position')=='absolute')) {
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
  };
@@ -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
  /*--------------------------------------------------------------------------*/
@@ -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;
@@ -49,20 +49,21 @@ var Droppables = {
49
49
  ((!drop._containers) ||
50
50
  this.isContained(element, drop)) &&
51
51
  ((!drop.accept) ||
52
- (Element.Class.has_any(element, drop.accept))) &&
52
+ (Element.classNames(element).detect(
53
+ function(v) { return drop.accept.include(v) } ) )) &&
53
54
  Position.within(drop.element, pX, pY) );
54
55
  },
55
56
 
56
57
  deactivate: function(drop) {
57
58
  if(drop.hoverclass)
58
- Element.Class.remove(drop.element, drop.hoverclass);
59
+ Element.removeClassName(drop.element, drop.hoverclass);
59
60
  this.last_active = null;
60
61
  },
61
62
 
62
63
  activate: function(drop) {
63
64
  if(this.last_active) this.deactivate(this.last_active);
64
65
  if(drop.hoverclass)
65
- Element.Class.add(drop.element, drop.hoverclass);
66
+ Element.addClassName(drop.element, drop.hoverclass);
66
67
  this.last_active = drop;
67
68
  },
68
69
 
@@ -105,13 +106,25 @@ var Droppables = {
105
106
  var Draggables = {
106
107
  observers: [],
107
108
  addObserver: function(observer) {
108
- this.observers.push(observer);
109
+ this.observers.push(observer);
110
+ this._cacheObserverCallbacks();
109
111
  },
110
- removeObserver: function(element) { // element instead of obsever fixes mem leaks
112
+ removeObserver: function(element) { // element instead of observer fixes mem leaks
111
113
  this.observers = this.observers.reject( function(o) { return o.element==element });
114
+ this._cacheObserverCallbacks();
115
+ },
116
+ notify: function(eventName, draggable, event) { // 'onStart', 'onEnd', 'onDrag'
117
+ if(this[eventName+'Count'] > 0)
118
+ this.observers.each( function(o) {
119
+ if(o[eventName]) o[eventName](eventName, draggable, event);
120
+ });
112
121
  },
113
- notify: function(eventName, draggable) { // 'onStart', 'onEnd'
114
- this.observers.invoke(eventName, draggable);
122
+ _cacheObserverCallbacks: function() {
123
+ ['onStart','onEnd','onDrag'].each( function(eventName) {
124
+ Draggables[eventName+'Count'] = Draggables.observers.select(
125
+ function(o) { return o[eventName]; }
126
+ ).length;
127
+ });
115
128
  }
116
129
  }
117
130
 
@@ -138,7 +151,7 @@ Draggable.prototype = {
138
151
 
139
152
  this.element = $(element);
140
153
  if(options.handle && (typeof options.handle == 'string'))
141
- this.handle = Element.Class.childrenWith(this.element, options.handle)[0];
154
+ this.handle = Element.childrenWithClassName(this.element, options.handle)[0];
142
155
 
143
156
  if(!this.handle) this.handle = $(options.handle);
144
157
  if(!this.handle) this.handle = this.element;
@@ -219,7 +232,7 @@ Draggable.prototype = {
219
232
  }
220
233
 
221
234
  if(success) Droppables.fire(event, this.element);
222
- Draggables.notify('onEnd', this);
235
+ Draggables.notify('onEnd', this, event);
223
236
 
224
237
  var revert = this.options.revert;
225
238
  if(revert && typeof revert == 'function') revert = revert(this.element);
@@ -290,11 +303,12 @@ Draggable.prototype = {
290
303
  this.element.parentNode.insertBefore(this._clone, this.element);
291
304
  }
292
305
 
293
- Draggables.notify('onStart', this);
306
+ Draggables.notify('onStart', this, event);
294
307
  if(this.options.starteffect) this.options.starteffect(this.element);
295
308
  }
296
309
 
297
310
  Droppables.show(event, this.element);
311
+ Draggables.notify('onDrag', this, event);
298
312
  this.draw(event);
299
313
  if(this.options.change) this.options.change(this);
300
314
 
@@ -413,7 +427,7 @@ var Sortable = {
413
427
  (this.findElements(element, options) || []).each( function(e) {
414
428
  // handles are per-draggable
415
429
  var handle = options.handle ?
416
- Element.Class.childrenWith(e, options.handle)[0] : e;
430
+ Element.childrenWithClassName(e, options.handle)[0] : e;
417
431
  options.draggables.push(
418
432
  new Draggable(e, Object.extend(options_for_draggable, { handle: handle })));
419
433
  Droppables.add(e, options_for_droppable);
@@ -434,7 +448,7 @@ var Sortable = {
434
448
  var elements = [];
435
449
  $A(element.childNodes).each( function(e) {
436
450
  if(e.tagName && e.tagName==options.tag.toUpperCase() &&
437
- (!options.only || (Element.Class.has(e, options.only))))
451
+ (!options.only || (Element.hasClassName(e, options.only))))
438
452
  elements.push(e);
439
453
  if(options.tree) {
440
454
  var grandchildren = this.findElements(e, options);
@@ -491,14 +505,20 @@ var Sortable = {
491
505
  if(!Sortable._marker) {
492
506
  Sortable._marker = $('dropmarker') || document.createElement('DIV');
493
507
  Element.hide(Sortable._marker);
494
- Element.Class.add(Sortable._marker, 'dropmarker');
508
+ Element.addClassName(Sortable._marker, 'dropmarker');
495
509
  Sortable._marker.style.position = 'absolute';
496
510
  document.getElementsByTagName("body").item(0).appendChild(Sortable._marker);
497
511
  }
498
512
  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
513
  Sortable._marker.style.left = offsets[0] + 'px';
514
+ Sortable._marker.style.top = offsets[1] + 'px';
515
+
516
+ if(position=='after')
517
+ if(sortable.overlap == 'horizontal')
518
+ Sortable._marker.style.left = (offsets[0]+dropon.clientWidth) + 'px';
519
+ else
520
+ Sortable._marker.style.top = (offsets[1]+dropon.clientHeight) + 'px';
521
+
502
522
  Element.show(Sortable._marker);
503
523
  },
504
524