Project

General

Profile

1
/*  Prototype JavaScript framework, version 1.4.0
2
 *  (c) 2005 Sam Stephenson <sam@conio.net>
3
 *
4
 *  THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff
5
 *  against the source tree, available from the Prototype darcs repository.
6
 *
7
 *  Prototype is freely distributable under the terms of an MIT-style license.
8
 *
9
 *  For details, see the Prototype web site: http://prototype.conio.net/
10
 *
11
/*--------------------------------------------------------------------------*/
12

    
13
var Prototype = {
14
  Version: '1.4.0',
15
  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
16

    
17
  emptyFunction: function() {},
18
  K: function(x) {return x}
19
}
20

    
21
var Class = {
22
  create: function() {
23
    return function() {
24
      this.initialize.apply(this, arguments);
25
    }
26
  }
27
}
28

    
29
var Abstract = new Object();
30

    
31
Object.extend = function(destination, source) {
32
  for (property in source) {
33
    destination[property] = source[property];
34
  }
35
  return destination;
36
}
37

    
38
Object.inspect = function(object) {
39
  try {
40
    if (object == undefined) return 'undefined';
41
    if (object == null) return 'null';
42
    return object.inspect ? object.inspect() : object.toString();
43
  } catch (e) {
44
    if (e instanceof RangeError) return '...';
45
    throw e;
46
  }
47
}
48

    
49
Function.prototype.bind = function() {
50
  var __method = this, args = $A(arguments), object = args.shift();
51
  return function() {
52
    return __method.apply(object, args.concat($A(arguments)));
53
  }
54
}
55

    
56
Function.prototype.bindAsEventListener = function(object) {
57
  var __method = this;
58
  return function(event) {
59
    return __method.call(object, event || window.event);
60
  }
61
}
62

    
63
Object.extend(Number.prototype, {
64
  toColorPart: function() {
65
    var digits = this.toString(16);
66
    if (this < 16) return '0' + digits;
67
    return digits;
68
  },
69

    
70
  succ: function() {
71
    return this + 1;
72
  },
73

    
74
  times: function(iterator) {
75
    $R(0, this, true).each(iterator);
76
    return this;
77
  }
78
});
79

    
80
var Try = {
81
  these: function() {
82
    var returnValue;
83

    
84
    for (var i = 0; i < arguments.length; i++) {
85
      var lambda = arguments[i];
86
      try {
87
        returnValue = lambda();
88
        break;
89
      } catch (e) {}
90
    }
91

    
92
    return returnValue;
93
  }
94
}
95

    
96
/*--------------------------------------------------------------------------*/
97

    
98
var PeriodicalExecuter = Class.create();
99
PeriodicalExecuter.prototype = {
100
  initialize: function(callback, frequency) {
101
    this.callback = callback;
102
    this.frequency = frequency;
103
    this.currentlyExecuting = false;
104

    
105
    this.registerCallback();
106
  },
107

    
108
  registerCallback: function() {
109
    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
110
  },
111

    
112
  onTimerEvent: function() {
113
    if (!this.currentlyExecuting) {
114
      try {
115
        this.currentlyExecuting = true;
116
        this.callback();
117
      } finally {
118
        this.currentlyExecuting = false;
119
      }
120
    }
121
  }
122
}
123

    
124
/*--------------------------------------------------------------------------*/
125

    
126
function $() {
127
  var elements = new Array();
128

    
129
  for (var i = 0; i < arguments.length; i++) {
130
    var element = arguments[i];
131
    if (typeof element == 'string')
132
      element = document.getElementById(element);
133

    
134
    if (arguments.length == 1)
135
      return element;
136

    
137
    elements.push(element);
138
  }
139

    
140
  return elements;
141
}
142
Object.extend(String.prototype, {
143
  stripTags: function() {
144
    return this.replace(/<\/?[^>]+>/gi, '');
145
  },
146

    
147
  stripScripts: function() {
148
    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
149
  },
150

    
151
  trim: function() {
152
    return(this.replace(/^\s+/,'').replace(/\s+$/,''));
153
  },
154

    
155
  extractScripts: function() {
156
    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
157
    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
158
    return (this.match(matchAll) || []).map(function(scriptTag) {
159
      return (scriptTag.match(matchOne) || ['', ''])[1];
160
    });
161
  },
162

    
163
  evalScripts: function() {
164
    return this.extractScripts().map(eval);
165
  },
166

    
167
  escapeHTML: function() {
168
    var div = document.createElement('div');
169
    var text = document.createTextNode(this);
170
    div.appendChild(text);
171
    return div.innerHTML;
172
  },
173

    
174
  unescapeHTML: function() {
175
    var div = document.createElement('div');
176
    div.innerHTML = this.stripTags();
177
    return div.childNodes[0] ? div.childNodes[0].nodeValue : '';
178
  },
179

    
180
  toQueryParams: function() {
181
    var pairs = this.match(/^\??(.*)$/)[1].split('&');
182
    return pairs.inject({}, function(params, pairString) {
183
      var pair = pairString.split('=');
184
      params[pair[0]] = pair[1];
185
      return params;
186
    });
187
  },
188

    
189
  toArray: function() {
190
    return this.split('');
191
  },
192

    
193
  camelize: function() {
194
    var oStringList = this.split('-');
195
    if (oStringList.length == 1) return oStringList[0];
196

    
197
    var camelizedString = this.indexOf('-') == 0
198
      ? oStringList[0].charAt(0).toUpperCase() + oStringList[0].substring(1)
199
      : oStringList[0];
200

    
201
    for (var i = 1, len = oStringList.length; i < len; i++) {
202
      var s = oStringList[i];
203
      camelizedString += s.charAt(0).toUpperCase() + s.substring(1);
204
    }
205

    
206
    return camelizedString;
207
  },
208

    
209
  inspect: function() {
210
    return "'" + this.replace('\\', '\\\\').replace("'", '\\\'') + "'";
211
  }
212
});
213

    
214
String.prototype.parseQuery = String.prototype.toQueryParams;
215

    
216
var $break    = new Object();
217
var $continue = new Object();
218

    
219
var Enumerable = {
220
  each: function(iterator) {
221
    var index = 0;
222
    try {
223
      this._each(function(value) {
224
        try {
225
          iterator(value, index++);
226
        } catch (e) {
227
          if (e != $continue) throw e;
228
        }
229
      });
230
    } catch (e) {
231
      if (e != $break) throw e;
232
    }
233
  },
234

    
235
  all: function(iterator) {
236
    var result = true;
237
    this.each(function(value, index) {
238
      result = result && !!(iterator || Prototype.K)(value, index);
239
      if (!result) throw $break;
240
    });
241
    return result;
242
  },
243

    
244
  any: function(iterator) {
245
    var result = true;
246
    this.each(function(value, index) {
247
      if (result = !!(iterator || Prototype.K)(value, index))
248
        throw $break;
249
    });
250
    return result;
251
  },
252

    
253
  collect: function(iterator) {
254
    var results = [];
255
    this.each(function(value, index) {
256
      results.push(iterator(value, index));
257
    });
258
    return results;
259
  },
260

    
261
  detect: function (iterator) {
262
    var result;
263
    this.each(function(value, index) {
264
      if (iterator(value, index)) {
265
        result = value;
266
        throw $break;
267
      }
268
    });
269
    return result;
270
  },
271

    
272
  findAll: function(iterator) {
273
    var results = [];
274
    this.each(function(value, index) {
275
      if (iterator(value, index))
276
        results.push(value);
277
    });
278
    return results;
279
  },
280

    
281
  grep: function(pattern, iterator) {
282
    var results = [];
283
    this.each(function(value, index) {
284
      var stringValue = value.toString();
285
      if (stringValue.match(pattern))
286
        results.push((iterator || Prototype.K)(value, index));
287
    })
288
    return results;
289
  },
290

    
291
  include: function(object) {
292
    var found = false;
293
    this.each(function(value) {
294
      if (value == object) {
295
        found = true;
296
        throw $break;
297
      }
298
    });
299
    return found;
300
  },
301

    
302
  inject: function(memo, iterator) {
303
    this.each(function(value, index) {
304
      memo = iterator(memo, value, index);
305
    });
306
    return memo;
307
  },
308

    
309
  invoke: function(method) {
310
    var args = $A(arguments).slice(1);
311
    return this.collect(function(value) {
312
      return value[method].apply(value, args);
313
    });
314
  },
315

    
316
  max: function(iterator) {
317
    var result;
318
    this.each(function(value, index) {
319
      value = (iterator || Prototype.K)(value, index);
320
      if (value >= (result || value))
321
        result = value;
322
    });
323
    return result;
324
  },
325

    
326
  min: function(iterator) {
327
    var result;
328
    this.each(function(value, index) {
329
      value = (iterator || Prototype.K)(value, index);
330
      if (value <= (result || value))
331
        result = value;
332
    });
333
    return result;
334
  },
335

    
336
  partition: function(iterator) {
337
    var trues = [], falses = [];
338
    this.each(function(value, index) {
339
      ((iterator || Prototype.K)(value, index) ?
340
        trues : falses).push(value);
341
    });
342
    return [trues, falses];
343
  },
344

    
345
  pluck: function(property) {
346
    var results = [];
347
    this.each(function(value, index) {
348
      results.push(value[property]);
349
    });
350
    return results;
351
  },
352

    
353
  reject: function(iterator) {
354
    var results = [];
355
    this.each(function(value, index) {
356
      if (!iterator(value, index))
357
        results.push(value);
358
    });
359
    return results;
360
  },
361

    
362
  sortBy: function(iterator) {
363
    return this.collect(function(value, index) {
364
      return {value: value, criteria: iterator(value, index)};
365
    }).sort(function(left, right) {
366
      var a = left.criteria, b = right.criteria;
367
      return a < b ? -1 : a > b ? 1 : 0;
368
    }).pluck('value');
369
  },
370

    
371
  toArray: function() {
372
    return this.collect(Prototype.K);
373
  },
374

    
375
  zip: function() {
376
    var iterator = Prototype.K, args = $A(arguments);
377
    if (typeof args.last() == 'function')
378
      iterator = args.pop();
379

    
380
    var collections = [this].concat(args).map($A);
381
    return this.map(function(value, index) {
382
      iterator(value = collections.pluck(index));
383
      return value;
384
    });
385
  },
386

    
387
  inspect: function() {
388
    return '#<Enumerable:' + this.toArray().inspect() + '>';
389
  }
390
}
391

    
392
Object.extend(Enumerable, {
393
  map:     Enumerable.collect,
394
  find:    Enumerable.detect,
395
  select:  Enumerable.findAll,
396
  member:  Enumerable.include,
397
  entries: Enumerable.toArray
398
});
399
var $A = Array.from = function(iterable) {
400
  if (!iterable) return [];
401
  if (iterable.toArray) {
402
    return iterable.toArray();
403
  } else {
404
    var results = [];
405
    for (var i = 0; i < iterable.length; i++)
406
      results.push(iterable[i]);
407
    return results;
408
  }
409
}
410

    
411
Object.extend(Array.prototype, Enumerable);
412

    
413
Array.prototype._reverse = Array.prototype.reverse;
414

    
415
Object.extend(Array.prototype, {
416
  _each: function(iterator) {
417
    for (var i = 0; i < this.length; i++)
418
      iterator(this[i]);
419
  },
420

    
421
  clear: function() {
422
    this.length = 0;
423
    return this;
424
  },
425

    
426
  first: function() {
427
    return this[0];
428
  },
429

    
430
  last: function() {
431
    return this[this.length - 1];
432
  },
433

    
434
  compact: function() {
435
    return this.select(function(value) {
436
      return value != undefined || value != null;
437
    });
438
  },
439

    
440
  flatten: function() {
441
    return this.inject([], function(array, value) {
442
      return array.concat(value.constructor == Array ?
443
        value.flatten() : [value]);
444
    });
445
  },
446

    
447
  without: function() {
448
    var values = $A(arguments);
449
    return this.select(function(value) {
450
      return !values.include(value);
451
    });
452
  },
453

    
454
  indexOf: function(object) {
455
    for (var i = 0; i < this.length; i++)
456
      if (this[i] == object) return i;
457
    return -1;
458
  },
459

    
460
  reverse: function(inline) {
461
    return (inline !== false ? this : this.toArray())._reverse();
462
  },
463

    
464
  shift: function() {
465
    var result = this[0];
466
    for (var i = 0; i < this.length - 1; i++)
467
      this[i] = this[i + 1];
468
    this.length--;
469
    return result;
470
  },
471

    
472
  inspect: function() {
473
    return '[' + this.map(Object.inspect).join(', ') + ']';
474
  }
475
});
476
var Hash = {
477
  _each: function(iterator) {
478
    for (key in this) {
479
      var value = this[key];
480
      if (typeof value == 'function') continue;
481

    
482
      var pair = [key, value];
483
      pair.key = key;
484
      pair.value = value;
485
      iterator(pair);
486
    }
487
  },
488

    
489
  keys: function() {
490
    return this.pluck('key');
491
  },
492

    
493
  values: function() {
494
    return this.pluck('value');
495
  },
496

    
497
  merge: function(hash) {
498
    return $H(hash).inject($H(this), function(mergedHash, pair) {
499
      mergedHash[pair.key] = pair.value;
500
      return mergedHash;
501
    });
502
  },
503

    
504
  toQueryString: function() {
505
    return this.map(function(pair) {
506
      return pair.map(encodeURIComponent).join('=');
507
    }).join('&');
508
  },
509

    
510
  inspect: function() {
511
    return '#<Hash:{' + this.map(function(pair) {
512
      return pair.map(Object.inspect).join(': ');
513
    }).join(', ') + '}>';
514
  }
515
}
516

    
517
function $H(object) {
518
  var hash = Object.extend({}, object || {});
519
  Object.extend(hash, Enumerable);
520
  Object.extend(hash, Hash);
521
  return hash;
522
}
523
ObjectRange = Class.create();
524
Object.extend(ObjectRange.prototype, Enumerable);
525
Object.extend(ObjectRange.prototype, {
526
  initialize: function(start, end, exclusive) {
527
    this.start = start;
528
    this.end = end;
529
    this.exclusive = exclusive;
530
  },
531

    
532
  _each: function(iterator) {
533
    var value = this.start;
534
    do {
535
      iterator(value);
536
      value = value.succ();
537
    } while (this.include(value));
538
  },
539

    
540
  include: function(value) {
541
    if (value < this.start)
542
      return false;
543
    if (this.exclusive)
544
      return value < this.end;
545
    return value <= this.end;
546
  }
547
});
548

    
549
var $R = function(start, end, exclusive) {
550
  return new ObjectRange(start, end, exclusive);
551
}
552

    
553
var Ajax = {
554
  getTransport: function() {
555
    return Try.these(
556
      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
557
      function() {return new ActiveXObject('Microsoft.XMLHTTP')},
558
      function() {return new XMLHttpRequest()}
559
    ) || false;
560
  },
561

    
562
  activeRequestCount: 0
563
}
564

    
565
Ajax.Responders = {
566
  responders: [],
567

    
568
  _each: function(iterator) {
569
    this.responders._each(iterator);
570
  },
571

    
572
  register: function(responderToAdd) {
573
    if (!this.include(responderToAdd))
574
      this.responders.push(responderToAdd);
575
  },
576

    
577
  unregister: function(responderToRemove) {
578
    this.responders = this.responders.without(responderToRemove);
579
  },
580

    
581
  dispatch: function(callback, request, transport, json) {
582
    this.each(function(responder) {
583
      if (responder[callback] && typeof responder[callback] == 'function') {
584
        try {
585
          responder[callback].apply(responder, [request, transport, json]);
586
        } catch (e) {}
587
      }
588
    });
589
  }
590
};
591

    
592
Object.extend(Ajax.Responders, Enumerable);
593

    
594
Ajax.Responders.register({
595
  onCreate: function() {
596
    Ajax.activeRequestCount++;
597
  },
598

    
599
  onComplete: function() {
600
    Ajax.activeRequestCount--;
601
  }
602
});
603

    
604
Ajax.Base = function() {};
605
Ajax.Base.prototype = {
606
  setOptions: function(options) {
607
    this.options = {
608
      method:       'post',
609
      asynchronous: true,
610
      parameters:   ''
611
    }
612
    Object.extend(this.options, options || {});
613
  },
614

    
615
  responseIsSuccess: function() {
616
    return this.transport.status == undefined
617
        || this.transport.status == 0
618
        || (this.transport.status >= 200 && this.transport.status < 300);
619
  },
620

    
621
  responseIsFailure: function() {
622
    return !this.responseIsSuccess();
623
  }
624
}
625

    
626
Ajax.Request = Class.create();
627
Ajax.Request.Events =
628
  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
