nitro 0.19.0 → 0.20.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 (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
  }