nitro 0.19.0 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/CHANGELOG +187 -0
  2. data/INSTALL +5 -0
  3. data/README +11 -5
  4. data/doc/AUTHORS +11 -1
  5. data/doc/RELEASES +217 -0
  6. data/doc/tutorial.txt +1 -2
  7. data/lib/nitro.rb +9 -6
  8. data/lib/nitro/adapter/webrick.rb +13 -2
  9. data/lib/nitro/builder/form.rb +11 -9
  10. data/lib/nitro/builder/rss.rb +2 -2
  11. data/lib/nitro/builder/xhtml.rb +15 -0
  12. data/lib/nitro/caching.rb +15 -10
  13. data/lib/nitro/conf.rb +0 -5
  14. data/lib/nitro/controller.rb +118 -81
  15. data/lib/nitro/cookie.rb +6 -6
  16. data/lib/nitro/dispatcher.rb +62 -18
  17. data/lib/nitro/element.rb +4 -1
  18. data/lib/nitro/element/java_script.rb +15 -0
  19. data/lib/nitro/localization.rb +3 -4
  20. data/lib/nitro/markup.rb +4 -4
  21. data/lib/nitro/mixin/debug.rb +30 -0
  22. data/lib/nitro/mixin/helper.rb +14 -0
  23. data/lib/nitro/mixin/javascript.rb +137 -0
  24. data/lib/nitro/{ui → mixin}/pager.rb +110 -82
  25. data/lib/nitro/render.rb +20 -8
  26. data/lib/nitro/request.rb +6 -0
  27. data/lib/nitro/routing.rb +6 -5
  28. data/lib/nitro/runner.rb +21 -9
  29. data/lib/nitro/server.rb +95 -0
  30. data/lib/nitro/service.rb +0 -1
  31. data/lib/nitro/session.rb +4 -5
  32. data/lib/nitro/shaders.rb +2 -2
  33. data/lib/nitro/template.rb +1 -1
  34. data/lib/nitro/testing/assertions.rb +2 -4
  35. data/lib/nitro/testing/context.rb +4 -6
  36. data/proto/public/js/behaviour.js +254 -0
  37. data/proto/public/js/controls.js +446 -0
  38. data/proto/public/js/dragdrop.js +537 -0
  39. data/proto/public/js/effects.js +612 -0
  40. data/proto/public/js/prototype.js +644 -370
  41. data/proto/public/settings.xhtml +64 -0
  42. data/test/nitro/adapter/tc_cgi.rb +2 -2
  43. data/test/nitro/builder/tc_rss.rb +1 -1
  44. data/test/nitro/mixin/tc_pager.rb +35 -0
  45. data/test/nitro/tc_controller.rb +1 -1
  46. data/test/nitro/tc_cookie.rb +14 -0
  47. data/test/nitro/tc_dispatcher.rb +11 -6
  48. data/test/nitro/tc_server.rb +35 -0
  49. metadata +20 -15
  50. data/lib/nitro/builder/atom.rb +0 -74
  51. data/lib/nitro/part.rb +0 -22
  52. data/lib/nitro/simple.rb +0 -11
  53. data/lib/nitro/ui/popup.rb +0 -41
  54. data/lib/nitro/ui/tabs.rb +0 -25
  55. data/lib/nitro/uri.rb +0 -193
  56. data/test/nitro/builder/tc_atom.rb +0 -24
  57. data/test/nitro/tc_uri.rb +0 -97
  58. data/test/nitro/ui/tc_pager.rb +0 -49
@@ -1,4 +1,4 @@
1
- /* Prototype: an object-oriented Javascript library, version 1.2.0
1
+ /* Prototype JavaScript framework, version 1.3.1
2
2
  * (c) 2005 Sam Stephenson <sam@conio.net>
3
3
  *
4
4
  * THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff
@@ -11,7 +11,8 @@
11
11
  /*--------------------------------------------------------------------------*/
12
12
 
13
13
  var Prototype = {
14
- Version: '1.2.0'
14
+ Version: '1.3.1',
15
+ emptyFunction: function() {}
15
16
  }
16
17
 
