actionpack 1.12.5 → 1.13.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.
Files changed (179) hide show
  1. data/CHANGELOG +517 -15
  2. data/MIT-LICENSE +1 -1
  3. data/README +18 -20
  4. data/Rakefile +7 -4
  5. data/examples/address_book_controller.rb +3 -3
  6. data/examples/blog_controller.cgi +3 -3
  7. data/examples/debate_controller.cgi +5 -5
  8. data/lib/action_controller.rb +2 -2
  9. data/lib/action_controller/assertions.rb +73 -311
  10. data/lib/action_controller/{deprecated_assertions.rb → assertions/deprecated_assertions.rb} +32 -8
  11. data/lib/action_controller/assertions/dom_assertions.rb +25 -0
  12. data/lib/action_controller/assertions/model_assertions.rb +12 -0
  13. data/lib/action_controller/assertions/response_assertions.rb +140 -0
  14. data/lib/action_controller/assertions/routing_assertions.rb +82 -0
  15. data/lib/action_controller/assertions/selector_assertions.rb +571 -0
  16. data/lib/action_controller/assertions/tag_assertions.rb +117 -0
  17. data/lib/action_controller/base.rb +334 -163
  18. data/lib/action_controller/benchmarking.rb +3 -6
  19. data/lib/action_controller/caching.rb +83 -22
  20. data/lib/action_controller/cgi_ext/cgi_ext.rb +0 -7
  21. data/lib/action_controller/cgi_ext/cgi_methods.rb +167 -173
  22. data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +43 -22
  23. data/lib/action_controller/cgi_process.rb +50 -27
  24. data/lib/action_controller/components.rb +21 -25
  25. data/lib/action_controller/cookies.rb +10 -9
  26. data/lib/action_controller/{dependencies.rb → deprecated_dependencies.rb} +9 -27
  27. data/lib/action_controller/filters.rb +448 -225
  28. data/lib/action_controller/flash.rb +24 -20
  29. data/lib/action_controller/helpers.rb +2 -5
  30. data/lib/action_controller/integration.rb +40 -16
  31. data/lib/action_controller/layout.rb +11 -8
  32. data/lib/action_controller/macros/auto_complete.rb +3 -2
  33. data/lib/action_controller/macros/in_place_editing.rb +3 -2
  34. data/lib/action_controller/mime_responds.rb +41 -29
  35. data/lib/action_controller/mime_type.rb +68 -10
  36. data/lib/action_controller/pagination.rb +4 -3
  37. data/lib/action_controller/request.rb +22 -14
  38. data/lib/action_controller/rescue.rb +25 -22
  39. data/lib/action_controller/resources.rb +302 -0
  40. data/lib/action_controller/response.rb +20 -2
  41. data/lib/action_controller/response.rb.rej +17 -0
  42. data/lib/action_controller/routing.rb +1165 -567
  43. data/lib/action_controller/scaffolding.rb +30 -31
  44. data/lib/action_controller/session/active_record_store.rb +2 -0
  45. data/lib/action_controller/session/drb_store.rb +4 -0
  46. data/lib/action_controller/session/mem_cache_store.rb +4 -0
  47. data/lib/action_controller/session_management.rb +6 -9
  48. data/lib/action_controller/status_codes.rb +89 -0
  49. data/lib/action_controller/streaming.rb +6 -15
  50. data/lib/action_controller/templates/rescues/_request_and_response.rhtml +5 -5
  51. data/lib/action_controller/templates/rescues/diagnostics.rhtml +2 -2
  52. data/lib/action_controller/templates/rescues/routing_error.rhtml +4 -4
  53. data/lib/action_controller/templates/rescues/template_error.rhtml +1 -1
  54. data/lib/action_controller/templates/scaffolds/list.rhtml +1 -1
  55. data/lib/action_controller/test_process.rb +52 -30
  56. data/lib/action_controller/url_rewriter.rb +63 -29
  57. data/lib/action_controller/vendor/html-scanner/html/document.rb +1 -0
  58. data/lib/action_controller/vendor/html-scanner/html/node.rb +3 -4
  59. data/lib/action_controller/vendor/html-scanner/html/selector.rb +822 -0
  60. data/lib/action_controller/verification.rb +22 -11
  61. data/lib/action_pack.rb +1 -1
  62. data/lib/action_pack/version.rb +2 -2
  63. data/lib/action_view.rb +1 -1
  64. data/lib/action_view/base.rb +46 -43
  65. data/lib/action_view/compiled_templates.rb +1 -1
  66. data/lib/action_view/helpers/active_record_helper.rb +54 -17
  67. data/lib/action_view/helpers/asset_tag_helper.rb +97 -46
  68. data/lib/action_view/helpers/capture_helper.rb +1 -1
  69. data/lib/action_view/helpers/date_helper.rb +258 -136
  70. data/lib/action_view/helpers/debug_helper.rb +1 -1
  71. data/lib/action_view/helpers/deprecated_helper.rb +34 -0
  72. data/lib/action_view/helpers/form_helper.rb +75 -35
  73. data/lib/action_view/helpers/form_options_helper.rb +7 -5
  74. data/lib/action_view/helpers/form_tag_helper.rb +44 -6
  75. data/lib/action_view/helpers/java_script_macros_helper.rb +59 -46
  76. data/lib/action_view/helpers/javascript_helper.rb +71 -10
  77. data/lib/action_view/helpers/javascripts/controls.js +41 -23
  78. data/lib/action_view/helpers/javascripts/dragdrop.js +105 -76
  79. data/lib/action_view/helpers/javascripts/effects.js +293 -163
  80. data/lib/action_view/helpers/javascripts/prototype.js +897 -389
  81. data/lib/action_view/helpers/javascripts/prototype.js.rej +561 -0
  82. data/lib/action_view/helpers/number_helper.rb +111 -65
  83. data/lib/action_view/helpers/prototype_helper.rb +84 -109
  84. data/lib/action_view/helpers/scriptaculous_helper.rb +5 -0
  85. data/lib/action_view/helpers/tag_helper.rb +69 -16
  86. data/lib/action_view/helpers/text_helper.rb +149 -112
  87. data/lib/action_view/helpers/url_helper.rb +200 -107
  88. data/lib/action_view/template_error.rb +66 -42
  89. data/test/abstract_unit.rb +4 -2
  90. data/test/active_record_unit.rb +84 -56
  91. data/test/activerecord/active_record_assertions_test.rb +26 -18
  92. data/test/activerecord/active_record_store_test.rb +4 -36
  93. data/test/activerecord/pagination_test.rb +1 -6
  94. data/test/controller/action_pack_assertions_test.rb +230 -113
  95. data/test/controller/addresses_render_test.rb +2 -6
  96. data/test/controller/assert_select_test.rb +576 -0
  97. data/test/controller/base_test.rb +73 -3
  98. data/test/controller/caching_test.rb +228 -0
  99. data/test/controller/capture_test.rb +12 -10
  100. data/test/controller/cgi_test.rb +89 -12
  101. data/test/controller/components_test.rb +24 -2
  102. data/test/controller/content_type_test.rb +139 -0
  103. data/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb +0 -0
  104. data/test/controller/controller_fixtures/app/controllers/user_controller.rb +0 -0
  105. data/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb +0 -0
  106. data/test/controller/cookie_test.rb +33 -25
  107. data/test/controller/deprecated_instance_variables_test.rb +48 -0
  108. data/test/controller/deprecation/deprecated_base_methods_test.rb +60 -0
  109. data/test/controller/fake_controllers.rb +0 -1
  110. data/test/controller/filters_test.rb +301 -16
  111. data/test/controller/flash_test.rb +19 -2
  112. data/test/controller/helper_test.rb +2 -2
  113. data/test/controller/integration_test.rb +154 -0
  114. data/test/controller/layout_test.rb +115 -1
  115. data/test/controller/mime_responds_test.rb +94 -0
  116. data/test/controller/mime_type_test.rb +9 -0
  117. data/test/controller/new_render_test.rb +161 -11
  118. data/test/controller/raw_post_test.rb +52 -15
  119. data/test/controller/redirect_test.rb +27 -14
  120. data/test/controller/render_test.rb +76 -29
  121. data/test/controller/request_test.rb +55 -4
  122. data/test/controller/resources_test.rb +274 -0
  123. data/test/controller/routing_test.rb +1533 -824
  124. data/test/controller/selector_test.rb +628 -0
  125. data/test/controller/send_file_test.rb +9 -1
  126. data/test/controller/session_management_test.rb +51 -0
  127. data/test/controller/test_test.rb +113 -29
  128. data/test/controller/url_rewriter_test.rb +86 -17
  129. data/test/controller/verification_test.rb +19 -17
  130. data/test/controller/webservice_test.rb +0 -7
  131. data/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml +1 -0
  132. data/test/fixtures/content_type/render_default_for_rhtml.rhtml +1 -0
  133. data/test/fixtures/content_type/render_default_for_rjs.rjs +1 -0
  134. data/test/fixtures/content_type/render_default_for_rxml.rxml +1 -0
  135. data/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml +1 -0
  136. data/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml +1 -0
  137. data/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml +1 -0
  138. data/test/fixtures/deprecated_instance_variables/_flash_method.rhtml +1 -0
  139. data/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml +1 -0
  140. data/test/fixtures/deprecated_instance_variables/_headers_method.rhtml +1 -0
  141. data/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml +1 -0
  142. data/test/fixtures/deprecated_instance_variables/_params_method.rhtml +1 -0
  143. data/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml +1 -0
  144. data/test/fixtures/deprecated_instance_variables/_request_method.rhtml +1 -0
  145. data/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml +1 -0
  146. data/test/fixtures/deprecated_instance_variables/_response_method.rhtml +1 -0
  147. data/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml +1 -0
  148. data/test/fixtures/deprecated_instance_variables/_session_method.rhtml +1 -0
  149. data/test/fixtures/multipart/binary_file +0 -0
  150. data/test/fixtures/public/javascripts/application.js +1 -0
  151. data/test/fixtures/test/_hello.rxml +1 -0
  152. data/test/fixtures/test/hello_world_container.rxml +3 -0
  153. data/test/fixtures/topic.rb +2 -2
  154. data/test/template/active_record_helper_test.rb +83 -12
  155. data/test/template/asset_tag_helper_test.rb +75 -95
  156. data/test/template/compiled_templates_test.rb +1 -0
  157. data/test/template/date_helper_test.rb +873 -181
  158. data/test/template/deprecated_helper_test.rb +36 -0
  159. data/test/template/deprecated_instance_variables_test.rb +43 -0
  160. data/test/template/form_helper_test.rb +77 -1
  161. data/test/template/form_options_helper_test.rb +4 -0
  162. data/test/template/form_tag_helper_test.rb +66 -2
  163. data/test/template/java_script_macros_helper_test.rb +4 -1
  164. data/test/template/javascript_helper_test.rb +29 -0
  165. data/test/template/number_helper_test.rb +63 -27
  166. data/test/template/prototype_helper_test.rb +77 -34
  167. data/test/template/tag_helper_test.rb +34 -6
  168. data/test/template/text_helper_test.rb +69 -34
  169. data/test/template/url_helper_test.rb +168 -16
  170. data/test/testing_sandbox.rb +7 -22
  171. metadata +66 -20
  172. data/filler.txt +0 -50
  173. data/lib/action_controller/code_generation.rb +0 -235
  174. data/lib/action_controller/vendor/xml_simple.rb +0 -1019
  175. data/test/controller/caching_filestore.rb +0 -74
  176. data/test/fixtures/application_root/app/controllers/a_class_that_contains_a_controller/poorly_placed_controller.rb +0 -7
  177. data/test/fixtures/application_root/app/controllers/module_that_holds_controllers/nested_controller.rb +0 -3
  178. data/test/fixtures/application_root/app/models/a_class_that_contains_a_controller.rb +0 -7
  179. data/test/fixtures/dont_load.rb +0 -3
