Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(169)

Side by Side Diff: chrome/test/data/dromaeo/tests/v8-raytrace.html

Issue 269054: Importing dromaeo performance tests to src/chrome/test/data.... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: Created 11 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 <html>
2 <head>
3 <script src="../htmlrunner.js"></script>
4 <script>
5 // The ray tracer code in this file is written by Adam Burmister. It
6 // is available in its original form from:
7 //
8 // http://labs.flog.nz.co/raytracer/
9 //
10 // It has been modified slightly by Google to work as a standalone
11 // benchmark, but the all the computational code remains
12 // untouched. This file also contains a copy of the Prototype
13 // JavaScript framework which is used by the ray tracer.
14
15
16 var checkNumber;
17
18 // Create dummy objects if we're not running in a browser.
19 if (typeof document == 'undefined') {
20 document = { };
21 window = { opera: null };
22 navigator = { userAgent: null, appVersion: "" };
23 }
24
25
26 // ------------------------------------------------------------------------
27 // ------------------------------------------------------------------------
28
29
30 /* Prototype JavaScript framework, version 1.5.0
31 * (c) 2005-2007 Sam Stephenson
32 *
33 * Prototype is freely distributable under the terms of an MIT-style license.
34 * For details, see the Prototype web site: http://prototype.conio.net/
35 *
36 /*--------------------------------------------------------------------------*/
37
38 //--------------------
39 var Prototype = {
40 Version: '1.5.0',
41 BrowserFeatures: {
42 XPath: !!document.evaluate
43 },
44
45 ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
46 emptyFunction: function() {},
47 K: function(x) { return x }
48 }
49
50 var Class = {
51 create: function() {
52 return function() {
53 this.initialize.apply(this, arguments);
54 }
55 }
56 }
57
58 var Abstract = new Object();
59
60 Object.extend = function(destination, source) {
61 for (var property in source) {
62 destination[property] = source[property];
63 }
64 return destination;
65 }
66
67 Object.extend(Object, {
68 inspect: function(object) {
69 try {
70 if (object === undefined) return 'undefined';
71 if (object === null) return 'null';
72 return object.inspect ? object.inspect() : object.toString();
73 } catch (e) {
74 if (e instanceof RangeError) return '...';
75 throw e;
76 }
77 },
78
79 keys: function(object) {
80 var keys = [];
81 for (var property in object)
82 keys.push(property);
83 return keys;
84 },
85
86 values: function(object) {
87 var values = [];
88 for (var property in object)
89 values.push(object[property]);
90 return values;
91 },
92
93 clone: function(object) {
94 return Object.extend({}, object);
95 }
96 });
97
98 Function.prototype.bind = function() {
99 var __method = this, args = $A(arguments), object = args.shift();
100 return function() {
101 return __method.apply(object, args.concat($A(arguments)));
102 }
103 }
104
105 Function.prototype.bindAsEventListener = function(object) {
106 var __method = this, args = $A(arguments), object = args.shift();
107 return function(event) {
108 return __method.apply(object, [( event || window.event)].concat(args).concat ($A(arguments)));
109 }
110 }
111
112 Object.extend(Number.prototype, {
113 toColorPart: function() {
114 var digits = this.toString(16);
115 if (this < 16) return '0' + digits;
116 return digits;
117 },
118
119 succ: function() {
120 return this + 1;
121 },
122
123 times: function(iterator) {
124 $R(0, this, true).each(iterator);
125 return this;
126 }
127 });
128
129 var Try = {
130 these: function() {
131 var returnValue;
132
133 for (var i = 0, length = arguments.length; i < length; i++) {
134 var lambda = arguments[i];
135 try {
136 returnValue = lambda();
137 break;
138 } catch (e) {}
139 }
140
141 return returnValue;
142 }
143 }
144
145 /*--------------------------------------------------------------------------*/
146
147 var PeriodicalExecuter = Class.create();
148 PeriodicalExecuter.prototype = {
149 initialize: function(callback, frequency) {
150 this.callback = callback;
151 this.frequency = frequency;
152 this.currentlyExecuting = false;
153
154 this.registerCallback();
155 },
156
157 registerCallback: function() {
158 this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000 );
159 },
160
161 stop: function() {
162 if (!this.timer) return;
163 clearInterval(this.timer);
164 this.timer = null;
165 },
166
167 onTimerEvent: function() {
168 if (!this.currentlyExecuting) {
169 try {
170 this.currentlyExecuting = true;
171 this.callback(this);
172 } finally {
173 this.currentlyExecuting = false;
174 }
175 }
176 }
177 }
178 String.interpret = function(value){
179 return value == null ? '' : String(value);
180 }
181
182 Object.extend(String.prototype, {
183 gsub: function(pattern, replacement) {
184 var result = '', source = this, match;
185 replacement = arguments.callee.prepareReplacement(replacement);
186
187 while (source.length > 0) {
188 if (match = source.match(pattern)) {
189 result += source.slice(0, match.index);
190 result += String.interpret(replacement(match));
191 source = source.slice(match.index + match[0].length);
192 } else {
193 result += source, source = '';
194 }
195 }
196 return result;
197 },
198
199 sub: function(pattern, replacement, count) {
200 replacement = this.gsub.prepareReplacement(replacement);
201 count = count === undefined ? 1 : count;
202
203 return this.gsub(pattern, function(match) {
204 if (--count < 0) return match[0];
205 return replacement(match);
206 });
207 },
208
209 scan: function(pattern, iterator) {
210 this.gsub(pattern, iterator);
211 return this;
212 },
213
214 truncate: function(length, truncation) {
215 length = length || 30;
216 truncation = truncation === undefined ? '...' : truncation;
217 return this.length > length ?
218 this.slice(0, length - truncation.length) + truncation : this;
219 },
220
221 strip: function() {
222 return this.replace(/^\s+/, '').replace(/\s+$/, '');
223 },
224
225 stripTags: function() {
226 return this.replace(/<\/?[^>]+>/gi, '');
227 },
228
229 stripScripts: function() {
230 return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
231 },
232
233 extractScripts: function() {
234 var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
235 var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
236 return (this.match(matchAll) || []).map(function(scriptTag) {
237 return (scriptTag.match(matchOne) || ['', ''])[1];
238 });
239 },
240
241 evalScripts: function() {
242 return this.extractScripts().map(function(script) { return eval(script) });
243 },
244
245 escapeHTML: function() {
246 var div = document.createElement('div');
247 var text = document.createTextNode(this);
248 div.appendChild(text);
249 return div.innerHTML;
250 },
251
252 unescapeHTML: function() {
253 var div = document.createElement('div');
254 div.innerHTML = this.stripTags();
255 return div.childNodes[0] ? (div.childNodes.length > 1 ?
256 $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeVal ue }) :
257 div.childNodes[0].nodeValue) : '';
258 },
259
260 toQueryParams: function(separator) {
261 var match = this.strip().match(/([^?#]*)(#.*)?$/);
262 if (!match) return {};
263
264 return match[1].split(separator || '&').inject({}, function(hash, pair) {
265 if ((pair = pair.split('='))[0]) {
266 var name = decodeURIComponent(pair[0]);
267 var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
268
269 if (hash[name] !== undefined) {
270 if (hash[name].constructor != Array)
271 hash[name] = [hash[name]];
272 if (value) hash[name].push(value);
273 }
274 else hash[name] = value;
275 }
276 return hash;
277 });
278 },
279
280 toArray: function() {
281 return this.split('');
282 },
283
284 succ: function() {
285 return this.slice(0, this.length - 1) +
286 String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
287 },
288
289 camelize: function() {
290 var parts = this.split('-'), len = parts.length;
291 if (len == 1) return parts[0];
292
293 var camelized = this.charAt(0) == '-'
294 ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
295 : parts[0];
296
297 for (var i = 1; i < len; i++)
298 camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
299
300 return camelized;
301 },
302
303 capitalize: function(){
304 return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
305 },
306
307 underscore: function() {
308 return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/( [a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
309 },
310
311 dasherize: function() {
312 return this.gsub(/_/,'-');
313 },
314
315 inspect: function(useDoubleQuotes) {
316 var escapedString = this.replace(/\\/g, '\\\\');
317 if (useDoubleQuotes)
318 return '"' + escapedString.replace(/"/g, '\\"') + '"';
319 else
320 return "'" + escapedString.replace(/'/g, '\\\'') + "'";
321 }
322 });
323
324 String.prototype.gsub.prepareReplacement = function(replacement) {
325 if (typeof replacement == 'function') return replacement;
326 var template = new Template(replacement);
327 return function(match) { return template.evaluate(match) };
328 }
329
330 String.prototype.parseQuery = String.prototype.toQueryParams;
331
332 var Template = Class.create();
333 Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
334 Template.prototype = {
335 initialize: function(template, pattern) {
336 this.template = template.toString();
337 this.pattern = pattern || Template.Pattern;
338 },
339
340 evaluate: function(object) {
341 return this.template.gsub(this.pattern, function(match) {
342 var before = match[1];
343 if (before == '\\') return match[2];
344 return before + String.interpret(object[match[3]]);
345 });
346 }
347 }
348
349 var $break = new Object();
350 var $continue = new Object();
351
352 var Enumerable = {
353 each: function(iterator) {
354 var index = 0;
355 try {
356 this._each(function(value) {
357 try {
358 iterator(value, index++);
359 } catch (e) {
360 if (e != $continue) throw e;
361 }
362 });
363 } catch (e) {
364 if (e != $break) throw e;
365 }
366 return this;
367 },
368
369 eachSlice: function(number, iterator) {
370 var index = -number, slices = [], array = this.toArray();
371 while ((index += number) < array.length)
372 slices.push(array.slice(index, index+number));
373 return slices.map(iterator);
374 },
375
376 all: function(iterator) {
377 var result = true;
378 this.each(function(value, index) {
379 result = result && !!(iterator || Prototype.K)(value, index);
380 if (!result) throw $break;
381 });
382 return result;
383 },
384
385 any: function(iterator) {
386 var result = false;
387 this.each(function(value, index) {
388 if (result = !!(iterator || Prototype.K)(value, index))
389 throw $break;
390 });
391 return result;
392 },
393
394 collect: function(iterator) {
395 var results = [];
396 this.each(function(value, index) {
397 results.push((iterator || Prototype.K)(value, index));
398 });
399 return results;
400 },
401
402 detect: function(iterator) {
403 var result;
404 this.each(function(value, index) {
405 if (iterator(value, index)) {
406 result = value;
407 throw $break;
408 }
409 });
410 return result;
411 },
412
413 findAll: function(iterator) {
414 var results = [];
415 this.each(function(value, index) {
416 if (iterator(value, index))
417 results.push(value);
418 });
419 return results;
420 },
421
422 grep: function(pattern, iterator) {
423 var results = [];
424 this.each(function(value, index) {
425 var stringValue = value.toString();
426 if (stringValue.match(pattern))
427 results.push((iterator || Prototype.K)(value, index));
428 })
429 return results;
430 },
431
432 include: function(object) {
433 var found = false;
434 this.each(function(value) {
435 if (value == object) {
436 found = true;
437 throw $break;
438 }
439 });
440 return found;
441 },
442
443 inGroupsOf: function(number, fillWith) {
444 fillWith = fillWith === undefined ? null : fillWith;
445 return this.eachSlice(number, function(slice) {
446 while(slice.length < number) slice.push(fillWith);
447 return slice;
448 });
449 },
450
451 inject: function(memo, iterator) {
452 this.each(function(value, index) {
453 memo = iterator(memo, value, index);
454 });
455 return memo;
456 },
457
458 invoke: function(method) {
459 var args = $A(arguments).slice(1);
460 return this.map(function(value) {
461 return value[method].apply(value, args);
462 });
463 },
464
465 max: function(iterator) {
466 var result;
467 this.each(function(value, index) {
468 value = (iterator || Prototype.K)(value, index);
469 if (result == undefined || value >= result)
470 result = value;
471 });
472 return result;
473 },
474
475 min: function(iterator) {
476 var result;
477 this.each(function(value, index) {
478 value = (iterator || Prototype.K)(value, index);
479 if (result == undefined || value < result)
480 result = value;
481 });
482 return result;
483 },
484
485 partition: function(iterator) {
486 var trues = [], falses = [];
487 this.each(function(value, index) {
488 ((iterator || Prototype.K)(value, index) ?
489 trues : falses).push(value);
490 });
491 return [trues, falses];
492 },
493
494 pluck: function(property) {
495 var results = [];
496 this.each(function(value, index) {
497 results.push(value[property]);
498 });
499 return results;
500 },
501
502 reject: function(iterator) {
503 var results = [];
504 this.each(function(value, index) {
505 if (!iterator(value, index))
506 results.push(value);
507 });
508 return results;
509 },
510
511 sortBy: function(iterator) {
512 return this.map(function(value, index) {
513 return {value: value, criteria: iterator(value, index)};
514 }).sort(function(left, right) {
515 var a = left.criteria, b = right.criteria;
516 return a < b ? -1 : a > b ? 1 : 0;
517 }).pluck('value');
518 },
519
520 toArray: function() {
521 return this.map();
522 },
523
524 zip: function() {
525 var iterator = Prototype.K, args = $A(arguments);
526 if (typeof args.last() == 'function')
527 iterator = args.pop();
528
529 var collections = [this].concat(args).map($A);
530 return this.map(function(value, index) {
531 return iterator(collections.pluck(index));
532 });
533 },
534
535 size: function() {
536 return this.toArray().length;
537 },
538
539 inspect: function() {
540 return '#<Enumerable:' + this.toArray().inspect() + '>';
541 }
542 }
543
544 Object.extend(Enumerable, {
545 map: Enumerable.collect,
546 find: Enumerable.detect,
547 select: Enumerable.findAll,
548 member: Enumerable.include,
549 entries: Enumerable.toArray
550 });
551 var $A = Array.from = function(iterable) {
552 if (!iterable) return [];
553 if (iterable.toArray) {
554 return iterable.toArray();
555 } else {
556 var results = [];
557 for (var i = 0, length = iterable.length; i < length; i++)
558 results.push(iterable[i]);
559 return results;
560 }
561 }
562
563 Object.extend(Array.prototype, Enumerable);
564
565 if (!Array.prototype._reverse)
566 Array.prototype._reverse = Array.prototype.reverse;
567
568 Object.extend(Array.prototype, {
569 _each: function(iterator) {
570 for (var i = 0, length = this.length; i < length; i++)
571 iterator(this[i]);
572 },
573
574 clear: function() {
575 this.length = 0;
576 return this;
577 },
578
579 first: function() {
580 return this[0];
581 },
582
583 last: function() {
584 return this[this.length - 1];
585 },
586
587 compact: function() {
588 return this.select(function(value) {
589 return value != null;
590 });
591 },
592
593 flatten: function() {
594 return this.inject([], function(array, value) {
595 return array.concat(value && value.constructor == Array ?
596 value.flatten() : [value]);
597 });
598 },
599
600 without: function() {
601 var values = $A(arguments);
602 return this.select(function(value) {
603 return !values.include(value);
604 });
605 },
606
607 indexOf: function(object) {
608 for (var i = 0, length = this.length; i < length; i++)
609 if (this[i] == object) return i;
610 return -1;
611 },
612
613 reverse: function(inline) {
614 return (inline !== false ? this : this.toArray())._reverse();
615 },
616
617 reduce: function() {
618 return this.length > 1 ? this : this[0];
619 },
620
621 uniq: function() {
622 return this.inject([], function(array, value) {
623 return array.include(value) ? array : array.concat([value]);
624 });
625 },
626
627 clone: function() {
628 return [].concat(this);
629 },
630
631 size: function() {
632 return this.length;
633 },
634
635 inspect: function() {
636 return '[' + this.map(Object.inspect).join(', ') + ']';
637 }
638 });
639
640 Array.prototype.toArray = Array.prototype.clone;
641
642 function $w(string){
643 string = string.strip();
644 return string ? string.split(/\s+/) : [];
645 }
646
647 if(window.opera){
648 Array.prototype.concat = function(){
649 var array = [];
650 for(var i = 0, length = this.length; i < length; i++) array.push(this[i]);
651 for(var i = 0, length = arguments.length; i < length; i++) {
652 if(arguments[i].constructor == Array) {
653 for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
654 array.push(arguments[i][j]);
655 } else {
656 array.push(arguments[i]);
657 }
658 }
659 return array;
660 }
661 }
662 var Hash = function(obj) {
663 Object.extend(this, obj || {});
664 };
665
666 Object.extend(Hash, {
667 toQueryString: function(obj) {
668 var parts = [];
669
670 this.prototype._each.call(obj, function(pair) {
671 if (!pair.key) return;
672
673 if (pair.value && pair.value.constructor == Array) {
674 var values = pair.value.compact();
675 if (values.length < 2) pair.value = values.reduce();
676 else {
677 key = encodeURIComponent(pair.key);
678 values.each(function(value) {
679 value = value != undefined ? encodeURIComponent(value) : '';
680 parts.push(key + '=' + encodeURIComponent(value));
681 });
682 return;
683 }
684 }
685 if (pair.value == undefined) pair[1] = '';
686 parts.push(pair.map(encodeURIComponent).join('='));
687 });
688
689 return parts.join('&');
690 }
691 });
692
693 Object.extend(Hash.prototype, Enumerable);
694 Object.extend(Hash.prototype, {
695 _each: function(iterator) {
696 for (var key in this) {
697 var value = this[key];
698 if (value && value == Hash.prototype[key]) continue;
699
700 var pair = [key, value];
701 pair.key = key;
702 pair.value = value;
703 iterator(pair);
704 }
705 },
706
707 keys: function() {
708 return this.pluck('key');
709 },
710
711 values: function() {
712 return this.pluck('value');
713 },
714
715 merge: function(hash) {
716 return $H(hash).inject(this, function(mergedHash, pair) {
717 mergedHash[pair.key] = pair.value;
718 return mergedHash;
719 });
720 },
721
722 remove: function() {
723 var result;
724 for(var i = 0, length = arguments.length; i < length; i++) {
725 var value = this[arguments[i]];
726 if (value !== undefined){
727 if (result === undefined) result = value;
728 else {
729 if (result.constructor != Array) result = [result];
730 result.push(value)
731 }
732 }
733 delete this[arguments[i]];
734 }
735 return result;
736 },
737
738 toQueryString: function() {
739 return Hash.toQueryString(this);
740 },
741
742 inspect: function() {
743 return '#<Hash:{' + this.map(function(pair) {
744 return pair.map(Object.inspect).join(': ');
745 }).join(', ') + '}>';
746 }
747 });
748
749 function $H(object) {
750 if (object && object.constructor == Hash) return object;
751 return new Hash(object);
752 };
753 ObjectRange = Class.create();
754 Object.extend(ObjectRange.prototype, Enumerable);
755 Object.extend(ObjectRange.prototype, {
756 initialize: function(start, end, exclusive) {
757 this.start = start;
758 this.end = end;
759 this.exclusive = exclusive;
760 },
761
762 _each: function(iterator) {
763 var value = this.start;
764 while (this.include(value)) {
765 iterator(value);
766 value = value.succ();
767 }
768 },
769
770 include: function(value) {
771 if (value < this.start)
772 return false;
773 if (this.exclusive)
774 return value < this.end;
775 return value <= this.end;
776 }
777 });
778
779 var $R = function(start, end, exclusive) {
780 return new ObjectRange(start, end, exclusive);
781 }
782
783 var Ajax = {
784 getTransport: function() {
785 return Try.these(
786 function() {return new XMLHttpRequest()},
787 function() {return new ActiveXObject('Msxml2.XMLHTTP')},
788 function() {return new ActiveXObject('Microsoft.XMLHTTP')}
789 ) || false;
790 },
791
792 activeRequestCount: 0
793 }
794
795 Ajax.Responders = {
796 responders: [],
797
798 _each: function(iterator) {
799 this.responders._each(iterator);
800 },
801
802 register: function(responder) {
803 if (!this.include(responder))
804 this.responders.push(responder);
805 },
806
807 unregister: function(responder) {
808 this.responders = this.responders.without(responder);
809 },
810
811 dispatch: function(callback, request, transport, json) {
812 this.each(function(responder) {
813 if (typeof responder[callback] == 'function') {
814 try {
815 responder[callback].apply(responder, [request, transport, json]);
816 } catch (e) {}
817 }
818 });
819 }
820 };
821
822 Object.extend(Ajax.Responders, Enumerable);
823
824 Ajax.Responders.register({
825 onCreate: function() {
826 Ajax.activeRequestCount++;
827 },
828 onComplete: function() {
829 Ajax.activeRequestCount--;
830 }
831 });
832
833 Ajax.Base = function() {};
834 Ajax.Base.prototype = {
835 setOptions: function(options) {
836 this.options = {
837 method: 'post',
838 asynchronous: true,
839 contentType: 'application/x-www-form-urlencoded',
840 encoding: 'UTF-8',
841 parameters: ''
842 }
843 Object.extend(this.options, options || {});
844
845 this.options.method = this.options.method.toLowerCase();
846 if (typeof this.options.parameters == 'string')
847 this.options.parameters = this.options.parameters.toQueryParams();
848 }
849 }
850
851 Ajax.Request = Class.create();
852 Ajax.Request.Events =
853 ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
854
855 Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
856 _complete: false,
857
858 initialize: function(url, options) {
859 this.transport = Ajax.getTransport();
860 this.setOptions(options);
861 this.request(url);
862 },
863
864 request: function(url) {
865 this.url = url;
866 this.method = this.options.method;
867 var params = this.options.parameters;
868
869 if (!['get', 'post'].include(this.method)) {
870 // simulate other verbs over post
871 params['_method'] = this.method;
872 this.method = 'post';
873 }
874
875 params = Hash.toQueryString(params);
876 if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='
877
878 // when GET, append parameters to URL
879 if (this.method == 'get' && params)
880 this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
881
882 try {
883 Ajax.Responders.dispatch('onCreate', this, this.transport);
884
885 this.transport.open(this.method.toUpperCase(), this.url,
886 this.options.asynchronous);
887
888 if (this.options.asynchronous)
889 setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
890
891 this.transport.onreadystatechange = this.onStateChange.bind(this);
892 this.setRequestHeaders();
893
894 var body = this.method == 'post' ? (this.options.postBody || params) : nul l;
895
896 this.transport.send(body);
897
898 /* Force Firefox to handle ready state 4 for synchronous requests */
899 if (!this.options.asynchronous && this.transport.overrideMimeType)
900 this.onStateChange();
901
902 }
903 catch (e) {
904 this.dispatchException(e);
905 }
906 },
907
908 onStateChange: function() {
909 var readyState = this.transport.readyState;
910 if (readyState > 1 && !((readyState == 4) && this._complete))
911 this.respondToReadyState(this.transport.readyState);
912 },
913
914 setRequestHeaders: function() {
915 var headers = {
916 'X-Requested-With': 'XMLHttpRequest',
917 'X-Prototype-Version': Prototype.Version,
918 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
919 };
920
921 if (this.method == 'post') {
922 headers['Content-type'] = this.options.contentType +
923 (this.options.encoding ? '; charset=' + this.options.encoding : '');
924
925 /* Force "Connection: close" for older Mozilla browsers to work
926 * around a bug where XMLHttpRequest sends an incorrect
927 * Content-length header. See Mozilla Bugzilla #246651.
928 */
929 if (this.transport.overrideMimeType &&
930 (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
931 headers['Connection'] = 'close';
932 }
933
934 // user-defined headers
935 if (typeof this.options.requestHeaders == 'object') {
936 var extras = this.options.requestHeaders;
937
938 if (typeof extras.push == 'function')
939 for (var i = 0, length = extras.length; i < length; i += 2)
940 headers[extras[i]] = extras[i+1];
941 else
942 $H(extras).each(function(pair) { headers[pair.key] = pair.value });
943 }
944
945 for (var name in headers)
946 this.transport.setRequestHeader(name, headers[name]);
947 },
948
949 success: function() {
950 return !this.transport.status
951 || (this.transport.status >= 200 && this.transport.status < 300);
952 },
953
954 respondToReadyState: function(readyState) {
955 var state = Ajax.Request.Events[readyState];
956 var transport = this.transport, json = this.evalJSON();
957
958 if (state == 'Complete') {
959 try {
960 this._complete = true;
961 (this.options['on' + this.transport.status]
962 || this.options['on' + (this.success() ? 'Success' : 'Failure')]
963 || Prototype.emptyFunction)(transport, json);
964 } catch (e) {
965 this.dispatchException(e);
966 }
967
968 if ((this.getHeader('Content-type') || 'text/javascript').strip().
969 match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
970 this.evalResponse();
971 }
972
973 try {
974 (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
975 Ajax.Responders.dispatch('on' + state, this, transport, json);
976 } catch (e) {
977 this.dispatchException(e);
978 }
979
980 if (state == 'Complete') {
981 // avoid memory leak in MSIE: clean up
982 this.transport.onreadystatechange = Prototype.emptyFunction;
983 }
984 },
985
986 getHeader: function(name) {
987 try {
988 return this.transport.getResponseHeader(name);
989 } catch (e) { return null }
990 },
991
992 evalJSON: function() {
993 try {
994 var json = this.getHeader('X-JSON');
995 return json ? eval('(' + json + ')') : null;
996 } catch (e) { return null }
997 },
998
999 evalResponse: function() {
1000 try {
1001 return eval(this.transport.responseText);
1002 } catch (e) {
1003 this.dispatchException(e);
1004 }
1005 },
1006
1007 dispatchException: function(exception) {
1008 (this.options.onException || Prototype.emptyFunction)(this, exception);
1009 Ajax.Responders.dispatch('onException', this, exception);
1010 }
1011 });
1012
1013 Ajax.Updater = Class.create();
1014
1015 Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
1016 initialize: function(container, url, options) {
1017 this.container = {
1018 success: (container.success || container),
1019 failure: (container.failure || (container.success ? null : container))
1020 }
1021
1022 this.transport = Ajax.getTransport();
1023 this.setOptions(options);
1024
1025 var onComplete = this.options.onComplete || Prototype.emptyFunction;
1026 this.options.onComplete = (function(transport, param) {
1027 this.updateContent();
1028 onComplete(transport, param);
1029 }).bind(this);
1030
1031 this.request(url);
1032 },
1033
1034 updateContent: function() {
1035 var receiver = this.container[this.success() ? 'success' : 'failure'];
1036 var response = this.transport.responseText;
1037
1038 if (!this.options.evalScripts) response = response.stripScripts();
1039
1040 if (receiver = $(receiver)) {
1041 if (this.options.insertion)
1042 new this.options.insertion(receiver, response);
1043 else
1044 receiver.update(response);
1045 }
1046
1047 if (this.success()) {
1048 if (this.onComplete)
1049 setTimeout(this.onComplete.bind(this), 10);
1050 }
1051 }
1052 });
1053
1054 Ajax.PeriodicalUpdater = Class.create();
1055 Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
1056 initialize: function(container, url, options) {
1057 this.setOptions(options);
1058 this.onComplete = this.options.onComplete;
1059
1060 this.frequency = (this.options.frequency || 2);
1061 this.decay = (this.options.decay || 1);
1062
1063 this.updater = {};
1064 this.container = container;
1065 this.url = url;
1066
1067 this.start();
1068 },
1069
1070 start: function() {
1071 this.options.onComplete = this.updateComplete.bind(this);
1072 this.onTimerEvent();
1073 },
1074
1075 stop: function() {
1076 this.updater.options.onComplete = undefined;
1077 clearTimeout(this.timer);
1078 (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
1079 },
1080
1081 updateComplete: function(request) {
1082 if (this.options.decay) {
1083 this.decay = (request.responseText == this.lastText ?
1084 this.decay * this.options.decay : 1);
1085
1086 this.lastText = request.responseText;
1087 }
1088 this.timer = setTimeout(this.onTimerEvent.bind(this),
1089 this.decay * this.frequency * 1000);
1090 },
1091
1092 onTimerEvent: function() {
1093 this.updater = new Ajax.Updater(this.container, this.url, this.options);
1094 }
1095 });
1096 function $(element) {
1097 if (arguments.length > 1) {
1098 for (var i = 0, elements = [], length = arguments.length; i < length; i++)
1099 elements.push($(arguments[i]));
1100 return elements;
1101 }
1102 if (typeof element == 'string')
1103 element = document.getElementById(element);
1104 return Element.extend(element);
1105 }
1106
1107 if (Prototype.BrowserFeatures.XPath) {
1108 document._getElementsByXPath = function(expression, parentElement) {
1109 var results = [];
1110 var query = document.evaluate(expression, $(parentElement) || document,
1111 null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
1112 for (var i = 0, length = query.snapshotLength; i < length; i++)
1113 results.push(query.snapshotItem(i));
1114 return results;
1115 };
1116 }
1117
1118 document.getElementsByClassName = function(className, parentElement) {
1119 if (Prototype.BrowserFeatures.XPath) {
1120 var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
1121 return document._getElementsByXPath(q, parentElement);
1122 } else {
1123 var children = ($(parentElement) || document.body).getElementsByTagName('*') ;
1124 var elements = [], child;
1125 for (var i = 0, length = children.length; i < length; i++) {
1126 child = children[i];
1127 if (Element.hasClassName(child, className))
1128 elements.push(Element.extend(child));
1129 }
1130 return elements;
1131 }
1132 };
1133
1134 /*--------------------------------------------------------------------------*/
1135
1136 if (!window.Element)
1137 var Element = new Object();
1138
1139 Element.extend = function(element) {
1140 if (!element || _nativeExtensions || element.nodeType == 3) return element;
1141
1142 if (!element._extended && element.tagName && element != window) {
1143 var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
1144
1145 if (element.tagName == 'FORM')
1146 Object.extend(methods, Form.Methods);
1147 if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
1148 Object.extend(methods, Form.Element.Methods);
1149
1150 Object.extend(methods, Element.Methods.Simulated);
1151
1152 for (var property in methods) {
1153 var value = methods[property];
1154 if (typeof value == 'function' && !(property in element))
1155 element[property] = cache.findOrStore(value);
1156 }
1157 }
1158
1159 element._extended = true;
1160 return element;
1161 };
1162
1163 Element.extend.cache = {
1164 findOrStore: function(value) {
1165 return this[value] = this[value] || function() {
1166 return value.apply(null, [this].concat($A(arguments)));
1167 }
1168 }
1169 };
1170
1171 Element.Methods = {
1172 visible: function(element) {
1173 return $(element).style.display != 'none';
1174 },
1175
1176 toggle: function(element) {
1177 element = $(element);
1178 Element[Element.visible(element) ? 'hide' : 'show'](element);
1179 return element;
1180 },
1181
1182 hide: function(element) {
1183 $(element).style.display = 'none';
1184 return element;
1185 },
1186
1187 show: function(element) {
1188 $(element).style.display = '';
1189 return element;
1190 },
1191
1192 remove: function(element) {
1193 element = $(element);
1194 element.parentNode.removeChild(element);
1195 return element;
1196 },
1197
1198 update: function(element, html) {
1199 html = typeof html == 'undefined' ? '' : html.toString();
1200 $(element).innerHTML = html.stripScripts();
1201 setTimeout(function() {html.evalScripts()}, 10);
1202 return element;
1203 },
1204
1205 replace: function(element, html) {
1206 element = $(element);
1207 html = typeof html == 'undefined' ? '' : html.toString();
1208 if (element.outerHTML) {
1209 element.outerHTML = html.stripScripts();
1210 } else {
1211 var range = element.ownerDocument.createRange();
1212 range.selectNodeContents(element);
1213 element.parentNode.replaceChild(
1214 range.createContextualFragment(html.stripScripts()), element);
1215 }
1216 setTimeout(function() {html.evalScripts()}, 10);
1217 return element;
1218 },
1219
1220 inspect: function(element) {
1221 element = $(element);
1222 var result = '<' + element.tagName.toLowerCase();
1223 $H({'id': 'id', 'className': 'class'}).each(function(pair) {
1224 var property = pair.first(), attribute = pair.last();
1225 var value = (element[property] || '').toString();
1226 if (value) result += ' ' + attribute + '=' + value.inspect(true);
1227 });
1228 return result + '>';
1229 },
1230
1231 recursivelyCollect: function(element, property) {
1232 element = $(element);
1233 var elements = [];
1234 while (element = element[property])
1235 if (element.nodeType == 1)
1236 elements.push(Element.extend(element));
1237 return elements;
1238 },
1239
1240 ancestors: function(element) {
1241 return $(element).recursivelyCollect('parentNode');
1242 },
1243
1244 descendants: function(element) {
1245 return $A($(element).getElementsByTagName('*'));
1246 },
1247
1248 immediateDescendants: function(element) {
1249 if (!(element = $(element).firstChild)) return [];
1250 while (element && element.nodeType != 1) element = element.nextSibling;
1251 if (element) return [element].concat($(element).nextSiblings());
1252 return [];
1253 },
1254
1255 previousSiblings: function(element) {
1256 return $(element).recursivelyCollect('previousSibling');
1257 },
1258
1259 nextSiblings: function(element) {
1260 return $(element).recursivelyCollect('nextSibling');
1261 },
1262
1263 siblings: function(element) {
1264 element = $(element);
1265 return element.previousSiblings().reverse().concat(element.nextSiblings());
1266 },
1267
1268 match: function(element, selector) {
1269 if (typeof selector == 'string')
1270 selector = new Selector(selector);
1271 return selector.match($(element));
1272 },
1273
1274 up: function(element, expression, index) {
1275 return Selector.findElement($(element).ancestors(), expression, index);
1276 },
1277
1278 down: function(element, expression, index) {
1279 return Selector.findElement($(element).descendants(), expression, index);
1280 },
1281
1282 previous: function(element, expression, index) {
1283 return Selector.findElement($(element).previousSiblings(), expression, index );
1284 },
1285
1286 next: function(element, expression, index) {
1287 return Selector.findElement($(element).nextSiblings(), expression, index);
1288 },
1289
1290 getElementsBySelector: function() {
1291 var args = $A(arguments), element = $(args.shift());
1292 return Selector.findChildElements(element, args);
1293 },
1294
1295 getElementsByClassName: function(element, className) {
1296 return document.getElementsByClassName(className, element);
1297 },
1298
1299 readAttribute: function(element, name) {
1300 element = $(element);
1301 if (document.all && !window.opera) {
1302 var t = Element._attributeTranslations;
1303 if (t.values[name]) return t.values[name](element, name);
1304 if (t.names[name]) name = t.names[name];
1305 var attribute = element.attributes[name];
1306 if(attribute) return attribute.nodeValue;
1307 }
1308 return element.getAttribute(name);
1309 },
1310
1311 getHeight: function(element) {
1312 return $(element).getDimensions().height;
1313 },
1314
1315 getWidth: function(element) {
1316 return $(element).getDimensions().width;
1317 },
1318
1319 classNames: function(element) {
1320 return new Element.ClassNames(element);
1321 },
1322
1323 hasClassName: function(element, className) {
1324 if (!(element = $(element))) return;
1325 var elementClassName = element.className;
1326 if (elementClassName.length == 0) return false;
1327 if (elementClassName == className ||
1328 elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
1329 return true;
1330 return false;
1331 },
1332
1333 addClassName: function(element, className) {
1334 if (!(element = $(element))) return;
1335 Element.classNames(element).add(className);
1336 return element;
1337 },
1338
1339 removeClassName: function(element, className) {
1340 if (!(element = $(element))) return;
1341 Element.classNames(element).remove(className);
1342 return element;
1343 },
1344
1345 toggleClassName: function(element, className) {
1346 if (!(element = $(element))) return;
1347 Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'ad d'](className);
1348 return element;
1349 },
1350
1351 observe: function() {
1352 Event.observe.apply(Event, arguments);
1353 return $A(arguments).first();
1354 },
1355
1356 stopObserving: function() {
1357 Event.stopObserving.apply(Event, arguments);
1358 return $A(arguments).first();
1359 },
1360
1361 // removes whitespace-only text node children
1362 cleanWhitespace: function(element) {
1363 element = $(element);
1364 var node = element.firstChild;
1365 while (node) {
1366 var nextNode = node.nextSibling;
1367 if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
1368 element.removeChild(node);
1369 node = nextNode;
1370 }
1371 return element;
1372 },
1373
1374 empty: function(element) {
1375 return $(element).innerHTML.match(/^\s*$/);
1376 },
1377
1378 descendantOf: function(element, ancestor) {
1379 element = $(element), ancestor = $(ancestor);
1380 while (element = element.parentNode)
1381 if (element == ancestor) return true;
1382 return false;
1383 },
1384
1385 scrollTo: function(element) {
1386 element = $(element);
1387 var pos = Position.cumulativeOffset(element);
1388 window.scrollTo(pos[0], pos[1]);
1389 return element;
1390 },
1391
1392 getStyle: function(element, style) {
1393 element = $(element);
1394 if (['float','cssFloat'].include(style))
1395 style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : ' cssFloat');
1396 style = style.camelize();
1397 var value = element.style[style];
1398 if (!value) {
1399 if (document.defaultView && document.defaultView.getComputedStyle) {
1400 var css = document.defaultView.getComputedStyle(element, null);
1401 value = css ? css[style] : null;
1402 } else if (element.currentStyle) {
1403 value = element.currentStyle[style];
1404 }
1405 }
1406
1407 if((value == 'auto') && ['width','height'].include(style) && (element.getSty le('display') != 'none'))
1408 value = element['offset'+style.capitalize()] + 'px';
1409
1410 if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
1411 if (Element.getStyle(element, 'position') == 'static') value = 'auto';
1412 if(style == 'opacity') {
1413 if(value) return parseFloat(value);
1414 if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\) /))
1415 if(value[1]) return parseFloat(value[1]) / 100;
1416 return 1.0;
1417 }
1418 return value == 'auto' ? null : value;
1419 },
1420
1421 setStyle: function(element, style) {
1422 element = $(element);
1423 for (var name in style) {
1424 var value = style[name];
1425 if(name == 'opacity') {
1426 if (value == 1) {
1427 value = (/Gecko/.test(navigator.userAgent) &&
1428 !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1. 0;
1429 if(/MSIE/.test(navigator.userAgent) && !window.opera)
1430 element.style.filter = element.getStyle('filter').replace(/alpha\([^ \)]*\)/gi,'');
1431 } else if(value == '') {
1432 if(/MSIE/.test(navigator.userAgent) && !window.opera)
1433 element.style.filter = element.getStyle('filter').replace(/alpha\([^ \)]*\)/gi,'');
1434 } else {
1435 if(value < 0.00001) value = 0;
1436 if(/MSIE/.test(navigator.userAgent) && !window.opera)
1437 element.style.filter = element.getStyle('filter').replace(/alpha\([^ \)]*\)/gi,'') +
1438 'alpha(opacity='+value*100+')';
1439 }
1440 } else if(['float','cssFloat'].include(name)) name = (typeof element.style .styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
1441 element.style[name.camelize()] = value;
1442 }
1443 return element;
1444 },
1445
1446 getDimensions: function(element) {
1447 element = $(element);
1448 var display = $(element).getStyle('display');
1449 if (display != 'none' && display != null) // Safari bug
1450 return {width: element.offsetWidth, height: element.offsetHeight};
1451
1452 // All *Width and *Height properties give 0 on elements with display none,
1453 // so enable the element temporarily
1454 var els = element.style;
1455 var originalVisibility = els.visibility;
1456 var originalPosition = els.position;
1457 var originalDisplay = els.display;
1458 els.visibility = 'hidden';
1459 els.position = 'absolute';
1460 els.display = 'block';
1461 var originalWidth = element.clientWidth;
1462 var originalHeight = element.clientHeight;
1463 els.display = originalDisplay;
1464 els.position = originalPosition;
1465 els.visibility = originalVisibility;
1466 return {width: originalWidth, height: originalHeight};
1467 },
1468
1469 makePositioned: function(element) {
1470 element = $(element);
1471 var pos = Element.getStyle(element, 'position');
1472 if (pos == 'static' || !pos) {
1473 element._madePositioned = true;
1474 element.style.position = 'relative';
1475 // Opera returns the offset relative to the positioning context, when an
1476 // element is position relative but top and left have not been defined
1477 if (window.opera) {
1478 element.style.top = 0;
1479 element.style.left = 0;
1480 }
1481 }
1482 return element;
1483 },
1484
1485 undoPositioned: function(element) {
1486 element = $(element);
1487 if (element._madePositioned) {
1488 element._madePositioned = undefined;
1489 element.style.position =
1490 element.style.top =
1491 element.style.left =
1492 element.style.bottom =
1493 element.style.right = '';
1494 }
1495 return element;
1496 },
1497
1498 makeClipping: function(element) {
1499 element = $(element);
1500 if (element._overflow) return element;
1501 element._overflow = element.style.overflow || 'auto';
1502 if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
1503 element.style.overflow = 'hidden';
1504 return element;
1505 },
1506
1507 undoClipping: function(element) {
1508 element = $(element);
1509 if (!element._overflow) return element;
1510 element.style.overflow = element._overflow == 'auto' ? '' : element._overflo w;
1511 element._overflow = null;
1512 return element;
1513 }
1514 };
1515
1516 Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
1517
1518 Element._attributeTranslations = {};
1519
1520 Element._attributeTranslations.names = {
1521 colspan: "colSpan",
1522 rowspan: "rowSpan",
1523 valign: "vAlign",
1524 datetime: "dateTime",
1525 accesskey: "accessKey",
1526 tabindex: "tabIndex",
1527 enctype: "encType",
1528 maxlength: "maxLength",
1529 readonly: "readOnly",
1530 longdesc: "longDesc"
1531 };
1532
1533 Element._attributeTranslations.values = {
1534 _getAttr: function(element, attribute) {
1535 return element.getAttribute(attribute, 2);
1536 },
1537
1538 _flag: function(element, attribute) {
1539 return $(element).hasAttribute(attribute) ? attribute : null;
1540 },
1541
1542 style: function(element) {
1543 return element.style.cssText.toLowerCase();
1544 },
1545
1546 title: function(element) {
1547 var node = element.getAttributeNode('title');
1548 return node.specified ? node.nodeValue : null;
1549 }
1550 };
1551
1552 Object.extend(Element._attributeTranslations.values, {
1553 href: Element._attributeTranslations.values._getAttr,
1554 src: Element._attributeTranslations.values._getAttr,
1555 disabled: Element._attributeTranslations.values._flag,
1556 checked: Element._attributeTranslations.values._flag,
1557 readonly: Element._attributeTranslations.values._flag,
1558 multiple: Element._attributeTranslations.values._flag
1559 });
1560
1561 Element.Methods.Simulated = {
1562 hasAttribute: function(element, attribute) {
1563 var t = Element._attributeTranslations;
1564 attribute = t.names[attribute] || attribute;
1565 return $(element).getAttributeNode(attribute).specified;
1566 }
1567 };
1568
1569 // IE is missing .innerHTML support for TABLE-related elements
1570 if (document.all && !window.opera){
1571 Element.Methods.update = function(element, html) {
1572 element = $(element);
1573 html = typeof html == 'undefined' ? '' : html.toString();
1574 var tagName = element.tagName.toUpperCase();
1575 if (['THEAD','TBODY','TR','TD'].include(tagName)) {
1576 var div = document.createElement('div');
1577 switch (tagName) {
1578 case 'THEAD':
1579 case 'TBODY':
1580 div.innerHTML = '<table><tbody>' + html.stripScripts() + '</tbody></t able>';
1581 depth = 2;
1582 break;
1583 case 'TR':
1584 div.innerHTML = '<table><tbody><tr>' + html.stripScripts() + '</tr></ tbody></table>';
1585 depth = 3;
1586 break;
1587 case 'TD':
1588 div.innerHTML = '<table><tbody><tr><td>' + html.stripScripts() + '</t d></tr></tbody></table>';
1589 depth = 4;
1590 }
1591 $A(element.childNodes).each(function(node){
1592 element.removeChild(node)
1593 });
1594 depth.times(function(){ div = div.firstChild });
1595
1596 $A(div.childNodes).each(
1597 function(node){ element.appendChild(node) });
1598 } else {
1599 element.innerHTML = html.stripScripts();
1600 }
1601 setTimeout(function() {html.evalScripts()}, 10);
1602 return element;
1603 }
1604 };
1605
1606 Object.extend(Element, Element.Methods);
1607
1608 var _nativeExtensions = false;
1609
1610 if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
1611 ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
1612 var className = 'HTML' + tag + 'Element';
1613 if(window[className]) return;
1614 var klass = window[className] = {};
1615 klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__ proto__;
1616 });
1617
1618 Element.addMethods = function(methods) {
1619 Object.extend(Element.Methods, methods || {});
1620
1621 function copy(methods, destination, onlyIfAbsent) {
1622 onlyIfAbsent = onlyIfAbsent || false;
1623 var cache = Element.extend.cache;
1624 for (var property in methods) {
1625 var value = methods[property];
1626 if (!onlyIfAbsent || !(property in destination))
1627 destination[property] = cache.findOrStore(value);
1628 }
1629 }
1630
1631 if (typeof HTMLElement != 'undefined') {
1632 copy(Element.Methods, HTMLElement.prototype);
1633 copy(Element.Methods.Simulated, HTMLElement.prototype, true);
1634 copy(Form.Methods, HTMLFormElement.prototype);
1635 [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(kla ss) {
1636 copy(Form.Element.Methods, klass.prototype);
1637 });
1638 _nativeExtensions = true;
1639 }
1640 }
1641
1642 var Toggle = new Object();
1643 Toggle.display = Element.toggle;
1644
1645 /*--------------------------------------------------------------------------*/
1646
1647 Abstract.Insertion = function(adjacency) {
1648 this.adjacency = adjacency;
1649 }
1650
1651 Abstract.Insertion.prototype = {
1652 initialize: function(element, content) {
1653 this.element = $(element);
1654 this.content = content.stripScripts();
1655
1656 if (this.adjacency && this.element.insertAdjacentHTML) {
1657 try {
1658 this.element.insertAdjacentHTML(this.adjacency, this.content);
1659 } catch (e) {
1660 var tagName = this.element.tagName.toUpperCase();
1661 if (['TBODY', 'TR'].include(tagName)) {
1662 this.insertContent(this.contentFromAnonymousTable());
1663 } else {
1664 throw e;
1665 }
1666 }
1667 } else {
1668 this.range = this.element.ownerDocument.createRange();
1669 if (this.initializeRange) this.initializeRange();
1670 this.insertContent([this.range.createContextualFragment(this.content)]);
1671 }
1672
1673 setTimeout(function() {content.evalScripts()}, 10);
1674 },
1675
1676 contentFromAnonymousTable: function() {
1677 var div = document.createElement('div');
1678 div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
1679 return $A(div.childNodes[0].childNodes[0].childNodes);
1680 }
1681 }
1682
1683 var Insertion = new Object();
1684
1685 Insertion.Before = Class.create();
1686 Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin') , {
1687 initializeRange: function() {
1688 this.range.setStartBefore(this.element);
1689 },
1690
1691 insertContent: function(fragments) {
1692 fragments.each((function(fragment) {
1693 this.element.parentNode.insertBefore(fragment, this.element);
1694 }).bind(this));
1695 }
1696 });
1697
1698 Insertion.Top = Class.create();
1699 Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
1700 initializeRange: function() {
1701 this.range.selectNodeContents(this.element);
1702 this.range.collapse(true);
1703 },
1704
1705 insertContent: function(fragments) {
1706 fragments.reverse(false).each((function(fragment) {
1707 this.element.insertBefore(fragment, this.element.firstChild);
1708 }).bind(this));
1709 }
1710 });
1711
1712 Insertion.Bottom = Class.create();
1713 Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
1714 initializeRange: function() {
1715 this.range.selectNodeContents(this.element);
1716 this.range.collapse(this.element);
1717 },
1718
1719 insertContent: function(fragments) {
1720 fragments.each((function(fragment) {
1721 this.element.appendChild(fragment);
1722 }).bind(this));
1723 }
1724 });
1725
1726 Insertion.After = Class.create();
1727 Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
1728 initializeRange: function() {
1729 this.range.setStartAfter(this.element);
1730 },
1731
1732 insertContent: function(fragments) {
1733 fragments.each((function(fragment) {
1734 this.element.parentNode.insertBefore(fragment,
1735 this.element.nextSibling);
1736 }).bind(this));
1737 }
1738 });
1739
1740 /*--------------------------------------------------------------------------*/
1741
1742 Element.ClassNames = Class.create();
1743 Element.ClassNames.prototype = {
1744 initialize: function(element) {
1745 this.element = $(element);
1746 },
1747
1748 _each: function(iterator) {
1749 this.element.className.split(/\s+/).select(function(name) {
1750 return name.length > 0;
1751 })._each(iterator);
1752 },
1753
1754 set: function(className) {
1755 this.element.className = className;
1756 },
1757
1758 add: function(classNameToAdd) {
1759 if (this.include(classNameToAdd)) return;
1760 this.set($A(this).concat(classNameToAdd).join(' '));
1761 },
1762
1763 remove: function(classNameToRemove) {
1764 if (!this.include(classNameToRemove)) return;
1765 this.set($A(this).without(classNameToRemove).join(' '));
1766 },
1767
1768 toString: function() {
1769 return $A(this).join(' ');
1770 }
1771 };
1772
1773 Object.extend(Element.ClassNames.prototype, Enumerable);
1774 var Selector = Class.create();
1775 Selector.prototype = {
1776 initialize: function(expression) {
1777 this.params = {classNames: []};
1778 this.expression = expression.toString().strip();
1779 this.parseExpression();
1780 this.compileMatcher();
1781 },
1782
1783 parseExpression: function() {
1784 function abort(message) { throw 'Parse error in selector: ' + message; }
1785
1786 if (this.expression == '') abort('empty expression');
1787
1788 var params = this.params, expr = this.expression, match, modifier, clause, r est;
1789 while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|( [^\]\s]*)))?\]$/i)) {
1790 params.attributes = params.attributes || [];
1791 params.attributes.push({name: match[2], operator: match[3], value: match[4 ] || match[5] || ''});
1792 expr = match[1];
1793 }
1794
1795 if (expr == '*') return this.params.wildcard = true;
1796
1797 while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
1798 modifier = match[1], clause = match[2], rest = match[3];
1799 switch (modifier) {
1800 case '#': params.id = clause; break;
1801 case '.': params.classNames.push(clause); break;
1802 case '':
1803 case undefined: params.tagName = clause.toUpperCase(); break;
1804 default: abort(expr.inspect());
1805 }
1806 expr = rest;
1807 }
1808
1809 if (expr.length > 0) abort(expr.inspect());
1810 },
1811
1812 buildMatchExpression: function() {
1813 var params = this.params, conditions = [], clause;
1814
1815 if (params.wildcard)
1816 conditions.push('true');
1817 if (clause = params.id)
1818 conditions.push('element.readAttribute("id") == ' + clause.inspect());
1819 if (clause = params.tagName)
1820 conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
1821 if ((clause = params.classNames).length > 0)
1822 for (var i = 0, length = clause.length; i < length; i++)
1823 conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
1824 if (clause = params.attributes) {
1825 clause.each(function(attribute) {
1826 var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
1827 var splitValueBy = function(delimiter) {
1828 return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
1829 }
1830
1831 switch (attribute.operator) {
1832 case '=': conditions.push(value + ' == ' + attribute.value.inspe ct()); break;
1833 case '~=': conditions.push(splitValueBy(' ') + '.include(' + attr ibute.value.inspect() + ')'); break;
1834 case '|=': conditions.push(
1835 splitValueBy('-') + '.first().toUpperCase() == ' + a ttribute.value.toUpperCase().inspect()
1836 ); break;
1837 case '!=': conditions.push(value + ' != ' + attribute.value.inspe ct()); break;
1838 case '':
1839 case undefined: conditions.push('element.hasAttribute(' + attribute.na me.inspect() + ')'); break;
1840 default: throw 'Unknown operator ' + attribute.operator + ' in selector';
1841 }
1842 });
1843 }
1844
1845 return conditions.join(' && ');
1846 },
1847
1848 compileMatcher: function() {
1849 this.match = new Function('element', 'if (!element.tagName) return false; \
1850 element = $(element); \
1851 return ' + this.buildMatchExpression());
1852 },
1853
1854 findElements: function(scope) {
1855 var element;
1856
1857 if (element = $(this.params.id))
1858 if (this.match(element))
1859 if (!scope || Element.childOf(element, scope))
1860 return [element];
1861
1862 scope = (scope || document).getElementsByTagName(this.params.tagName || '*') ;
1863
1864 var results = [];
1865 for (var i = 0, length = scope.length; i < length; i++)
1866 if (this.match(element = scope[i]))
1867 results.push(Element.extend(element));
1868
1869 return results;
1870 },
1871
1872 toString: function() {
1873 return this.expression;
1874 }
1875 }
1876
1877 Object.extend(Selector, {
1878 matchElements: function(elements, expression) {
1879 var selector = new Selector(expression);
1880 return elements.select(selector.match.bind(selector)).map(Element.extend);
1881 },
1882
1883 findElement: function(elements, expression, index) {
1884 if (typeof expression == 'number') index = expression, expression = false;
1885 return Selector.matchElements(elements, expression || '*')[index || 0];
1886 },
1887
1888 findChildElements: function(element, expressions) {
1889 return expressions.map(function(expression) {
1890 return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], func tion(results, expr) {
1891 var selector = new Selector(expr);
1892 return results.inject([], function(elements, result) {
1893 return elements.concat(selector.findElements(result || element));
1894 });
1895 });
1896 }).flatten();
1897 }
1898 });
1899
1900 function $$() {
1901 return Selector.findChildElements(document, $A(arguments));
1902 }
1903 var Form = {
1904 reset: function(form) {
1905 $(form).reset();
1906 return form;
1907 },
1908
1909 serializeElements: function(elements, getHash) {
1910 var data = elements.inject({}, function(result, element) {
1911 if (!element.disabled && element.name) {
1912 var key = element.name, value = $(element).getValue();
1913 if (value != undefined) {
1914 if (result[key]) {
1915 if (result[key].constructor != Array) result[key] = [result[key]];
1916 result[key].push(value);
1917 }
1918 else result[key] = value;
1919 }
1920 }
1921 return result;
1922 });
1923
1924 return getHash ? data : Hash.toQueryString(data);
1925 }
1926 };
1927
1928 Form.Methods = {
1929 serialize: function(form, getHash) {
1930 return Form.serializeElements(Form.getElements(form), getHash);
1931 },
1932
1933 getElements: function(form) {
1934 return $A($(form).getElementsByTagName('*')).inject([],
1935 function(elements, child) {
1936 if (Form.Element.Serializers[child.tagName.toLowerCase()])
1937 elements.push(Element.extend(child));
1938 return elements;
1939 }
1940 );
1941 },
1942
1943 getInputs: function(form, typeName, name) {
1944 form = $(form);
1945 var inputs = form.getElementsByTagName('input');
1946
1947 if (!typeName && !name) return $A(inputs).map(Element.extend);
1948
1949 for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++ ) {
1950 var input = inputs[i];
1951 if ((typeName && input.type != typeName) || (name && input.name != name))
1952 continue;
1953 matchingInputs.push(Element.extend(input));
1954 }
1955
1956 return matchingInputs;
1957 },
1958
1959 disable: function(form) {
1960 form = $(form);
1961 form.getElements().each(function(element) {
1962 element.blur();
1963 element.disabled = 'true';
1964 });
1965 return form;
1966 },
1967
1968 enable: function(form) {
1969 form = $(form);
1970 form.getElements().each(function(element) {
1971 element.disabled = '';
1972 });
1973 return form;
1974 },
1975
1976 findFirstElement: function(form) {
1977 return $(form).getElements().find(function(element) {
1978 return element.type != 'hidden' && !element.disabled &&
1979 ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
1980 });
1981 },
1982
1983 focusFirstElement: function(form) {
1984 form = $(form);
1985 form.findFirstElement().activate();
1986 return form;
1987 }
1988 }
1989
1990 Object.extend(Form, Form.Methods);
1991
1992 /*--------------------------------------------------------------------------*/
1993
1994 Form.Element = {
1995 focus: function(element) {
1996 $(element).focus();
1997 return element;
1998 },
1999
2000 select: function(element) {
2001 $(element).select();
2002 return element;
2003 }
2004 }
2005
2006 Form.Element.Methods = {
2007 serialize: function(element) {
2008 element = $(element);
2009 if (!element.disabled && element.name) {
2010 var value = element.getValue();
2011 if (value != undefined) {
2012 var pair = {};
2013 pair[element.name] = value;
2014 return Hash.toQueryString(pair);
2015 }
2016 }
2017 return '';
2018 },
2019
2020 getValue: function(element) {
2021 element = $(element);
2022 var method = element.tagName.toLowerCase();
2023 return Form.Element.Serializers[method](element);
2024 },
2025
2026 clear: function(element) {
2027 $(element).value = '';
2028 return element;
2029 },
2030
2031 present: function(element) {
2032 return $(element).value != '';
2033 },
2034
2035 activate: function(element) {
2036 element = $(element);
2037 element.focus();
2038 if (element.select && ( element.tagName.toLowerCase() != 'input' ||
2039 !['button', 'reset', 'submit'].include(element.type) ) )
2040 element.select();
2041 return element;
2042 },
2043
2044 disable: function(element) {
2045 element = $(element);
2046 element.disabled = true;
2047 return element;
2048 },
2049
2050 enable: function(element) {
2051 element = $(element);
2052 element.blur();
2053 element.disabled = false;
2054 return element;
2055 }
2056 }
2057
2058 Object.extend(Form.Element, Form.Element.Methods);
2059 var Field = Form.Element;
2060 var $F = Form.Element.getValue;
2061
2062 /*--------------------------------------------------------------------------*/
2063
2064 Form.Element.Serializers = {
2065 input: function(element) {
2066 switch (element.type.toLowerCase()) {
2067 case 'checkbox':
2068 case 'radio':
2069 return Form.Element.Serializers.inputSelector(element);
2070 default:
2071 return Form.Element.Serializers.textarea(element);
2072 }
2073 },
2074
2075 inputSelector: function(element) {
2076 return element.checked ? element.value : null;
2077 },
2078
2079 textarea: function(element) {
2080 return element.value;
2081 },
2082
2083 select: function(element) {
2084 return this[element.type == 'select-one' ?
2085 'selectOne' : 'selectMany'](element);
2086 },
2087
2088 selectOne: function(element) {
2089 var index = element.selectedIndex;
2090 return index >= 0 ? this.optionValue(element.options[index]) : null;
2091 },
2092
2093 selectMany: function(element) {
2094 var values, length = element.length;
2095 if (!length) return null;
2096
2097 for (var i = 0, values = []; i < length; i++) {
2098 var opt = element.options[i];
2099 if (opt.selected) values.push(this.optionValue(opt));
2100 }
2101 return values;
2102 },
2103
2104 optionValue: function(opt) {
2105 // extend element because hasAttribute may not be native
2106 return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
2107 }
2108 }
2109
2110 /*--------------------------------------------------------------------------*/
2111
2112 Abstract.TimedObserver = function() {}
2113 Abstract.TimedObserver.prototype = {
2114 initialize: function(element, frequency, callback) {
2115 this.frequency = frequency;
2116 this.element = $(element);
2117 this.callback = callback;
2118
2119 this.lastValue = this.getValue();
2120 this.registerCallback();
2121 },
2122
2123 registerCallback: function() {
2124 setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
2125 },
2126
2127 onTimerEvent: function() {
2128 var value = this.getValue();
2129 var changed = ('string' == typeof this.lastValue && 'string' == typeof value
2130 ? this.lastValue != value : String(this.lastValue) != String(value));
2131 if (changed) {
2132 this.callback(this.element, value);
2133 this.lastValue = value;
2134 }
2135 }
2136 }
2137
2138 Form.Element.Observer = Class.create();
2139 Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2140 getValue: function() {
2141 return Form.Element.getValue(this.element);
2142 }
2143 });
2144
2145 Form.Observer = Class.create();
2146 Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
2147 getValue: function() {
2148 return Form.serialize(this.element);
2149 }
2150 });
2151
2152 /*--------------------------------------------------------------------------*/
2153
2154 Abstract.EventObserver = function() {}
2155 Abstract.EventObserver.prototype = {
2156 initialize: function(element, callback) {
2157 this.element = $(element);
2158 this.callback = callback;
2159
2160 this.lastValue = this.getValue();
2161 if (this.element.tagName.toLowerCase() == 'form')
2162 this.registerFormCallbacks();
2163 else
2164 this.registerCallback(this.element);
2165 },
2166
2167 onElementEvent: function() {
2168 var value = this.getValue();
2169 if (this.lastValue != value) {
2170 this.callback(this.element, value);
2171 this.lastValue = value;
2172 }
2173 },
2174
2175 registerFormCallbacks: function() {
2176 Form.getElements(this.element).each(this.registerCallback.bind(this));
2177 },
2178
2179 registerCallback: function(element) {
2180 if (element.type) {
2181 switch (element.type.toLowerCase()) {
2182 case 'checkbox':
2183 case 'radio':
2184 Event.observe(element, 'click', this.onElementEvent.bind(this));
2185 break;
2186 default:
2187 Event.observe(element, 'change', this.onElementEvent.bind(this));
2188 break;
2189 }
2190 }
2191 }
2192 }
2193
2194 Form.Element.EventObserver = Class.create();
2195 Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver( ), {
2196 getValue: function() {
2197 return Form.Element.getValue(this.element);
2198 }
2199 });
2200
2201 Form.EventObserver = Class.create();
2202 Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
2203 getValue: function() {
2204 return Form.serialize(this.element);
2205 }
2206 });
2207 if (!window.Event) {
2208 var Event = new Object();
2209 }
2210
2211 Object.extend(Event, {
2212 KEY_BACKSPACE: 8,
2213 KEY_TAB: 9,
2214 KEY_RETURN: 13,
2215 KEY_ESC: 27,
2216 KEY_LEFT: 37,
2217 KEY_UP: 38,
2218 KEY_RIGHT: 39,
2219 KEY_DOWN: 40,
2220 KEY_DELETE: 46,
2221 KEY_HOME: 36,
2222 KEY_END: 35,
2223 KEY_PAGEUP: 33,
2224 KEY_PAGEDOWN: 34,
2225
2226 element: function(event) {
2227 return event.target || event.srcElement;
2228 },
2229
2230 isLeftClick: function(event) {
2231 return (((event.which) && (event.which == 1)) ||
2232 ((event.button) && (event.button == 1)));
2233 },
2234
2235 pointerX: function(event) {
2236 return event.pageX || (event.clientX +
2237 (document.documentElement.scrollLeft || document.body.scrollLeft));
2238 },
2239
2240 pointerY: function(event) {
2241 return event.pageY || (event.clientY +
2242 (document.documentElement.scrollTop || document.body.scrollTop));
2243 },
2244
2245 stop: function(event) {
2246 if (event.preventDefault) {
2247 event.preventDefault();
2248 event.stopPropagation();
2249 } else {
2250 event.returnValue = false;
2251 event.cancelBubble = true;
2252 }
2253 },
2254
2255 // find the first node with the given tagName, starting from the
2256 // node the event was triggered on; traverses the DOM upwards
2257 findElement: function(event, tagName) {
2258 var element = Event.element(event);
2259 while (element.parentNode && (!element.tagName ||
2260 (element.tagName.toUpperCase() != tagName.toUpperCase())))
2261 element = element.parentNode;
2262 return element;
2263 },
2264
2265 observers: false,
2266
2267 _observeAndCache: function(element, name, observer, useCapture) {
2268 if (!this.observers) this.observers = [];
2269 if (element.addEventListener) {
2270 this.observers.push([element, name, observer, useCapture]);
2271 element.addEventListener(name, observer, useCapture);
2272 } else if (element.attachEvent) {
2273 this.observers.push([element, name, observer, useCapture]);
2274 element.attachEvent('on' + name, observer);
2275 }
2276 },
2277
2278 unloadCache: function() {
2279 if (!Event.observers) return;
2280 for (var i = 0, length = Event.observers.length; i < length; i++) {
2281 Event.stopObserving.apply(this, Event.observers[i]);
2282 Event.observers[i][0] = null;
2283 }
2284 Event.observers = false;
2285 },
2286
2287 observe: function(element, name, observer, useCapture) {
2288 element = $(element);
2289 useCapture = useCapture || false;
2290
2291 if (name == 'keypress' &&
2292 (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
2293 || element.attachEvent))
2294 name = 'keydown';
2295
2296 Event._observeAndCache(element, name, observer, useCapture);
2297 },
2298
2299 stopObserving: function(element, name, observer, useCapture) {
2300 element = $(element);
2301 useCapture = useCapture || false;
2302
2303 if (name == 'keypress' &&
2304 (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
2305 || element.detachEvent))
2306 name = 'keydown';
2307
2308 if (element.removeEventListener) {
2309 element.removeEventListener(name, observer, useCapture);
2310 } else if (element.detachEvent) {
2311 try {
2312 element.detachEvent('on' + name, observer);
2313 } catch (e) {}
2314 }
2315 }
2316 });
2317
2318 /* prevent memory leaks in IE */
2319 if (navigator.appVersion.match(/\bMSIE\b/))
2320 Event.observe(window, 'unload', Event.unloadCache, false);
2321 var Position = {
2322 // set to true if needed, warning: firefox performance problems
2323 // NOT neeeded for page scrolling, only if draggable contained in
2324 // scrollable elements
2325 includeScrollOffsets: false,
2326
2327 // must be called before calling withinIncludingScrolloffset, every time the
2328 // page is scrolled
2329 prepare: function() {
2330 this.deltaX = window.pageXOffset
2331 || document.documentElement.scrollLeft
2332 || document.body.scrollLeft
2333 || 0;
2334 this.deltaY = window.pageYOffset
2335 || document.documentElement.scrollTop
2336 || document.body.scrollTop
2337 || 0;
2338 },
2339
2340 realOffset: function(element) {
2341 var valueT = 0, valueL = 0;
2342 do {
2343 valueT += element.scrollTop || 0;
2344 valueL += element.scrollLeft || 0;
2345 element = element.parentNode;
2346 } while (element);
2347 return [valueL, valueT];
2348 },
2349
2350 cumulativeOffset: function(element) {
2351 var valueT = 0, valueL = 0;
2352 do {
2353 valueT += element.offsetTop || 0;
2354 valueL += element.offsetLeft || 0;
2355 element = element.offsetParent;
2356 } while (element);
2357 return [valueL, valueT];
2358 },
2359
2360 positionedOffset: function(element) {
2361 var valueT = 0, valueL = 0;
2362 do {
2363 valueT += element.offsetTop || 0;
2364 valueL += element.offsetLeft || 0;
2365 element = element.offsetParent;
2366 if (element) {
2367 if(element.tagName=='BODY') break;
2368 var p = Element.getStyle(element, 'position');
2369 if (p == 'relative' || p == 'absolute') break;
2370 }
2371 } while (element);
2372 return [valueL, valueT];
2373 },
2374
2375 offsetParent: function(element) {
2376 if (element.offsetParent) return element.offsetParent;
2377 if (element == document.body) return element;
2378
2379 while ((element = element.parentNode) && element != document.body)
2380 if (Element.getStyle(element, 'position') != 'static')
2381 return element;
2382
2383 return document.body;
2384 },
2385
2386 // caches x/y coordinate pair to use with overlap
2387 within: function(element, x, y) {
2388 if (this.includeScrollOffsets)
2389 return this.withinIncludingScrolloffsets(element, x, y);
2390 this.xcomp = x;
2391 this.ycomp = y;
2392 this.offset = this.cumulativeOffset(element);
2393
2394 return (y >= this.offset[1] &&
2395 y < this.offset[1] + element.offsetHeight &&
2396 x >= this.offset[0] &&
2397 x < this.offset[0] + element.offsetWidth);
2398 },
2399
2400 withinIncludingScrolloffsets: function(element, x, y) {
2401 var offsetcache = this.realOffset(element);
2402
2403 this.xcomp = x + offsetcache[0] - this.deltaX;
2404 this.ycomp = y + offsetcache[1] - this.deltaY;
2405 this.offset = this.cumulativeOffset(element);
2406
2407 return (this.ycomp >= this.offset[1] &&
2408 this.ycomp < this.offset[1] + element.offsetHeight &&
2409 this.xcomp >= this.offset[0] &&
2410 this.xcomp < this.offset[0] + element.offsetWidth);
2411 },
2412
2413 // within must be called directly before
2414 overlap: function(mode, element) {
2415 if (!mode) return 0;
2416 if (mode == 'vertical')
2417 return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
2418 element.offsetHeight;
2419 if (mode == 'horizontal')
2420 return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
2421 element.offsetWidth;
2422 },
2423
2424 page: function(forElement) {
2425 var valueT = 0, valueL = 0;
2426
2427 var element = forElement;
2428 do {
2429 valueT += element.offsetTop || 0;
2430 valueL += element.offsetLeft || 0;
2431
2432 // Safari fix
2433 if (element.offsetParent==document.body)
2434 if (Element.getStyle(element,'position')=='absolute') break;
2435
2436 } while (element = element.offsetParent);
2437
2438 element = forElement;
2439 do {
2440 if (!window.opera || element.tagName=='BODY') {
2441 valueT -= element.scrollTop || 0;
2442 valueL -= element.scrollLeft || 0;
2443 }
2444 } while (element = element.parentNode);
2445
2446 return [valueL, valueT];
2447 },
2448
2449 clone: function(source, target) {
2450 var options = Object.extend({
2451 setLeft: true,
2452 setTop: true,
2453 setWidth: true,
2454 setHeight: true,
2455 offsetTop: 0,
2456 offsetLeft: 0
2457 }, arguments[2] || {})
2458
2459 // find page position of source
2460 source = $(source);
2461 var p = Position.page(source);
2462
2463 // find coordinate system to use
2464 target = $(target);
2465 var delta = [0, 0];
2466 var parent = null;
2467 // delta [0,0] will do fine with position: fixed elements,
2468 // position:absolute needs offsetParent deltas
2469 if (Element.getStyle(target,'position') == 'absolute') {
2470 parent = Position.offsetParent(target);
2471 delta = Position.page(parent);
2472 }
2473
2474 // correct by body offsets (fixes Safari)
2475 if (parent == document.body) {
2476 delta[0] -= document.body.offsetLeft;
2477 delta[1] -= document.body.offsetTop;
2478 }
2479
2480 // set position
2481 if(options.setLeft) target.style.left = (p[0] - delta[0] + options.offset Left) + 'px';
2482 if(options.setTop) target.style.top = (p[1] - delta[1] + options.offset Top) + 'px';
2483 if(options.setWidth) target.style.width = source.offsetWidth + 'px';
2484 if(options.setHeight) target.style.height = source.offsetHeight + 'px';
2485 },
2486
2487 absolutize: function(element) {
2488 element = $(element);
2489 if (element.style.position == 'absolute') return;
2490 Position.prepare();
2491
2492 var offsets = Position.positionedOffset(element);
2493 var top = offsets[1];
2494 var left = offsets[0];
2495 var width = element.clientWidth;
2496 var height = element.clientHeight;
2497
2498 element._originalLeft = left - parseFloat(element.style.left || 0);
2499 element._originalTop = top - parseFloat(element.style.top || 0);
2500 element._originalWidth = element.style.width;
2501 element._originalHeight = element.style.height;
2502
2503 element.style.position = 'absolute';
2504 element.style.top = top + 'px';
2505 element.style.left = left + 'px';
2506 element.style.width = width + 'px';
2507 element.style.height = height + 'px';
2508 },
2509
2510 relativize: function(element) {
2511 element = $(element);
2512 if (element.style.position == 'relative') return;
2513 Position.prepare();
2514
2515 element.style.position = 'relative';
2516 var top = parseFloat(element.style.top || 0) - (element._originalTop || 0) ;
2517 var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0 );
2518
2519 element.style.top = top + 'px';
2520 element.style.left = left + 'px';
2521 element.style.height = element._originalHeight;
2522 element.style.width = element._originalWidth;
2523 }
2524 }
2525
2526 // Safari returns margins on body which is incorrect if the child is absolutely
2527 // positioned. For performance reasons, redefine Position.cumulativeOffset for
2528 // KHTML/WebKit only.
2529 if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
2530 Position.cumulativeOffset = function(element) {
2531 var valueT = 0, valueL = 0;
2532 do {
2533 valueT += element.offsetTop || 0;
2534 valueL += element.offsetLeft || 0;
2535 if (element.offsetParent == document.body)
2536 if (Element.getStyle(element, 'position') == 'absolute') break;
2537
2538 element = element.offsetParent;
2539 } while (element);
2540
2541 return [valueL, valueT];
2542 }
2543 }
2544
2545 Element.addMethods();
2546
2547
2548 // ------------------------------------------------------------------------
2549 // ------------------------------------------------------------------------
2550
2551 // The rest of this file is the actual ray tracer written by Adam
2552 // Burmister. It's a concatenation of the following files:
2553 //
2554 // flog/color.js
2555 // flog/light.js
2556 // flog/vector.js
2557 // flog/ray.js
2558 // flog/scene.js
2559 // flog/material/basematerial.js
2560 // flog/material/solid.js
2561 // flog/material/chessboard.js
2562 // flog/shape/baseshape.js
2563 // flog/shape/sphere.js
2564 // flog/shape/plane.js
2565 // flog/intersectioninfo.js
2566 // flog/camera.js
2567 // flog/background.js
2568 // flog/engine.js
2569
2570
2571 /* Fake a Flog.* namespace */
2572 if(typeof(Flog) == 'undefined') var Flog = {};
2573 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
2574
2575 Flog.RayTracer.Color = Class.create();
2576
2577 Flog.RayTracer.Color.prototype = {
2578 red : 0.0,
2579 green : 0.0,
2580 blue : 0.0,
2581
2582 initialize : function(r, g, b) {
2583 if(!r) r = 0.0;
2584 if(!g) g = 0.0;
2585 if(!b) b = 0.0;
2586
2587 this.red = r;
2588 this.green = g;
2589 this.blue = b;
2590 },
2591
2592 add : function(c1, c2){
2593 var result = new Flog.RayTracer.Color(0,0,0);
2594
2595 result.red = c1.red + c2.red;
2596 result.green = c1.green + c2.green;
2597 result.blue = c1.blue + c2.blue;
2598
2599 return result;
2600 },
2601
2602 addScalar: function(c1, s){
2603 var result = new Flog.RayTracer.Color(0,0,0);
2604
2605 result.red = c1.red + s;
2606 result.green = c1.green + s;
2607 result.blue = c1.blue + s;
2608
2609 result.limit();
2610
2611 return result;
2612 },
2613
2614 subtract: function(c1, c2){
2615 var result = new Flog.RayTracer.Color(0,0,0);
2616
2617 result.red = c1.red - c2.red;
2618 result.green = c1.green - c2.green;
2619 result.blue = c1.blue - c2.blue;
2620
2621 return result;
2622 },
2623
2624 multiply : function(c1, c2) {
2625 var result = new Flog.RayTracer.Color(0,0,0);
2626
2627 result.red = c1.red * c2.red;
2628 result.green = c1.green * c2.green;
2629 result.blue = c1.blue * c2.blue;
2630
2631 return result;
2632 },
2633
2634 multiplyScalar : function(c1, f) {
2635 var result = new Flog.RayTracer.Color(0,0,0);
2636
2637 result.red = c1.red * f;
2638 result.green = c1.green * f;
2639 result.blue = c1.blue * f;
2640
2641 return result;
2642 },
2643
2644 divideFactor : function(c1, f) {
2645 var result = new Flog.RayTracer.Color(0,0,0);
2646
2647 result.red = c1.red / f;
2648 result.green = c1.green / f;
2649 result.blue = c1.blue / f;
2650
2651 return result;
2652 },
2653
2654 limit: function(){
2655 this.red = (this.red > 0.0) ? ( (this.red > 1.0) ? 1.0 : this.red ) : 0. 0;
2656 this.green = (this.green > 0.0) ? ( (this.green > 1.0) ? 1.0 : this.gree n ) : 0.0;
2657 this.blue = (this.blue > 0.0) ? ( (this.blue > 1.0) ? 1.0 : this.blue ) : 0.0;
2658 },
2659
2660 distance : function(color) {
2661 var d = Math.abs(this.red - color.red) + Math.abs(this.green - color.gre en) + Math.abs(this.blue - color.blue);
2662 return d;
2663 },
2664
2665 blend: function(c1, c2, w){
2666 var result = new Flog.RayTracer.Color(0,0,0);
2667 result = Flog.RayTracer.Color.prototype.add(
2668 Flog.RayTracer.Color.prototype.multiplyScalar(c1, 1 - w),
2669 Flog.RayTracer.Color.prototype.multiplyScalar(c2, w)
2670 );
2671 return result;
2672 },
2673
2674 brightness : function() {
2675 var r = Math.floor(this.red*255);
2676 var g = Math.floor(this.green*255);
2677 var b = Math.floor(this.blue*255);
2678 return (r * 77 + g * 150 + b * 29) >> 8;
2679 },
2680
2681 toString : function () {
2682 var r = Math.floor(this.red*255);
2683 var g = Math.floor(this.green*255);
2684 var b = Math.floor(this.blue*255);
2685
2686 return "rgb("+ r +","+ g +","+ b +")";
2687 }
2688 }
2689 /* Fake a Flog.* namespace */
2690 if(typeof(Flog) == 'undefined') var Flog = {};
2691 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
2692
2693 Flog.RayTracer.Light = Class.create();
2694
2695 Flog.RayTracer.Light.prototype = {
2696 position: null,
2697 color: null,
2698 intensity: 10.0,
2699
2700 initialize : function(pos, color, intensity) {
2701 this.position = pos;
2702 this.color = color;
2703 this.intensity = (intensity ? intensity : 10.0);
2704 },
2705
2706 getIntensity: function(distance){
2707 if(distance >= intensity) return 0;
2708
2709 return Math.pow((intensity - distance) / strength, 0.2);
2710 },
2711
2712 toString : function () {
2713 return 'Light [' + this.position.x + ',' + this.position.y + ',' + this. position.z + ']';
2714 }
2715 }
2716 /* Fake a Flog.* namespace */
2717 if(typeof(Flog) == 'undefined') var Flog = {};
2718 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
2719
2720 Flog.RayTracer.Vector = Class.create();
2721
2722 Flog.RayTracer.Vector.prototype = {
2723 x : 0.0,
2724 y : 0.0,
2725 z : 0.0,
2726
2727 initialize : function(x, y, z) {
2728 this.x = (x ? x : 0);
2729 this.y = (y ? y : 0);
2730 this.z = (z ? z : 0);
2731 },
2732
2733 copy: function(vector){
2734 this.x = vector.x;
2735 this.y = vector.y;
2736 this.z = vector.z;
2737 },
2738
2739 normalize : function() {
2740 var m = this.magnitude();
2741 return new Flog.RayTracer.Vector(this.x / m, this.y / m, this.z / m);
2742 },
2743
2744 magnitude : function() {
2745 return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this. z));
2746 },
2747
2748 cross : function(w) {
2749 return new Flog.RayTracer.Vector(
2750 -this.z * w.y + this.y * w.z,
2751 this.z * w.x - this.x * w.z,
2752 -this.y * w.x + this.x * w.y);
2753 },
2754
2755 dot : function(w) {
2756 return this.x * w.x + this.y * w.y + this.z * w.z;
2757 },
2758
2759 add : function(v, w) {
2760 return new Flog.RayTracer.Vector(w.x + v.x, w.y + v.y, w.z + v.z);
2761 },
2762
2763 subtract : function(v, w) {
2764 if(!w || !v) throw 'Vectors must be defined [' + v + ',' + w + ']';
2765 return new Flog.RayTracer.Vector(v.x - w.x, v.y - w.y, v.z - w.z);
2766 },
2767
2768 multiplyVector : function(v, w) {
2769 return new Flog.RayTracer.Vector(v.x * w.x, v.y * w.y, v.z * w.z);
2770 },
2771
2772 multiplyScalar : function(v, w) {
2773 return new Flog.RayTracer.Vector(v.x * w, v.y * w, v.z * w);
2774 },
2775
2776 toString : function () {
2777 return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']';
2778 }
2779 }
2780 /* Fake a Flog.* namespace */
2781 if(typeof(Flog) == 'undefined') var Flog = {};
2782 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
2783
2784 Flog.RayTracer.Ray = Class.create();
2785
2786 Flog.RayTracer.Ray.prototype = {
2787 position : null,
2788 direction : null,
2789 initialize : function(pos, dir) {
2790 this.position = pos;
2791 this.direction = dir;
2792 },
2793
2794 toString : function () {
2795 return 'Ray [' + this.position + ',' + this.direction + ']';
2796 }
2797 }
2798 /* Fake a Flog.* namespace */
2799 if(typeof(Flog) == 'undefined') var Flog = {};
2800 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
2801
2802 Flog.RayTracer.Scene = Class.create();
2803
2804 Flog.RayTracer.Scene.prototype = {
2805 camera : null,
2806 shapes : [],
2807 lights : [],
2808 background : null,
2809
2810 initialize : function() {
2811 this.camera = new Flog.RayTracer.Camera(
2812 new Flog.RayTracer.Vector(0,0,-5),
2813 new Flog.RayTracer.Vector(0,0,1),
2814 new Flog.RayTracer.Vector(0,1,0)
2815 );
2816 this.shapes = new Array();
2817 this.lights = new Array();
2818 this.background = new Flog.RayTracer.Background(new Flog.RayTracer.Color (0,0,0.5), 0.2);
2819 }
2820 }
2821 /* Fake a Flog.* namespace */
2822 if(typeof(Flog) == 'undefined') var Flog = {};
2823 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
2824 if(typeof(Flog.RayTracer.Material) == 'undefined') Flog.RayTracer.Material = {};
2825
2826 Flog.RayTracer.Material.BaseMaterial = Class.create();
2827
2828 Flog.RayTracer.Material.BaseMaterial.prototype = {
2829
2830 gloss: 2.0, // [0...infinity] 0 = matt
2831 transparency: 0.0, // 0=opaque
2832 reflection: 0.0, // [0...infinity] 0 = no reflection
2833 refraction: 0.50,
2834 hasTexture: false,
2835
2836 initialize : function() {
2837
2838 },
2839
2840 getColor: function(u, v){
2841
2842 },
2843
2844 wrapUp: function(t){
2845 t = t % 2.0;
2846 if(t < -1) t += 2.0;
2847 if(t >= 1) t -= 2.0;
2848 return t;
2849 },
2850
2851 toString : function () {
2852 return 'Material [gloss=' + this.gloss + ', transparency=' + this.transp arency + ', hasTexture=' + this.hasTexture +']';
2853 }
2854 }
2855 /* Fake a Flog.* namespace */
2856 if(typeof(Flog) == 'undefined') var Flog = {};
2857 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
2858
2859 Flog.RayTracer.Material.Solid = Class.create();
2860
2861 Flog.RayTracer.Material.Solid.prototype = Object.extend(
2862 new Flog.RayTracer.Material.BaseMaterial(), {
2863 initialize : function(color, reflection, refraction, transparency, gloss ) {
2864 this.color = color;
2865 this.reflection = reflection;
2866 this.transparency = transparency;
2867 this.gloss = gloss;
2868 this.hasTexture = false;
2869 },
2870
2871 getColor: function(u, v){
2872 return this.color;
2873 },
2874
2875 toString : function () {
2876 return 'SolidMaterial [gloss=' + this.gloss + ', transparency=' + th is.transparency + ', hasTexture=' + this.hasTexture +']';
2877 }
2878 }
2879 );
2880 /* Fake a Flog.* namespace */
2881 if(typeof(Flog) == 'undefined') var Flog = {};
2882 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
2883
2884 Flog.RayTracer.Material.Chessboard = Class.create();
2885
2886 Flog.RayTracer.Material.Chessboard.prototype = Object.extend(
2887 new Flog.RayTracer.Material.BaseMaterial(), {
2888 colorEven: null,
2889 colorOdd: null,
2890 density: 0.5,
2891
2892 initialize : function(colorEven, colorOdd, reflection, transparency, glo ss, density) {
2893 this.colorEven = colorEven;
2894 this.colorOdd = colorOdd;
2895 this.reflection = reflection;
2896 this.transparency = transparency;
2897 this.gloss = gloss;
2898 this.density = density;
2899 this.hasTexture = true;
2900 },
2901
2902 getColor: function(u, v){
2903 var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density );
2904
2905 if(t < 0.0)
2906 return this.colorEven;
2907 else
2908 return this.colorOdd;
2909 },
2910
2911 toString : function () {
2912 return 'ChessMaterial [gloss=' + this.gloss + ', transparency=' + th is.transparency + ', hasTexture=' + this.hasTexture +']';
2913 }
2914 }
2915 );
2916 /* Fake a Flog.* namespace */
2917 if(typeof(Flog) == 'undefined') var Flog = {};
2918 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
2919 if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
2920
2921 Flog.RayTracer.Shape.BaseShape = Class.create();
2922
2923 Flog.RayTracer.Shape.BaseShape.prototype = {
2924 position: null,
2925 material: null,
2926
2927 initialize : function() {
2928 this.position = new Vector(0,0,0);
2929 this.material = new Flog.RayTracer.Material.SolidMaterial(
2930 new Flog.RayTracer.Color(1,0,1),
2931 0,
2932 0,
2933 0
2934 );
2935 },
2936
2937 toString : function () {
2938 return 'Material [gloss=' + this.gloss + ', transparency=' + this.transp arency + ', hasTexture=' + this.hasTexture +']';
2939 }
2940 }
2941 /* Fake a Flog.* namespace */
2942 if(typeof(Flog) == 'undefined') var Flog = {};
2943 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
2944 if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
2945
2946 Flog.RayTracer.Shape.Sphere = Class.create();
2947
2948 Flog.RayTracer.Shape.Sphere.prototype = {
2949 initialize : function(pos, radius, material) {
2950 this.radius = radius;
2951 this.position = pos;
2952 this.material = material;
2953 },
2954
2955 intersect: function(ray){
2956 var info = new Flog.RayTracer.IntersectionInfo();
2957 info.shape = this;
2958
2959 var dst = Flog.RayTracer.Vector.prototype.subtract(ray.position, this.po sition);
2960
2961 var B = dst.dot(ray.direction);
2962 var C = dst.dot(dst) - (this.radius * this.radius);
2963 var D = (B * B) - C;
2964
2965 if(D > 0){ // intersection!
2966 info.isHit = true;
2967 info.distance = (-B) - Math.sqrt(D);
2968 info.position = Flog.RayTracer.Vector.prototype.add(
2969 ray.position,
2970 Flog.RayTracer.Vector.prototype. multiplyScalar(
2971 ray.direction,
2972 info.distance
2973 )
2974 );
2975 info.normal = Flog.RayTracer.Vector.prototype.subtract(
2976 info.position,
2977 this.position
2978 ).normalize();
2979
2980 info.color = this.material.getColor(0,0);
2981 } else {
2982 info.isHit = false;
2983 }
2984 return info;
2985 },
2986
2987 toString : function () {
2988 return 'Sphere [position=' + this.position + ', radius=' + this.radius + ']';
2989 }
2990 }
2991 /* Fake a Flog.* namespace */
2992 if(typeof(Flog) == 'undefined') var Flog = {};
2993 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
2994 if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
2995
2996 Flog.RayTracer.Shape.Plane = Class.create();
2997
2998 Flog.RayTracer.Shape.Plane.prototype = {
2999 d: 0.0,
3000
3001 initialize : function(pos, d, material) {
3002 this.position = pos;
3003 this.d = d;
3004 this.material = material;
3005 },
3006
3007 intersect: function(ray){
3008 var info = new Flog.RayTracer.IntersectionInfo();
3009
3010 var Vd = this.position.dot(ray.direction);
3011 if(Vd == 0) return info; // no intersection
3012
3013 var t = -(this.position.dot(ray.position) + this.d) / Vd;
3014 if(t <= 0) return info;
3015
3016 info.shape = this;
3017 info.isHit = true;
3018 info.position = Flog.RayTracer.Vector.prototype.add(
3019 ray.position,
3020 Flog.RayTracer.Vector.prototype.mult iplyScalar(
3021 ray.direction,
3022 t
3023 )
3024 );
3025 info.normal = this.position;
3026 info.distance = t;
3027
3028 if(this.material.hasTexture){
3029 var vU = new Flog.RayTracer.Vector(this.position.y, this.position.z, -this.position.x);
3030 var vV = vU.cross(this.position);
3031 var u = info.position.dot(vU);
3032 var v = info.position.dot(vV);
3033 info.color = this.material.getColor(u,v);
3034 } else {
3035 info.color = this.material.getColor(0,0);
3036 }
3037
3038 return info;
3039 },
3040
3041 toString : function () {
3042 return 'Plane [' + this.position + ', d=' + this.d + ']';
3043 }
3044 }
3045 /* Fake a Flog.* namespace */
3046 if(typeof(Flog) == 'undefined') var Flog = {};
3047 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
3048
3049 Flog.RayTracer.IntersectionInfo = Class.create();
3050
3051 Flog.RayTracer.IntersectionInfo.prototype = {
3052 isHit: false,
3053 hitCount: 0,
3054 shape: null,
3055 position: null,
3056 normal: null,
3057 color: null,
3058 distance: null,
3059
3060 initialize : function() {
3061 this.color = new Flog.RayTracer.Color(0,0,0);
3062 },
3063
3064 toString : function () {
3065 return 'Intersection [' + this.position + ']';
3066 }
3067 }
3068 /* Fake a Flog.* namespace */
3069 if(typeof(Flog) == 'undefined') var Flog = {};
3070 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
3071
3072 Flog.RayTracer.Camera = Class.create();
3073
3074 Flog.RayTracer.Camera.prototype = {
3075 position: null,
3076 lookAt: null,
3077 equator: null,
3078 up: null,
3079 screen: null,
3080
3081 initialize : function(pos, lookAt, up) {
3082 this.position = pos;
3083 this.lookAt = lookAt;
3084 this.up = up;
3085 this.equator = lookAt.normalize().cross(this.up);
3086 this.screen = Flog.RayTracer.Vector.prototype.add(this.position, this.lo okAt);
3087 },
3088
3089 getRay: function(vx, vy){
3090 var pos = Flog.RayTracer.Vector.prototype.subtract(
3091 this.screen,
3092 Flog.RayTracer.Vector.prototype.subtract(
3093 Flog.RayTracer.Vector.prototype.multiplyScalar(this.equator, vx) ,
3094 Flog.RayTracer.Vector.prototype.multiplyScalar(this.up, vy)
3095 )
3096 );
3097 pos.y = pos.y * -1;
3098 var dir = Flog.RayTracer.Vector.prototype.subtract(
3099 pos,
3100 this.position
3101 );
3102
3103 var ray = new Flog.RayTracer.Ray(pos, dir.normalize());
3104
3105 return ray;
3106 },
3107
3108 toString : function () {
3109 return 'Ray []';
3110 }
3111 }
3112 /* Fake a Flog.* namespace */
3113 if(typeof(Flog) == 'undefined') var Flog = {};
3114 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
3115
3116 Flog.RayTracer.Background = Class.create();
3117
3118 Flog.RayTracer.Background.prototype = {
3119 color : null,
3120 ambience : 0.0,
3121
3122 initialize : function(color, ambience) {
3123 this.color = color;
3124 this.ambience = ambience;
3125 }
3126 }
3127 /* Fake a Flog.* namespace */
3128 if(typeof(Flog) == 'undefined') var Flog = {};
3129 if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
3130
3131 Flog.RayTracer.Engine = Class.create();
3132
3133 Flog.RayTracer.Engine.prototype = {
3134 canvas: null, /* 2d context we can render to */
3135
3136 initialize: function(options){
3137 this.options = Object.extend({
3138 canvasHeight: 100,
3139 canvasWidth: 100,
3140 pixelWidth: 2,
3141 pixelHeight: 2,
3142 renderDiffuse: false,
3143 renderShadows: false,
3144 renderHighlights: false,
3145 renderReflections: false,
3146 rayDepth: 2
3147 }, options || {});
3148
3149 this.options.canvasHeight /= this.options.pixelHeight;
3150 this.options.canvasWidth /= this.options.pixelWidth;
3151
3152 /* TODO: dynamically include other scripts */
3153 },
3154
3155 setPixel: function(x, y, color){
3156 var pxW, pxH;
3157 pxW = this.options.pixelWidth;
3158 pxH = this.options.pixelHeight;
3159
3160 if (this.canvas) {
3161 this.canvas.fillStyle = color.toString();
3162 this.canvas.fillRect (x * pxW, y * pxH, pxW, pxH);
3163 } else {
3164 if (x === y) {
3165 checkNumber += color.brightness();
3166 }
3167 // print(x * pxW, y * pxH, pxW, pxH);
3168 }
3169 },
3170
3171 renderScene: function(scene, canvas){
3172 checkNumber = 0;
3173 /* Get canvas */
3174 if (canvas) {
3175 this.canvas = canvas.getContext("2d");
3176 } else {
3177 this.canvas = null;
3178 }
3179
3180 var canvasHeight = this.options.canvasHeight;
3181 var canvasWidth = this.options.canvasWidth;
3182
3183 for(var y=0; y < canvasHeight; y++){
3184 for(var x=0; x < canvasWidth; x++){
3185 var yp = y * 1.0 / canvasHeight * 2 - 1;
3186 var xp = x * 1.0 / canvasWidth * 2 - 1;
3187
3188 var ray = scene.camera.getRay(xp, yp);
3189
3190 var color = this.getPixelColor(ray, scene);
3191
3192 this.setPixel(x, y, color);
3193 }
3194 }
3195 if (checkNumber !== 2321) {
3196 throw new Error("Scene rendered incorrectly");
3197 }
3198 },
3199
3200 getPixelColor: function(ray, scene){
3201 var info = this.testIntersection(ray, scene, null);
3202 if(info.isHit){
3203 var color = this.rayTrace(info, ray, scene, 0);
3204 return color;
3205 }
3206 return scene.background.color;
3207 },
3208
3209 testIntersection: function(ray, scene, exclude){
3210 var hits = 0;
3211 var best = new Flog.RayTracer.IntersectionInfo();
3212 best.distance = 2000;
3213
3214 for(var i=0; i<scene.shapes.length; i++){
3215 var shape = scene.shapes[i];
3216
3217 if(shape != exclude){
3218 var info = shape.intersect(ray);
3219 if(info.isHit && info.distance >= 0 && info.distance < best.dist ance){
3220 best = info;
3221 hits++;
3222 }
3223 }
3224 }
3225 best.hitCount = hits;
3226 return best;
3227 },
3228
3229 getReflectionRay: function(P,N,V){
3230 var c1 = -N.dot(V);
3231 var R1 = Flog.RayTracer.Vector.prototype.add(
3232 Flog.RayTracer.Vector.prototype.multiplyScalar(N, 2*c1),
3233 V
3234 );
3235 return new Flog.RayTracer.Ray(P, R1);
3236 },
3237
3238 rayTrace: function(info, ray, scene, depth){
3239 // Calc ambient
3240 var color = Flog.RayTracer.Color.prototype.multiplyScalar(info.color, sc ene.background.ambience);
3241 var oldColor = color;
3242 var shininess = Math.pow(10, info.shape.material.gloss + 1);
3243
3244 for(var i=0; i<scene.lights.length; i++){
3245 var light = scene.lights[i];
3246
3247 // Calc diffuse lighting
3248 var v = Flog.RayTracer.Vector.prototype.subtract(
3249 light.position,
3250 info.position
3251 ).normalize();
3252
3253 if(this.options.renderDiffuse){
3254 var L = v.dot(info.normal);
3255 if(L > 0.0){
3256 color = Flog.RayTracer.Color.prototype.add(
3257 color,
3258 Flog.RayTracer.Color.prototype.multiply(
3259 info.color,
3260 Flog.RayTracer.Color.prototype.multi plyScalar(
3261 light.color,
3262 L
3263 )
3264 )
3265 );
3266 }
3267 }
3268
3269 // The greater the depth the more accurate the colours, but
3270 // this is exponentially (!) expensive
3271 if(depth <= this.options.rayDepth){
3272 // calculate reflection ray
3273 if(this.options.renderReflections && info.shape.material.reflection > 0)
3274 {
3275 var reflectionRay = this.getReflectionRay(info.position, info.norm al, ray.direction);
3276 var refl = this.testIntersection(reflectionRay, scene, info.shape) ;
3277
3278 if (refl.isHit && refl.distance > 0){
3279 refl.color = this.rayTrace(refl, reflectionRay, scene, depth + 1);
3280 } else {
3281 refl.color = scene.background.color;
3282 }
3283
3284 color = Flog.RayTracer.Color.prototype.blend(
3285 color,
3286 refl.color,
3287 info.shape.material.reflection
3288 );
3289 }
3290
3291 // Refraction
3292 /* TODO */
3293 }
3294
3295 /* Render shadows and highlights */
3296
3297 var shadowInfo = new Flog.RayTracer.IntersectionInfo();
3298
3299 if(this.options.renderShadows){
3300 var shadowRay = new Flog.RayTracer.Ray(info.position, v);
3301
3302 shadowInfo = this.testIntersection(shadowRay, scene, info.shape) ;
3303 if(shadowInfo.isHit && shadowInfo.shape != info.shape /*&& shado wInfo.shape.type != 'PLANE'*/){
3304 var vA = Flog.RayTracer.Color.prototype.multiplyScalar(color , 0.5);
3305 var dB = (0.5 * Math.pow(shadowInfo.shape.material.transpare ncy, 0.5));
3306 color = Flog.RayTracer.Color.prototype.addScalar(vA,dB);
3307 }
3308 }
3309
3310 // Phong specular highlights
3311 if(this.options.renderHighlights && !shadowInfo.isHit && info.shape.materi al.gloss > 0){
3312 var Lv = Flog.RayTracer.Vector.prototype.subtract(
3313 info.shape.position,
3314 light.position
3315 ).normalize();
3316
3317 var E = Flog.RayTracer.Vector.prototype.subtract(
3318 scene.camera.position,
3319 info.shape.position
3320 ).normalize();
3321
3322 var H = Flog.RayTracer.Vector.prototype.subtract(
3323 E,
3324 Lv
3325 ).normalize();
3326
3327 var glossWeight = Math.pow(Math.max(info.normal.dot(H), 0), shininess);
3328 color = Flog.RayTracer.Color.prototype.add(
3329 Flog.RayTracer.Color.prototype.multiplyScalar(light. color, glossWeight),
3330 color
3331 );
3332 }
3333 }
3334 color.limit();
3335 return color;
3336 }
3337 };
3338
3339
3340 function renderScene(){
3341 var scene = new Flog.RayTracer.Scene();
3342
3343 scene.camera = new Flog.RayTracer.Camera(
3344 new Flog.RayTracer.Vector(0, 0, -15),
3345 new Flog.RayTracer.Vector(-0.2, 0, 5),
3346 new Flog.RayTracer.Vector(0, 1, 0)
3347 );
3348
3349 scene.background = new Flog.RayTracer.Background(
3350 new Flog.RayTracer.Color(0.5, 0.5, 0.5),
3351 0.4
3352 );
3353
3354 var sphere = new Flog.RayTracer.Shape.Sphere(
3355 new Flog.RayTracer.Vector(-1.5, 1.5, 2),
3356 1.5,
3357 new Flog.RayTracer.Material.Solid(
3358 new Flog.RayTracer.Color(0,0.5,0.5),
3359 0.3,
3360 0.0,
3361 0.0,
3362 2.0
3363 )
3364 );
3365
3366 var sphere1 = new Flog.RayTracer.Shape.Sphere(
3367 new Flog.RayTracer.Vector(1, 0.25, 1),
3368 0.5,
3369 new Flog.RayTracer.Material.Solid(
3370 new Flog.RayTracer.Color(0.9,0.9,0.9),
3371 0.1,
3372 0.0,
3373 0.0,
3374 1.5
3375 )
3376 );
3377
3378 var plane = new Flog.RayTracer.Shape.Plane(
3379 new Flog.RayTracer.Vector(0.1, 0.9, -0.5).normal ize(),
3380 1.2,
3381 new Flog.RayTracer.Material.Chessboard(
3382 new Flog.RayTracer.Color(1,1,1),
3383 new Flog.RayTracer.Color(0,0,0),
3384 0.2,
3385 0.0,
3386 1.0,
3387 0.7
3388 )
3389 );
3390
3391 scene.shapes.push(plane);
3392 scene.shapes.push(sphere);
3393 scene.shapes.push(sphere1);
3394
3395 var light = new Flog.RayTracer.Light(
3396 new Flog.RayTracer.Vector(5, 10, -1),
3397 new Flog.RayTracer.Color(0.8, 0.8, 0.8)
3398 );
3399
3400 var light1 = new Flog.RayTracer.Light(
3401 new Flog.RayTracer.Vector(-3, 5, -15),
3402 new Flog.RayTracer.Color(0.8, 0.8, 0.8),
3403 100
3404 );
3405
3406 scene.lights.push(light);
3407 scene.lights.push(light1);
3408
3409 var imageWidth = 100; // $F('imageWidth');
3410 var imageHeight = 100; // $F('imageHeight');
3411 var pixelSize = "5,5".split(','); // $F('pixelSize').split(',');
3412 var renderDiffuse = true; // $F('renderDiffuse');
3413 var renderShadows = true; // $F('renderShadows');
3414 var renderHighlights = true; // $F('renderHighlights');
3415 var renderReflections = true; // $F('renderReflections');
3416 var rayDepth = 2;//$F('rayDepth');
3417
3418 var raytracer = new Flog.RayTracer.Engine(
3419 {
3420 canvasWidth: imageWidth,
3421 canvasHeight: imageHeight,
3422 pixelWidth: pixelSize[0],
3423 pixelHeight: pixelSize[1],
3424 "renderDiffuse": renderDiffuse,
3425 "renderHighlights": renderHighlights,
3426 "renderShadows": renderShadows,
3427 "renderReflections": renderReflections,
3428 "rayDepth": rayDepth
3429 }
3430 );
3431
3432 raytracer.renderScene(scene, null, 0);
3433 }
3434
3435 window.onload = function(){
3436 startTest("v8-raytrace", '');
3437
3438 test("RayTrace", renderScene);
3439
3440 endTest();
3441 };
3442 </script>
3443 </body>
3444 </html>
OLDNEW
« no previous file with comments | « chrome/test/data/dromaeo/tests/v8-earley-boyer.html ('k') | chrome/test/data/dromaeo/tests/v8-richards.html » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698