629

    
630
Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
631
  initialize: function(url, options) {
632
    this.transport = Ajax.getTransport();
633
    this.setOptions(options);
634
    this.request(url);
635
  },
636

    
637
  request: function(url) {
638
    var parameters = this.options.parameters || '';
639
    if (parameters.length > 0) parameters += '&_=';
640

    
641
    try {
642
      this.url = url;
643
      if (this.options.method == 'get' && parameters.length > 0)
644
        this.url += (this.url.match(/\?/) ? '&' : '?') + parameters;
645

    
646
      Ajax.Responders.dispatch('onCreate', this, this.transport);
647

    
648
      this.transport.open(this.options.method, this.url,
649
        this.options.asynchronous);
650

    
651
      if (this.options.asynchronous) {
652
        this.transport.onreadystatechange = this.onStateChange.bind(this);
653
        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
654
      }
655

    
656
      this.setRequestHeaders();
657

    
658
      var body = this.options.postBody ? this.options.postBody : parameters;
659
      this.transport.send(this.options.method == 'post' ? body : null);
660

    
661
    } catch (e) {
662
      this.dispatchException(e);
663
    }
664
  },
665

    
666
  setRequestHeaders: function() {
667
    var requestHeaders =
668
      ['X-Requested-With', 'XMLHttpRequest',
669
       'X-Prototype-Version', Prototype.Version];
670

    
671
    if (this.options.method == 'post') {
672
      requestHeaders.push('Content-type',
673
        'application/x-www-form-urlencoded');
674

    
675
      /* Force "Connection: close" for Mozilla browsers to work around
676
       * a bug where XMLHttpReqeuest sends an incorrect Content-length
677
       * header. See Mozilla Bugzilla #246651.
678
       */
679
      if (this.transport.overrideMimeType)
680
        requestHeaders.push('Connection', 'close');
681
    }
682

    
683
    if (this.options.requestHeaders)
684
      requestHeaders.push.apply(requestHeaders, this.options.requestHeaders);
685

    
686
    for (var i = 0; i < requestHeaders.length; i += 2)
687
      this.transport.setRequestHeader(requestHeaders[i], requestHeaders[i+1]);
688
  },
