radiant 0.6.2 → 0.6.3

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

Potentially problematic release.


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

Files changed (65) hide show
  1. data/CHANGELOG +37 -0
  2. data/CONTRIBUTORS +9 -0
  3. data/app/controllers/admin/page_controller.rb +3 -2
  4. data/app/controllers/admin/user_controller.rb +2 -2
  5. data/app/controllers/admin/welcome_controller.rb +3 -3
  6. data/app/controllers/application.rb +9 -1
  7. data/app/helpers/admin/abstract_model_helper.rb +2 -0
  8. data/app/helpers/admin/page_helper.rb +4 -0
  9. data/app/helpers/application_helper.rb +4 -6
  10. data/app/models/page.rb +4 -1
  11. data/app/models/response_cache.rb +10 -7
  12. data/app/models/standard_tags.rb +7 -5
  13. data/app/views/admin/layout/index.rhtml +1 -1
  14. data/app/views/admin/layout/remove.rhtml +1 -1
  15. data/app/views/admin/page/_node.rhtml +4 -4
  16. data/app/views/admin/snippet/index.rhtml +1 -1
  17. data/app/views/admin/user/index.rhtml +1 -1
  18. data/app/views/layouts/application.rhtml +4 -4
  19. data/config/environment.rb +77 -70
  20. data/config/environments/test.rb +9 -2
  21. data/db/migrate/016_add_sessions.rb +16 -0
  22. data/db/schema.rb +10 -1
  23. data/lib/generators/extension/templates/tasks.rake +13 -2
  24. data/lib/generators/extension/templates/test_helper.rb +3 -2
  25. data/lib/login_system.rb +19 -4
  26. data/lib/plugins/extension_patches/init.rb +1 -1
  27. data/lib/plugins/extension_patches/lib/fixture_loading_extension.rb +32 -33
  28. data/lib/radiant.rb +1 -1
  29. data/lib/radiant/extension.rb +14 -0
  30. data/lib/radiant/extension_loader.rb +135 -106
  31. data/lib/radiant/initializer.rb +2 -0
  32. data/lib/tasks/extensions.rake +16 -1
  33. data/public/javascripts/prototype.js +305 -175
  34. data/public/javascripts/sitemap.js +1 -1
  35. data/public/stylesheets/admin/main.css +5 -2
  36. data/test/fixtures/extensions/01_basic/vendor/plugins/multiple/init.rb +1 -0
  37. data/test/fixtures/extensions/01_basic/vendor/plugins/multiple/lib/multiple.rb +2 -0
  38. data/test/fixtures/extensions/01_basic/vendor/plugins/plugin_normal/init.rb +1 -0
  39. data/test/fixtures/extensions/01_basic/vendor/plugins/plugin_normal/lib/normal_plugin.rb +2 -0
  40. data/test/fixtures/extensions/02_overriding/overriding_extension.rb +1 -1
  41. data/test/fixtures/extensions/02_overriding/vendor/plugins/multiple/init.rb +1 -0
  42. data/test/fixtures/extensions/02_overriding/vendor/plugins/multiple/lib/multiple.rb +2 -0
  43. data/test/fixtures/extensions/load_order_blue/load_order_blue_extension.rb +11 -0
  44. data/test/fixtures/extensions/load_order_green/load_order_green_extension.rb +11 -0
  45. data/test/fixtures/extensions/load_order_red/load_order_red_extension.rb +11 -0
  46. data/test/fixtures/pages.yml +16 -1
  47. data/test/functional/admin/abstract_model_controller_test.rb +2 -2
  48. data/test/functional/admin/export_controller_test.rb +2 -1
  49. data/test/functional/admin/extension_controller_test.rb +2 -1
  50. data/test/functional/admin/layout_controller_test.rb +6 -4
  51. data/test/functional/admin/page_controller_test.rb +48 -22
  52. data/test/functional/admin/user_controller_test.rb +12 -9
  53. data/test/functional/admin/welcome_controller_test.rb +4 -4
  54. data/test/functional/application_controller_test.rb +3 -2
  55. data/test/functional/extension_initialization_test.rb +31 -5
  56. data/test/functional/extension_load_order_test.rb +49 -0
  57. data/test/functional/login_system_test.rb +21 -11
  58. data/test/helpers/difference_test_helper.rb +13 -0
  59. data/test/helpers/extension_tag_test_helper.rb +3 -3
  60. data/test/helpers/login_test_helper.rb +12 -0
  61. data/test/helpers/page_test_helper.rb +4 -1
  62. data/test/unit/page_test.rb +5 -0
  63. data/test/unit/response_cache_test.rb +27 -6
  64. data/test/unit/standard_tags_test.rb +6 -3
  65. metadata +1789 -1767
@@ -6,10 +6,12 @@ module Radiant
6
6
  class Configuration < Rails::Configuration
7
7
  attr_accessor :view_paths
8
8
  attr_accessor :extension_paths
9
+ attr_accessor :extensions
9
10
 
10
11
  def initialize
11
12
  self.view_paths = default_view_paths
12
13
  self.extension_paths = default_extension_paths
14
+ self.extensions = nil
13
15
  super
14
16
  end
15
17
 
@@ -8,6 +8,17 @@ namespace :db do
8
8
  Radiant::ExtensionMigrator.migrate_extensions
9
9
  end
10
10
  end