17
18
  var Class = {
@@ -24,24 +25,28 @@ var Class = {
24
25
 
25
26
  var Abstract = new Object();
26
27
 
27
- Object.prototype.extend = function(object) {
28
- for (property in object) {
29
- this[property] = object[property];
28
+ Object.extend = function(destination, source) {
29
+ for (property in source) {
30
+ destination[property] = source[property];
30
31
  }
31
- return this;
32
+ return destination;
33
+ }
34
+
35
+ Object.prototype.extend = function(object) {
36
+ return Object.extend.apply(this, [this, object]);
32
37
  }
33
38
 
34
39
  Function.prototype.bind = function(object) {
35
- var method = this;
40
+ var __method = this;
36
41
  return function() {
37
- method.apply(object, arguments);
42
+ __method.apply(object, arguments);
38
43
  }
39
44
  }
40
45
 
41
46
  Function.prototype.bindAsEventListener = function(object) {
42
- var method = this;
47
+ var __method = this;
43
48
  return function(event) {
44
- method.call(object, event || window.event);
49
+ __method.call(object, event || window.event);
45
50
  }
46
51
  }
47
52
 
@@ -54,7 +59,7 @@ Number.prototype.toColorPart = function() {
54
59
  var Try = {
55
60
  these: function() {
56
61
  var returnValue;
57
-
62
+
58
63
  for (var i = 0; i < arguments.length; i++) {
59
64
  var lambda = arguments[i];
60
65
  try {
@@ -62,7 +67,7 @@ var Try = {
62
67
  break;
63
68
  } catch (e) {}
64
69
  }
65
-
70
+
66
71
  return returnValue;
67
72
  }
68
73
  }
@@ -75,14 +80,14 @@ PeriodicalExecuter.prototype = {
75
80
  this.callback = callback;
76
81
  this.frequency = frequency;
77
82
  this.currentlyExecuting = false;
78
-
83
+
79
84
  this.registerCallback();
80
85
  },
81
-
86
+
82
87
  registerCallback: function() {
83
- setTimeout(this.onTimerEvent.bind(this), this.frequency * 1000);
88
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
84
89
  },
85
-
90
+
86
91
  onTimerEvent: function() {
87
92
  if (!this.currentlyExecuting) {
88
93
  try {
@@ -92,8 +97,6 @@ PeriodicalExecuter.prototype = {
92
97
  this.currentlyExecuting = false;
93
98
  }
94
99
  }
95
-
96
- this.registerCallback();
97
100
  }
98
101
  }
99
102
 
@@ -101,7 +104,7 @@ PeriodicalExecuter.prototype = {
101
104
 
102
105
  function $() {
103
106
  var elements = new Array();
104
-
107
+
105
108
  for (var i = 0; i < arguments.length; i++) {
106
109
  var element = arguments[i];
107
110
  if (typeof element == 'string')
@@ -109,15 +112,13 @@ function $() {
109
112
 
110
113
  if (arguments.length == 1)
111
114
  return element;
112
-
115
+
113
116
  elements.push(element);
114
117
  }
115
-
118
+
116
119
  return elements;
117
120
  }
118
121
 
119
- /*--------------------------------------------------------------------------*/
120
-
121
122
  if (!Array.prototype.push) {
122
123
  Array.prototype.push = function() {
123
124
  var startLength = this.length;
@@ -135,10 +136,10 @@ if (!Function.prototype.apply) {
135
136
  if (!parameters) parameters = new Array();
136
137
 
137
138
  for (var i = 0; i < parameters.length; i++)
138
- parameterStrings[i] = 'x[' + i + ']';
139
+ parameterStrings[i] = 'parameters[' + i + ']';
139
140
 
140
141
  object.__apply__ = this;
141
- var result = eval('obj.__apply__(' +
142
+ var result = eval('object.__apply__(' +
142
143
  parameterStrings[i].join(', ') + ')');
143
144
  object.__apply__ = null;
144
145
 
@@ -146,7 +147,24 @@ if (!Function.prototype.apply) {
146
147
  }
147
148
  }
148
149
 
149
- /*--------------------------------------------------------------------------*/
150
+ String.prototype.extend({
151
+ stripTags: function() {
152
+ return this.replace(/<\/?[^>]+>/gi, '');
153
+ },
154
+
155
+ escapeHTML: function() {
156
+ var div = document.createElement('div');
157
+ var text = document.createTextNode(this);
158
+ div.appendChild(text);
159
+ return div.innerHTML;
160
+ },
161
+
162
+ unescapeHTML: function() {
163
+ var div = document.createElement('div');
164
+ div.innerHTML = this.stripTags();
165
+ return div.childNodes[0].nodeValue;
166
+ }
167
+ });
150
168
 
151
169
  var Ajax = {
152
170
  getTransport: function() {
@@ -155,9 +173,7 @@ var Ajax = {
155
173
  function() {return new ActiveXObject('Microsoft.XMLHTTP')},
156
174
  function() {return new XMLHttpRequest()}
157
175
  ) || false;
158
- },
159
-
160
- emptyFunction: function() {}
176
+ }
161
177
  }
162
178
 
163
179
  Ajax.Base = function() {};
@@ -168,6 +184,16 @@ Ajax.Base.prototype = {
168
184
  asynchronous: true,
169
185
  parameters: ''
170
186
  }.extend(options || {});
187
+ },
188
+
189
+ responseIsSuccess: function() {
190
+ return this.transport.status == undefined
191
+ || this.transport.status == 0
192
+ || (this.transport.status >= 200 && this.transport.status < 300);
193
+ },
194
+
195
+ responseIsFailure: function() {
196
+ return !this.responseIsSuccess();
171
197
  }
172
198
  }
173
199
 
@@ -179,79 +205,354 @@ Ajax.Request.prototype = (new Ajax.Base()).extend({
179
205
  initialize: function(url, options) {
180
206
  this.transport = Ajax.getTransport();
181
207
  this.setOptions(options);
182
-
208
+ this.request(url);
209
+ },
210
+
211
+ request: function(url) {
212
+ var parameters = this.options.parameters || '';
213
+ if (parameters.length > 0) parameters += '&_=';
214
+
183
215
  try {
184
216
  if (this.options.method == 'get')
185
- url += '?' + this.options.parameters + '&_=';
186
-
187
- this.transport.open(this.options.method, url, true);
188
-
217
+ url += '?' + parameters;
218
+
219
+ this.transport.open(this.options.method, url,
220
+ this.options.asynchronous);
221
+
189
222
  if (this.options.asynchronous) {
190
223
  this.transport.onreadystatechange = this.onStateChange.bind(this);
191
224
  setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
192
225
  }
193
-
194
- this.transport.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
195
- this.transport.setRequestHeader('X-Prototype-Version', Prototype.Version);
196
-
197
- if (this.options.method == 'post') {
198
- this.transport.setRequestHeader('Connection', 'close');
199
- this.transport.setRequestHeader('Content-type',
200
- 'application/x-www-form-urlencoded');
201
- }
202
-
203
- this.transport.send(this.options.method == 'post' ?
204
- this.options.parameters + '&_=' : null);
205
-
226
+
227
+ this.setRequestHeaders();
228
+
229
+ var body = this.options.postBody ? this.options.postBody : parameters;
230
+ this.transport.send(this.options.method == 'post' ? body : null);
231
+
206
232
  } catch (e) {
207
- }
233
+ }
208
234
  },
209
-
235
+
236
+ setRequestHeaders: function() {
237
+ var requestHeaders =
238
+ ['X-Requested-With', 'XMLHttpRequest',
239
+ 'X-Prototype-Version', Prototype.Version];
240
+
241
+ if (this.options.method == 'post') {
242
+ requestHeaders.push('Content-type',
243
+ 'application/x-www-form-urlencoded');
244
+
245
+ /* Force "Connection: close" for Mozilla browsers to work around
246
+ * a bug where XMLHttpReqeuest sends an incorrect Content-length
247
+ * header. See Mozilla Bugzilla #246651.
248
+ */
249
+ if (this.transport.overrideMimeType)
250
+ requestHeaders.push('Connection', 'close');
251
+ }
252
+
253
+ if (this.options.requestHeaders)
254
+ requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
255
+
256
+ for (var i = 0; i < requestHeaders.length; i += 2)
257
+ this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
258
+ },
259
+
210
260
  onStateChange: function() {
211
261
  var readyState = this.transport.readyState;
212
262
  if (readyState != 1)
213
263
  this.respondToReadyState(this.transport.readyState);
214
264
  },
215
-
265
+
216
266
  respondToReadyState: function(readyState) {
217
267
  var event = Ajax.Request.Events[readyState];
218
- (this.options['on' + event] || Ajax.emptyFunction)(this.transport);
268
+
269
+ if (event == 'Complete')
270
+ (this.options['on' + this.transport.status]
271
+ || this.options['on' + this.responseIsSuccess() ? 'Success' : 'Failure']
272
+ || Prototype.emptyFunction)(this.transport);
273
+
274
+ (this.options['on' + event] || Prototype.emptyFunction)(this.transport);
275
+
276
+ /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
277
+ if (event == 'Complete')
278
+ this.transport.onreadystatechange = Prototype.emptyFunction;
219
279
  }
220
280
  });
221
281
 
222
282
  Ajax.Updater = Class.create();
223
- Ajax.Updater.prototype = (new Ajax.Base()).extend({
283
+ Ajax.Updater.ScriptFragment = '(?:<script.*?>)((\n|.)*?)(?:<\/script>)';
284
+
285
+ Ajax.Updater.prototype.extend(Ajax.Request.prototype).extend({
224
286
  initialize: function(container, url, options) {
225
- this.container = $(container);
226
- this.setOptions(options);
227
-
228
- if (this.options.asynchronous) {
229
- this.onComplete = this.options.onComplete;
230
- this.options.onComplete = this.updateContent.bind(this);
287
+ this.containers = {
288
+ success: container.success ? $(container.success) : $(container),
289
+ failure: container.failure ? $(container.failure) :
290
+ (container.success ? null : $(container))
231
291
  }
232
-
233
- this.request = new Ajax.Request(url, this.options);
234
-
235
- if (!this.options.asynchronous)
292
+
293
+ this.transport = Ajax.getTransport();
294
+ this.setOptions(options);
295
+
296
+ var onComplete = this.options.onComplete || Prototype.emptyFunction;
297
+ this.options.onComplete = (function() {
236
298
  this.updateContent();
299
+ onComplete(this.transport);
300
+ }).bind(this);
301
+
302
+ this.request(url);
237
303
  },
238
-
304
+
239
305
  updateContent: function() {
240
- if (this.options.insertion) {
241
- new this.options.insertion(this.container,
242
- this.request.transport.responseText);
243
- } else {
244
- this.container.innerHTML = this.request.transport.responseText;
306
+ var receiver = this.responseIsSuccess() ?
307
+ this.containers.success : this.containers.failure;
308
+
309
+ var match = new RegExp(Ajax.Updater.ScriptFragment, 'img');
310
+ var response = this.transport.responseText.replace(match, '');
311
+ var scripts = this.transport.responseText.match(match);
312
+
313
+ if (receiver) {
314
+ if (this.options.insertion) {
315
+ new this.options.insertion(receiver, response);
316
+ } else {
317
+ receiver.innerHTML = response;
318
+ }
319
+ }
320
+
321
+ if (this.responseIsSuccess()) {
322
+ if (this.onComplete)
323
+ setTimeout((function() {this.onComplete(
324
+ this.transport)}).bind(this), 10);
325
+ }
326
+
327
+ if (this.options.evalScripts && scripts) {
328
+ match = new RegExp(Ajax.Updater.ScriptFragment, 'im');
329
+ setTimeout((function() {
330
+ for (var i = 0; i < scripts.length; i++)
331
+ eval(scripts[i].match(match)[1]);
332
+ }).bind(this), 10);
245
333
  }
334
+ }
335
+ });
336
+
337
+ Ajax.PeriodicalUpdater = Class.create();
338
+ Ajax.PeriodicalUpdater.prototype = (new Ajax.Base()).extend({
339
+ initialize: function(container, url, options) {
340
+ this.setOptions(options);
341
+ this.onComplete = this.options.onComplete;
342
+
343
+ this.frequency = (this.options.frequency || 2);
344
+ this.decay = 1;
345
+
346
+ this.updater = {};
347
+ this.container = container;
348
+ this.url = url;
349
+
350
+ this.start();
351
+ },
352
+
353
+ start: function() {
354
+ this.options.onComplete = this.updateComplete.bind(this);
355
+ this.onTimerEvent();
356
+ },
357
+
358
+ stop: function() {
359
+ this.updater.onComplete = undefined;
360
+ clearTimeout(this.timer);
361
+ (this.onComplete || Ajax.emptyFunction).apply(this, arguments);
362
+ },
246
363
 
247
- if (this.onComplete) {
248
- setTimeout((function() {this.onComplete(this.request)}).bind(this), 10);
364
+ updateComplete: function(request) {
365
+ if (this.options.decay) {
366
+ this.decay = (request.responseText == this.lastText ?
367
+ this.decay * this.options.decay : 1);
368
+
369
+ this.lastText = request.responseText;
249
370
  }
371
+ this.timer = setTimeout(this.onTimerEvent.bind(this),
372
+ this.decay * this.frequency * 1000);
373
+ },
374
+
375
+ onTimerEvent: function() {
376
+ this.updater = new Ajax.Updater(this.container, this.url, this.options);
250
377
  }
251
378
  });
252
379
 
380
+ document.getElementsByClassName = function(className) {
381
+ var children = document.getElementsByTagName('*') || document.all;
382
+ var elements = new Array();
383
+
384
+ for (var i = 0; i < children.length; i++) {
385
+ var child = children[i];
386
+ var classNames = child.className.split(' ');
387
+ for (var j = 0; j < classNames.length; j++) {
388
+ if (classNames[j] == className) {
389
+ elements.push(child);
390
+ break;
391
+ }
392
+ }
393
+ }
394
+
395
+ return elements;
396
+ }
397
+
253
398
  /*--------------------------------------------------------------------------*/
254
399
 
400
+ if (!window.Element) {
401
+ var Element = new Object();
402
+ }
403
+
404
+ Object.extend(Element, {
405
+ toggle: function() {
406
+ for (var i = 0; i < arguments.length; i++) {
407
+ var element = $(arguments[i]);
408
+ element.style.display =
409
+ (element.style.display == 'none' ? '' : 'none');
410
+ }
411
+ },
412
+
413
+ hide: function() {
414
+ for (var i = 0; i < arguments.length; i++) {
415
+ var element = $(arguments[i]);
416
+ element.style.display = 'none';
417
+ }
418
+ },
419
+
420
+ show: function() {
421
+ for (var i = 0; i < arguments.length; i++) {
422
+ var element = $(arguments[i]);
423
+ element.style.display = '';
424
+ }
425
+ },
426
+
427
+ remove: function(element) {
428
+ element = $(element);
429
+ element.parentNode.removeChild(element);
430
+ },
431
+
432
+ getHeight: function(element) {
433
+ element = $(element);
434
+ return element.offsetHeight;
435
+ },
436
+
437
+ hasClassName: function(element, className) {
438
+ element = $(element);
439
+ if (!element)
440
+ return;
441
+ var a = element.className.split(' ');
442
+ for (var i = 0; i < a.length; i++) {
443
+ if (a[i] == className)
444
+ return true;
445
+ }
446
+ return false;
447
+ },
448
+
449
+ addClassName: function(element, className) {
450
+ element = $(element);
451
+ Element.removeClassName(element, className);
452
+ element.className += ' ' + className;
453
+ },
454
+
455
+ removeClassName: function(element, className) {
456
+ element = $(element);
457
+ if (!element)
458
+ return;
459
+ var newClassName = '';
460
+ var a = element.className.split(' ');
461
+ for (var i = 0; i < a.length; i++) {
462
+ if (a[i] != className) {
463
+ if (i > 0)
464
+ newClassName += ' ';
465
+ newClassName += a[i];
466
+ }
467
+ }
468
+ element.className = newClassName;
469
+ },
470
+
471
+ // removes whitespace-only text node children
472
+ cleanWhitespace: function(element) {
473
+ var element = $(element);
474
+ for (var i = 0; i < element.childNodes.length; i++) {
475
+ var node = element.childNodes[i];
476
+ if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
477
+ Element.remove(node);
478
+ }
479
+ }
480
+ });
481
+
482
+ var Toggle = new Object();
483
+ Toggle.display = Element.toggle;
484
+
485
+ /*--------------------------------------------------------------------------*/
486
+
487
+ Abstract.Insertion = function(adjacency) {
488
+ this.adjacency = adjacency;
489
+ }
490
+
491
+ Abstract.Insertion.prototype = {
492
+ initialize: function(element, content) {
493
+ this.element = $(element);
494
+ this.content = content;
495
+
496
+ if (this.adjacency && this.element.insertAdjacentHTML) {
497
+ this.element.insertAdjacentHTML(this.adjacency, this.content);
498
+ } else {
499
+ this.range = this.element.ownerDocument.createRange();
500
+ if (this.initializeRange) this.initializeRange();
501
+ this.fragment = this.range.createContextualFragment(this.content);
502
+ this.insertContent();
503
+ }
504
+ }
505
+ }
506
+
507
+ var Insertion = new Object();
508
+
509
+ Insertion.Before = Class.create();
510
+ Insertion.Before.prototype = (new Abstract.Insertion('beforeBegin')).extend({
511
+ initializeRange: function() {
512
+ this.range.setStartBefore(this.element);
513
+ },
514
+
515
+ insertContent: function() {
516
+ this.element.parentNode.insertBefore(this.fragment, this.element);
517
+ }
518
+ });
519
+
520
+ Insertion.Top = Class.create();
521
+ Insertion.Top.prototype = (new Abstract.Insertion('afterBegin')).extend({
522
+ initializeRange: function() {
523
+ this.range.selectNodeContents(this.element);
524
+ this.range.collapse(true);
525
+ },
526
+
527
+ insertContent: function() {
528
+ this.element.insertBefore(this.fragment, this.element.firstChild);
529
+ }
530
+ });
531
+
532
+ Insertion.Bottom = Class.create();
533
+ Insertion.Bottom.prototype = (new Abstract.Insertion('beforeEnd')).extend({
534
+ initializeRange: function() {
535
+ this.range.selectNodeContents(this.element);
536
+ this.range.collapse(this.element);
537
+ },
538
+
539
+ insertContent: function() {
540
+ this.element.appendChild(this.fragment);
541
+ }
542
+ });
543
+
544
+ Insertion.After = Class.create();
545
+ Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend({
546
+ initializeRange: function() {
547
+ this.range.setStartAfter(this.element);
548
+ },
549
+
550
+ insertContent: function() {
551
+ this.element.parentNode.insertBefore(this.fragment,
552
+ this.element.nextSibling);
553
+ }
554
+ });
555
+
255
556
  var Field = {
256
557
  clear: function() {
257
558
  for (var i = 0; i < arguments.length; i++)
@@ -295,7 +596,7 @@ var Form = {
295
596
  },
296
597
 
297
598
  getElements: function(form) {
298
- form = $(form);
599
+ var form = $(form);
299
600
  var elements = new Array();
300
601
 
301
602
  for (tagName in Form.Element.Serializers) {
@@ -306,17 +607,44 @@ var Form = {
306
607
  return elements;
307
608
  },
308
609
 
610
+ getInputs: function(form, typeName, name) {
611
+ var form = $(form);
612
+ var inputs = form.getElementsByTagName('input');
613
+
614
+ if (!typeName && !name)
615
+ return inputs;
616
+
617
+ var matchingInputs = new Array();
618
+ for (var i = 0; i < inputs.length; i++) {
619
+ var input = inputs[i];
620
+ if ((typeName && input.type != typeName) ||
621
+ (name && input.name != name))
622
+ continue;
623
+ matchingInputs.push(input);
624
+ }
625
+
626
+ return matchingInputs;
627
+ },
628
+
309
629
  disable: function(form) {
310
630
  var elements = Form.getElements(form);
311
631
  for (var i = 0; i < elements.length; i++) {
312
632
  var element = elements[i];
313
633
  element.blur();
314
- element.disable = 'true';
634
+ element.disabled = 'true';
635
+ }
636
+ },
637
+
638
+ enable: function(form) {
639
+ var elements = Form.getElements(form);
640
+ for (var i = 0; i < elements.length; i++) {
641
+ var element = elements[i];
642
+ element.disabled = '';
315
643
  }
316
644
  },
317
645
 
318
646
  focusFirstElement: function(form) {
319
- form = $(form);
647
+ var form = $(form);
320
648
  var elements = Form.getElements(form);
321
649
  for (var i = 0; i < elements.length; i++) {
322
650
  var element = elements[i];
@@ -334,7 +662,7 @@ var Form = {
334
662
 
335
663
  Form.Element = {
336
664
  serialize: function(element) {
337
- element = $(element);
665
+ var element = $(element);
338
666
  var method = element.tagName.toLowerCase();
339
667
  var parameter = Form.Element.Serializers[method](element);
340
668
 
@@ -344,7 +672,7 @@ Form.Element = {
344
672
  },
345
673
 
346
674
  getValue: function(element) {
347
- element = $(element);
675
+ var element = $(element);
348
676
  var method = element.tagName.toLowerCase();
349
677
  var parameter = Form.Element.Serializers[method](element);
350
678
 
@@ -356,6 +684,7 @@ Form.Element = {
356
684
  Form.Element.Serializers = {
357
685
  input: function(element) {
358
686
  switch (element.type.toLowerCase()) {
687
+ case 'submit':
359
688
  case 'hidden':
360
689
  case 'password':
361
690
  case 'text':
@@ -377,9 +706,20 @@ Form.Element.Serializers = {
377
706
  },
378
707
 
379
708
  select: function(element) {
380
- var index = element.selectedIndex;
381
- var value = element.options[index].value || element.options[index].text;
382
- return [element.name, (index >= 0) ? value : ''];
709
+ var value = '';
710
+ if (element.type == 'select-one') {
711
+ var index = element.selectedIndex;
712
+ if (index >= 0)
713
+ value = element.options[index].value || element.options[index].text;
714
+ } else {
715
+ value = new Array();
716
+ for (var i = 0; i < element.length; i++) {
717
+ var opt = element.options[i];
718
+ if (opt.selected)
719
+ value.push(opt.value || opt.text);
720
+ }
721
+ }
722
+ return [element.name, value];
383
723
  }
384
724
  }
385
725
 
@@ -401,7 +741,7 @@ Abstract.TimedObserver.prototype = {
401
741
  },
402
742
 
403
743
  registerCallback: function() {
404
- setTimeout(this.onTimerEvent.bind(this), this.frequency * 1000);
744
+ setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
405
745
  },
406
746
 
407
747
  onTimerEvent: function() {
@@ -410,8 +750,6 @@ Abstract.TimedObserver.prototype = {
410
750
  this.callback(this.element, value);
411
751
  this.lastValue = value;
412
752
  }
413
-
414
- this.registerCallback();
415
753
  }
416
754
  }
417
755
 
@@ -429,336 +767,272 @@ Form.Observer.prototype = (new Abstract.TimedObserver()).extend({
429
767
  }
430
768
  });
431
769
 
432
-
433
- /*--------------------------------------------------------------------------*/
434
-
435
- document.getElementsByClassName = function(className) {
436
- var children = document.getElementsByTagName('*') || document.all;
437
- var elements = new Array();
438
-
439
- for (var i = 0; i < children.length; i++) {
440
- var child = children[i];
441
- var classNames = child.className.split(' ');
442
- for (var j = 0; j < classNames.length; j++) {
443
- if (classNames[j] == className) {
444
- elements.push(child);
445
- break;
446
- }
447
- }
448
- }
449
-
450
- return elements;
451
- }
452
-
453
770
  /*--------------------------------------------------------------------------*/
454
771
 
455
- var Element = {
456
- toggle: function() {
457
- for (var i = 0; i < arguments.length; i++) {
458
- var element = $(arguments[i]);
459
- element.style.display =
460
- (element.style.display == 'none' ? '' : 'none');
461
- }
462
- },
463
-
464
- hide: function() {
465
- for (var i = 0; i < arguments.length; i++) {
466
- var element = $(arguments[i]);
467
- element.style.display = 'none';
468
- }
772
+ Abstract.EventObserver = function() {}
773
+ Abstract.EventObserver.prototype = {
774
+ initialize: function(element, callback) {
775
+ this.element = $(element);
776
+ this.callback = callback;
777
+
778
+ this.lastValue = this.getValue();
779
+ if (this.element.tagName.toLowerCase() == 'form')
780
+ this.registerFormCallbacks();
781
+ else
782
+ this.registerCallback(this.element);
469
783
  },
470
-
471
- show: function() {
472
- for (var i = 0; i < arguments.length; i++) {
473
- var element = $(arguments[i]);
474
- element.style.display = '';
784
+
785
+ onElementEvent: function() {
786
+ var value = this.getValue();
787
+ if (this.lastValue != value) {
788
+ this.callback(this.element, value);
789
+ this.lastValue = value;
475
790
  }
476
791
  },
477
-
478
- remove: function(element) {
479
- element = $(element);
480
- element.parentNode.removeChild(element);
792
+
793
+ registerFormCallbacks: function() {
794
+ var elements = Form.getElements(this.element);
795
+ for (var i = 0; i < elements.length; i++)
796
+ this.registerCallback(elements[i]);
481
797
  },
482
-
483
- getHeight: function(element) {
484
- element = $(element);
485
- return element.offsetHeight;
798
+
799
+ registerCallback: function(element) {
800
+ if (element.type) {
801
+ switch (element.type.toLowerCase()) {
802
+ case 'checkbox':
803
+ case 'radio':
804
+ element.target = this;
805
+ element.prev_onclick = element.onclick || Prototype.emptyFunction;
806
+ element.onclick = function() {
807
+ this.prev_onclick();
808
+ this.target.onElementEvent();
809
+ }
810
+ break;
811
+ case 'password':
812
+ case 'text':
813
+ case 'textarea':
814
+ case 'select-one':
815
+ case 'select-multiple':
816
+ element.target = this;
817
+ element.prev_onchange = element.onchange || Prototype.emptyFunction;
818
+ element.onchange = function() {
819
+ this.prev_onchange();
820
+ this.target.onElementEvent();
821
+ }
822
+ break;
823
+ }
824
+ }
486
825
  }
487
826
  }
488
827
 
489
- var Toggle = new Object();
490
- Toggle.display = Element.toggle;
828
+ Form.Element.EventObserver = Class.create();
829
+ Form.Element.EventObserver.prototype = (new Abstract.EventObserver()).extend({
830
+ getValue: function() {
831
+ return Form.Element.getValue(this.element);
832
+ }
833
+ });
491
834
 
492
- /*--------------------------------------------------------------------------*/
835
+ Form.EventObserver = Class.create();
836
+ Form.EventObserver.prototype = (new Abstract.EventObserver()).extend({
837
+ getValue: function() {
838
+ return Form.serialize(this.element);
839
+ }
840
+ });
493
841
 
494
- Abstract.Insertion = function(adjacency) {
495
- this.adjacency = adjacency;
496
- }
497
842
 
498
- Abstract.Insertion.prototype = {
499
- initialize: function(element, content) {
500
- this.element = $(element);
501
- this.content = content;
502
-
503
- if (this.adjacency && this.element.insertAdjacentHTML) {
504
- this.element.insertAdjacentHTML(this.adjacency, this.content);
505
- } else {
506
- this.range = this.element.ownerDocument.createRange();
507
- if (this.initializeRange) this.initializeRange();
508
- this.fragment = this.range.createContextualFragment(this.content);
509
- this.insertContent();
510
- }
511
- }
843
+ if (!window.Event) {
844
+ var Event = new Object();
512
845
  }
513
846
 
514
- var Insertion = new Object();
515
-
516
- Insertion.Before = Class.create();
517
- Insertion.Before.prototype = (new Abstract.Insertion('beforeBegin')).extend({
518
- initializeRange: function() {
519
- this.range.setStartBefore(this.element);
847
+ Object.extend(Event, {
848
+ KEY_BACKSPACE: 8,
849
+ KEY_TAB: 9,
850
+ KEY_RETURN: 13,
851
+ KEY_ESC: 27,
852
+ KEY_LEFT: 37,
853
+ KEY_UP: 38,
854
+ KEY_RIGHT: 39,
855
+ KEY_DOWN: 40,
856
+ KEY_DELETE: 46,
857
+
858
+ element: function(event) {
859
+ return event.target || event.srcElement;
520
860
  },
521
-
522
- insertContent: function() {
523
- this.element.parentNode.insertBefore(this.fragment, this.element);
524
- }
525
- });
526
861
 
527
- Insertion.Top = Class.create();
528
- Insertion.Top.prototype = (new Abstract.Insertion('afterBegin')).extend({
529
- initializeRange: function() {
530
- this.range.selectNodeContents(this.element);
531
- this.range.collapse(true);
862
+ isLeftClick: function(event) {
863
+ return (((event.which) && (event.which == 1)) ||
864
+ ((event.button) && (event.button == 1)));
532
865
  },
533
-
534
- insertContent: function() {
535
- this.element.insertBefore(this.fragment, this.element.firstChild);
536
- }
537
- });
538
866
 
539
- Insertion.Bottom = Class.create();
540
- Insertion.Bottom.prototype = (new Abstract.Insertion('beforeEnd')).extend({
541
- initializeRange: function() {
542
- this.range.selectNodeContents(this.element);
543
- this.range.collapse(this.element);
867
+ pointerX: function(event) {
868
+ return event.pageX || (event.clientX +
869
+ (document.documentElement.scrollLeft || document.body.scrollLeft));
544
870
  },
545
-
546
- insertContent: function() {
547
- this.element.appendChild(this.fragment);
548
- }
549
- });
550
871
 
551
- Insertion.After = Class.create();
552
- Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend({
553
- initializeRange: function() {
554
- this.range.setStartAfter(this.element);
872
+ pointerY: function(event) {
873
+ return event.pageY || (event.clientY +
874
+ (document.documentElement.scrollTop || document.body.scrollTop));
555
875
  },
556
-
557
- insertContent: function() {
558
- this.element.parentNode.insertBefore(this.fragment,
559
- this.element.nextSibling);
560
- }
561
- });
562
-
563
- /*--------------------------------------------------------------------------*/
564
-
565
- var Effect = new Object();
566
876
 
567
- Effect.Highlight = Class.create();
568
- Effect.Highlight.prototype = {
569
- initialize: function(element) {
570
- this.element = $(element);
571
- this.start = 153;
572
- this.finish = 255;
573
- this.current = this.start;
574
- this.fade();
575
- },
576
-
577
- fade: function() {
578
- if (this.isFinished()) return;
579
- if (this.timer) clearTimeout(this.timer);
580
- this.highlight(this.element, this.current);
581
- this.current += 17;
582
- this.timer = setTimeout(this.fade.bind(this), 250);
583
- },
584
-
585
- isFinished: function() {
586
- return this.current > this.finish;
877
+ stop: function(event) {
878
+ if (event.preventDefault) {
879
+ event.preventDefault();
880
+ event.stopPropagation();
881
+ } else {
882
+ event.returnValue = false;
883
+ }
587
884
  },
588
-
589
- highlight: function(element, current) {
590
- element.style.backgroundColor = "#ffff" + current.toColorPart();
591
- }
592
- }
593
885
 
594
-
595
- Effect.Fade = Class.create();
596
- Effect.Fade.prototype = {
597
- initialize: function(element) {
598
- this.element = $(element);
599
- this.start = 100;
600
- this.finish = 0;
601
- this.current = this.start;
602
- this.fade();
886
+ // find the first node with the given tagName, starting from the
887
+ // node the event was triggered on; traverses the DOM upwards
888
+ findElement: function(event, tagName) {
889
+ var element = Event.element(event);
890
+ while (element.parentNode && (!element.tagName ||
891
+ (element.tagName.toUpperCase() != tagName.toUpperCase())))
892
+ element = element.parentNode;
893
+ return element;
603
894
  },
895
+
896
+ observers: false,
604
897
 
605
- fade: function() {
606
- if (this.isFinished()) { this.element.style.display = 'none'; return; }
607
- if (this.timer) clearTimeout(this.timer);
608
- this.setOpacity(this.element, this.current);
609
- this.current -= 10;
610
- this.timer = setTimeout(this.fade.bind(this), 50);
898
+ _observeAndCache: function(element, name, observer, useCapture) {
899
+ if (!this.observers) this.observers = [];
900
+ if (element.addEventListener) {
901
+ this.observers.push([element, name, observer, useCapture]);
902
+ element.addEventListener(name, observer, useCapture);
903
+ } else if (element.attachEvent) {
904
+ this.observers.push([element, name, observer, useCapture]);
905
+ element.attachEvent('on' + name, observer);
906
+ }
611
907
  },
612
908
 
613
- isFinished: function() {
614
- return this.current <= this.finish;
909
+ unloadCache: function() {
910
+ if (!Event.observers) return;
911
+ for (var i = 0; i < Event.observers.length; i++) {
912
+ Event.stopObserving.apply(this, Event.observers[i]);
913
+ Event.observers[i][0] = null;
914
+ }
915
+ Event.observers = false;
615
916
  },
616
-
617
- setOpacity: function(element, opacity) {
618
- opacity = (opacity == 100) ? 99.999 : opacity;
619
- element.style.filter = "alpha(opacity:"+opacity+")";
620
- element.style.opacity = opacity/100 /*//*/;
621
- }
622
- }
623
917
 
624
- Effect.Scale = Class.create();
625
- Effect.Scale.prototype = {
626
- initialize: function(element, percent) {
627
- this.element = $(element);
628
- this.startScale = 1.0;
629
- this.startHeight = this.element.offsetHeight;
630
- this.startWidth = this.element.offsetWidth;
631
- this.currentHeight = this.startHeight;
632
- this.currentWidth = this.startWidth;
633
- this.finishScale = (percent/100) /*//*/;
634
- if (this.element.style.fontSize=="") this.sizeEm = 1.0;
635
- if (this.element.style.fontSize.indexOf("em")>0)
636
- this.sizeEm = parseFloat(this.element.style.fontSize);
637
- if(this.element.effect_scale) {
638
- clearTimeout(this.element.effect_scale.timer);
639
- this.startScale = this.element.effect_scale.currentScale;
640
- this.startHeight = this.element.effect_scale.startHeight;
641
- this.startWidth = this.element.effect_scale.startWidth;
642
- if(this.element.effect_scale.sizeEm)
643
- this.sizeEm = this.element.effect_scale.sizeEm;
644
- }
645
- this.element.effect_scale = this;
646
- this.currentScale = this.startScale;
647
- this.factor = this.finishScale - this.startScale;
648
- this.options = arguments[2] || {};
649
- this.scale();
650
- },
651
-
652
- scale: function() {
653
- if (this.isFinished()) {
654
- this.setDimensions(this.element, this.startWidth*this.finishScale, this.startHeight*this.finishScale);
655
- if(this.sizeEm) this.element.style.fontSize = this.sizeEm*this.finishScale + "em";
656
- if(this.options.complete) this.options.complete(this);
657
- return;
658
- }
659
- if (this.timer) clearTimeout(this.timer);
660
- if (this.options.step) this.options.step(this);
661
- this.setDimensions(this.element, this.currentWidth, this.currentHeight);
662
- if(this.sizeEm) this.element.style.fontSize = this.sizeEm*this.currentScale + "em";
663
- this.currentScale += (this.factor/10) /*//*/;
664
- this.currentWidth = this.startWidth * this.currentScale;
665
- this.currentHeight = this.startHeight * this.currentScale;
666
- this.timer = setTimeout(this.scale.bind(this), 50);
667
- },
668
-
669
- isFinished: function() {
670
- return (this.factor < 0) ?
671
- this.currentScale <= this.finishScale : this.currentScale >= this.finishScale;
918
+ observe: function(element, name, observer, useCapture) {
919
+ var element = $(element);
920
+ useCapture = useCapture || false;
921
+
922
+ if (name == 'keypress' &&
923
+ ((navigator.appVersion.indexOf('AppleWebKit') > 0)
924
+ || element.attachEvent))
925
+ name = 'keydown';
926
+
927
+ this._observeAndCache(element, name, observer, useCapture);
672
928
  },
673
-
674
- setDimensions: function(element, width, height) {
675
- element.style.width = width + 'px';
676
- element.style.height = height + 'px';
929
+
930
+ stopObserving: function(element, name, observer, useCapture) {
931
+ var element = $(element);
932
+ useCapture = useCapture || false;
933
+
934
+ if (name == 'keypress' &&
935
+ ((navigator.appVersion.indexOf('AppleWebKit') > 0)
936
+ || element.detachEvent))
937
+ name = 'keydown';
938
+
939
+ if (element.removeEventListener) {
940
+ element.removeEventListener(name, observer, useCapture);
941
+ } else if (element.detachEvent) {
942
+ element.detachEvent('on' + name, observer);
943
+ }
677
944
  }
678
- }
945
+ });
679
946
 
680
- Effect.Squish = Class.create();
681
- Effect.Squish.prototype = {
682
- initialize: function(element) {
683
- this.element = $(element);
684
- new Effect.Scale(this.element, 1, { complete: this.hide.bind(this) } );
947
+ /* prevent memory leaks in IE */
948
+ Event.observe(window, 'unload', Event.unloadCache, false);
949
+
950
+ var Position = {
951
+
952
+ // set to true if needed, warning: firefox performance problems
953
+ // NOT neeeded for page scrolling, only if draggable contained in
954
+ // scrollable elements
955
+ includeScrollOffsets: false,
956
+
957
+ // must be called before calling withinIncludingScrolloffset, every time the
958
+ // page is scrolled
959
+ prepare: function() {
960
+ this.deltaX = window.pageXOffset
961
+ || document.documentElement.scrollLeft
962
+ || document.body.scrollLeft
963
+ || 0;
964
+ this.deltaY = window.pageYOffset
965
+ || document.documentElement.scrollTop
966
+ || document.body.scrollTop
967
+ || 0;
685
968
  },
686
- hide: function() {
687
- this.element.style.display = 'none';
688
- }
689
- }
690
969
 
691
- Effect.Puff = Class.create();
692
- Effect.Puff.prototype = {
693
- initialize: function(element) {
694
- this.element = $(element);
695
- this.opacity = 100;
696
- this.startTop = this.element.top || this.element.offsetTop;
697
- this.startLeft = this.element.left || this.element.offsetLeft;
698
- new Effect.Scale(this.element, 200, { step: this.fade.bind(this), complete: this.hide.bind(this) } );
699
- },
700
- fade: function(effect) {
701
- topd = (((effect.currentScale)*effect.startHeight) - effect.startHeight)/2;
702
- leftd = (((effect.currentScale)*effect.startWidth) - effect.startWidth)/2;
703
- this.element.style.position='absolute';
704
- this.element.style.top = this.startTop-topd + "px";
705
- this.element.style.left = this.startLeft-leftd + "px";
706
- this.opacity -= 10;
707
- this.setOpacity(this.element, this.opacity);
708
- if(navigator.appVersion.indexOf('AppleWebKit')>0) this.element.innerHTML += ''; //force redraw on safari
970
+ realOffset: function(element) {
971
+ var valueT = 0, valueL = 0;
972
+ do {
973
+ valueT += element.scrollTop || 0;
974
+ valueL += element.scrollLeft || 0;
975
+ element = element.parentNode;
976
+ } while (element);
977
+ return [valueL, valueT];
709
978
  },
710
- hide: function() {
711
- this.element.style.display = 'none';
979
+
980
+ cumulativeOffset: function(element) {
981
+ var valueT = 0, valueL = 0;
982
+ do {
983
+ valueT += element.offsetTop || 0;
984
+ valueL += element.offsetLeft || 0;
985
+ element = element.offsetParent;
986
+ } while (element);
987
+ return [valueL, valueT];
712
988
  },
713
- setOpacity: function(element, opacity) {
714
- opacity = (opacity == 100) ? 99.999 : opacity;
715
- element.style.filter = "alpha(opacity:"+opacity+")";
716
- element.style.opacity = opacity/100 /*//*/;
717
- }
718
- }
719
989
 
720
- Effect.Appear = Class.create();
721
- Effect.Appear.prototype = {
722
- initialize: function(element) {
723
- this.element = $(element);
724
- this.start = 0;
725
- this.finish = 100;
726
- this.current = this.start;
727
- this.fade();
990
+ // caches x/y coordinate pair to use with overlap
991
+ within: function(element, x, y) {
992
+ if (this.includeScrollOffsets)
993
+ return this.withinIncludingScrolloffsets(element, x, y);
994
+ this.xcomp = x;
995
+ this.ycomp = y;
996
+ this.offset = this.cumulativeOffset(element);
997
+
998
+ return (y >= this.offset[1] &&
999
+ y < this.offset[1] + element.offsetHeight &&
1000
+ x >= this.offset[0] &&
1001
+ x < this.offset[0] + element.offsetWidth);
728
1002
  },
729
-
730
- fade: function() {
731
- if (this.isFinished()) return;
732
- if (this.timer) clearTimeout(this.timer);
733
- this.setOpacity(this.element, this.current);
734
- this.current += 10;
735
- this.timer = setTimeout(this.fade.bind(this), 50);
1003
+
1004
+ withinIncludingScrolloffsets: function(element, x, y) {
1005
+ var offsetcache = this.realOffset(element);
1006
+
1007
+ this.xcomp = x + offsetcache[0] - this.deltaX;
1008
+ this.ycomp = y + offsetcache[1] - this.deltaY;
1009
+ this.offset = this.cumulativeOffset(element);
1010
+
1011
+ return (this.ycomp >= this.offset[1] &&
1012
+ this.ycomp < this.offset[1] + element.offsetHeight &&
1013
+ this.xcomp >= this.offset[0] &&
1014
+ this.xcomp < this.offset[0] + element.offsetWidth);
736
1015
  },
737
-
738
- isFinished: function() {
739
- return this.current > this.finish;
1016
+
1017
+ // within must be called directly before
1018
+ overlap: function(mode, element) {
1019
+ if (!mode) return 0;
1020
+ if (mode == 'vertical')
1021
+ return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
1022
+ element.offsetHeight;
1023
+ if (mode == 'horizontal')
1024
+ return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
1025
+ element.offsetWidth;
740
1026
  },
741
-
742
- setOpacity: function(element, opacity) {
743
- opacity = (opacity == 100) ? 99.999 : opacity;
744
- element.style.filter = "alpha(opacity:"+opacity+")";
745
- element.style.opacity = opacity/100 /*//*/;
746
- element.style.display = '';
747
- }
748
- }
749
1027
 
750
- Effect.ContentZoom = Class.create();
751
- Effect.ContentZoom.prototype = {
752
- initialize: function(element, percent) {
753
- this.element = $(element);
754
- if (this.element.style.fontSize=="") this.sizeEm = 1.0;
755
- if (this.element.style.fontSize.indexOf("em")>0)
756
- this.sizeEm = parseFloat(this.element.style.fontSize);
757
- if(this.element.effect_contentzoom) {
758
- this.sizeEm = this.element.effect_contentzoom.sizeEm;
759
- }
760
- this.element.effect_contentzoom = this;
761
- this.element.style.fontSize = this.sizeEm*(percent/100) + "em" /*//*/;
762
- if(navigator.appVersion.indexOf('AppleWebKit')>0) { this.element.scrollTop -= 1; };
1028
+ clone: function(source, target) {
1029
+ source = $(source);
1030
+ target = $(target);
1031
+ target.style.position = 'absolute';
1032
+ var offsets = this.cumulativeOffset(source);
1033
+ target.style.top = offsets[1] + 'px';
1034
+ target.style.left = offsets[0] + 'px';
1035
+ target.style.width = source.offsetWidth + 'px';
1036
+ target.style.height = source.offsetHeight + 'px';
763
1037
  }
764
1038
  }