689

    
690
  onStateChange: function() {
691
    var readyState = this.transport.readyState;
692
    if (readyState != 1)
693
      this.respondToReadyState(this.transport.readyState);
694
  },
695

    
696
  header: function(name) {
697
    try {
698
      return this.transport.getResponseHeader(name);
699
    } catch (e) {}
700
  },
701

    
702
  evalJSON: function() {
703
    try {
704
      return eval(this.header('X-JSON'));
705
    } catch (e) {}
706
  },
707

    
708
  evalResponse: function() {
709
    try {
710
      return eval(this.transport.responseText);
711
    } catch (e) {
712
      this.dispatchException(e);
713
    }
714
  },
715

    
716
  respondToReadyState: function(readyState) {
717
    var event = Ajax.Request.Events[readyState];
718
    var transport = this.transport, json = this.evalJSON();
719

    
720
    if (event == 'Complete') {
721
      try {
722
        (this.options['on' + this.transport.status]
723
         || this.options['on' + (this.responseIsSuccess() ? 'Success' : 'Failure')]
724
         || Prototype.emptyFunction)(transport, json);
725
      } catch (e) {
726
        this.dispatchException(e);
727
      }
728

    
729
      if ((this.header('Content-type') || '').match(/^text\/javascript/i))
730
        this.evalResponse();
731
    }
732

    
733
    try {
734
      (this.options['on' + event] || Prototype.emptyFunction)(transport, json);
735
      Ajax.Responders.dispatch('on' + event, this, transport, json);
736
    } catch (e) {
737
      this.dispatchException(e);
738
    }
739

    
740
    /* Avoid memory leak in MSIE: clean up the oncomplete event handler */
741
    if (event == 'Complete')
742
      this.transport.onreadystatechange = Prototype.emptyFunction;
743
  },