11
+ namespace :remigrate do
12
+ desc "Migrate down and back up all Radiant extension migrations"
13
+ task :extensions => :environment do
14
+ require 'highline/import'
15
+ if agree("This task will destroy any data stored by extensions in the database. Are you sure you want to \ncontinue? [yn] ")
16
+ require 'radiant/extension_migrator'
17
+ Radiant::Extension.descendants.map(&:migrator).each {|m| m.migrate(0) }
18
+ Rake::Task['db:migrate:extensions'].invoke
19
+ end
20
+ end
21
+ end
11
22
  end
12
23
 
13
24
  namespace :test do
@@ -15,7 +26,11 @@ namespace :test do
15
26
  task :extensions => "db:test:prepare" do
16
27
  Dir["#{RAILS_ROOT}/vendor/extensions/*"].sort.select { |f| File.directory?(f) }.each do |directory|
17
28
  chdir directory do
18
- system "rake test"
29
+ if RUBY_PLATFORM =~ /win32/
30
+ system "rake.cmd test"
31
+ else
32
+ system "rake test"
33
+ end
19
34
  end
20
35
  end
21
36
  end
@@ -1,5 +1,5 @@
1
- /* Prototype JavaScript framework, version 1.5.0_rc2
2
- * (c) 2005, 2006 Sam Stephenson <sam@conio.net>
1
+ /* Prototype JavaScript framework, version 1.5.0
2
+ * (c) 2005-2007 Sam Stephenson
3
3
  *
4
4
  * Prototype is freely distributable under the terms of an MIT-style license.
5
5
  * For details, see the Prototype web site: http://prototype.conio.net/
@@ -7,7 +7,7 @@
7
7
  /*--------------------------------------------------------------------------*/
8
8
 
9
9
  var Prototype = {
10
- Version: '1.5.0_rc2',
10
+ Version: '1.5.0',
11
11
  BrowserFeatures: {
12
12
  XPath: !!document.evaluate
13
13
  },
@@ -145,6 +145,10 @@ PeriodicalExecuter.prototype = {
145
145
  }
146
146
  }
147
147
  }