@@ -1,15 +1,16 @@
1
- // Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
1
+ // Copyright (c) 2005, 2006 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2
2
  // Contributors:
3
3
  // Justin Palmer (http://encytemedia.com/)
4
4
  // Mark Pilgrim (http://diveintomark.org/)
5
5
  // Martin Bialasinki
6
6
  //
7
- // See scriptaculous.js for full license.
7
+ // script.aculo.us is freely distributable under the terms of an MIT-style license.
8
+ // For details, see the script.aculo.us web site: http://script.aculo.us/
8
9
 
9
10
  // converts rgb() and #xxx to #xxxxxx format,
10
11
  // returns self (or first argument) if not convertable
11
12
  String.prototype.parseColor = function() {
12
- var color = '#';
13
+ var color = '#';
13
14
  if(this.slice(0,4) == 'rgb(') {
14
15
  var cols = this.slice(4,this.length-1).split(',');
15
16
  var i=0; do { color += parseInt(cols[i]).toColorPart() } while (++i<3);
@@ -41,15 +42,17 @@ Element.collectTextNodesIgnoreClass = function(element, className) {
41
42
 
42
43
  Element.setContentZoom = function(element, percent) {
43
44
  element = $(element);
44
- Element.setStyle(element, {fontSize: (percent/100) + 'em'});
45
+ element.setStyle({fontSize: (percent/100) + 'em'});
45
46
  if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
47
+ return element;
46
48
  }
47
49
 
48
- Element.getOpacity = function(element){
50
+ Element.getOpacity = function(element){
51
+ element = $(element);
49
52
  var opacity;
50
- if (opacity = Element.getStyle(element, 'opacity'))
53
+ if (opacity = element.getStyle('opacity'))
51
54
  return parseFloat(opacity);
52
- if (opacity = (Element.getStyle(element, 'filter') || '').match(/alpha\(opacity=(.*)\)/))
55
+ if (opacity = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
53
56
  if(opacity[1]) return parseFloat(opacity[1]) / 100;
54
57
  return 1.0;
55
58
  }
@@ -57,34 +60,26 @@ Element.getOpacity = function(element){
57
60
  Element.setOpacity = function(element, value){
58
61
  element= $(element);
59
62
  if (value == 1){
60
- Element.setStyle(element, { opacity:
63
+ element.setStyle({ opacity:
61
64
  (/Gecko/.test(navigator.userAgent) && !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ?
62
- 0.999999 : null });
63
- if(/MSIE/.test(navigator.userAgent))
64
- Element.setStyle(element, {filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
65
+ 0.999999 : 1.0 });
66
+ if(/MSIE/.test(navigator.userAgent) && !window.opera)
67
+ element.setStyle({filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'')});
65
68
  } else {
66
69
  if(value < 0.00001) value = 0;
67
- Element.setStyle(element, {opacity: value});
68
- if(/MSIE/.test(navigator.userAgent))
69
- Element.setStyle(element,
70
- { filter: Element.getStyle(element,'filter').replace(/alpha\([^\)]*\)/gi,'') +
71
- 'alpha(opacity='+value*100+')' });
70
+ element.setStyle({opacity: value});
71
+ if(/MSIE/.test(navigator.userAgent) && !window.opera)
72
+ element.setStyle(
73
+ { filter: element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
74
+ 'alpha(opacity='+value*100+')' });
72
75
  }
76
+ return element;
73
77
  }
74
78
 
75
79
  Element.getInlineOpacity = function(element){
76
80
  return $(element).style.opacity || '';
77
81
  }
78
82
 
79
- Element.childrenWithClassName = function(element, className, findFirst) {
80
- var classNameRegExp = new RegExp("(^|\\s)" + className + "(\\s|$)");
81
- var results = $A($(element).getElementsByTagName('*'))[findFirst ? 'detect' : 'select']( function(c) {
82
- return (c.className && c.className.match(classNameRegExp));
83
- });
84
- if(!results) results = [];
85
- return results;
86
- }
87
-
88
83
  Element.forceRerendering = function(element) {
89
84
  try {
90
85
  element = $(element);
@@ -104,9 +99,17 @@ Array.prototype.call = function() {
104
99
  /*--------------------------------------------------------------------------*/
105
100
 
106
101
  var Effect = {
102
+ _elementDoesNotExistError: {
103
+ name: 'ElementDoesNotExistError',
104
+ message: 'The specified DOM element does not exist, but is required for this effect to operate'
105
+ },
107
106
  tagifyText: function(element) {
107
+ if(typeof Builder == 'undefined')
108
+ throw("Effect.tagifyText requires including script.aculo.us' builder.js library");
109
+
108
110
  var tagifyStyle = 'position:relative';
109
- if(/MSIE/.test(navigator.userAgent)) tagifyStyle += ';zoom:1';
111
+ if(/MSIE/.test(navigator.userAgent) && !window.opera) tagifyStyle += ';zoom:1';
112
+
110
113
  element = $(element);
111
114
  $A(element.childNodes).each( function(child) {
112
115
  if(child.nodeType==3) {
@@ -159,33 +162,35 @@ var Effect2 = Effect; // deprecated
159
162
 
160
163
  /* ------------- transitions ------------- */
161
164
 
162
- Effect.Transitions = {}
163
-
164
- Effect.Transitions.linear = function(pos) {
165
- return pos;
166
- }
167
- Effect.Transitions.sinoidal = function(pos) {
168
- return (-Math.cos(pos*Math.PI)/2) + 0.5;
169
- }
170
- Effect.Transitions.reverse = function(pos) {
171
- return 1-pos;
172
- }
173
- Effect.Transitions.flicker = function(pos) {
174
- return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
175
- }
176
- Effect.Transitions.wobble = function(pos) {
177
- return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
178
- }
179
- Effect.Transitions.pulse = function(pos) {
180
- return (Math.floor(pos*10) % 2 == 0 ?
181
- (pos*10-Math.floor(pos*10)) : 1-(pos*10-Math.floor(pos*10)));
182
- }
183
- Effect.Transitions.none = function(pos) {
184
- return 0;
185
- }
186
- Effect.Transitions.full = function(pos) {
187
- return 1;
188
- }
165
+ Effect.Transitions = {
166
+ linear: Prototype.K,
167
+ sinoidal: function(pos) {
168
+ return (-Math.cos(pos*Math.PI)/2) + 0.5;
169
+ },
170
+ reverse: function(pos) {
171
+ return 1-pos;
172
+ },
173
+ flicker: function(pos) {
174
+ return ((-Math.cos(pos*Math.PI)/4) + 0.75) + Math.random()/4;
175
+ },
176
+ wobble: function(pos) {
177
+ return (-Math.cos(pos*Math.PI*(9*pos))/2) + 0.5;
178
+ },
179
+ pulse: function(pos, pulses) {
180
+ pulses = pulses || 5;
181
+ return (
182
+ Math.round((pos % (1/pulses)) * pulses) == 0 ?
183
+ ((pos * pulses * 2) - Math.floor(pos * pulses * 2)) :
184
+ 1 - ((pos * pulses * 2) - Math.floor(pos * pulses * 2))
185
+ );
186
+ },
187
+ none: function(pos) {
188
+ return 0;
189
+ },
190
+ full: function(pos) {
191
+ return 1;
192
+ }
193
+ };
189
194
 
190
195
  /* ------------- core effects ------------- */
191
196
 
@@ -212,6 +217,9 @@ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
212
217
  e.finishOn += effect.finishOn;
213
218
  });
214
219
  break;
220
+ case 'with-last':
221
+ timestamp = this.effects.pluck('startOn').max() || timestamp;
222
+ break;
215
223
  case 'end':
216
224
  // start effect after last queued effect has finished
217
225
  timestamp = this.effects.pluck('finishOn').max() || timestamp;
@@ -348,12 +356,24 @@ Object.extend(Object.extend(Effect.Parallel.prototype, Effect.Base.prototype), {
348
356
  }
349
357
  });
350
358
 
359
+ Effect.Event = Class.create();
360
+ Object.extend(Object.extend(Effect.Event.prototype, Effect.Base.prototype), {
361
+ initialize: function() {
362
+ var options = Object.extend({
363
+ duration: 0
364
+ }, arguments[0] || {});
365
+ this.start(options);
366
+ },
367
+ update: Prototype.emptyFunction
368
+ });
369
+
351
370
  Effect.Opacity = Class.create();
352
371
  Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
353
372
  initialize: function(element) {
354
373
  this.element = $(element);
374
+ if(!this.element) throw(Effect._elementDoesNotExistError);
355
375
  // make this work on IE on elements without 'layout'
356
- if(/MSIE/.test(navigator.userAgent) && (!this.element.hasLayout))
376
+ if(/MSIE/.test(navigator.userAgent) && !window.opera && (!this.element.currentStyle.hasLayout))
357
377
  this.element.setStyle({zoom: 1});
358
378
  var options = Object.extend({
359
379
  from: this.element.getOpacity() || 0.0,
@@ -370,6 +390,7 @@ Effect.Move = Class.create();
370
390
  Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
371
391
  initialize: function(element) {
372
392
  this.element = $(element);
393
+ if(!this.element) throw(Effect._elementDoesNotExistError);
373
394
  var options = Object.extend({
374
395
  x: 0,
375
396
  y: 0,
@@ -393,8 +414,8 @@ Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
393
414
  },
394
415
  update: function(position) {
395
416
  this.element.setStyle({
396
- left: this.options.x * position + this.originalLeft + 'px',
397
- top: this.options.y * position + this.originalTop + 'px'
417
+ left: Math.round(this.options.x * position + this.originalLeft) + 'px',
418
+ top: Math.round(this.options.y * position + this.originalTop) + 'px'
398
419
  });
399
420
  }
400
421
  });
@@ -408,7 +429,8 @@ Effect.MoveBy = function(element, toTop, toLeft) {
408
429
  Effect.Scale = Class.create();
409
430
  Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
410
431
  initialize: function(element, percent) {
411
- this.element = $(element)
432
+ this.element = $(element);
433
+ if(!this.element) throw(Effect._elementDoesNotExistError);
412
434
  var options = Object.extend({
413
435
  scaleX: true,
414
436
  scaleY: true,
@@ -433,7 +455,7 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
433
455
  this.originalLeft = this.element.offsetLeft;
434
456
 
435
457
  var fontSize = this.element.getStyle('font-size') || '100%';
436
- ['em','px','%'].each( function(fontSizeType) {
458
+ ['em','px','%','pt'].each( function(fontSizeType) {
437
459
  if(fontSize.indexOf(fontSizeType)>0) {
438
460
  this.fontSize = parseFloat(fontSize);
439
461
  this.fontSizeType = fontSizeType;
@@ -458,12 +480,12 @@ Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
458
480
  this.setDimensions(this.dims[0] * currentScale, this.dims[1] * currentScale);
459
481
  },
460
482
  finish: function(position) {
461
- if (this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
483
+ if(this.restoreAfterFinish) this.element.setStyle(this.originalStyle);
462
484
  },
463
485
  setDimensions: function(height, width) {
464
486
  var d = {};
465
- if(this.options.scaleX) d.width = width + 'px';
466
- if(this.options.scaleY) d.height = height + 'px';
487
+ if(this.options.scaleX) d.width = Math.round(width) + 'px';
488
+ if(this.options.scaleY) d.height = Math.round(height) + 'px';
467
489
  if(this.options.scaleFromCenter) {
468
490
  var topd = (height - this.dims[0])/2;
469
491
  var leftd = (width - this.dims[1])/2;
@@ -483,6 +505,7 @@ Effect.Highlight = Class.create();
483
505
  Object.extend(Object.extend(Effect.Highlight.prototype, Effect.Base.prototype), {
484
506
  initialize: function(element) {
485
507
  this.element = $(element);
508
+ if(!this.element) throw(Effect._elementDoesNotExistError);
486
509
  var options = Object.extend({ startcolor: '#ffff99' }, arguments[1] || {});
487
510
  this.start(options);
488
511
  },
@@ -547,8 +570,7 @@ Effect.Fade = function(element) {
547
570
  to: 0.0,
548
571
  afterFinishInternal: function(effect) {
549
572
  if(effect.options.to!=0) return;
550
- effect.element.hide();
551
- effect.element.setStyle({opacity: oldOpacity});
573
+ effect.element.hide().setStyle({opacity: oldOpacity});
552
574
  }}, arguments[1] || {});
553
575
  return new Effect.Opacity(element,options);
554
576
  }
@@ -563,25 +585,31 @@ Effect.Appear = function(element) {
563
585
  effect.element.forceRerendering();
564
586
  },
565
587
  beforeSetup: function(effect) {
566
- effect.element.setOpacity(effect.options.from);
567
- effect.element.show();
588
+ effect.element.setOpacity(effect.options.from).show();
568
589
  }}, arguments[1] || {});
569
590
  return new Effect.Opacity(element,options);
570
591
  }
571
592
 
572
593
  Effect.Puff = function(element) {
573
594
  element = $(element);
574
- var oldStyle = { opacity: element.getInlineOpacity(), position: element.getStyle('position') };
595
+ var oldStyle = {
596
+ opacity: element.getInlineOpacity(),
597
+ position: element.getStyle('position'),
598
+ top: element.style.top,
599
+ left: element.style.left,
600
+ width: element.style.width,
601
+ height: element.style.height
602
+ };
575
603
  return new Effect.Parallel(
576
604
  [ new Effect.Scale(element, 200,
577
605
  { sync: true, scaleFromCenter: true, scaleContent: true, restoreAfterFinish: true }),
578
606
  new Effect.Opacity(element, { sync: true, to: 0.0 } ) ],
579
607
  Object.extend({ duration: 1.0,
580
608
  beforeSetupInternal: function(effect) {
581
- effect.effects[0].element.setStyle({position: 'absolute'}); },
609
+ Position.absolutize(effect.effects[0].element)
610
+ },
582
611
  afterFinishInternal: function(effect) {
583
- effect.effects[0].element.hide();
584
- effect.effects[0].element.setStyle(oldStyle); }
612
+ effect.effects[0].element.hide().setStyle(oldStyle); }
585
613
  }, arguments[1] || {})
586
614
  );
587
615
  }
@@ -589,13 +617,12 @@ Effect.Puff = function(element) {
589
617
  Effect.BlindUp = function(element) {
590
618
  element = $(element);
591
619
  element.makeClipping();
592
- return new Effect.Scale(element, 0,
620
+ return new Effect.Scale(element, 0,
593
621
  Object.extend({ scaleContent: false,
594
622
  scaleX: false,
595
623
  restoreAfterFinish: true,
596
624
  afterFinishInternal: function(effect) {
597
- effect.element.hide();
598
- effect.element.undoClipping();
625
+ effect.element.hide().undoClipping();
599
626
  }
600
627
  }, arguments[1] || {})
601
628
  );
@@ -604,28 +631,25 @@ Effect.BlindUp = function(element) {
604
631
  Effect.BlindDown = function(element) {
605
632
  element = $(element);
606
633
  var elementDimensions = element.getDimensions();
607
- return new Effect.Scale(element, 100,
608
- Object.extend({ scaleContent: false,
609
- scaleX: false,
610
- scaleFrom: 0,
611
- scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
612
- restoreAfterFinish: true,
613
- afterSetup: function(effect) {
614
- effect.element.makeClipping();
615
- effect.element.setStyle({height: '0px'});
616
- effect.element.show();
617
- },
618
- afterFinishInternal: function(effect) {
619
- effect.element.undoClipping();
620
- }
621
- }, arguments[1] || {})
622
- );
634
+ return new Effect.Scale(element, 100, Object.extend({
635
+ scaleContent: false,
636
+ scaleX: false,
637
+ scaleFrom: 0,
638
+ scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
639
+ restoreAfterFinish: true,
640
+ afterSetup: function(effect) {
641
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
642
+ },
643
+ afterFinishInternal: function(effect) {
644
+ effect.element.undoClipping();
645
+ }
646
+ }, arguments[1] || {}));
623
647
  }
624
648
 
625
649
  Effect.SwitchOff = function(element) {
626
650
  element = $(element);
627
651
  var oldOpacity = element.getInlineOpacity();
628
- return new Effect.Appear(element, {
652
+ return new Effect.Appear(element, Object.extend({
629
653
  duration: 0.4,
630
654
  from: 0,
631
655
  transition: Effect.Transitions.flicker,
@@ -634,18 +658,14 @@ Effect.SwitchOff = function(element) {
634
658
  duration: 0.3, scaleFromCenter: true,
635
659
  scaleX: false, scaleContent: false, restoreAfterFinish: true,
636
660
  beforeSetup: function(effect) {
637
- effect.element.makePositioned();
638
- effect.element.makeClipping();
661
+ effect.element.makePositioned().makeClipping();
639
662
  },
640
663
  afterFinishInternal: function(effect) {
641
- effect.element.hide();
642
- effect.element.undoClipping();
643
- effect.element.undoPositioned();
644
- effect.element.setStyle({opacity: oldOpacity});
664
+ effect.element.hide().undoClipping().undoPositioned().setStyle({opacity: oldOpacity});
645
665
  }
646
666
  })
647
667
  }
648
- });
668
+ }, arguments[1] || {}));
649
669
  }
650
670
 
651
671
  Effect.DropOut = function(element) {
@@ -663,9 +683,7 @@ Effect.DropOut = function(element) {
663
683
  effect.effects[0].element.makePositioned();
664
684
  },
665
685
  afterFinishInternal: function(effect) {
666
- effect.effects[0].element.hide();
667
- effect.effects[0].element.undoPositioned();
668
- effect.effects[0].element.setStyle(oldStyle);
686
+ effect.effects[0].element.hide().undoPositioned().setStyle(oldStyle);
669
687
  }
670
688
  }, arguments[1] || {}));
671
689
  }
@@ -687,54 +705,42 @@ Effect.Shake = function(element) {
687
705
  { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
688
706
  new Effect.Move(effect.element,
689
707
  { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
690
- effect.element.undoPositioned();
691
- effect.element.setStyle(oldStyle);
708
+ effect.element.undoPositioned().setStyle(oldStyle);
692
709
  }}) }}) }}) }}) }}) }});
693
710
  }
694
711
 
695
712
  Effect.SlideDown = function(element) {
696
- element = $(element);
697
- element.cleanWhitespace();
713
+ element = $(element).cleanWhitespace();
698
714
  // SlideDown need to have the content of the element wrapped in a container element with fixed height!
699
- var oldInnerBottom = $(element.firstChild).getStyle('bottom');
715
+ var oldInnerBottom = element.down().getStyle('bottom');
700
716
  var elementDimensions = element.getDimensions();
701
717
  return new Effect.Scale(element, 100, Object.extend({
702
718
  scaleContent: false,
703
719
  scaleX: false,
704
- scaleFrom: 0,
720
+ scaleFrom: window.opera ? 0 : 1,
705
721
  scaleMode: {originalHeight: elementDimensions.height, originalWidth: elementDimensions.width},
706
722
  restoreAfterFinish: true,
707
723
  afterSetup: function(effect) {
708
724
  effect.element.makePositioned();
709
- effect.element.firstChild.makePositioned();
725
+ effect.element.down().makePositioned();
710
726
  if(window.opera) effect.element.setStyle({top: ''});
711
- effect.element.makeClipping();
712
- effect.element.setStyle({height: '0px'});
713
- effect.element.show(); },
727
+ effect.element.makeClipping().setStyle({height: '0px'}).show();
728
+ },
714
729
  afterUpdateInternal: function(effect) {
715
- effect.element.firstChild.setStyle({bottom:
730
+ effect.element.down().setStyle({bottom:
716
731
  (effect.dims[0] - effect.element.clientHeight) + 'px' });
717
732
  },
718
733
  afterFinishInternal: function(effect) {
719
- effect.element.undoClipping();
720
- // IE will crash if child is undoPositioned first
721
- if(/MSIE/.test(navigator.userAgent)){
722
- effect.element.undoPositioned();
723
- effect.element.firstChild.undoPositioned();
724
- }else{
725
- effect.element.firstChild.undoPositioned();
726
- effect.element.undoPositioned();
727
- }
728
- effect.element.firstChild.setStyle({bottom: oldInnerBottom}); }
734
+ effect.element.undoClipping().undoPositioned();
735
+ effect.element.down().undoPositioned().setStyle({bottom: oldInnerBottom}); }
729
736
  }, arguments[1] || {})
730
737
  );
731
738
  }
732
-
739
+
733
740
  Effect.SlideUp = function(element) {
734
- element = $(element);
735
- element.cleanWhitespace();
736
- var oldInnerBottom = $(element.firstChild).getStyle('bottom');
737
- return new Effect.Scale(element, 0,
741
+ element = $(element).cleanWhitespace();
742
+ var oldInnerBottom = element.down().getStyle('bottom');
743
+ return new Effect.Scale(element, window.opera ? 0 : 1,
738
744
  Object.extend({ scaleContent: false,
739
745
  scaleX: false,
740
746
  scaleMode: 'box',
@@ -742,32 +748,32 @@ Effect.SlideUp = function(element) {
742
748
  restoreAfterFinish: true,
743
749
  beforeStartInternal: function(effect) {
744
750
  effect.element.makePositioned();
745
- effect.element.firstChild.makePositioned();
751
+ effect.element.down().makePositioned();
746
752
  if(window.opera) effect.element.setStyle({top: ''});
747
- effect.element.makeClipping();
748
- effect.element.show(); },
753
+ effect.element.makeClipping().show();
754
+ },
749
755
  afterUpdateInternal: function(effect) {
750
- effect.element.firstChild.setStyle({bottom:
751
- (effect.dims[0] - effect.element.clientHeight) + 'px' }); },
756
+ effect.element.down().setStyle({bottom:
757
+ (effect.dims[0] - effect.element.clientHeight) + 'px' });
758
+ },
752
759
  afterFinishInternal: function(effect) {
753
- effect.element.hide();
754
- effect.element.undoClipping();
755
- effect.element.firstChild.undoPositioned();
756
- effect.element.undoPositioned();
757
- effect.element.setStyle({bottom: oldInnerBottom}); }
760
+ effect.element.hide().undoClipping().undoPositioned().setStyle({bottom: oldInnerBottom});
761
+ effect.element.down().undoPositioned();
762
+ }
758
763
  }, arguments[1] || {})
759
764
  );
760
765
  }
761
766
 
762
767
  // Bug in opera makes the TD containing this element expand for a instance after finish
763
768
  Effect.Squish = function(element) {
764
- return new Effect.Scale(element, window.opera ? 1 : 0,
765
- { restoreAfterFinish: true,
766
- beforeSetup: function(effect) {
767
- effect.element.makeClipping(effect.element); },
768
- afterFinishInternal: function(effect) {
769
- effect.element.hide(effect.element);
770
- effect.element.undoClipping(effect.element); }
769
+ return new Effect.Scale(element, window.opera ? 1 : 0, {
770
+ restoreAfterFinish: true,
771
+ beforeSetup: function(effect) {
772
+ effect.element.makeClipping();
773
+ },
774
+ afterFinishInternal: function(effect) {
775
+ effect.element.hide().undoClipping();
776
+ }
771
777
  });
772
778
  }
773
779
 
@@ -823,9 +829,7 @@ Effect.Grow = function(element) {
823
829
  y: initialMoveY,
824
830
  duration: 0.01,
825
831
  beforeSetup: function(effect) {
826
- effect.element.hide();
827
- effect.element.makeClipping();
828
- effect.element.makePositioned();
832
+ effect.element.hide().makeClipping().makePositioned();
829
833
  },
830
834
  afterFinishInternal: function(effect) {
831
835
  new Effect.Parallel(
@@ -836,13 +840,10 @@ Effect.Grow = function(element) {
836
840
  sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
837
841
  ], Object.extend({
838
842
  beforeSetup: function(effect) {
839
- effect.effects[0].element.setStyle({height: '0px'});
840
- effect.effects[0].element.show();
843
+ effect.effects[0].element.setStyle({height: '0px'}).show();
841
844
  },
842
845
  afterFinishInternal: function(effect) {
843
- effect.effects[0].element.undoClipping();
844
- effect.effects[0].element.undoPositioned();
845
- effect.effects[0].element.setStyle(oldStyle);
846
+ effect.effects[0].element.undoClipping().undoPositioned().setStyle(oldStyle);
846
847
  }
847
848
  }, options)
848
849
  )
@@ -896,13 +897,10 @@ Effect.Shrink = function(element) {
896
897
  new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
897
898
  ], Object.extend({
898
899
  beforeStartInternal: function(effect) {
899
- effect.effects[0].element.makePositioned();
900
- effect.effects[0].element.makeClipping(); },
900
+ effect.effects[0].element.makePositioned().makeClipping();
901
+ },
901
902
  afterFinishInternal: function(effect) {
902
- effect.effects[0].element.hide();
903
- effect.effects[0].element.undoClipping();
904
- effect.effects[0].element.undoPositioned();
905
- effect.effects[0].element.setStyle(oldStyle); }
903
+ effect.effects[0].element.hide().undoClipping().undoPositioned().setStyle(oldStyle); }
906
904
  }, options)
907
905
  );
908
906
  }
@@ -912,10 +910,10 @@ Effect.Pulsate = function(element) {
912
910
  var options = arguments[1] || {};
913
911
  var oldOpacity = element.getInlineOpacity();
914
912
  var transition = options.transition || Effect.Transitions.sinoidal;
915
- var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos)) };
913
+ var reverser = function(pos){ return transition(1-Effect.Transitions.pulse(pos, options.pulses)) };
916
914
  reverser.bind(transition);
917
915
  return new Effect.Opacity(element,
918
- Object.extend(Object.extend({ duration: 3.0, from: 0,
916
+ Object.extend(Object.extend({ duration: 2.0, from: 0,
919
917
  afterFinishInternal: function(effect) { effect.element.setStyle({opacity: oldOpacity}); }
920
918
  }, options), {transition: reverser}));
921
919
  }
@@ -927,7 +925,7 @@ Effect.Fold = function(element) {
927
925
  left: element.style.left,
928
926
  width: element.style.width,
929
927
  height: element.style.height };
930
- Element.makeClipping(element);
928
+ element.makeClipping();
931
929
  return new Effect.Scale(element, 5, Object.extend({
932
930
  scaleContent: false,
933
931
  scaleX: false,
@@ -936,15 +934,147 @@ Effect.Fold = function(element) {
936
934
  scaleContent: false,
937
935
  scaleY: false,
938
936
  afterFinishInternal: function(effect) {
939
- effect.element.hide();
940
- effect.element.undoClipping();
941
- effect.element.setStyle(oldStyle);
937
+ effect.element.hide().undoClipping().setStyle(oldStyle);
942
938
  } });
943
939
  }}, arguments[1] || {}));
944
940
  };
945
941
 
942
+ Effect.Morph = Class.create();
943
+ Object.extend(Object.extend(Effect.Morph.prototype, Effect.Base.prototype), {
944
+ initialize: function(element) {
945
+ this.element = $(element);
946
+ if(!this.element) throw(Effect._elementDoesNotExistError);
947
+ var options = Object.extend({
948
+ style: ''
949
+ }, arguments[1] || {});
950
+ this.start(options);
951
+ },
952
+ setup: function(){
953
+ function parseColor(color){
954
+ if(!color || ['rgba(0, 0, 0, 0)','transparent'].include(color)) color = '#ffffff';
955
+ color = color.parseColor();
956
+ return $R(0,2).map(function(i){
957
+ return parseInt( color.slice(i*2+1,i*2+3), 16 )
958
+ });
959
+ }
960
+ this.transforms = this.options.style.parseStyle().map(function(property){
961
+ var originalValue = this.element.getStyle(property[0]);
962
+ return $H({
963
+ style: property[0],
964
+ originalValue: property[1].unit=='color' ?
965
+ parseColor(originalValue) : parseFloat(originalValue || 0),
966
+ targetValue: property[1].unit=='color' ?
967
+ parseColor(property[1].value) : property[1].value,
968
+ unit: property[1].unit
969
+ });
970
+ }.bind(this)).reject(function(transform){
971
+ return (
972
+ (transform.originalValue == transform.targetValue) ||
973
+ (
974
+ transform.unit != 'color' &&
975
+ (isNaN(transform.originalValue) || isNaN(transform.targetValue))
976
+ )
977
+ )
978
+ });
979
+ },
980
+ update: function(position) {
981
+ var style = $H(), value = null;
982
+ this.transforms.each(function(transform){
983
+ value = transform.unit=='color' ?
984
+ $R(0,2).inject('#',function(m,v,i){
985
+ return m+(Math.round(transform.originalValue[i]+
986
+ (transform.targetValue[i] - transform.originalValue[i])*position)).toColorPart() }) :
987
+ transform.originalValue + Math.round(
988
+ ((transform.targetValue - transform.originalValue) * position) * 1000)/1000 + transform.unit;
989
+ style[transform.style] = value;
990
+ });
991
+ this.element.setStyle(style);
992
+ }
993
+ });
994
+
995
+ Effect.Transform = Class.create();
996
+ Object.extend(Effect.Transform.prototype, {
997
+ initialize: function(tracks){
998
+ this.tracks = [];
999
+ this.options = arguments[1] || {};
1000
+ this.addTracks(tracks);
1001
+ },
1002
+ addTracks: function(tracks){
1003
+ tracks.each(function(track){
1004
+ var data = $H(track).values().first();
1005
+ this.tracks.push($H({
1006
+ ids: $H(track).keys().first(),
1007
+ effect: Effect.Morph,
1008
+ options: { style: data }
1009
+ }));
1010
+ }.bind(this));
1011
+ return this;
1012
+ },
1013
+ play: function(){
1014
+ return new Effect.Parallel(
1015
+ this.tracks.map(function(track){
1016
+ var elements = [$(track.ids) || $$(track.ids)].flatten();
1017
+ return elements.map(function(e){ return new track.effect(e, Object.extend({ sync:true }, track.options)) });
1018
+ }).flatten(),
1019
+ this.options
1020
+ );
1021
+ }
1022
+ });
1023
+
1024
+ Element.CSS_PROPERTIES = ['azimuth', 'backgroundAttachment', 'backgroundColor', 'backgroundImage',
1025
+ 'backgroundPosition', 'backgroundRepeat', 'borderBottomColor', 'borderBottomStyle',
1026
+ 'borderBottomWidth', 'borderCollapse', 'borderLeftColor', 'borderLeftStyle', 'borderLeftWidth',
1027
+ 'borderRightColor', 'borderRightStyle', 'borderRightWidth', 'borderSpacing', 'borderTopColor',
1028
+ 'borderTopStyle', 'borderTopWidth', 'bottom', 'captionSide', 'clear', 'clip', 'color', 'content',
1029
+ 'counterIncrement', 'counterReset', 'cssFloat', 'cueAfter', 'cueBefore', 'cursor', 'direction',
1030
+ 'display', 'elevation', 'emptyCells', 'fontFamily', 'fontSize', 'fontSizeAdjust', 'fontStretch',
1031
+ 'fontStyle', 'fontVariant', 'fontWeight', 'height', 'left', 'letterSpacing', 'lineHeight',
1032
+ 'listStyleImage', 'listStylePosition', 'listStyleType', 'marginBottom', 'marginLeft', 'marginRight',
1033
+ 'marginTop', 'markerOffset', 'marks', 'maxHeight', 'maxWidth', 'minHeight', 'minWidth', 'opacity',
1034
+ 'orphans', 'outlineColor', 'outlineOffset', 'outlineStyle', 'outlineWidth', 'overflowX', 'overflowY',
1035
+ 'paddingBottom', 'paddingLeft', 'paddingRight', 'paddingTop', 'page', 'pageBreakAfter', 'pageBreakBefore',
1036
+ 'pageBreakInside', 'pauseAfter', 'pauseBefore', 'pitch', 'pitchRange', 'position', 'quotes',
1037
+ 'richness', 'right', 'size', 'speakHeader', 'speakNumeral', 'speakPunctuation', 'speechRate', 'stress',
1038
+ 'tableLayout', 'textAlign', 'textDecoration', 'textIndent', 'textShadow', 'textTransform', 'top',
1039
+ 'unicodeBidi', 'verticalAlign', 'visibility', 'voiceFamily', 'volume', 'whiteSpace', 'widows',
1040
+ 'width', 'wordSpacing', 'zIndex'];
1041
+
1042
+ Element.CSS_LENGTH = /^(([\+\-]?[0-9\.]+)(em|ex|px|in|cm|mm|pt|pc|\%))|0$/;
1043
+
1044
+ String.prototype.parseStyle = function(){
1045
+ var element = Element.extend(document.createElement('div'));
1046
+ element.innerHTML = '<div style="' + this + '"></div>';
1047
+ var style = element.down().style, styleRules = $H();
1048
+
1049
+ Element.CSS_PROPERTIES.each(function(property){
1050
+ if(style[property]) styleRules[property] = style[property];
1051
+ });
1052
+
1053
+ var result = $H();
1054
+
1055
+ styleRules.each(function(pair){
1056
+ var property = pair[0], value = pair[1], unit = null;
1057
+
1058
+ if(value.parseColor('#zzzzzz') != '#zzzzzz') {
1059
+ value = value.parseColor();
1060
+ unit = 'color';
1061
+ } else if(Element.CSS_LENGTH.test(value))
1062
+ var components = value.match(/^([\+\-]?[0-9\.]+)(.*)$/),
1063
+ value = parseFloat(components[1]), unit = (components.length == 3) ? components[2] : null;
1064
+
1065
+ result[property.underscore().dasherize()] = $H({ value:value, unit:unit });
1066
+ }.bind(this));
1067
+
1068
+ return result;
1069
+ };
1070
+
1071
+ Element.morph = function(element, style) {
1072
+ new Effect.Morph(element, Object.extend({ style: style }, arguments[2] || {}));
1073
+ return element;
1074
+ };
1075
+
946
1076
  ['setOpacity','getOpacity','getInlineOpacity','forceRerendering','setContentZoom',
947
- 'collectTextNodes','collectTextNodesIgnoreClass','childrenWithClassName'].each(
1077
+ 'collectTextNodes','collectTextNodesIgnoreClass','morph'].each(
948
1078
  function(f) { Element.Methods[f] = Element[f]; }
949
1079
  );
950
1080