744

    
745
  dispatchException: function(exception) {
746
    (this.options.onException || Prototype.emptyFunction)(this, exception);
747
    Ajax.Responders.dispatch('onException', this, exception);
748
  }
749
});
750

    
751
Ajax.Updater = Class.create();
752

    
753
Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
754
  initialize: function(container, url, options) {
755
    this.containers = {
756
      success: container.success ? $(container.success) : $(container),
757
      failure: container.failure ? $(container.failure) :
758
        (container.success ? null : $(container))
759
    }
760

    
761
    this.transport = Ajax.getTransport();
762
    this.setOptions(options);
763

    
764
    var onComplete = this.options.onComplete || Prototype.emptyFunction;
765
    this.options.onComplete = (function(transport, object) {
766
      this.updateContent();
767
      onComplete(transport, object);
768
    }).bind(this);
769

    
770
    this.request(url);
771
  },
772

    
773
  updateContent: function() {
774
    var receiver = this.responseIsSuccess() ?
775
      this.containers.success : this.containers.failure;
776
    var response = this.transport.responseText;
777

    
778
    if (!this.options.evalScripts)
779
      response = response.stripScripts();
780

    
781
    if (receiver) {
782
      if (this.options.insertion) {
783
        new this.options.insertion(receiver, response);
784
      } else {
785
        Element.update(receiver, response);
786
      }
787
    }
788

    
789
    if (this.responseIsSuccess()) {
790
      if (this.onComplete)
791
        setTimeout(this.onComplete.bind(this), 10);
792
    }
793
  }
794
});
795

    
796
Ajax.PeriodicalUpdater = Class.create();
797
Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
798
  initialize: function(container, url, options) {
799
    this.setOptions(options);
800
    this.onComplete = this.options.onComplete;
801

    
802
    this.frequency = (this.options.frequency || 2);
803
    this.decay = (this.options.decay || 1);
804

    
805
    this.updater = {};
806
    this.container = container;
807
    this.url = url;
808

    
809
    this.start();
810
  },
811

    
812
  start: function() {
813
    this.options.onComplete = this.updateComplete.bind(this);
814
    this.onTimerEvent();
815
  },
816

    
817
  stop: function() {
818
    this.updater.onComplete = undefined;
819
    clearTimeout(this.timer);
820
    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
821
  },
822

    
823
  updateComplete: function(request) {
824
    if (this.options.decay) {
825
      this.decay = (request.responseText == this.lastText ?
826
        this.decay * this.options.decay : 1);
827

    
828
      this.lastText = request.responseText;
829
    }
830
    this.timer = setTimeout(this.onTimerEvent.bind(this),
831
      this.decay * this.frequency * 1000);
832
  },
833

    
834
  onTimerEvent: function() {
835
    this.updater = new Ajax.Updater(this.container, this.url, this.options);
836
  }