148
+ String.interpret = function(value){
149
+ return value == null ? '' : String(value);
150
+ }
151
+
148
152
  Object.extend(String.prototype, {
149
153
  gsub: function(pattern, replacement) {
150
154
  var result = '', source = this, match;
@@ -153,7 +157,7 @@ Object.extend(String.prototype, {
153
157
  while (source.length > 0) {
154
158
  if (match = source.match(pattern)) {
155
159
  result += source.slice(0, match.index);
156
- result += (replacement(match) || '').toString();
160
+ result += String.interpret(replacement(match));
157
161
  source = source.slice(match.index + match[0].length);
158
162
  } else {
159
163
  result += source, source = '';
@@ -247,24 +251,31 @@ Object.extend(String.prototype, {
247
251
  return this.split('');
248
252
  },
249
253
 
254
+ succ: function() {
255
+ return this.slice(0, this.length - 1) +
256
+ String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
257
+ },
258
+
250
259
  camelize: function() {
251
- var oStringList = this.split('-');
252
- if (oStringList.length == 1) return oStringList[0];
260
+ var parts = this.split('-'), len = parts.length;
261
+ if (len == 1) return parts[0];
253
262
 
254
- var camelizedString = this.indexOf('-') == 0
255
- ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
256
- : oStringList[0];
263
+ var camelized = this.charAt(0) == '-'
264
+ ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
265
+ : parts[0];
257
266
 
258
- for (var i = 1, length = oStringList.length; i < length; i++) {
259
- var s = oStringList[i];
260
- camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
261
- }
267
+ for (var i = 1; i < len; i++)
268
+ camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
269
+
270
+ return camelized;
271
+ },
262
272
 
263
- return camelizedString;
273
+ capitalize: function(){
274
+ return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
264
275
  },
265
276
 
266
277
  underscore: function() {
267
- return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'-').toLowerCase();
278
+ return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
268
279
  },
269
280
 
270
281
  dasherize: function() {
@@ -300,7 +311,7 @@ Template.prototype = {
300
311
  return this.template.gsub(this.pattern, function(match) {
301
312
  var before = match[1];
302
313
  if (before == '\\') return match[2];
303
- return before + (object[match[3]] || '').toString();
314
+ return before + String.interpret(object[match[3]]);
304
315
  });
305
316
  }
306
317
  }
@@ -329,7 +340,7 @@ var Enumerable = {
329
340
  var index = -number, slices = [], array = this.toArray();
330
341
  while ((index += number) < array.length)
331
342
  slices.push(array.slice(index, index+number));
332
- return slices.collect(iterator || Prototype.K);
343
+ return slices.map(iterator);
333
344
  },
334
345
 
335
346
  all: function(iterator) {
@@ -353,7 +364,7 @@ var Enumerable = {
353
364
  collect: function(iterator) {
354
365
  var results = [];
355
366
  this.each(function(value, index) {
356
- results.push(iterator(value, index));
367
+ results.push((iterator || Prototype.K)(value, index));
357
368
  });
358
369
  return results;
359
370
  },
@@ -400,12 +411,11 @@ var Enumerable = {
400
411
  },
401
412
 
402
413
  inGroupsOf: function(number, fillWith) {
403
- fillWith = fillWith || null;
404
- var results = this.eachSlice(number);
405
- if (results.length > 0) (number - results.last().length).times(function() {
406
- results.last().push(fillWith)
414
+ fillWith = fillWith === undefined ? null : fillWith;
415
+ return this.eachSlice(number, function(slice) {
416
+ while(slice.length < number) slice.push(fillWith);
417
+ return slice;
407
418
  });
408
- return results;
409
419
  },
410
420
 
411
421
  inject: function(memo, iterator) {
@@ -417,7 +427,7 @@ var Enumerable = {
417
427
 
418
428
  invoke: function(method) {
419
429
  var args = $A(arguments).slice(1);
420
- return this.collect(function(value) {
430
+ return this.map(function(value) {
421
431
  return value[method].apply(value, args);
422
432
  });
423
433
  },
@@ -469,7 +479,7 @@ var Enumerable = {
469
479
  },
470
480
 
471
481
  sortBy: function(iterator) {
472
- return this.collect(function(value, index) {
482
+ return this.map(function(value, index) {
473
483
  return {value: value, criteria: iterator(value, index)};
474
484
  }).sort(function(left, right) {
475
485
  var a = left.criteria, b = right.criteria;
@@ -478,7 +488,7 @@ var Enumerable = {
478
488
  },
479
489
 
480
490
  toArray: function() {
481
- return this.collect(Prototype.K);
491
+ return this.map();
482
492
  },
483
493
 
484
494
  zip: function() {
@@ -492,6 +502,10 @@ var Enumerable = {
492
502
  });
493
503
  },
494
504
 
505
+ size: function() {
506
+ return this.toArray().length;
507
+ },
508
+
495
509
  inspect: function() {
496
510
  return '#<Enumerable:' + this.toArray().inspect() + '>';
497
511
  }
@@ -542,7 +556,7 @@ Object.extend(Array.prototype, {
542
556
 
543
557
  compact: function() {
544
558
  return this.select(function(value) {
545
- return value != undefined || value != null;
559
+ return value != null;
546
560
  });
547
561
  },
548
562
 
@@ -584,6 +598,10 @@ Object.extend(Array.prototype, {
584
598
  return [].concat(this);
585
599
  },
586
600
 
601
+ size: function() {
602
+ return this.length;
603
+ },
604
+
587
605
  inspect: function() {
588
606
  return '[' + this.map(Object.inspect).join(', ') + ']';
589
607
  }
@@ -591,6 +609,11 @@ Object.extend(Array.prototype, {
591
609
 
592
610
  Array.prototype.toArray = Array.prototype.clone;
593
611
 
612
+ function $w(string){
613
+ string = string.strip();
614
+ return string ? string.split(/\s+/) : [];
615
+ }
616
+
594
617
  if(window.opera){
595
618
  Array.prototype.concat = function(){
596
619
  var array = [];
@@ -606,11 +629,43 @@ if(window.opera){
606
629
  return array;
607
630
  }
608
631
  }
609
- var Hash = {
632
+ var Hash = function(obj) {
633
+ Object.extend(this, obj || {});
634
+ };
635
+
636
+ Object.extend(Hash, {
637
+ toQueryString: function(obj) {
638
+ var parts = [];
639
+
640
+ this.prototype._each.call(obj, function(pair) {
641
+ if (!pair.key) return;
642
+
643
+ if (pair.value && pair.value.constructor == Array) {
644
+ var values = pair.value.compact();
645
+ if (values.length < 2) pair.value = values.reduce();
646
+ else {
647
+ key = encodeURIComponent(pair.key);
648
+ values.each(function(value) {
649
+ value = value != undefined ? encodeURIComponent(value) : '';
650
+ parts.push(key + '=' + encodeURIComponent(value));
651
+ });
652
+ return;
653
+ }
654
+ }
655
+ if (pair.value == undefined) pair[1] = '';
656
+ parts.push(pair.map(encodeURIComponent).join('='));
657
+ });
658
+
659
+ return parts.join('&');
660
+ }
661
+ });
662
+
663
+ Object.extend(Hash.prototype, Enumerable);
664
+ Object.extend(Hash.prototype, {
610
665
  _each: function(iterator) {
611
666
  for (var key in this) {
612
667
  var value = this[key];
613
- if (typeof value == 'function') continue;
668
+ if (value && value == Hash.prototype[key]) continue;
614
669
 
615
670
  var pair = [key, value];
616
671
  pair.key = key;
@@ -634,26 +689,24 @@ var Hash = {
634
689
  });
635
690
  },
636
691
 
637
- toQueryString: function() {
638
- return this.map(function(pair) {
639
- if (!pair.key) return null;
640
-
641
- if (pair.value && pair.value.constructor == Array) {
642
- pair.value = pair.value.compact();
643
-
644
- if (pair.value.length < 2) {
645
- pair.value = pair.value.reduce();
646
- } else {
647
- var key = encodeURIComponent(pair.key);
648
- return pair.value.map(function(value) {
649
- return key + '=' + encodeURIComponent(value);
650
- }).join('&');
692
+ remove: function() {
693
+ var result;
694
+ for(var i = 0, length = arguments.length; i < length; i++) {
695
+ var value = this[arguments[i]];
696
+ if (value !== undefined){
697
+ if (result === undefined) result = value;
698
+ else {
699
+ if (result.constructor != Array) result = [result];
700
+ result.push(value)
651
701
  }
652
702
  }
703
+ delete this[arguments[i]];
704
+ }
705
+ return result;
706
+ },
653
707
 
654
- if (pair.value == undefined) pair[1] = '';
655
- return pair.map(encodeURIComponent).join('=');
656
- }).join('&');
708
+ toQueryString: function() {
709
+ return Hash.toQueryString(this);
657
710
  },
658
711
 
659
712
  inspect: function() {
@@ -661,14 +714,12 @@ var Hash = {
661
714
  return pair.map(Object.inspect).join(': ');
662
715
  }).join(', ') + '}>';
663
716
  }
664
- }
717
+ });
665
718
 
666
719
  function $H(object) {
667
- var hash = Object.extend({}, object || {});
668
- Object.extend(hash, Enumerable);
669
- Object.extend(hash, Hash);
670
- return hash;
671
- }
720
+ if (object && object.constructor == Hash) return object;
721
+ return new Hash(object);
722
+ };
672
723
  ObjectRange = Class.create();
673
724
  Object.extend(ObjectRange.prototype, Enumerable);
674
725
  Object.extend(ObjectRange.prototype, {
@@ -762,8 +813,8 @@ Ajax.Base.prototype = {
762
813
  Object.extend(this.options, options || {});
763
814
 
764
815
  this.options.method = this.options.method.toLowerCase();
765
- this.options.parameters = $H(typeof this.options.parameters == 'string' ?
766
- this.options.parameters.toQueryParams() : this.options.parameters);
816
+ if (typeof this.options.parameters == 'string')
817
+ this.options.parameters = this.options.parameters.toQueryParams();
767
818
  }
768
819
  }
769
820
 
@@ -781,28 +832,28 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
781
832
  },
782
833
 
783
834
  request: function(url) {
835
+ this.url = url;
836
+ this.method = this.options.method;
784
837
  var params = this.options.parameters;
785
- if (params.any()) params['_'] = '';
786
838
 
787
- if (!['get', 'post'].include(this.options.method)) {
839
+ if (!['get', 'post'].include(this.method)) {
788
840
  // simulate other verbs over post
789
- params['_method'] = this.options.method;
790
- this.options.method = 'post';
841
+ params['_method'] = this.method;
842
+ this.method = 'post';
791
843
  }
792
844
 
793
- this.url = url;
845
+ params = Hash.toQueryString(params);
846
+ if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='
794
847
 
795
848
  // when GET, append parameters to URL
796
- if (this.options.method == 'get' && params.any())
797
- this.url += (this.url.indexOf('?') >= 0 ? '&' : '?') +
798
- params.toQueryString();
849
+ if (this.method == 'get' && params)
850
+ this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
799
851
 
800
852
  try {
801
853
  Ajax.Responders.dispatch('onCreate', this, this.transport);
802
854
 
803
- this.transport.open(this.options.method.toUpperCase(), this.url,
804
- this.options.asynchronous, this.options.username,
805
- this.options.password);
855
+ this.transport.open(this.method.toUpperCase(), this.url,
856
+ this.options.asynchronous);
806
857
 
807
858
  if (this.options.asynchronous)
808
859
  setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
@@ -810,8 +861,7 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
810
861
  this.transport.onreadystatechange = this.onStateChange.bind(this);
811
862
  this.setRequestHeaders();
812
863
 
813
- var body = this.options.method == 'post' ?
814
- (this.options.postBody || params.toQueryString()) : null;
864
+ var body = this.method == 'post' ? (this.options.postBody || params) : null;
815
865
 
816
866
  this.transport.send(body);
817
867
 
@@ -838,7 +888,7 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
838
888
  'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
839
889
  };
840
890
 
841
- if (this.options.method == 'post') {
891
+ if (this.method == 'post') {
842
892
  headers['Content-type'] = this.options.contentType +
843
893
  (this.options.encoding ? '; charset=' + this.options.encoding : '');
844
894
 
@@ -884,6 +934,10 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
884
934
  } catch (e) {
885
935
  this.dispatchException(e);
886
936
  }
937
+
938
+ if ((this.getHeader('Content-type') || 'text/javascript').strip().
939
+ match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
940
+ this.evalResponse();
887
941
  }
888
942
 
889
943
  try {
@@ -894,10 +948,6 @@ Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
894
948
  }
895
949
 
896
950
  if (state == 'Complete') {
897
- if ((this.getHeader('Content-type') || '').strip().
898
- match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
899
- this.evalResponse();
900
-
901
951
  // avoid memory leak in MSIE: clean up
902
952
  this.transport.onreadystatechange = Prototype.emptyFunction;
903
953
  }
@@ -1032,7 +1082,7 @@ if (Prototype.BrowserFeatures.XPath) {
1032
1082
  for (var i = 0, length = query.snapshotLength; i < length; i++)
1033
1083
  results.push(query.snapshotItem(i));
1034
1084
  return results;
1035
- }
1085
+ };
1036
1086
  }
1037
1087
 
1038
1088
  document.getElementsByClassName = function(className, parentElement) {
@@ -1049,7 +1099,7 @@ document.getElementsByClassName = function(className, parentElement) {
1049
1099
  }
1050
1100
  return elements;
1051
1101
  }
1052
- }
1102
+ };
1053
1103
 
1054
1104
  /*--------------------------------------------------------------------------*/
1055
1105
 
@@ -1057,8 +1107,7 @@ if (!window.Element)
1057
1107
  var Element = new Object();
1058
1108
 
1059
1109
  Element.extend = function(element) {
1060
- if (!element) return;
1061
- if (_nativeExtensions || element.nodeType == 3) return element;
1110
+ if (!element || _nativeExtensions || element.nodeType == 3) return element;
1062
1111
 
1063
1112
  if (!element._extended && element.tagName && element != window) {
1064
1113
  var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
@@ -1079,7 +1128,7 @@ Element.extend = function(element) {
1079
1128
 
1080
1129
  element._extended = true;
1081
1130
  return element;
1082
- }
1131
+ };
1083
1132
 
1084
1133
  Element.extend.cache = {
1085
1134
  findOrStore: function(value) {
@@ -1087,7 +1136,7 @@ Element.extend.cache = {
1087
1136
  return value.apply(null, [this].concat($A(arguments)));
1088
1137
  }
1089
1138
  }
1090
- }
1139
+ };
1091
1140
 
1092
1141
  Element.Methods = {
1093
1142
  visible: function(element) {
@@ -1125,6 +1174,7 @@ Element.Methods = {
1125
1174
 
1126
1175
  replace: function(element, html) {
1127
1176
  element = $(element);
1177
+ html = typeof html == 'undefined' ? '' : html.toString();
1128
1178
  if (element.outerHTML) {
1129
1179
  element.outerHTML = html.stripScripts();
1130
1180
  } else {
@@ -1162,8 +1212,7 @@ Element.Methods = {
1162
1212
  },
1163
1213
 
1164
1214
  descendants: function(element) {
1165
- element = $(element);
1166
- return $A(element.getElementsByTagName('*'));
1215
+ return $A($(element).getElementsByTagName('*'));
1167
1216
  },
1168
1217
 
1169
1218
  immediateDescendants: function(element) {
@@ -1187,10 +1236,9 @@ Element.Methods = {
1187
1236
  },
1188
1237
 
1189
1238
  match: function(element, selector) {
1190
- element = $(element);
1191
1239
  if (typeof selector == 'string')
1192
1240
  selector = new Selector(selector);
1193
- return selector.match(element);
1241
+ return selector.match($(element));
1194
1242
  },
1195
1243
 
1196
1244
  up: function(element, expression, index) {
@@ -1215,17 +1263,27 @@ Element.Methods = {
1215
1263
  },
1216
1264
 
1217
1265
  getElementsByClassName: function(element, className) {
1218
- element = $(element);
1219
1266
  return document.getElementsByClassName(className, element);
1220
1267
  },
1221
1268
 
1222
1269
  readAttribute: function(element, name) {
1223
- return $(element).getAttribute(name);
1270
+ element = $(element);
1271
+ if (document.all && !window.opera) {
1272
+ var t = Element._attributeTranslations;
1273
+ if (t.values[name]) return t.values[name](element, name);
1274
+ if (t.names[name]) name = t.names[name];
1275
+ var attribute = element.attributes[name];
1276
+ if(attribute) return attribute.nodeValue;
1277
+ }
1278
+ return element.getAttribute(name);
1224
1279
  },
1225
1280
 
1226
1281
  getHeight: function(element) {
1227
- element = $(element);
1228
- return element.offsetHeight;
1282
+ return $(element).getDimensions().height;
1283
+ },
1284
+
1285
+ getWidth: function(element) {
1286
+ return $(element).getDimensions().width;
1229
1287
  },
1230
1288
 
1231
1289
  classNames: function(element) {
@@ -1254,6 +1312,12 @@ Element.Methods = {
1254
1312
  return element;
1255
1313
  },
1256
1314
 
1315
+ toggleClassName: function(element, className) {
1316
+ if (!(element = $(element))) return;
1317
+ Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
1318
+ return element;
1319
+ },
1320
+
1257
1321
  observe: function() {
1258
1322
  Event.observe.apply(Event, arguments);
1259
1323
  return $A(arguments).first();
@@ -1281,7 +1345,7 @@ Element.Methods = {
1281
1345
  return $(element).innerHTML.match(/^\s*$/);
1282
1346
  },
1283
1347
 
1284
- childOf: function(element, ancestor) {
1348
+ descendantOf: function(element, ancestor) {
1285
1349
  element = $(element), ancestor = $(ancestor);
1286
1350
  while (element = element.parentNode)
1287
1351
  if (element == ancestor) return true;
@@ -1290,47 +1354,69 @@ Element.Methods = {
1290
1354
 
1291
1355
  scrollTo: function(element) {
1292
1356
  element = $(element);
1293
- var x = element.x ? element.x : element.offsetLeft,
1294
- y = element.y ? element.y : element.offsetTop;
1295
- window.scrollTo(x, y);
1357
+ var pos = Position.cumulativeOffset(element);
1358
+ window.scrollTo(pos[0], pos[1]);
1296
1359
  return element;
1297
1360
  },
1298
1361
 
1299
1362
  getStyle: function(element, style) {
1300
1363
  element = $(element);
1301
- var inline = (style == 'float' ?
1302
- (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat') : style);
1303
- var value = element.style[inline.camelize()];
1364
+ if (['float','cssFloat'].include(style))
1365
+ style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
1366
+ style = style.camelize();
1367
+ var value = element.style[style];
1304
1368
  if (!value) {
1305
1369
  if (document.defaultView && document.defaultView.getComputedStyle) {
1306
1370
  var css = document.defaultView.getComputedStyle(element, null);
1307
- value = css ? css.getPropertyValue(style) : null;
1371
+ value = css ? css[style] : null;
1308
1372
  } else if (element.currentStyle) {
1309
- value = element.currentStyle[inline.camelize()];
1373
+ value = element.currentStyle[style];
1310
1374
  }
1311
1375
  }
1312
1376
 
1313
1377
  if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
1314
- value = element['offset'+style.charAt(0).toUpperCase()+style.substring(1)] + 'px';
1378
+ value = element['offset'+style.capitalize()] + 'px';
1315
1379
 
1316
1380
  if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
1317
1381
  if (Element.getStyle(element, 'position') == 'static') value = 'auto';
1318
-
1382
+ if(style == 'opacity') {
1383
+ if(value) return parseFloat(value);
1384
+ if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
1385
+ if(value[1]) return parseFloat(value[1]) / 100;
1386
+ return 1.0;
1387
+ }
1319
1388
  return value == 'auto' ? null : value;
1320
1389
  },
1321
1390
 
1322
1391
  setStyle: function(element, style) {
1323
1392
  element = $(element);
1324
- for (var name in style)
1325
- element.style[ (name == 'float' ?
1326
- ((typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat') : name).camelize()
1327
- ] = style[name];
1393
+ for (var name in style) {
1394
+ var value = style[name];
1395
+ if(name == 'opacity') {
1396
+ if (value == 1) {
1397
+ value = (/Gecko/.test(navigator.userAgent) &&
1398
+ !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
1399
+ if(/MSIE/.test(navigator.userAgent) && !window.opera)
1400
+ element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1401
+ } else if(value == '') {
1402
+ if(/MSIE/.test(navigator.userAgent) && !window.opera)
1403
+ element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
1404
+ } else {
1405
+ if(value < 0.00001) value = 0;
1406
+ if(/MSIE/.test(navigator.userAgent) && !window.opera)
1407
+ element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
1408
+ 'alpha(opacity='+value*100+')';
1409
+ }
1410
+ } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
1411
+ element.style[name.camelize()] = value;
1412
+ }
1328
1413
  return element;
1329
1414
  },
1330
1415
 
1331
1416
  getDimensions: function(element) {
1332
1417
  element = $(element);
1333
- if (Element.getStyle(element, 'display') != 'none')
1418
+ var display = $(element).getStyle('display');
1419
+ if (display != 'none' && display != null) // Safari bug
1334
1420
  return {width: element.offsetWidth, height: element.offsetHeight};
1335
1421
 
1336
1422
  // All *Width and *Height properties give 0 on elements with display none,
@@ -1338,12 +1424,13 @@ Element.Methods = {
1338
1424
  var els = element.style;
1339
1425
  var originalVisibility = els.visibility;
1340
1426
  var originalPosition = els.position;
1427
+ var originalDisplay = els.display;
1341
1428
  els.visibility = 'hidden';
1342
1429
  els.position = 'absolute';
1343
- els.display = '';
1430
+ els.display = 'block';
1344
1431
  var originalWidth = element.clientWidth;
1345
1432
  var originalHeight = element.clientHeight;
1346
- els.display = 'none';
1433
+ els.display = originalDisplay;
1347
1434
  els.position = originalPosition;
1348
1435
  els.visibility = originalVisibility;
1349
1436
  return {width: originalWidth, height: originalHeight};
@@ -1394,16 +1481,63 @@ Element.Methods = {
1394
1481
  element._overflow = null;
1395
1482
  return element;
1396
1483
  }
1397
- }
1484
+ };
1485
+
1486
+ Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
1487
+
1488
+ Element._attributeTranslations = {};
1489
+
1490
+ Element._attributeTranslations.names = {
1491
+ colspan: "colSpan",
1492
+ rowspan: "rowSpan",
1493
+ valign: "vAlign",
1494
+ datetime: "dateTime",
1495
+ accesskey: "accessKey",
1496
+ tabindex: "tabIndex",
1497
+ enctype: "encType",
1498
+ maxlength: "maxLength",
1499
+ readonly: "readOnly",
1500
+ longdesc: "longDesc"
1501
+ };
1502
+
1503
+ Element._attributeTranslations.values = {
1504
+ _getAttr: function(element, attribute) {
1505
+ return element.getAttribute(attribute, 2);
1506
+ },
1507
+
1508
+ _flag: function(element, attribute) {
1509
+ return $(element).hasAttribute(attribute) ? attribute : null;
1510
+ },
1511
+
1512
+ style: function(element) {
1513
+ return element.style.cssText.toLowerCase();
1514
+ },
1515
+
1516
+ title: function(element) {
1517
+ var node = element.getAttributeNode('title');
1518
+ return node.specified ? node.nodeValue : null;
1519
+ }
1520
+ };
1521
+
1522
+ Object.extend(Element._attributeTranslations.values, {
1523
+ href: Element._attributeTranslations.values._getAttr,
1524
+ src: Element._attributeTranslations.values._getAttr,
1525
+ disabled: Element._attributeTranslations.values._flag,
1526
+ checked: Element._attributeTranslations.values._flag,
1527
+ readonly: Element._attributeTranslations.values._flag,
1528
+ multiple: Element._attributeTranslations.values._flag
1529
+ });
1398
1530
 
1399
1531
  Element.Methods.Simulated = {
1400
1532
  hasAttribute: function(element, attribute) {
1533
+ var t = Element._attributeTranslations;
1534
+ attribute = t.names[attribute] || attribute;
1401
1535
  return $(element).getAttributeNode(attribute).specified;
1402
1536
  }
1403
- }
1537
+ };
1404
1538
 
1405
1539
  // IE is missing .innerHTML support for TABLE-related elements
1406
- if(document.all){
1540
+ if (document.all && !window.opera){
1407
1541
  Element.Methods.update = function(element, html) {
1408
1542
  element = $(element);
1409
1543
  html = typeof html == 'undefined' ? '' : html.toString();
@@ -1437,7 +1571,7 @@ if(document.all){
1437
1571
  setTimeout(function() {html.evalScripts()}, 10);
1438
1572
  return element;
1439
1573
  }
1440
- }
1574
+ };
1441
1575
 
1442
1576
  Object.extend(Element, Element.Methods);
1443
1577
 
@@ -1604,7 +1738,7 @@ Element.ClassNames.prototype = {
1604
1738
  toString: function() {
1605
1739
  return $A(this).join(' ');
1606
1740
  }
1607
- }
1741
+ };
1608
1742
 
1609
1743
  Object.extend(Element.ClassNames.prototype, Enumerable);
1610
1744
  var Selector = Class.create();
@@ -1651,15 +1785,15 @@ Selector.prototype = {
1651
1785
  if (params.wildcard)
1652
1786
  conditions.push('true');
1653
1787
  if (clause = params.id)
1654
- conditions.push('element.id == ' + clause.inspect());
1788
+ conditions.push('element.readAttribute("id") == ' + clause.inspect());
1655
1789
  if (clause = params.tagName)
1656
1790
  conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
1657
1791
  if ((clause = params.classNames).length > 0)
1658
1792
  for (var i = 0, length = clause.length; i < length; i++)
1659
- conditions.push('Element.hasClassName(element, ' + clause[i].inspect() + ')');
1793
+ conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
1660
1794
  if (clause = params.attributes) {
1661
1795
  clause.each(function(attribute) {
1662
- var value = 'element.getAttribute(' + attribute.name.inspect() + ')';
1796
+ var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
1663
1797
  var splitValueBy = function(delimiter) {
1664
1798
  return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
1665
1799
  }
@@ -1672,7 +1806,7 @@ Selector.prototype = {
1672
1806
  ); break;
1673
1807
  case '!=': conditions.push(value + ' != ' + attribute.value.inspect()); break;
1674
1808
  case '':
1675
- case undefined: conditions.push(value + ' != null'); break;
1809
+ case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
1676
1810
  default: throw 'Unknown operator ' + attribute.operator + ' in selector';
1677
1811
  }
1678
1812
  });
@@ -1683,6 +1817,7 @@ Selector.prototype = {
1683
1817
 
1684
1818
  compileMatcher: function() {
1685
1819
  this.match = new Function('element', 'if (!element.tagName) return false; \
1820
+ element = $(element); \
1686
1821
  return ' + this.buildMatchExpression());
1687
1822
  },
1688
1823
 
@@ -1712,7 +1847,7 @@ Selector.prototype = {
1712
1847
  Object.extend(Selector, {
1713
1848
  matchElements: function(elements, expression) {
1714
1849
  var selector = new Selector(expression);
1715
- return elements.select(selector.match.bind(selector)).collect(Element.extend);
1850
+ return elements.select(selector.match.bind(selector)).map(Element.extend);
1716
1851
  },
1717
1852
 
1718
1853
  findElement: function(elements, expression, index) {
@@ -1722,7 +1857,7 @@ Object.extend(Selector, {
1722
1857
 
1723
1858
  findChildElements: function(element, expressions) {
1724
1859
  return expressions.map(function(expression) {
1725
- return expression.strip().split(/\s+/).inject([null], function(results, expr) {
1860
+ return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
1726
1861
  var selector = new Selector(expr);
1727
1862
  return results.inject([], function(elements, result) {
1728
1863
  return elements.concat(selector.findElements(result || element));
@@ -1741,18 +1876,28 @@ var Form = {
1741
1876
  return form;
1742
1877
  },
1743
1878
 
1744
- serializeElements: function(elements) {
1745
- return elements.inject([], function(queryComponents, element) {
1746
- var queryComponent = Form.Element.serialize(element);
1747
- if (queryComponent) queryComponents.push(queryComponent);
1748
- return queryComponents;
1749
- }).join('&');
1879
+ serializeElements: function(elements, getHash) {
1880
+ var data = elements.inject({}, function(result, element) {
1881
+ if (!element.disabled && element.name) {
1882
+ var key = element.name, value = $(element).getValue();
1883
+ if (value != undefined) {
1884
+ if (result[key]) {
1885
+ if (result[key].constructor != Array) result[key] = [result[key]];
1886
+ result[key].push(value);
1887
+ }
1888
+ else result[key] = value;
1889
+ }
1890
+ }
1891
+ return result;
1892
+ });
1893
+
1894
+ return getHash ? data : Hash.toQueryString(data);
1750
1895
  }
1751
1896
  };
1752
1897
 
1753
1898
  Form.Methods = {
1754
- serialize: function(form) {
1755
- return Form.serializeElements($(form).getElements());
1899
+ serialize: function(form, getHash) {
1900
+ return Form.serializeElements(Form.getElements(form), getHash);
1756
1901
  },
1757
1902
 
1758
1903
  getElements: function(form) {
@@ -1769,14 +1914,11 @@ Form.Methods = {
1769
1914
  form = $(form);
1770
1915
  var inputs = form.getElementsByTagName('input');
1771
1916
 
1772
- if (!typeName && !name)
1773
- return inputs;
1917
+ if (!typeName && !name) return $A(inputs).map(Element.extend);
1774
1918
 
1775
- var matchingInputs = new Array();
1776
- for (var i = 0, length = inputs.length; i < length; i++) {
1919
+ for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
1777
1920
  var input = inputs[i];
1778
- if ((typeName && input.type != typeName) ||
1779
- (name && input.name != name))
1921
+ if ((typeName && input.type != typeName) || (name && input.name != name))
1780
1922
  continue;
1781
1923
  matchingInputs.push(Element.extend(input));
1782
1924
  }
@@ -1834,30 +1976,21 @@ Form.Element = {
1834
1976
  Form.Element.Methods = {
1835
1977
  serialize: function(element) {
1836
1978
  element = $(element);
1837
- if (element.disabled) return '';
1838
- var method = element.tagName.toLowerCase();
1839
- var parameter = Form.Element.Serializers[method](element);
1840
-
1841
- if (parameter) {
1842
- var key = encodeURIComponent(parameter[0]);
1843
- if (key.length == 0) return;
1844
-
1845
- if (parameter[1].constructor != Array)
1846
- parameter[1] = [parameter[1]];
1847
-
1848
- return parameter[1].map(function(value) {
1849
- return key + '=' + encodeURIComponent(value);
1850
- }).join('&');
1979
+ if (!element.disabled && element.name) {
1980
+ var value = element.getValue();
1981
+ if (value != undefined) {
1982
+ var pair = {};
1983
+ pair[element.name] = value;
1984
+ return Hash.toQueryString(pair);
1985
+ }
1851
1986
  }
1987
+ return '';
1852
1988
  },
1853
1989
 
1854
1990
  getValue: function(element) {
1855
1991
  element = $(element);
1856
1992
  var method = element.tagName.toLowerCase();
1857
- var parameter = Form.Element.Serializers[method](element);
1858
-
1859
- if (parameter)
1860
- return parameter[1];
1993
+ return Form.Element.Serializers[method](element);
1861
1994
  },
1862
1995
 
1863
1996
  clear: function(element) {
@@ -1894,6 +2027,7 @@ Form.Element.Methods = {
1894
2027
 
1895
2028
  Object.extend(Form.Element, Form.Element.Methods);
1896
2029
  var Field = Form.Element;
2030
+ var $F = Form.Element.getValue;
1897
2031
 
1898
2032
  /*--------------------------------------------------------------------------*/
1899
2033
 
@@ -1906,51 +2040,45 @@ Form.Element.Serializers = {
1906
2040
  default:
1907
2041
  return Form.Element.Serializers.textarea(element);
1908
2042
  }
1909
- return false;
1910
2043
  },
1911
2044
 
1912
2045
  inputSelector: function(element) {
1913
- if (element.checked)
1914
- return [element.name, element.value];
2046
+ return element.checked ? element.value : null;
1915
2047
  },
1916
2048
 
1917
2049
  textarea: function(element) {
1918
- return [element.name, element.value];
2050
+ return element.value;
1919
2051
  },
1920
2052
 
1921
2053
  select: function(element) {
1922
- return Form.Element.Serializers[element.type == 'select-one' ?
2054
+ return this[element.type == 'select-one' ?
1923
2055
  'selectOne' : 'selectMany'](element);
1924
2056
  },
1925
2057
 
1926
2058
  selectOne: function(element) {
1927
- var value = '', opt, index = element.selectedIndex;
1928
- if (index >= 0) {
1929
- opt = Element.extend(element.options[index]);
1930
- // Uses the new potential extension if hasAttribute isn't native.
1931
- value = opt.hasAttribute('value') ? opt.value : opt.text;
1932
- }
1933
- return [element.name, value];
2059
+ var index = element.selectedIndex;
2060
+ return index >= 0 ? this.optionValue(element.options[index]) : null;
1934
2061
  },
1935
2062
 
1936
2063
  selectMany: function(element) {
1937
- var value = [];
1938
- for (var i = 0, length = element.length; i < length; i++) {
1939
- var opt = Element.extend(element.options[i]);
1940
- if (opt.selected)
1941
- // Uses the new potential extension if hasAttribute isn't native.
1942
- value.push(opt.hasAttribute('value') ? opt.value : opt.text);
2064
+ var values, length = element.length;
2065
+ if (!length) return null;
2066
+
2067
+ for (var i = 0, values = []; i < length; i++) {
2068
+ var opt = element.options[i];
2069
+ if (opt.selected) values.push(this.optionValue(opt));
1943
2070
  }
1944
- return [element.name, value];
2071
+ return values;
2072
+ },
2073
+
2074
+ optionValue: function(opt) {
2075
+ // extend element because hasAttribute may not be native
2076
+ return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
1945
2077
  }
1946
2078
  }
1947
2079
 
1948
2080
  /*--------------------------------------------------------------------------*/
1949
2081
 
1950
- var $F = Form.Element.getValue;
1951
-
1952
- /*--------------------------------------------------------------------------*/
1953
-
1954
2082
  Abstract.TimedObserver = function() {}
1955
2083
  Abstract.TimedObserver.prototype = {
1956
2084
  initialize: function(element, frequency, callback) {
@@ -1968,7 +2096,9 @@ Abstract.TimedObserver.prototype = {
1968
2096
 
1969
2097
  onTimerEvent: function() {
1970
2098
  var value = this.getValue();
1971
- if (this.lastValue != value) {
2099
+ var changed = ('string' == typeof this.lastValue && 'string' == typeof value
2100
+ ? this.lastValue != value : String(this.lastValue) != String(value));
2101
+ if (changed) {
1972
2102
  this.callback(this.element, value);
1973
2103
  this.lastValue = value;
1974
2104
  }
@@ -2341,10 +2471,10 @@ var Position = {
2341
2471
  element._originalHeight = element.style.height;
2342
2472
 
2343
2473
  element.style.position = 'absolute';
2344
- element.style.top = top + 'px';;
2345
- element.style.left = left + 'px';;
2346
- element.style.width = width + 'px';;
2347
- element.style.height = height + 'px';;
2474
+ element.style.top = top + 'px';
2475
+ element.style.left = left + 'px';
2476
+ element.style.width = width + 'px';
2477
+ element.style.height = height + 'px';
2348
2478
  },
2349
2479
 
2350
2480
  relativize: function(element) {