js/src/devtools/jint/v8/raytrace.js

Wed, 31 Dec 2014 06:55:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:55:50 +0100
changeset 2
7e26c7da4463
permissions
-rw-r--r--

Added tag UPSTREAM_283F7C6 for changeset ca08bd8f51b2

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

mercurial