837
});
838
document.getElementsByClassName = function(className, parentElement) {
839
  var children = ($(parentElement) || document.body).getElementsByTagName('*');
840
  return $A(children).inject([], function(elements, child) {
841
    if (child.className.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
842
      elements.push(child);
843
    return elements;
844
  });
845
}
846

    
847
/*--------------------------------------------------------------------------*/
848

    
849
if (!window.Element) {
850
  var Element = new Object();
851
}
852

    
853
Object.extend(Element, {
854
  visible: function(element) {
855
    return $(element).style.display != 'none';
856
  },
857

    
858
  toggle: function() {
859
    for (var i = 0; i < arguments.length; i++) {
860
      var element = $(arguments[i]);
861
      Element[Element.visible(element) ? 'hide' : 'show'](element);
862
    }
863
  },
864

    
865
  hide: function() {
866
    for (var i = 0; i < arguments.length; i++) {
867
      var element = $(arguments[i]);
868
      element.style.display = 'none';
869
    }
870
  },
871

    
872
  show: function() {
873
    for (var i = 0; i < arguments.length; i++) {
874
      var element = $(arguments[i]);
875
      element.style.display = '';
876
    }
877
  },
878

    
879
  remove: function(element) {
880
    element = $(element);
881
    element.parentNode.removeChild(element);
882
  },
883

    
884
  update: function(element, html) {
885
    $(element).innerHTML = html.stripScripts();
886
    setTimeout(function() {html.evalScripts()}, 10);
887
  },
888

    
889
  getHeight: function(element) {
890
    element = $(element);
891
    return element.offsetHeight;
892
  },
893

    
894
  classNames: function(element) {
895
    return new Element.ClassNames(element);
896
  },
897

    
898
  hasClassName: function(element, className) {
899
    if (!(element = $(element))) return;
900
    return Element.classNames(element).include(className);
901
  },
902

    
903
  addClassName: function(element, className) {
904
    if (!(element = $(element))) return;
905
    return Element.classNames(element).add(className);
906
  },
907

    
908
  removeClassName: function(element, className) {
909
    if (!(element = $(element))) return;
910
    return Element.classNames(element).remove(className);
911
  },
912

    
913
  // removes whitespace-only text node children
914
  cleanWhitespace: function(element) {
915
    element = $(element);
916
    for (var i = 0; i < element.childNodes.length; i++) {
917
      var node = element.childNodes[i];
918
      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
919
        Element.remove(node);
920
    }
921
  },
922

    
923
  empty: function(element) {
924
    return $(element).innerHTML.match(/^\s*$/);
925
  },
926

    
927
  scrollTo: function(element) {
928
    element = $(element);
929
    var x = element.x ? element.x : element.offsetLeft,
930
        y = element.y ? element.y : element.offsetTop;
931
    window.scrollTo(x, y);
932
  },
933

    
934
  getStyle: function(element, style) {
935
    element = $(element);
936
    var value = element.style[style.camelize()];
937
    if (!value) {
938
      if (document.defaultView && document.defaultView.getComputedStyle) {
939
        var css = document.defaultView.getComputedStyle(element, null);
940
        value = css ? css.getPropertyValue(style) : null;
941
      } else if (element.currentStyle) {
942
        value = element.currentStyle[style.camelize()];
943
      }
944
    }
945

    
946
    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
947
      if (Element.getStyle(element, 'position') == 'static') value = 'auto';
948

    
949
    return value == 'auto' ? null : value;
950
  },
951

    
952
  setStyle: function(element, style) {
953
    element = $(element);
954
    for (name in style)
955
      element.style[name.camelize()] = style[name];
956
  },
957

    
958
  getDimensions: function(element) {
959
    element = $(element);
960
    if (Element.getStyle(element, 'display') != 'none')
961
      return {width: element.offsetWidth, height: element.offsetHeight};
962

    
963
    // All *Width and *Height properties give 0 on elements with display none,
964
    // so enable the element temporarily
965
    var els = element.style;
966
    var originalVisibility = els.visibility;
967
    var originalPosition = els.position;
968
    els.visibility = 'hidden';
969
    els.position = 'absolute';
970
    els.display = '';
971
    var originalWidth = element.clientWidth;
972
    var originalHeight = element.clientHeight;
973
    els.display = 'none';
974
    els.position = originalPosition;
975
    els.visibility = originalVisibility;
976
    return {width: originalWidth, height: originalHeight};
977
  },
978

    
979
  makePositioned: function(element) {
980
    element = $(element);
981
    var pos = Element.getStyle(element, 'position');
982
    if (pos == 'static' || !pos) {
983
      element._madePositioned = true;
984
      element.style.position = 'relative';
985
      // Opera returns the offset relative to the positioning context, when an
986
      // element is position relative but top and left have not been defined
987
      if (window.opera) {
988
        element.style.top = 0;
989
        element.style.left = 0;
990
      }
991
    }
992
  },
993

    
994
  undoPositioned: function(element) {
995
    element = $(element);
996
    if (element._madePositioned) {
997
      element._madePositioned = undefined;
998
      element.style.position =
999
        element.style.top =
1000
        element.style.left =
1001
        element.style.bottom =
1002
        element.style.right = '';
1003
    }
1004
  },
1005

    
1006
  makeClipping: function(element) {
1007
    element = $(element);
1008
    if (element._overflow) return;
1009
    element._overflow = element.style.overflow;
1010
    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
1011
      element.style.overflow = 'hidden';
1012
  },
1013

    
1014
  undoClipping: function(element) {
1015
    element = $(element);
1016
    if (element._overflow) return;
1017
    element.style.overflow = element._overflow;
1018
    element._overflow = undefined;
1019
  }
1020
});
1021

    
1022
var Toggle = new Object();
1023
Toggle.display = Element.toggle;
1024

    
1025
/*--------------------------------------------------------------------------*/
1026

    
1027
Abstract.Insertion = function(adjacency) {
1028
  this.adjacency = adjacency;
1029
}
1030

    
1031
Abstract.Insertion.prototype = {
1032
  initialize: function(element, content) {
1033
    this.element = $(element);
1034
    this.content = content.stripScripts();
1035

    
1036
    if (this.adjacency && this.element.insertAdjacentHTML) {
1037
      try {
1038
        this.element.insertAdjacentHTML(this.adjacency, this.content);
1039
      } catch (e) {
1040
        if (this.element.tagName.toLowerCase() == 'tbody') {
1041
          this.insertContent(this.contentFromAnonymousTable());
1042
        } else {
1043
          throw e;
1044
        }
1045
      }
1046
    } else {
1047
      this.range = this.element.ownerDocument.createRange();
1048
      if (this.initializeRange) this.initializeRange();
1049
      this.insertContent([this.range.createContextualFragment(this.content)]);
1050
    }
1051

    
1052
    setTimeout(function() {content.evalScripts()}, 10);
1053
  },
1054

    
1055
  contentFromAnonymousTable: function() {
1056
    var div = document.createElement('div');
1057
    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
1058
    return $A(div.childNodes[0].childNodes[0].childNodes);
1059
  }
1060
}
1061

    
1062
var Insertion = new Object();
1063

    
1064
Insertion.Before = Class.create();
1065
Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
1066
  initializeRange: function() {
1067
    this.range.setStartBefore(this.element);
1068
  },
1069

    
1070
  insertContent: function(fragments) {
1071
    fragments.each((function(fragment) {
1072
      this.element.parentNode.insertBefore(fragment, this.element);
1073
    }).bind(this));
1074
  }
1075
});
1076

    
1077
Insertion.Top = Class.create();
1078
Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
1079
  initializeRange: function() {
1080
    this.range.selectNodeContents(this.element);
1081
    this.range.collapse(true);
1082
  },
1083

    
1084
  insertContent: function(fragments) {
1085
    fragments.reverse(false).each((function(fragment) {
1086
      this.element.insertBefore(fragment, this.element.firstChild);
1087
    }).bind(this));
1088
  }
1089
});
1090

    
1091
Insertion.Bottom = Class.create();
1092
Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
1093
  initializeRange: function() {
1094
    this.range.selectNodeContents(this.element);
1095
    this.range.collapse(this.element);
1096
  },
1097

    
1098
  insertContent: function(fragments) {
1099
    fragments.each((function(fragment) {
1100
      this.element.appendChild(fragment);
1101
    }).bind(this));
1102
  }
1103
});
1104

    
1105
Insertion.After = Class.create();
1106
Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
1107
  initializeRange: function() {
1108
    this.range.setStartAfter(this.element);
1109
  },
1110

    
1111
  insertContent: function(fragments) {
1112
    fragments.each((function(fragment) {
1113
      this.element.parentNode.insertBefore(fragment,
1114
        this.element.nextSibling);
1115
    }).bind(this));
1116
  }
1117
});
1118

    
1119
/*--------------------------------------------------------------------------*/
1120

    
1121
Element.ClassNames = Class.create();
1122
Element.ClassNames.prototype = {
1123
  initialize: function(element) {
1124
    this.element = $(element);
1125
  },
1126

    
1127
  _each: function(iterator) {
1128
    this.element.className.split(/\s+/).select(function(name) {
1129
      return name.length > 0;
1130
    })._each(iterator);
1131
  },
1132

    
1133
  set: function(className) {
1134
    this.element.className = className;
1135
  },
1136

    
1137
  add: function(classNameToAdd) {
1138
    if (this.include(classNameToAdd)) return;
1139
    this.set(this.toArray().concat(classNameToAdd).join(' '));
1140
  },
1141

    
1142
  remove: function(classNameToRemove) {
1143
    if (!this.include(classNameToRemove)) return;
1144
    this.set(this.select(function(className) {
1145
      return className != classNameToRemove;
1146
    }).join(' '));
1147
  },
1148

    
1149
  toString: function() {
1150
    return this.toArray().join(' ');
1151
  }
1152
}
1153

    
1154
Object.extend(Element.ClassNames.prototype, Enumerable);
1155
var Field = {
1156
  clear: function() {
1157
    for (var i = 0; i < arguments.length; i++)
1158
      $(arguments[i]).value = '';
1159
  },
1160

    
1161
  focus: function(element) {
1162
    $(element).focus();
1163
  },
1164

    
1165
  present: function() {
1166
    for (var i = 0; i < arguments.length; i++)
1167
      if ($(arguments[i]).value == '') return false;
1168
    return true;
1169
  },
1170

    
1171
  select: function(element) {
1172
    $(element).select();
1173
  },
1174

    
1175
  activate: function(element) {
1176
    element = $(element);
1177
    element.focus();
1178
    if (element.select)
1179
      element.select();
1180
  }
1181
}
1182

    
1183
/*--------------------------------------------------------------------------*/
1184

    
1185
var Form = {
1186
  serialize: function(form) {
1187
    var elements = Form.getElements($(form));
1188
    var queryComponents = new Array();
1189

    
1190
    for (var i = 0; i < elements.length; i++) {
1191
      var queryComponent = Form.Element.serialize(elements[i]);
1192
      if (queryComponent)
1193
        queryComponents.push(queryComponent);
1194
    }
1195

    
1196
    return queryComponents.join('&');
1197
  },
1198

    
1199
  getElements: function(form) {
1200
    form = $(form);
1201
    var elements = new Array();
1202

    
1203
    for (tagName in Form.Element.Serializers) {
1204
      var tagElements = form.getElementsByTagName(tagName);
1205
      for (var j = 0; j < tagElements.length; j++)
1206
        elements.push(tagElements[j]);
1207
    }
1208
    return elements;
1209
  },
1210

    
1211
  getInputs: function(form, typeName, name) {
1212
    form = $(form);
1213
    var inputs = form.getElementsByTagName('input');
1214

    
1215
    if (!typeName && !name)
1216
      return inputs;
1217

    
1218
    var matchingInputs = new Array();
1219
    for (var i = 0; i < inputs.length; i++) {
1220
      var input = inputs[i];
1221
      if ((typeName && input.type != typeName) ||
1222
          (name && input.name != name))
1223
        continue;
1224
      matchingInputs.push(input);
1225
    }
1226

    
1227
    return matchingInputs;
1228
  },
1229

    
1230
  disable: function(form) {
1231
    var elements = Form.getElements(form);
1232
    for (var i = 0; i < elements.length; i++) {
1233
      var element = elements[i];
1234
      element.blur();
1235
      element.disabled = 'true';
1236
    }
1237
  },
1238

    
1239
  enable: function(form) {
1240
    var elements = Form.getElements(form);
1241
    for (var i = 0; i < elements.length; i++) {
1242
      var element = elements[i];
1243
      element.disabled = '';
1244
    }
1245
  },
1246

    
1247
  findFirstElement: function(form) {
1248
    return Form.getElements(form).find(function(element) {
1249
      return element.type != 'hidden' && !element.disabled &&
1250
        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
1251
    });
1252
  },
1253

    
1254
  focusFirstElement: function(form) {
1255
    Field.activate(Form.findFirstElement(form));
1256
  },
1257

    
1258
  reset: function(form) {
1259
    $(form).reset();
1260
  }
1261
}
1262

    
1263
Form.Element = {
1264
  serialize: function(element) {
1265
    element = $(element);
1266
    var method = element.tagName.toLowerCase();
1267
    var parameter = Form.Element.Serializers[method](element);
1268

    
1269
    if (parameter) {
1270
      var key = encodeURIComponent(parameter[0]);
1271
      if (key.length == 0) return;
1272

    
1273
      if (parameter[1].constructor != Array)
1274
        parameter[1] = [parameter[1]];
1275

    
1276
      return parameter[1].map(function(value) {
1277
        return key + '=' + encodeURIComponent(value);
1278
      }).join('&');
1279
    }
1280
  },
1281

    
1282
  getValue: function(element) {
1283
    element = $(element);
1284
    var method = element.tagName.toLowerCase();
1285
    var parameter = Form.Element.Serializers[method](element);
1286

    
1287
    if (parameter)
1288
      return parameter[1];
1289
  }
1290
}
1291

    
1292
Form.Element.Serializers = {
1293
  input: function(element) {
1294
    switch (element.type.toLowerCase()) {
1295
      case 'submit':
1296
      case 'hidden':
1297
      case 'password':
1298
      case 'text':
1299
        return Form.Element.Serializers.textarea(element);
1300
      case 'checkbox':
1301
      case 'radio':
1302
        return Form.Element.Serializers.inputSelector(element);
1303
    }
1304
    return false;
1305
  },
1306

    
1307
  inputSelector: function(element) {
1308
    if (element.checked)
1309
      return [element.name, element.value];
1310
  },
1311

    
1312
  textarea: function(element) {
1313
    return [element.name, element.value];
1314
  },
1315

    
1316
  select: function(element) {
1317
    return Form.Element.Serializers[element.type == 'select-one' ?
1318
      'selectOne' : 'selectMany'](element);
1319
  },
1320

    
1321
  selectOne: function(element) {
1322
    var value = '', opt, index = element.selectedIndex;
1323
    if (index >= 0) {
1324
      opt = element.options[index];
1325
      value = opt.value;
1326
      if (!value && !('value' in opt))
1327
        value = opt.text;
1328
    }
1329
    return [element.name, value];
1330
  },
1331

    
1332
  selectMany: function(element) {
1333
    var value = new Array();
1334
    for (var i = 0; i < element.length; i++) {
1335
      var opt = element.options[i];
1336
      if (opt.selected) {
1337
        var optValue = opt.value;
1338
        if (!optValue && !('value' in opt))
1339
          optValue = opt.text;
1340
        value.push(optValue);
1341
      }
1342
    }
1343
    return [element.name, value];
1344
  }
1345
}
1346

    
1347
/*--------------------------------------------------------------------------*/
1348

    
1349
var $F = Form.Element.getValue;
1350

    
1351
/*--------------------------------------------------------------------------*/
1352

    
1353
Abstract.TimedObserver = function() {}
1354
Abstract.TimedObserver.prototype = {
1355
  initialize: function(element, frequency, callback) {
1356
    this.frequency = frequency;
1357
    this.element   = $(element);
1358
    this.callback  = callback;
1359

    
1360
    this.lastValue = this.getValue();
1361
    this.registerCallback();
1362
  },
1363

    
1364
  registerCallback: function() {
1365
    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
1366
  },
1367

    
1368
  onTimerEvent: function() {
1369
    var value = this.getValue();
1370
    if (this.lastValue != value) {
1371
      this.callback(this.element, value);
1372
      this.lastValue = value;
1373
    }
1374
  }
1375
}
1376

    
1377
Form.Element.Observer = Class.create();
1378
Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
1379
  getValue: function() {
1380
    return Form.Element.getValue(this.element);
1381
  }
1382
});
1383

    
1384
Form.Observer = Class.create();
1385
Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
1386
  getValue: function() {
1387
    return Form.serialize(this.element);
1388
  }
1389
});
1390

    
1391
/*--------------------------------------------------------------------------*/
1392

    
1393
Abstract.EventObserver = function() {}
1394
Abstract.EventObserver.prototype = {
1395
  initialize: function(element, callback) {
1396
    this.element  = $(element);
1397
    this.callback = callback;
1398

    
1399
    this.lastValue = this.getValue();
1400
    if (this.element.tagName.toLowerCase() == 'form')
1401
      this.registerFormCallbacks();
1402
    else
1403
      this.registerCallback(this.element);
1404
  },
1405

    
1406
  onElementEvent: function() {
1407
    var value = this.getValue();
1408
    if (this.lastValue != value) {
1409
      this.callback(this.element, value);
1410
      this.lastValue = value;
1411
    }
1412
  },
1413

    
1414
  registerFormCallbacks: function() {
1415
    var elements = Form.getElements(this.element);
1416
    for (var i = 0; i < elements.length; i++)
1417
      this.registerCallback(elements[i]);
1418
  },
1419

    
1420
  registerCallback: function(element) {
1421
    if (element.type) {
1422
      switch (element.type.toLowerCase()) {
1423
        case 'checkbox':
1424
        case 'radio':
1425
          Event.observe(element, 'click', this.onElementEvent.bind(this));
1426
          break;
1427
        case 'password':
1428
        case 'text':
1429
        case 'textarea':
1430
        case 'select-one':
1431
        case 'select-multiple':
1432
          Event.observe(element, 'change', this.onElementEvent.bind(this));
1433
          break;
1434
      }
1435
    }
1436
  }
1437
}
1438

    
1439
Form.Element.EventObserver = Class.create();
1440
Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
1441
  getValue: function() {
1442
    return Form.Element.getValue(this.element);
1443
  }
1444
});
1445

    
1446
Form.EventObserver = Class.create();
1447
Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
1448
  getValue: function() {
1449
    return Form.serialize(this.element);
1450
  }
1451
});
1452
if (!window.Event) {
1453
  var Event = new Object();
1454
}
1455

    
1456
Object.extend(Event, {
1457
  KEY_BACKSPACE: 8,
1458
  KEY_TAB:       9,
1459
  KEY_RETURN:   13,
1460
  KEY_ESC:      27,
1461
  KEY_LEFT:     37,
1462
  KEY_UP:       38,
1463
  KEY_RIGHT:    39,
1464
  KEY_DOWN:     40,
1465
  KEY_DELETE:   46,
1466

    
1467
  element: function(event) {
1468
    return event.target || event.srcElement;
1469
  },
1470

    
1471
  isLeftClick: function(event) {
1472
    return (((event.which) && (event.which == 1)) ||
1473
            ((event.button) && (event.button == 1)));
1474
  },
1475

    
1476
  pointerX: function(event) {
1477
    return event.pageX || (event.clientX +
1478
      (document.documentElement.scrollLeft || document.body.scrollLeft));
1479
  },
1480

    
1481
  pointerY: function(event) {
1482
    return event.pageY || (event.clientY +
1483
      (document.documentElement.scrollTop || document.body.scrollTop));
1484
  },
1485

    
1486
  stop: function(event) {
1487
    if (event.preventDefault) {
1488
      event.preventDefault();
1489
      event.stopPropagation();
1490
    } else {
1491
      event.returnValue = false;
1492
      event.cancelBubble = true;
1493
    }
1494
  },
1495

    
1496
  // find the first node with the given tagName, starting from the
1497
  // node the event was triggered on; traverses the DOM upwards
1498
  findElement: function(event, tagName) {
1499
    var element = Event.element(event);
1500
    while (element.parentNode && (!element.tagName ||
1501
        (element.tagName.toUpperCase() != tagName.toUpperCase())))
1502
      element = element.parentNode;
1503
    return element;
1504
  },
1505

    
1506
  observers: false,
1507

    
1508
  _observeAndCache: function(element, name, observer, useCapture) {
1509
    if (!this.observers) this.observers = [];
1510
    if (element.addEventListener) {
1511
      this.observers.push([element, name, observer, useCapture]);
1512
      element.addEventListener(name, observer, useCapture);
1513
    } else if (element.attachEvent) {
1514
      this.observers.push([element, name, observer, useCapture]);
1515
      element.attachEvent('on' + name, observer);
1516
    }
1517
  },
1518

    
1519
  unloadCache: function() {
1520
    if (!Event.observers) return;
1521
    for (var i = 0; i < Event.observers.length; i++) {
1522
      Event.stopObserving.apply(this, Event.observers[i]);
1523
      Event.observers[i][0] = null;
1524
    }
1525
    Event.observers = false;
1526
  },
1527

    
1528
  observe: function(element, name, observer, useCapture) {
1529
    var element = $(element);
1530
    useCapture = useCapture || false;
1531

    
1532
    if (name == 'keypress' &&
1533
        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
1534
        || element.attachEvent))
1535
      name = 'keydown';
1536

    
1537
    this._observeAndCache(element, name, observer, useCapture);
1538
  },
1539

    
1540
  stopObserving: function(element, name, observer, useCapture) {
1541
    var element = $(element);
1542
    useCapture = useCapture || false;
1543

    
1544
    if (name == 'keypress' &&
1545
        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
1546
        || element.detachEvent))
1547
      name = 'keydown';
1548

    
1549
    if (element.removeEventListener) {
1550
      element.removeEventListener(name, observer, useCapture);
1551
    } else if (element.detachEvent) {
1552
      element.detachEvent('on' + name, observer);
1553
    }
1554
  }
1555
});
1556

    
1557
/* prevent memory leaks in IE */
1558
Event.observe(window, 'unload', Event.unloadCache, false);
1559
var Position = {
1560
  // set to true if needed, warning: firefox performance problems
1561
  // NOT neeeded for page scrolling, only if draggable contained in
1562
  // scrollable elements
1563
  includeScrollOffsets: false,
1564

    
1565
  // must be called before calling withinIncludingScrolloffset, every time the
1566
  // page is scrolled
1567
  prepare: function() {
1568
    this.deltaX =  window.pageXOffset
1569
                || document.documentElement.scrollLeft
1570
                || document.body.scrollLeft
1571
                || 0;
1572
    this.deltaY =  window.pageYOffset
1573
                || document.documentElement.scrollTop
1574
                || document.body.scrollTop
1575
                || 0;
1576
  },
1577

    
1578
  realOffset: function(element) {
1579
    var valueT = 0, valueL = 0;
1580
    do {
1581
      valueT += element.scrollTop  || 0;
1582
      valueL += element.scrollLeft || 0;
1583
      element = element.parentNode;
1584
    } while (element);
1585
    return [valueL, valueT];
1586
  },
1587

    
1588
  cumulativeOffset: function(element) {
1589
    var valueT = 0, valueL = 0;
1590
    do {
1591
      valueT += element.offsetTop  || 0;
1592
      valueL += element.offsetLeft || 0;
1593
      element = element.offsetParent;
1594
    } while (element);
1595
    return [valueL, valueT];
1596
  },
1597

    
1598
  positionedOffset: function(element) {
1599
    var valueT = 0, valueL = 0;
1600
    do {
1601
      valueT += element.offsetTop  || 0;
1602
      valueL += element.offsetLeft || 0;
1603
      element = element.offsetParent;
1604
      if (element) {
1605
        p = Element.getStyle(element, 'position');
1606
        if (p == 'relative' || p == 'absolute') break;
1607
      }
1608
    } while (element);
1609
    return [valueL, valueT];
1610
  },
1611

    
1612
  offsetParent: function(element) {
1613
    if (element.offsetParent) return element.offsetParent;
1614
    if (element == document.body) return element;
1615

    
1616
    while ((element = element.parentNode) && element != document.body)
1617
      if (Element.getStyle(element, 'position') != 'static')
1618
        return element;
1619

    
1620
    return document.body;
1621
  },
1622

    
1623
  // caches x/y coordinate pair to use with overlap
1624
  within: function(element, x, y) {
1625
    if (this.includeScrollOffsets)
1626
      return this.withinIncludingScrolloffsets(element, x, y);
1627
    this.xcomp = x;
1628
    this.ycomp = y;
1629
    this.offset = this.cumulativeOffset(element);
1630

    
1631
    return (y >= this.offset[1] &&
1632
            y <  this.offset[1] + element.offsetHeight &&
1633
            x >= this.offset[0] &&
1634
            x <  this.offset[0] + element.offsetWidth);
1635
  },
1636

    
1637
  withinIncludingScrolloffsets: function(element, x, y) {
1638
    var offsetcache = this.realOffset(element);
1639

    
1640
    this.xcomp = x + offsetcache[0] - this.deltaX;
1641
    this.ycomp = y + offsetcache[1] - this.deltaY;
1642
    this.offset = this.cumulativeOffset(element);
1643

    
1644
    return (this.ycomp >= this.offset[1] &&
1645
            this.ycomp <  this.offset[1] + element.offsetHeight &&
1646
            this.xcomp >= this.offset[0] &&
1647
            this.xcomp <  this.offset[0] + element.offsetWidth);
1648
  },
1649

    
1650
  // within must be called directly before
1651
  overlap: function(mode, element) {
1652
    if (!mode) return 0;
1653
    if (mode == 'vertical')
1654
      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
1655
        element.offsetHeight;
1656
    if (mode == 'horizontal')
1657
      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
1658
        element.offsetWidth;
1659
  },
1660

    
1661
  clone: function(source, target) {
1662
    source = $(source);
1663
    target = $(target);
1664
    target.style.position = 'absolute';
1665
    var offsets = this.cumulativeOffset(source);
1666
    target.style.top    = offsets[1] + 'px';
1667
    target.style.left   = offsets[0] + 'px';
1668
    target.style.width  = source.offsetWidth + 'px';
1669
    target.style.height = source.offsetHeight + 'px';
1670
  },
1671

    
1672
  page: function(forElement) {
1673
    var valueT = 0, valueL = 0;
1674

    
1675
    var element = forElement;
1676
    do {
1677
      valueT += element.offsetTop  || 0;
1678
      valueL += element.offsetLeft || 0;
1679

    
1680
      // Safari fix
1681
      if (element.offsetParent==document.body)
1682
        if (Element.getStyle(element,'position')=='absolute') break;
1683

    
1684
    } while (element = element.offsetParent);
1685

    
1686
    element = forElement;
1687
    do {
1688
      valueT -= element.scrollTop  || 0;
1689
      valueL -= element.scrollLeft || 0;
1690
    } while (element = element.parentNode);
1691

    
1692
    return [valueL, valueT];
1693
  },
1694

    
1695
  clone: function(source, target) {
1696
    var options = Object.extend({
1697
      setLeft:    true,
1698
      setTop:     true,
1699
      setWidth:   true,
1700
      setHeight:  true,
1701
      offsetTop:  0,
1702
      offsetLeft: 0
1703
    }, arguments[2] || {})
1704

    
1705
    // find page position of source
1706
    source = $(source);
1707
    var p = Position.page(source);
1708

    
1709
    // find coordinate system to use
1710
    target = $(target);
1711
    var delta = [0, 0];
1712
    var parent = null;
1713
    // delta [0,0] will do fine with position: fixed elements,
1714
    // position:absolute needs offsetParent deltas
1715
    if (Element.getStyle(target,'position') == 'absolute') {
1716
      parent = Position.offsetParent(target);
1717
      delta = Position.page(parent);
1718
    }
1719

    
1720
    // correct by body offsets (fixes Safari)
1721
    if (parent == document.body) {
1722
      delta[0] -= document.body.offsetLeft;
1723
      delta[1] -= document.body.offsetTop;
1724
    }
1725

    
1726
    // set position
1727
    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
1728
    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
1729
    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
1730
    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
1731
  },
1732

    
1733
  absolutize: function(element) {
1734
    element = $(element);
1735
    if (element.style.position == 'absolute') return;
1736
    Position.prepare();
1737

    
1738
    var offsets = Position.positionedOffset(element);
1739
    var top     = offsets[1];
1740
    var left    = offsets[0];
1741
    var width   = element.clientWidth;
1742
    var height  = element.clientHeight;
1743

    
1744
    element._originalLeft   = left - parseFloat(element.style.left  || 0);
1745
    element._originalTop    = top  - parseFloat(element.style.top || 0);
1746
    element._originalWidth  = element.style.width;
1747
    element._originalHeight = element.style.height;
1748

    
1749
    element.style.position = 'absolute';
1750
    element.style.top    = top + 'px';;
1751
    element.style.left   = left + 'px';;
1752
    element.style.width  = width + 'px';;
1753
    element.style.height = height + 'px';;
1754
  },
1755

    
1756
  relativize: function(element) {
1757
    element = $(element);
1758
    if (element.style.position == 'relative') return;
1759
    Position.prepare();
1760

    
1761
    element.style.position = 'relative';
1762
    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
1763
    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
1764

    
1765
    element.style.top    = top + 'px';
1766
    element.style.left   = left + 'px';
1767
    element.style.height = element._originalHeight;
1768
    element.style.width  = element._originalWidth;
1769
  }
1770
}
1771

    
1772
// Safari returns margins on body which is incorrect if the child is absolutely
1773
// positioned.  For performance reasons, redefine Position.cumulativeOffset for
1774
// KHTML/WebKit only.
1775
if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
1776
  Position.cumulativeOffset = function(element) {
1777
    var valueT = 0, valueL = 0;
1778
    do {
1779
      valueT += element.offsetTop  || 0;
1780
      valueL += element.offsetLeft || 0;
1781
      if (element.offsetParent == document.body)
1782
        if (Element.getStyle(element, 'position') == 'absolute') break;
1783

    
1784
      element = element.offsetParent;
1785
    } while (element);
1786

    
1787
    return [valueL, valueT];
1788
  }
1789
}
(18-18/33)