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

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/devtools/jint/v8/raytrace.js	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,3418 @@
     1.4 +// The ray tracer code in this file is written by Adam Burmister. It
     1.5 +// is available in its original form from:
     1.6 +//
     1.7 +//   http://labs.flog.nz.co/raytracer/
     1.8 +//
     1.9 +// It has been modified slightly by Google to work as a standalone
    1.10 +// benchmark, but the all the computational code remains
    1.11 +// untouched. This file also contains a copy of the Prototype
    1.12 +// JavaScript framework which is used by the ray tracer.
    1.13 +
    1.14 +var RayTrace = new BenchmarkSuite('RayTrace', 932666, [
    1.15 +  new Benchmark('RayTrace', renderScene)
    1.16 +]);
    1.17 +
    1.18 +
    1.19 +// Create dummy objects if we're not running in a browser.
    1.20 +if (typeof document == 'undefined') {
    1.21 +  document = { };
    1.22 +  window = { opera: null };
    1.23 +  navigator = { userAgent: null, appVersion: "" };
    1.24 +}
    1.25 +
    1.26 +
    1.27 +// ------------------------------------------------------------------------
    1.28 +// ------------------------------------------------------------------------
    1.29 +
    1.30 +
    1.31 +/*  Prototype JavaScript framework, version 1.5.0
    1.32 + *  (c) 2005-2007 Sam Stephenson
    1.33 + *
    1.34 + *  Prototype is freely distributable under the terms of an MIT-style license.
    1.35 + *  For details, see the Prototype web site: http://prototype.conio.net/
    1.36 + *
    1.37 +/*--------------------------------------------------------------------------*/
    1.38 +
    1.39 +//--------------------
    1.40 +var Prototype = {
    1.41 +  Version: '1.5.0',
    1.42 +  BrowserFeatures: {
    1.43 +    XPath: !!document.evaluate
    1.44 +  },
    1.45 +
    1.46 +  ScriptFragment: '(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)',
    1.47 +  emptyFunction: function() {},
    1.48 +  K: function(x) { return x }
    1.49 +}
    1.50 +
    1.51 +var Class = {
    1.52 +  create: function() {
    1.53 +    return function() {
    1.54 +      this.initialize.apply(this, arguments);
    1.55 +    }
    1.56 +  }
    1.57 +}
    1.58 +
    1.59 +var Abstract = new Object();
    1.60 +
    1.61 +Object.extend = function(destination, source) {
    1.62 +  for (var property in source) {
    1.63 +    destination[property] = source[property];
    1.64 +  }
    1.65 +  return destination;
    1.66 +}
    1.67 +
    1.68 +Object.extend(Object, {
    1.69 +  inspect: function(object) {
    1.70 +    try {
    1.71 +      if (object === undefined) return 'undefined';
    1.72 +      if (object === null) return 'null';
    1.73 +      return object.inspect ? object.inspect() : object.toString();
    1.74 +    } catch (e) {
    1.75 +      if (e instanceof RangeError) return '...';
    1.76 +      throw e;
    1.77 +    }
    1.78 +  },
    1.79 +
    1.80 +  keys: function(object) {
    1.81 +    var keys = [];
    1.82 +    for (var property in object)
    1.83 +      keys.push(property);
    1.84 +    return keys;
    1.85 +  },
    1.86 +
    1.87 +  values: function(object) {
    1.88 +    var values = [];
    1.89 +    for (var property in object)
    1.90 +      values.push(object[property]);
    1.91 +    return values;
    1.92 +  },
    1.93 +
    1.94 +  clone: function(object) {
    1.95 +    return Object.extend({}, object);
    1.96 +  }
    1.97 +});
    1.98 +
    1.99 +Function.prototype.bind = function() {
   1.100 +  var __method = this, args = $A(arguments), object = args.shift();
   1.101 +  return function() {
   1.102 +    return __method.apply(object, args.concat($A(arguments)));
   1.103 +  }
   1.104 +}
   1.105 +
   1.106 +Function.prototype.bindAsEventListener = function(object) {
   1.107 +  var __method = this, args = $A(arguments), object = args.shift();
   1.108 +  return function(event) {
   1.109 +    return __method.apply(object, [( event || window.event)].concat(args).concat($A(arguments)));
   1.110 +  }
   1.111 +}
   1.112 +
   1.113 +Object.extend(Number.prototype, {
   1.114 +  toColorPart: function() {
   1.115 +    var digits = this.toString(16);
   1.116 +    if (this < 16) return '0' + digits;
   1.117 +    return digits;
   1.118 +  },
   1.119 +
   1.120 +  succ: function() {
   1.121 +    return this + 1;
   1.122 +  },
   1.123 +
   1.124 +  times: function(iterator) {
   1.125 +    $R(0, this, true).each(iterator);
   1.126 +    return this;
   1.127 +  }
   1.128 +});
   1.129 +
   1.130 +var Try = {
   1.131 +  these: function() {
   1.132 +    var returnValue;
   1.133 +
   1.134 +    for (var i = 0, length = arguments.length; i < length; i++) {
   1.135 +      var lambda = arguments[i];
   1.136 +      try {
   1.137 +        returnValue = lambda();
   1.138 +        break;
   1.139 +      } catch (e) {}
   1.140 +    }
   1.141 +
   1.142 +    return returnValue;
   1.143 +  }
   1.144 +}
   1.145 +
   1.146 +/*--------------------------------------------------------------------------*/
   1.147 +
   1.148 +var PeriodicalExecuter = Class.create();
   1.149 +PeriodicalExecuter.prototype = {
   1.150 +  initialize: function(callback, frequency) {
   1.151 +    this.callback = callback;
   1.152 +    this.frequency = frequency;
   1.153 +    this.currentlyExecuting = false;
   1.154 +
   1.155 +    this.registerCallback();
   1.156 +  },
   1.157 +
   1.158 +  registerCallback: function() {
   1.159 +    this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
   1.160 +  },
   1.161 +
   1.162 +  stop: function() {
   1.163 +    if (!this.timer) return;
   1.164 +    clearInterval(this.timer);
   1.165 +    this.timer = null;
   1.166 +  },
   1.167 +
   1.168 +  onTimerEvent: function() {
   1.169 +    if (!this.currentlyExecuting) {
   1.170 +      try {
   1.171 +        this.currentlyExecuting = true;
   1.172 +        this.callback(this);
   1.173 +      } finally {
   1.174 +        this.currentlyExecuting = false;
   1.175 +      }
   1.176 +    }
   1.177 +  }
   1.178 +}
   1.179 +String.interpret = function(value){
   1.180 +  return value == null ? '' : String(value);
   1.181 +}
   1.182 +
   1.183 +Object.extend(String.prototype, {
   1.184 +  gsub: function(pattern, replacement) {
   1.185 +    var result = '', source = this, match;
   1.186 +    replacement = arguments.callee.prepareReplacement(replacement);
   1.187 +
   1.188 +    while (source.length > 0) {
   1.189 +      if (match = source.match(pattern)) {
   1.190 +        result += source.slice(0, match.index);
   1.191 +        result += String.interpret(replacement(match));
   1.192 +        source  = source.slice(match.index + match[0].length);
   1.193 +      } else {
   1.194 +        result += source, source = '';
   1.195 +      }
   1.196 +    }
   1.197 +    return result;
   1.198 +  },
   1.199 +
   1.200 +  sub: function(pattern, replacement, count) {
   1.201 +    replacement = this.gsub.prepareReplacement(replacement);
   1.202 +    count = count === undefined ? 1 : count;
   1.203 +
   1.204 +    return this.gsub(pattern, function(match) {
   1.205 +      if (--count < 0) return match[0];
   1.206 +      return replacement(match);
   1.207 +    });
   1.208 +  },
   1.209 +
   1.210 +  scan: function(pattern, iterator) {
   1.211 +    this.gsub(pattern, iterator);
   1.212 +    return this;
   1.213 +  },
   1.214 +
   1.215 +  truncate: function(length, truncation) {
   1.216 +    length = length || 30;
   1.217 +    truncation = truncation === undefined ? '...' : truncation;
   1.218 +    return this.length > length ?
   1.219 +      this.slice(0, length - truncation.length) + truncation : this;
   1.220 +  },
   1.221 +
   1.222 +  strip: function() {
   1.223 +    return this.replace(/^\s+/, '').replace(/\s+$/, '');
   1.224 +  },
   1.225 +
   1.226 +  stripTags: function() {
   1.227 +    return this.replace(/<\/?[^>]+>/gi, '');
   1.228 +  },
   1.229 +
   1.230 +  stripScripts: function() {
   1.231 +    return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), '');
   1.232 +  },
   1.233 +
   1.234 +  extractScripts: function() {
   1.235 +    var matchAll = new RegExp(Prototype.ScriptFragment, 'img');
   1.236 +    var matchOne = new RegExp(Prototype.ScriptFragment, 'im');
   1.237 +    return (this.match(matchAll) || []).map(function(scriptTag) {
   1.238 +      return (scriptTag.match(matchOne) || ['', ''])[1];
   1.239 +    });
   1.240 +  },
   1.241 +
   1.242 +  evalScripts: function() {
   1.243 +    return this.extractScripts().map(function(script) { return eval(script) });
   1.244 +  },
   1.245 +
   1.246 +  escapeHTML: function() {
   1.247 +    var div = document.createElement('div');
   1.248 +    var text = document.createTextNode(this);
   1.249 +    div.appendChild(text);
   1.250 +    return div.innerHTML;
   1.251 +  },
   1.252 +
   1.253 +  unescapeHTML: function() {
   1.254 +    var div = document.createElement('div');
   1.255 +    div.innerHTML = this.stripTags();
   1.256 +    return div.childNodes[0] ? (div.childNodes.length > 1 ?
   1.257 +      $A(div.childNodes).inject('',function(memo,node){ return memo+node.nodeValue }) :
   1.258 +      div.childNodes[0].nodeValue) : '';
   1.259 +  },
   1.260 +
   1.261 +  toQueryParams: function(separator) {
   1.262 +    var match = this.strip().match(/([^?#]*)(#.*)?$/);
   1.263 +    if (!match) return {};
   1.264 +
   1.265 +    return match[1].split(separator || '&').inject({}, function(hash, pair) {
   1.266 +      if ((pair = pair.split('='))[0]) {
   1.267 +        var name = decodeURIComponent(pair[0]);
   1.268 +        var value = pair[1] ? decodeURIComponent(pair[1]) : undefined;
   1.269 +
   1.270 +        if (hash[name] !== undefined) {
   1.271 +          if (hash[name].constructor != Array)
   1.272 +            hash[name] = [hash[name]];
   1.273 +          if (value) hash[name].push(value);
   1.274 +        }
   1.275 +        else hash[name] = value;
   1.276 +      }
   1.277 +      return hash;
   1.278 +    });
   1.279 +  },
   1.280 +
   1.281 +  toArray: function() {
   1.282 +    return this.split('');
   1.283 +  },
   1.284 +
   1.285 +  succ: function() {
   1.286 +    return this.slice(0, this.length - 1) +
   1.287 +      String.fromCharCode(this.charCodeAt(this.length - 1) + 1);
   1.288 +  },
   1.289 +
   1.290 +  camelize: function() {
   1.291 +    var parts = this.split('-'), len = parts.length;
   1.292 +    if (len == 1) return parts[0];
   1.293 +
   1.294 +    var camelized = this.charAt(0) == '-'
   1.295 +      ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1)
   1.296 +      : parts[0];
   1.297 +
   1.298 +    for (var i = 1; i < len; i++)
   1.299 +      camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1);
   1.300 +
   1.301 +    return camelized;
   1.302 +  },
   1.303 +
   1.304 +  capitalize: function(){
   1.305 +    return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase();
   1.306 +  },
   1.307 +
   1.308 +  underscore: function() {
   1.309 +    return this.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'#{1}_#{2}').gsub(/([a-z\d])([A-Z])/,'#{1}_#{2}').gsub(/-/,'_').toLowerCase();
   1.310 +  },
   1.311 +
   1.312 +  dasherize: function() {
   1.313 +    return this.gsub(/_/,'-');
   1.314 +  },
   1.315 +
   1.316 +  inspect: function(useDoubleQuotes) {
   1.317 +    var escapedString = this.replace(/\\/g, '\\\\');
   1.318 +    if (useDoubleQuotes)
   1.319 +      return '"' + escapedString.replace(/"/g, '\\"') + '"';
   1.320 +    else
   1.321 +      return "'" + escapedString.replace(/'/g, '\\\'') + "'";
   1.322 +  }
   1.323 +});
   1.324 +
   1.325 +String.prototype.gsub.prepareReplacement = function(replacement) {
   1.326 +  if (typeof replacement == 'function') return replacement;
   1.327 +  var template = new Template(replacement);
   1.328 +  return function(match) { return template.evaluate(match) };
   1.329 +}
   1.330 +
   1.331 +String.prototype.parseQuery = String.prototype.toQueryParams;
   1.332 +
   1.333 +var Template = Class.create();
   1.334 +Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
   1.335 +Template.prototype = {
   1.336 +  initialize: function(template, pattern) {
   1.337 +    this.template = template.toString();
   1.338 +    this.pattern  = pattern || Template.Pattern;
   1.339 +  },
   1.340 +
   1.341 +  evaluate: function(object) {
   1.342 +    return this.template.gsub(this.pattern, function(match) {
   1.343 +      var before = match[1];
   1.344 +      if (before == '\\') return match[2];
   1.345 +      return before + String.interpret(object[match[3]]);
   1.346 +    });
   1.347 +  }
   1.348 +}
   1.349 +
   1.350 +var $break    = new Object();
   1.351 +var $continue = new Object();
   1.352 +
   1.353 +var Enumerable = {
   1.354 +  each: function(iterator) {
   1.355 +    var index = 0;
   1.356 +    try {
   1.357 +      this._each(function(value) {
   1.358 +        try {
   1.359 +          iterator(value, index++);
   1.360 +        } catch (e) {
   1.361 +          if (e != $continue) throw e;
   1.362 +        }
   1.363 +      });
   1.364 +    } catch (e) {
   1.365 +      if (e != $break) throw e;
   1.366 +    }
   1.367 +    return this;
   1.368 +  },
   1.369 +
   1.370 +  eachSlice: function(number, iterator) {
   1.371 +    var index = -number, slices = [], array = this.toArray();
   1.372 +    while ((index += number) < array.length)
   1.373 +      slices.push(array.slice(index, index+number));
   1.374 +    return slices.map(iterator);
   1.375 +  },
   1.376 +
   1.377 +  all: function(iterator) {
   1.378 +    var result = true;
   1.379 +    this.each(function(value, index) {
   1.380 +      result = result && !!(iterator || Prototype.K)(value, index);
   1.381 +      if (!result) throw $break;
   1.382 +    });
   1.383 +    return result;
   1.384 +  },
   1.385 +
   1.386 +  any: function(iterator) {
   1.387 +    var result = false;
   1.388 +    this.each(function(value, index) {
   1.389 +      if (result = !!(iterator || Prototype.K)(value, index))
   1.390 +        throw $break;
   1.391 +    });
   1.392 +    return result;
   1.393 +  },
   1.394 +
   1.395 +  collect: function(iterator) {
   1.396 +    var results = [];
   1.397 +    this.each(function(value, index) {
   1.398 +      results.push((iterator || Prototype.K)(value, index));
   1.399 +    });
   1.400 +    return results;
   1.401 +  },
   1.402 +
   1.403 +  detect: function(iterator) {
   1.404 +    var result;
   1.405 +    this.each(function(value, index) {
   1.406 +      if (iterator(value, index)) {
   1.407 +        result = value;
   1.408 +        throw $break;
   1.409 +      }
   1.410 +    });
   1.411 +    return result;
   1.412 +  },
   1.413 +
   1.414 +  findAll: function(iterator) {
   1.415 +    var results = [];
   1.416 +    this.each(function(value, index) {
   1.417 +      if (iterator(value, index))
   1.418 +        results.push(value);
   1.419 +    });
   1.420 +    return results;
   1.421 +  },
   1.422 +
   1.423 +  grep: function(pattern, iterator) {
   1.424 +    var results = [];
   1.425 +    this.each(function(value, index) {
   1.426 +      var stringValue = value.toString();
   1.427 +      if (stringValue.match(pattern))
   1.428 +        results.push((iterator || Prototype.K)(value, index));
   1.429 +    })
   1.430 +    return results;
   1.431 +  },
   1.432 +
   1.433 +  include: function(object) {
   1.434 +    var found = false;
   1.435 +    this.each(function(value) {
   1.436 +      if (value == object) {
   1.437 +        found = true;
   1.438 +        throw $break;
   1.439 +      }
   1.440 +    });
   1.441 +    return found;
   1.442 +  },
   1.443 +
   1.444 +  inGroupsOf: function(number, fillWith) {
   1.445 +    fillWith = fillWith === undefined ? null : fillWith;
   1.446 +    return this.eachSlice(number, function(slice) {
   1.447 +      while(slice.length < number) slice.push(fillWith);
   1.448 +      return slice;
   1.449 +    });
   1.450 +  },
   1.451 +
   1.452 +  inject: function(memo, iterator) {
   1.453 +    this.each(function(value, index) {
   1.454 +      memo = iterator(memo, value, index);
   1.455 +    });
   1.456 +    return memo;
   1.457 +  },
   1.458 +
   1.459 +  invoke: function(method) {
   1.460 +    var args = $A(arguments).slice(1);
   1.461 +    return this.map(function(value) {
   1.462 +      return value[method].apply(value, args);
   1.463 +    });
   1.464 +  },
   1.465 +
   1.466 +  max: function(iterator) {
   1.467 +    var result;
   1.468 +    this.each(function(value, index) {
   1.469 +      value = (iterator || Prototype.K)(value, index);
   1.470 +      if (result == undefined || value >= result)
   1.471 +        result = value;
   1.472 +    });
   1.473 +    return result;
   1.474 +  },
   1.475 +
   1.476 +  min: function(iterator) {
   1.477 +    var result;
   1.478 +    this.each(function(value, index) {
   1.479 +      value = (iterator || Prototype.K)(value, index);
   1.480 +      if (result == undefined || value < result)
   1.481 +        result = value;
   1.482 +    });
   1.483 +    return result;
   1.484 +  },
   1.485 +
   1.486 +  partition: function(iterator) {
   1.487 +    var trues = [], falses = [];
   1.488 +    this.each(function(value, index) {
   1.489 +      ((iterator || Prototype.K)(value, index) ?
   1.490 +        trues : falses).push(value);
   1.491 +    });
   1.492 +    return [trues, falses];
   1.493 +  },
   1.494 +
   1.495 +  pluck: function(property) {
   1.496 +    var results = [];
   1.497 +    this.each(function(value, index) {
   1.498 +      results.push(value[property]);
   1.499 +    });
   1.500 +    return results;
   1.501 +  },
   1.502 +
   1.503 +  reject: function(iterator) {
   1.504 +    var results = [];
   1.505 +    this.each(function(value, index) {
   1.506 +      if (!iterator(value, index))
   1.507 +        results.push(value);
   1.508 +    });
   1.509 +    return results;
   1.510 +  },
   1.511 +
   1.512 +  sortBy: function(iterator) {
   1.513 +    return this.map(function(value, index) {
   1.514 +      return {value: value, criteria: iterator(value, index)};
   1.515 +    }).sort(function(left, right) {
   1.516 +      var a = left.criteria, b = right.criteria;
   1.517 +      return a < b ? -1 : a > b ? 1 : 0;
   1.518 +    }).pluck('value');
   1.519 +  },
   1.520 +
   1.521 +  toArray: function() {
   1.522 +    return this.map();
   1.523 +  },
   1.524 +
   1.525 +  zip: function() {
   1.526 +    var iterator = Prototype.K, args = $A(arguments);
   1.527 +    if (typeof args.last() == 'function')
   1.528 +      iterator = args.pop();
   1.529 +
   1.530 +    var collections = [this].concat(args).map($A);
   1.531 +    return this.map(function(value, index) {
   1.532 +      return iterator(collections.pluck(index));
   1.533 +    });
   1.534 +  },
   1.535 +
   1.536 +  size: function() {
   1.537 +    return this.toArray().length;
   1.538 +  },
   1.539 +
   1.540 +  inspect: function() {
   1.541 +    return '#<Enumerable:' + this.toArray().inspect() + '>';
   1.542 +  }
   1.543 +}
   1.544 +
   1.545 +Object.extend(Enumerable, {
   1.546 +  map:     Enumerable.collect,
   1.547 +  find:    Enumerable.detect,
   1.548 +  select:  Enumerable.findAll,
   1.549 +  member:  Enumerable.include,
   1.550 +  entries: Enumerable.toArray
   1.551 +});
   1.552 +var $A = Array.from = function(iterable) {
   1.553 +  if (!iterable) return [];
   1.554 +  if (iterable.toArray) {
   1.555 +    return iterable.toArray();
   1.556 +  } else {
   1.557 +    var results = [];
   1.558 +    for (var i = 0, length = iterable.length; i < length; i++)
   1.559 +      results.push(iterable[i]);
   1.560 +    return results;
   1.561 +  }
   1.562 +}
   1.563 +
   1.564 +Object.extend(Array.prototype, Enumerable);
   1.565 +
   1.566 +if (!Array.prototype._reverse)
   1.567 +  Array.prototype._reverse = Array.prototype.reverse;
   1.568 +
   1.569 +Object.extend(Array.prototype, {
   1.570 +  _each: function(iterator) {
   1.571 +    for (var i = 0, length = this.length; i < length; i++)
   1.572 +      iterator(this[i]);
   1.573 +  },
   1.574 +
   1.575 +  clear: function() {
   1.576 +    this.length = 0;
   1.577 +    return this;
   1.578 +  },
   1.579 +
   1.580 +  first: function() {
   1.581 +    return this[0];
   1.582 +  },
   1.583 +
   1.584 +  last: function() {
   1.585 +    return this[this.length - 1];
   1.586 +  },
   1.587 +
   1.588 +  compact: function() {
   1.589 +    return this.select(function(value) {
   1.590 +      return value != null;
   1.591 +    });
   1.592 +  },
   1.593 +
   1.594 +  flatten: function() {
   1.595 +    return this.inject([], function(array, value) {
   1.596 +      return array.concat(value && value.constructor == Array ?
   1.597 +        value.flatten() : [value]);
   1.598 +    });
   1.599 +  },
   1.600 +
   1.601 +  without: function() {
   1.602 +    var values = $A(arguments);
   1.603 +    return this.select(function(value) {
   1.604 +      return !values.include(value);
   1.605 +    });
   1.606 +  },
   1.607 +
   1.608 +  indexOf: function(object) {
   1.609 +    for (var i = 0, length = this.length; i < length; i++)
   1.610 +      if (this[i] == object) return i;
   1.611 +    return -1;
   1.612 +  },
   1.613 +
   1.614 +  reverse: function(inline) {
   1.615 +    return (inline !== false ? this : this.toArray())._reverse();
   1.616 +  },
   1.617 +
   1.618 +  reduce: function() {
   1.619 +    return this.length > 1 ? this : this[0];
   1.620 +  },
   1.621 +
   1.622 +  uniq: function() {
   1.623 +    return this.inject([], function(array, value) {
   1.624 +      return array.include(value) ? array : array.concat([value]);
   1.625 +    });
   1.626 +  },
   1.627 +
   1.628 +  clone: function() {
   1.629 +    return [].concat(this);
   1.630 +  },
   1.631 +
   1.632 +  size: function() {
   1.633 +    return this.length;
   1.634 +  },
   1.635 +
   1.636 +  inspect: function() {
   1.637 +    return '[' + this.map(Object.inspect).join(', ') + ']';
   1.638 +  }
   1.639 +});
   1.640 +
   1.641 +Array.prototype.toArray = Array.prototype.clone;
   1.642 +
   1.643 +function $w(string){
   1.644 +  string = string.strip();
   1.645 +  return string ? string.split(/\s+/) : [];
   1.646 +}
   1.647 +
   1.648 +if(window.opera){
   1.649 +  Array.prototype.concat = function(){
   1.650 +    var array = [];
   1.651 +    for(var i = 0, length = this.length; i < length; i++) array.push(this[i]);
   1.652 +    for(var i = 0, length = arguments.length; i < length; i++) {
   1.653 +      if(arguments[i].constructor == Array) {
   1.654 +        for(var j = 0, arrayLength = arguments[i].length; j < arrayLength; j++)
   1.655 +          array.push(arguments[i][j]);
   1.656 +      } else {
   1.657 +        array.push(arguments[i]);
   1.658 +      }
   1.659 +    }
   1.660 +    return array;
   1.661 +  }
   1.662 +}
   1.663 +var Hash = function(obj) {
   1.664 +  Object.extend(this, obj || {});
   1.665 +};
   1.666 +
   1.667 +Object.extend(Hash, {
   1.668 +  toQueryString: function(obj) {
   1.669 +    var parts = [];
   1.670 +
   1.671 +	  this.prototype._each.call(obj, function(pair) {
   1.672 +      if (!pair.key) return;
   1.673 +
   1.674 +      if (pair.value && pair.value.constructor == Array) {
   1.675 +        var values = pair.value.compact();
   1.676 +        if (values.length < 2) pair.value = values.reduce();
   1.677 +        else {
   1.678 +        	key = encodeURIComponent(pair.key);
   1.679 +          values.each(function(value) {
   1.680 +            value = value != undefined ? encodeURIComponent(value) : '';
   1.681 +            parts.push(key + '=' + encodeURIComponent(value));
   1.682 +          });
   1.683 +          return;
   1.684 +        }
   1.685 +      }
   1.686 +      if (pair.value == undefined) pair[1] = '';
   1.687 +      parts.push(pair.map(encodeURIComponent).join('='));
   1.688 +	  });
   1.689 +
   1.690 +    return parts.join('&');
   1.691 +  }
   1.692 +});
   1.693 +
   1.694 +Object.extend(Hash.prototype, Enumerable);
   1.695 +Object.extend(Hash.prototype, {
   1.696 +  _each: function(iterator) {
   1.697 +    for (var key in this) {
   1.698 +      var value = this[key];
   1.699 +      if (value && value == Hash.prototype[key]) continue;
   1.700 +
   1.701 +      var pair = [key, value];
   1.702 +      pair.key = key;
   1.703 +      pair.value = value;
   1.704 +      iterator(pair);
   1.705 +    }
   1.706 +  },
   1.707 +
   1.708 +  keys: function() {
   1.709 +    return this.pluck('key');
   1.710 +  },
   1.711 +
   1.712 +  values: function() {
   1.713 +    return this.pluck('value');
   1.714 +  },
   1.715 +
   1.716 +  merge: function(hash) {
   1.717 +    return $H(hash).inject(this, function(mergedHash, pair) {
   1.718 +      mergedHash[pair.key] = pair.value;
   1.719 +      return mergedHash;
   1.720 +    });
   1.721 +  },
   1.722 +
   1.723 +  remove: function() {
   1.724 +    var result;
   1.725 +    for(var i = 0, length = arguments.length; i < length; i++) {
   1.726 +      var value = this[arguments[i]];
   1.727 +      if (value !== undefined){
   1.728 +        if (result === undefined) result = value;
   1.729 +        else {
   1.730 +          if (result.constructor != Array) result = [result];
   1.731 +          result.push(value)
   1.732 +        }
   1.733 +      }
   1.734 +      delete this[arguments[i]];
   1.735 +    }
   1.736 +    return result;
   1.737 +  },
   1.738 +
   1.739 +  toQueryString: function() {
   1.740 +    return Hash.toQueryString(this);
   1.741 +  },
   1.742 +
   1.743 +  inspect: function() {
   1.744 +    return '#<Hash:{' + this.map(function(pair) {
   1.745 +      return pair.map(Object.inspect).join(': ');
   1.746 +    }).join(', ') + '}>';
   1.747 +  }
   1.748 +});
   1.749 +
   1.750 +function $H(object) {
   1.751 +  if (object && object.constructor == Hash) return object;
   1.752 +  return new Hash(object);
   1.753 +};
   1.754 +ObjectRange = Class.create();
   1.755 +Object.extend(ObjectRange.prototype, Enumerable);
   1.756 +Object.extend(ObjectRange.prototype, {
   1.757 +  initialize: function(start, end, exclusive) {
   1.758 +    this.start = start;
   1.759 +    this.end = end;
   1.760 +    this.exclusive = exclusive;
   1.761 +  },
   1.762 +
   1.763 +  _each: function(iterator) {
   1.764 +    var value = this.start;
   1.765 +    while (this.include(value)) {
   1.766 +      iterator(value);
   1.767 +      value = value.succ();
   1.768 +    }
   1.769 +  },
   1.770 +
   1.771 +  include: function(value) {
   1.772 +    if (value < this.start)
   1.773 +      return false;
   1.774 +    if (this.exclusive)
   1.775 +      return value < this.end;
   1.776 +    return value <= this.end;
   1.777 +  }
   1.778 +});
   1.779 +
   1.780 +var $R = function(start, end, exclusive) {
   1.781 +  return new ObjectRange(start, end, exclusive);
   1.782 +}
   1.783 +
   1.784 +var Ajax = {
   1.785 +  getTransport: function() {
   1.786 +    return Try.these(
   1.787 +      function() {return new XMLHttpRequest()},
   1.788 +      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
   1.789 +      function() {return new ActiveXObject('Microsoft.XMLHTTP')}
   1.790 +    ) || false;
   1.791 +  },
   1.792 +
   1.793 +  activeRequestCount: 0
   1.794 +}
   1.795 +
   1.796 +Ajax.Responders = {
   1.797 +  responders: [],
   1.798 +
   1.799 +  _each: function(iterator) {
   1.800 +    this.responders._each(iterator);
   1.801 +  },
   1.802 +
   1.803 +  register: function(responder) {
   1.804 +    if (!this.include(responder))
   1.805 +      this.responders.push(responder);
   1.806 +  },
   1.807 +
   1.808 +  unregister: function(responder) {
   1.809 +    this.responders = this.responders.without(responder);
   1.810 +  },
   1.811 +
   1.812 +  dispatch: function(callback, request, transport, json) {
   1.813 +    this.each(function(responder) {
   1.814 +      if (typeof responder[callback] == 'function') {
   1.815 +        try {
   1.816 +          responder[callback].apply(responder, [request, transport, json]);
   1.817 +        } catch (e) {}
   1.818 +      }
   1.819 +    });
   1.820 +  }
   1.821 +};
   1.822 +
   1.823 +Object.extend(Ajax.Responders, Enumerable);
   1.824 +
   1.825 +Ajax.Responders.register({
   1.826 +  onCreate: function() {
   1.827 +    Ajax.activeRequestCount++;
   1.828 +  },
   1.829 +  onComplete: function() {
   1.830 +    Ajax.activeRequestCount--;
   1.831 +  }
   1.832 +});
   1.833 +
   1.834 +Ajax.Base = function() {};
   1.835 +Ajax.Base.prototype = {
   1.836 +  setOptions: function(options) {
   1.837 +    this.options = {
   1.838 +      method:       'post',
   1.839 +      asynchronous: true,
   1.840 +      contentType:  'application/x-www-form-urlencoded',
   1.841 +      encoding:     'UTF-8',
   1.842 +      parameters:   ''
   1.843 +    }
   1.844 +    Object.extend(this.options, options || {});
   1.845 +
   1.846 +    this.options.method = this.options.method.toLowerCase();
   1.847 +    if (typeof this.options.parameters == 'string')
   1.848 +      this.options.parameters = this.options.parameters.toQueryParams();
   1.849 +  }
   1.850 +}
   1.851 +
   1.852 +Ajax.Request = Class.create();
   1.853 +Ajax.Request.Events =
   1.854 +  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
   1.855 +
   1.856 +Ajax.Request.prototype = Object.extend(new Ajax.Base(), {
   1.857 +  _complete: false,
   1.858 +
   1.859 +  initialize: function(url, options) {
   1.860 +    this.transport = Ajax.getTransport();
   1.861 +    this.setOptions(options);
   1.862 +    this.request(url);
   1.863 +  },
   1.864 +
   1.865 +  request: function(url) {
   1.866 +    this.url = url;
   1.867 +    this.method = this.options.method;
   1.868 +    var params = this.options.parameters;
   1.869 +
   1.870 +    if (!['get', 'post'].include(this.method)) {
   1.871 +      // simulate other verbs over post
   1.872 +      params['_method'] = this.method;
   1.873 +      this.method = 'post';
   1.874 +    }
   1.875 +
   1.876 +    params = Hash.toQueryString(params);
   1.877 +    if (params && /Konqueror|Safari|KHTML/.test(navigator.userAgent)) params += '&_='
   1.878 +
   1.879 +    // when GET, append parameters to URL
   1.880 +    if (this.method == 'get' && params)
   1.881 +      this.url += (this.url.indexOf('?') > -1 ? '&' : '?') + params;
   1.882 +
   1.883 +    try {
   1.884 +      Ajax.Responders.dispatch('onCreate', this, this.transport);
   1.885 +
   1.886 +      this.transport.open(this.method.toUpperCase(), this.url,
   1.887 +        this.options.asynchronous);
   1.888 +
   1.889 +      if (this.options.asynchronous)
   1.890 +        setTimeout(function() { this.respondToReadyState(1) }.bind(this), 10);
   1.891 +
   1.892 +      this.transport.onreadystatechange = this.onStateChange.bind(this);
   1.893 +      this.setRequestHeaders();
   1.894 +
   1.895 +      var body = this.method == 'post' ? (this.options.postBody || params) : null;
   1.896 +
   1.897 +      this.transport.send(body);
   1.898 +
   1.899 +      /* Force Firefox to handle ready state 4 for synchronous requests */
   1.900 +      if (!this.options.asynchronous && this.transport.overrideMimeType)
   1.901 +        this.onStateChange();
   1.902 +
   1.903 +    }
   1.904 +    catch (e) {
   1.905 +      this.dispatchException(e);
   1.906 +    }
   1.907 +  },
   1.908 +
   1.909 +  onStateChange: function() {
   1.910 +    var readyState = this.transport.readyState;
   1.911 +    if (readyState > 1 && !((readyState == 4) && this._complete))
   1.912 +      this.respondToReadyState(this.transport.readyState);
   1.913 +  },
   1.914 +
   1.915 +  setRequestHeaders: function() {
   1.916 +    var headers = {
   1.917 +      'X-Requested-With': 'XMLHttpRequest',
   1.918 +      'X-Prototype-Version': Prototype.Version,
   1.919 +      'Accept': 'text/javascript, text/html, application/xml, text/xml, */*'
   1.920 +    };
   1.921 +
   1.922 +    if (this.method == 'post') {
   1.923 +      headers['Content-type'] = this.options.contentType +
   1.924 +        (this.options.encoding ? '; charset=' + this.options.encoding : '');
   1.925 +
   1.926 +      /* Force "Connection: close" for older Mozilla browsers to work
   1.927 +       * around a bug where XMLHttpRequest sends an incorrect
   1.928 +       * Content-length header. See Mozilla Bugzilla #246651.
   1.929 +       */
   1.930 +      if (this.transport.overrideMimeType &&
   1.931 +          (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005)
   1.932 +            headers['Connection'] = 'close';
   1.933 +    }
   1.934 +
   1.935 +    // user-defined headers
   1.936 +    if (typeof this.options.requestHeaders == 'object') {
   1.937 +      var extras = this.options.requestHeaders;
   1.938 +
   1.939 +      if (typeof extras.push == 'function')
   1.940 +        for (var i = 0, length = extras.length; i < length; i += 2)
   1.941 +          headers[extras[i]] = extras[i+1];
   1.942 +      else
   1.943 +        $H(extras).each(function(pair) { headers[pair.key] = pair.value });
   1.944 +    }
   1.945 +
   1.946 +    for (var name in headers)
   1.947 +      this.transport.setRequestHeader(name, headers[name]);
   1.948 +  },
   1.949 +
   1.950 +  success: function() {
   1.951 +    return !this.transport.status
   1.952 +        || (this.transport.status >= 200 && this.transport.status < 300);
   1.953 +  },
   1.954 +
   1.955 +  respondToReadyState: function(readyState) {
   1.956 +    var state = Ajax.Request.Events[readyState];
   1.957 +    var transport = this.transport, json = this.evalJSON();
   1.958 +
   1.959 +    if (state == 'Complete') {
   1.960 +      try {
   1.961 +        this._complete = true;
   1.962 +        (this.options['on' + this.transport.status]
   1.963 +         || this.options['on' + (this.success() ? 'Success' : 'Failure')]
   1.964 +         || Prototype.emptyFunction)(transport, json);
   1.965 +      } catch (e) {
   1.966 +        this.dispatchException(e);
   1.967 +      }
   1.968 +
   1.969 +      if ((this.getHeader('Content-type') || 'text/javascript').strip().
   1.970 +        match(/^(text|application)\/(x-)?(java|ecma)script(;.*)?$/i))
   1.971 +          this.evalResponse();
   1.972 +    }
   1.973 +
   1.974 +    try {
   1.975 +      (this.options['on' + state] || Prototype.emptyFunction)(transport, json);
   1.976 +      Ajax.Responders.dispatch('on' + state, this, transport, json);
   1.977 +    } catch (e) {
   1.978 +      this.dispatchException(e);
   1.979 +    }
   1.980 +
   1.981 +    if (state == 'Complete') {
   1.982 +      // avoid memory leak in MSIE: clean up
   1.983 +      this.transport.onreadystatechange = Prototype.emptyFunction;
   1.984 +    }
   1.985 +  },
   1.986 +
   1.987 +  getHeader: function(name) {
   1.988 +    try {
   1.989 +      return this.transport.getResponseHeader(name);
   1.990 +    } catch (e) { return null }
   1.991 +  },
   1.992 +
   1.993 +  evalJSON: function() {
   1.994 +    try {
   1.995 +      var json = this.getHeader('X-JSON');
   1.996 +      return json ? eval('(' + json + ')') : null;
   1.997 +    } catch (e) { return null }
   1.998 +  },
   1.999 +
  1.1000 +  evalResponse: function() {
  1.1001 +    try {
  1.1002 +      return eval(this.transport.responseText);
  1.1003 +    } catch (e) {
  1.1004 +      this.dispatchException(e);
  1.1005 +    }
  1.1006 +  },
  1.1007 +
  1.1008 +  dispatchException: function(exception) {
  1.1009 +    (this.options.onException || Prototype.emptyFunction)(this, exception);
  1.1010 +    Ajax.Responders.dispatch('onException', this, exception);
  1.1011 +  }
  1.1012 +});
  1.1013 +
  1.1014 +Ajax.Updater = Class.create();
  1.1015 +
  1.1016 +Object.extend(Object.extend(Ajax.Updater.prototype, Ajax.Request.prototype), {
  1.1017 +  initialize: function(container, url, options) {
  1.1018 +    this.container = {
  1.1019 +      success: (container.success || container),
  1.1020 +      failure: (container.failure || (container.success ? null : container))
  1.1021 +    }
  1.1022 +
  1.1023 +    this.transport = Ajax.getTransport();
  1.1024 +    this.setOptions(options);
  1.1025 +
  1.1026 +    var onComplete = this.options.onComplete || Prototype.emptyFunction;
  1.1027 +    this.options.onComplete = (function(transport, param) {
  1.1028 +      this.updateContent();
  1.1029 +      onComplete(transport, param);
  1.1030 +    }).bind(this);
  1.1031 +
  1.1032 +    this.request(url);
  1.1033 +  },
  1.1034 +
  1.1035 +  updateContent: function() {
  1.1036 +    var receiver = this.container[this.success() ? 'success' : 'failure'];
  1.1037 +    var response = this.transport.responseText;
  1.1038 +
  1.1039 +    if (!this.options.evalScripts) response = response.stripScripts();
  1.1040 +
  1.1041 +    if (receiver = $(receiver)) {
  1.1042 +      if (this.options.insertion)
  1.1043 +        new this.options.insertion(receiver, response);
  1.1044 +      else
  1.1045 +        receiver.update(response);
  1.1046 +    }
  1.1047 +
  1.1048 +    if (this.success()) {
  1.1049 +      if (this.onComplete)
  1.1050 +        setTimeout(this.onComplete.bind(this), 10);
  1.1051 +    }
  1.1052 +  }
  1.1053 +});
  1.1054 +
  1.1055 +Ajax.PeriodicalUpdater = Class.create();
  1.1056 +Ajax.PeriodicalUpdater.prototype = Object.extend(new Ajax.Base(), {
  1.1057 +  initialize: function(container, url, options) {
  1.1058 +    this.setOptions(options);
  1.1059 +    this.onComplete = this.options.onComplete;
  1.1060 +
  1.1061 +    this.frequency = (this.options.frequency || 2);
  1.1062 +    this.decay = (this.options.decay || 1);
  1.1063 +
  1.1064 +    this.updater = {};
  1.1065 +    this.container = container;
  1.1066 +    this.url = url;
  1.1067 +
  1.1068 +    this.start();
  1.1069 +  },
  1.1070 +
  1.1071 +  start: function() {
  1.1072 +    this.options.onComplete = this.updateComplete.bind(this);
  1.1073 +    this.onTimerEvent();
  1.1074 +  },
  1.1075 +
  1.1076 +  stop: function() {
  1.1077 +    this.updater.options.onComplete = undefined;
  1.1078 +    clearTimeout(this.timer);
  1.1079 +    (this.onComplete || Prototype.emptyFunction).apply(this, arguments);
  1.1080 +  },
  1.1081 +
  1.1082 +  updateComplete: function(request) {
  1.1083 +    if (this.options.decay) {
  1.1084 +      this.decay = (request.responseText == this.lastText ?
  1.1085 +        this.decay * this.options.decay : 1);
  1.1086 +
  1.1087 +      this.lastText = request.responseText;
  1.1088 +    }
  1.1089 +    this.timer = setTimeout(this.onTimerEvent.bind(this),
  1.1090 +      this.decay * this.frequency * 1000);
  1.1091 +  },
  1.1092 +
  1.1093 +  onTimerEvent: function() {
  1.1094 +    this.updater = new Ajax.Updater(this.container, this.url, this.options);
  1.1095 +  }
  1.1096 +});
  1.1097 +function $(element) {
  1.1098 +  if (arguments.length > 1) {
  1.1099 +    for (var i = 0, elements = [], length = arguments.length; i < length; i++)
  1.1100 +      elements.push($(arguments[i]));
  1.1101 +    return elements;
  1.1102 +  }
  1.1103 +  if (typeof element == 'string')
  1.1104 +    element = document.getElementById(element);
  1.1105 +  return Element.extend(element);
  1.1106 +}
  1.1107 +
  1.1108 +if (Prototype.BrowserFeatures.XPath) {
  1.1109 +  document._getElementsByXPath = function(expression, parentElement) {
  1.1110 +    var results = [];
  1.1111 +    var query = document.evaluate(expression, $(parentElement) || document,
  1.1112 +      null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
  1.1113 +    for (var i = 0, length = query.snapshotLength; i < length; i++)
  1.1114 +      results.push(query.snapshotItem(i));
  1.1115 +    return results;
  1.1116 +  };
  1.1117 +}
  1.1118 +
  1.1119 +document.getElementsByClassName = function(className, parentElement) {
  1.1120 +  if (Prototype.BrowserFeatures.XPath) {
  1.1121 +    var q = ".//*[contains(concat(' ', @class, ' '), ' " + className + " ')]";
  1.1122 +    return document._getElementsByXPath(q, parentElement);
  1.1123 +  } else {
  1.1124 +    var children = ($(parentElement) || document.body).getElementsByTagName('*');
  1.1125 +    var elements = [], child;
  1.1126 +    for (var i = 0, length = children.length; i < length; i++) {
  1.1127 +      child = children[i];
  1.1128 +      if (Element.hasClassName(child, className))
  1.1129 +        elements.push(Element.extend(child));
  1.1130 +    }
  1.1131 +    return elements;
  1.1132 +  }
  1.1133 +};
  1.1134 +
  1.1135 +/*--------------------------------------------------------------------------*/
  1.1136 +
  1.1137 +if (!window.Element)
  1.1138 +  var Element = new Object();
  1.1139 +
  1.1140 +Element.extend = function(element) {
  1.1141 +  if (!element || _nativeExtensions || element.nodeType == 3) return element;
  1.1142 +
  1.1143 +  if (!element._extended && element.tagName && element != window) {
  1.1144 +    var methods = Object.clone(Element.Methods), cache = Element.extend.cache;
  1.1145 +
  1.1146 +    if (element.tagName == 'FORM')
  1.1147 +      Object.extend(methods, Form.Methods);
  1.1148 +    if (['INPUT', 'TEXTAREA', 'SELECT'].include(element.tagName))
  1.1149 +      Object.extend(methods, Form.Element.Methods);
  1.1150 +
  1.1151 +    Object.extend(methods, Element.Methods.Simulated);
  1.1152 +
  1.1153 +    for (var property in methods) {
  1.1154 +      var value = methods[property];
  1.1155 +      if (typeof value == 'function' && !(property in element))
  1.1156 +        element[property] = cache.findOrStore(value);
  1.1157 +    }
  1.1158 +  }
  1.1159 +
  1.1160 +  element._extended = true;
  1.1161 +  return element;
  1.1162 +};
  1.1163 +
  1.1164 +Element.extend.cache = {
  1.1165 +  findOrStore: function(value) {
  1.1166 +    return this[value] = this[value] || function() {
  1.1167 +      return value.apply(null, [this].concat($A(arguments)));
  1.1168 +    }
  1.1169 +  }
  1.1170 +};
  1.1171 +
  1.1172 +Element.Methods = {
  1.1173 +  visible: function(element) {
  1.1174 +    return $(element).style.display != 'none';
  1.1175 +  },
  1.1176 +
  1.1177 +  toggle: function(element) {
  1.1178 +    element = $(element);
  1.1179 +    Element[Element.visible(element) ? 'hide' : 'show'](element);
  1.1180 +    return element;
  1.1181 +  },
  1.1182 +
  1.1183 +  hide: function(element) {
  1.1184 +    $(element).style.display = 'none';
  1.1185 +    return element;
  1.1186 +  },
  1.1187 +
  1.1188 +  show: function(element) {
  1.1189 +    $(element).style.display = '';
  1.1190 +    return element;
  1.1191 +  },
  1.1192 +
  1.1193 +  remove: function(element) {
  1.1194 +    element = $(element);
  1.1195 +    element.parentNode.removeChild(element);
  1.1196 +    return element;
  1.1197 +  },
  1.1198 +
  1.1199 +  update: function(element, html) {
  1.1200 +    html = typeof html == 'undefined' ? '' : html.toString();
  1.1201 +    $(element).innerHTML = html.stripScripts();
  1.1202 +    setTimeout(function() {html.evalScripts()}, 10);
  1.1203 +    return element;
  1.1204 +  },
  1.1205 +
  1.1206 +  replace: function(element, html) {
  1.1207 +    element = $(element);
  1.1208 +    html = typeof html == 'undefined' ? '' : html.toString();
  1.1209 +    if (element.outerHTML) {
  1.1210 +      element.outerHTML = html.stripScripts();
  1.1211 +    } else {
  1.1212 +      var range = element.ownerDocument.createRange();
  1.1213 +      range.selectNodeContents(element);
  1.1214 +      element.parentNode.replaceChild(
  1.1215 +        range.createContextualFragment(html.stripScripts()), element);
  1.1216 +    }
  1.1217 +    setTimeout(function() {html.evalScripts()}, 10);
  1.1218 +    return element;
  1.1219 +  },
  1.1220 +
  1.1221 +  inspect: function(element) {
  1.1222 +    element = $(element);
  1.1223 +    var result = '<' + element.tagName.toLowerCase();
  1.1224 +    $H({'id': 'id', 'className': 'class'}).each(function(pair) {
  1.1225 +      var property = pair.first(), attribute = pair.last();
  1.1226 +      var value = (element[property] || '').toString();
  1.1227 +      if (value) result += ' ' + attribute + '=' + value.inspect(true);
  1.1228 +    });
  1.1229 +    return result + '>';
  1.1230 +  },
  1.1231 +
  1.1232 +  recursivelyCollect: function(element, property) {
  1.1233 +    element = $(element);
  1.1234 +    var elements = [];
  1.1235 +    while (element = element[property])
  1.1236 +      if (element.nodeType == 1)
  1.1237 +        elements.push(Element.extend(element));
  1.1238 +    return elements;
  1.1239 +  },
  1.1240 +
  1.1241 +  ancestors: function(element) {
  1.1242 +    return $(element).recursivelyCollect('parentNode');
  1.1243 +  },
  1.1244 +
  1.1245 +  descendants: function(element) {
  1.1246 +    return $A($(element).getElementsByTagName('*'));
  1.1247 +  },
  1.1248 +
  1.1249 +  immediateDescendants: function(element) {
  1.1250 +    if (!(element = $(element).firstChild)) return [];
  1.1251 +    while (element && element.nodeType != 1) element = element.nextSibling;
  1.1252 +    if (element) return [element].concat($(element).nextSiblings());
  1.1253 +    return [];
  1.1254 +  },
  1.1255 +
  1.1256 +  previousSiblings: function(element) {
  1.1257 +    return $(element).recursivelyCollect('previousSibling');
  1.1258 +  },
  1.1259 +
  1.1260 +  nextSiblings: function(element) {
  1.1261 +    return $(element).recursivelyCollect('nextSibling');
  1.1262 +  },
  1.1263 +
  1.1264 +  siblings: function(element) {
  1.1265 +    element = $(element);
  1.1266 +    return element.previousSiblings().reverse().concat(element.nextSiblings());
  1.1267 +  },
  1.1268 +
  1.1269 +  match: function(element, selector) {
  1.1270 +    if (typeof selector == 'string')
  1.1271 +      selector = new Selector(selector);
  1.1272 +    return selector.match($(element));
  1.1273 +  },
  1.1274 +
  1.1275 +  up: function(element, expression, index) {
  1.1276 +    return Selector.findElement($(element).ancestors(), expression, index);
  1.1277 +  },
  1.1278 +
  1.1279 +  down: function(element, expression, index) {
  1.1280 +    return Selector.findElement($(element).descendants(), expression, index);
  1.1281 +  },
  1.1282 +
  1.1283 +  previous: function(element, expression, index) {
  1.1284 +    return Selector.findElement($(element).previousSiblings(), expression, index);
  1.1285 +  },
  1.1286 +
  1.1287 +  next: function(element, expression, index) {
  1.1288 +    return Selector.findElement($(element).nextSiblings(), expression, index);
  1.1289 +  },
  1.1290 +
  1.1291 +  getElementsBySelector: function() {
  1.1292 +    var args = $A(arguments), element = $(args.shift());
  1.1293 +    return Selector.findChildElements(element, args);
  1.1294 +  },
  1.1295 +
  1.1296 +  getElementsByClassName: function(element, className) {
  1.1297 +    return document.getElementsByClassName(className, element);
  1.1298 +  },
  1.1299 +
  1.1300 +  readAttribute: function(element, name) {
  1.1301 +    element = $(element);
  1.1302 +    if (document.all && !window.opera) {
  1.1303 +      var t = Element._attributeTranslations;
  1.1304 +      if (t.values[name]) return t.values[name](element, name);
  1.1305 +      if (t.names[name])  name = t.names[name];
  1.1306 +      var attribute = element.attributes[name];
  1.1307 +      if(attribute) return attribute.nodeValue;
  1.1308 +    }
  1.1309 +    return element.getAttribute(name);
  1.1310 +  },
  1.1311 +
  1.1312 +  getHeight: function(element) {
  1.1313 +    return $(element).getDimensions().height;
  1.1314 +  },
  1.1315 +
  1.1316 +  getWidth: function(element) {
  1.1317 +    return $(element).getDimensions().width;
  1.1318 +  },
  1.1319 +
  1.1320 +  classNames: function(element) {
  1.1321 +    return new Element.ClassNames(element);
  1.1322 +  },
  1.1323 +
  1.1324 +  hasClassName: function(element, className) {
  1.1325 +    if (!(element = $(element))) return;
  1.1326 +    var elementClassName = element.className;
  1.1327 +    if (elementClassName.length == 0) return false;
  1.1328 +    if (elementClassName == className ||
  1.1329 +        elementClassName.match(new RegExp("(^|\\s)" + className + "(\\s|$)")))
  1.1330 +      return true;
  1.1331 +    return false;
  1.1332 +  },
  1.1333 +
  1.1334 +  addClassName: function(element, className) {
  1.1335 +    if (!(element = $(element))) return;
  1.1336 +    Element.classNames(element).add(className);
  1.1337 +    return element;
  1.1338 +  },
  1.1339 +
  1.1340 +  removeClassName: function(element, className) {
  1.1341 +    if (!(element = $(element))) return;
  1.1342 +    Element.classNames(element).remove(className);
  1.1343 +    return element;
  1.1344 +  },
  1.1345 +
  1.1346 +  toggleClassName: function(element, className) {
  1.1347 +    if (!(element = $(element))) return;
  1.1348 +    Element.classNames(element)[element.hasClassName(className) ? 'remove' : 'add'](className);
  1.1349 +    return element;
  1.1350 +  },
  1.1351 +
  1.1352 +  observe: function() {
  1.1353 +    Event.observe.apply(Event, arguments);
  1.1354 +    return $A(arguments).first();
  1.1355 +  },
  1.1356 +
  1.1357 +  stopObserving: function() {
  1.1358 +    Event.stopObserving.apply(Event, arguments);
  1.1359 +    return $A(arguments).first();
  1.1360 +  },
  1.1361 +
  1.1362 +  // removes whitespace-only text node children
  1.1363 +  cleanWhitespace: function(element) {
  1.1364 +    element = $(element);
  1.1365 +    var node = element.firstChild;
  1.1366 +    while (node) {
  1.1367 +      var nextNode = node.nextSibling;
  1.1368 +      if (node.nodeType == 3 && !/\S/.test(node.nodeValue))
  1.1369 +        element.removeChild(node);
  1.1370 +      node = nextNode;
  1.1371 +    }
  1.1372 +    return element;
  1.1373 +  },
  1.1374 +
  1.1375 +  empty: function(element) {
  1.1376 +    return $(element).innerHTML.match(/^\s*$/);
  1.1377 +  },
  1.1378 +
  1.1379 +  descendantOf: function(element, ancestor) {
  1.1380 +    element = $(element), ancestor = $(ancestor);
  1.1381 +    while (element = element.parentNode)
  1.1382 +      if (element == ancestor) return true;
  1.1383 +    return false;
  1.1384 +  },
  1.1385 +
  1.1386 +  scrollTo: function(element) {
  1.1387 +    element = $(element);
  1.1388 +    var pos = Position.cumulativeOffset(element);
  1.1389 +    window.scrollTo(pos[0], pos[1]);
  1.1390 +    return element;
  1.1391 +  },
  1.1392 +
  1.1393 +  getStyle: function(element, style) {
  1.1394 +    element = $(element);
  1.1395 +    if (['float','cssFloat'].include(style))
  1.1396 +      style = (typeof element.style.styleFloat != 'undefined' ? 'styleFloat' : 'cssFloat');
  1.1397 +    style = style.camelize();
  1.1398 +    var value = element.style[style];
  1.1399 +    if (!value) {
  1.1400 +      if (document.defaultView && document.defaultView.getComputedStyle) {
  1.1401 +        var css = document.defaultView.getComputedStyle(element, null);
  1.1402 +        value = css ? css[style] : null;
  1.1403 +      } else if (element.currentStyle) {
  1.1404 +        value = element.currentStyle[style];
  1.1405 +      }
  1.1406 +    }
  1.1407 +
  1.1408 +    if((value == 'auto') && ['width','height'].include(style) && (element.getStyle('display') != 'none'))
  1.1409 +      value = element['offset'+style.capitalize()] + 'px';
  1.1410 +
  1.1411 +    if (window.opera && ['left', 'top', 'right', 'bottom'].include(style))
  1.1412 +      if (Element.getStyle(element, 'position') == 'static') value = 'auto';
  1.1413 +    if(style == 'opacity') {
  1.1414 +      if(value) return parseFloat(value);
  1.1415 +      if(value = (element.getStyle('filter') || '').match(/alpha\(opacity=(.*)\)/))
  1.1416 +        if(value[1]) return parseFloat(value[1]) / 100;
  1.1417 +      return 1.0;
  1.1418 +    }
  1.1419 +    return value == 'auto' ? null : value;
  1.1420 +  },
  1.1421 +
  1.1422 +  setStyle: function(element, style) {
  1.1423 +    element = $(element);
  1.1424 +    for (var name in style) {
  1.1425 +      var value = style[name];
  1.1426 +      if(name == 'opacity') {
  1.1427 +        if (value == 1) {
  1.1428 +          value = (/Gecko/.test(navigator.userAgent) &&
  1.1429 +            !/Konqueror|Safari|KHTML/.test(navigator.userAgent)) ? 0.999999 : 1.0;
  1.1430 +          if(/MSIE/.test(navigator.userAgent) && !window.opera)
  1.1431 +            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
  1.1432 +        } else if(value == '') {
  1.1433 +          if(/MSIE/.test(navigator.userAgent) && !window.opera)
  1.1434 +            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'');
  1.1435 +        } else {
  1.1436 +          if(value < 0.00001) value = 0;
  1.1437 +          if(/MSIE/.test(navigator.userAgent) && !window.opera)
  1.1438 +            element.style.filter = element.getStyle('filter').replace(/alpha\([^\)]*\)/gi,'') +
  1.1439 +              'alpha(opacity='+value*100+')';
  1.1440 +        }
  1.1441 +      } else if(['float','cssFloat'].include(name)) name = (typeof element.style.styleFloat != 'undefined') ? 'styleFloat' : 'cssFloat';
  1.1442 +      element.style[name.camelize()] = value;
  1.1443 +    }
  1.1444 +    return element;
  1.1445 +  },
  1.1446 +
  1.1447 +  getDimensions: function(element) {
  1.1448 +    element = $(element);
  1.1449 +    var display = $(element).getStyle('display');
  1.1450 +    if (display != 'none' && display != null) // Safari bug
  1.1451 +      return {width: element.offsetWidth, height: element.offsetHeight};
  1.1452 +
  1.1453 +    // All *Width and *Height properties give 0 on elements with display none,
  1.1454 +    // so enable the element temporarily
  1.1455 +    var els = element.style;
  1.1456 +    var originalVisibility = els.visibility;
  1.1457 +    var originalPosition = els.position;
  1.1458 +    var originalDisplay = els.display;
  1.1459 +    els.visibility = 'hidden';
  1.1460 +    els.position = 'absolute';
  1.1461 +    els.display = 'block';
  1.1462 +    var originalWidth = element.clientWidth;
  1.1463 +    var originalHeight = element.clientHeight;
  1.1464 +    els.display = originalDisplay;
  1.1465 +    els.position = originalPosition;
  1.1466 +    els.visibility = originalVisibility;
  1.1467 +    return {width: originalWidth, height: originalHeight};
  1.1468 +  },
  1.1469 +
  1.1470 +  makePositioned: function(element) {
  1.1471 +    element = $(element);
  1.1472 +    var pos = Element.getStyle(element, 'position');
  1.1473 +    if (pos == 'static' || !pos) {
  1.1474 +      element._madePositioned = true;
  1.1475 +      element.style.position = 'relative';
  1.1476 +      // Opera returns the offset relative to the positioning context, when an
  1.1477 +      // element is position relative but top and left have not been defined
  1.1478 +      if (window.opera) {
  1.1479 +        element.style.top = 0;
  1.1480 +        element.style.left = 0;
  1.1481 +      }
  1.1482 +    }
  1.1483 +    return element;
  1.1484 +  },
  1.1485 +
  1.1486 +  undoPositioned: function(element) {
  1.1487 +    element = $(element);
  1.1488 +    if (element._madePositioned) {
  1.1489 +      element._madePositioned = undefined;
  1.1490 +      element.style.position =
  1.1491 +        element.style.top =
  1.1492 +        element.style.left =
  1.1493 +        element.style.bottom =
  1.1494 +        element.style.right = '';
  1.1495 +    }
  1.1496 +    return element;
  1.1497 +  },
  1.1498 +
  1.1499 +  makeClipping: function(element) {
  1.1500 +    element = $(element);
  1.1501 +    if (element._overflow) return element;
  1.1502 +    element._overflow = element.style.overflow || 'auto';
  1.1503 +    if ((Element.getStyle(element, 'overflow') || 'visible') != 'hidden')
  1.1504 +      element.style.overflow = 'hidden';
  1.1505 +    return element;
  1.1506 +  },
  1.1507 +
  1.1508 +  undoClipping: function(element) {
  1.1509 +    element = $(element);
  1.1510 +    if (!element._overflow) return element;
  1.1511 +    element.style.overflow = element._overflow == 'auto' ? '' : element._overflow;
  1.1512 +    element._overflow = null;
  1.1513 +    return element;
  1.1514 +  }
  1.1515 +};
  1.1516 +
  1.1517 +Object.extend(Element.Methods, {childOf: Element.Methods.descendantOf});
  1.1518 +
  1.1519 +Element._attributeTranslations = {};
  1.1520 +
  1.1521 +Element._attributeTranslations.names = {
  1.1522 +  colspan:   "colSpan",
  1.1523 +  rowspan:   "rowSpan",
  1.1524 +  valign:    "vAlign",
  1.1525 +  datetime:  "dateTime",
  1.1526 +  accesskey: "accessKey",
  1.1527 +  tabindex:  "tabIndex",
  1.1528 +  enctype:   "encType",
  1.1529 +  maxlength: "maxLength",
  1.1530 +  readonly:  "readOnly",
  1.1531 +  longdesc:  "longDesc"
  1.1532 +};
  1.1533 +
  1.1534 +Element._attributeTranslations.values = {
  1.1535 +  _getAttr: function(element, attribute) {
  1.1536 +    return element.getAttribute(attribute, 2);
  1.1537 +  },
  1.1538 +
  1.1539 +  _flag: function(element, attribute) {
  1.1540 +    return $(element).hasAttribute(attribute) ? attribute : null;
  1.1541 +  },
  1.1542 +
  1.1543 +  style: function(element) {
  1.1544 +    return element.style.cssText.toLowerCase();
  1.1545 +  },
  1.1546 +
  1.1547 +  title: function(element) {
  1.1548 +    var node = element.getAttributeNode('title');
  1.1549 +    return node.specified ? node.nodeValue : null;
  1.1550 +  }
  1.1551 +};
  1.1552 +
  1.1553 +Object.extend(Element._attributeTranslations.values, {
  1.1554 +  href: Element._attributeTranslations.values._getAttr,
  1.1555 +  src:  Element._attributeTranslations.values._getAttr,
  1.1556 +  disabled: Element._attributeTranslations.values._flag,
  1.1557 +  checked:  Element._attributeTranslations.values._flag,
  1.1558 +  readonly: Element._attributeTranslations.values._flag,
  1.1559 +  multiple: Element._attributeTranslations.values._flag
  1.1560 +});
  1.1561 +
  1.1562 +Element.Methods.Simulated = {
  1.1563 +  hasAttribute: function(element, attribute) {
  1.1564 +    var t = Element._attributeTranslations;
  1.1565 +    attribute = t.names[attribute] || attribute;
  1.1566 +    return $(element).getAttributeNode(attribute).specified;
  1.1567 +  }
  1.1568 +};
  1.1569 +
  1.1570 +// IE is missing .innerHTML support for TABLE-related elements
  1.1571 +if (document.all && !window.opera){
  1.1572 +  Element.Methods.update = function(element, html) {
  1.1573 +    element = $(element);
  1.1574 +    html = typeof html == 'undefined' ? '' : html.toString();
  1.1575 +    var tagName = element.tagName.toUpperCase();
  1.1576 +    if (['THEAD','TBODY','TR','TD'].include(tagName)) {
  1.1577 +      var div = document.createElement('div');
  1.1578 +      switch (tagName) {
  1.1579 +        case 'THEAD':
  1.1580 +        case 'TBODY':
  1.1581 +          div.innerHTML = '<table><tbody>' +  html.stripScripts() + '</tbody></table>';
  1.1582 +          depth = 2;
  1.1583 +          break;
  1.1584 +        case 'TR':
  1.1585 +          div.innerHTML = '<table><tbody><tr>' +  html.stripScripts() + '</tr></tbody></table>';
  1.1586 +          depth = 3;
  1.1587 +          break;
  1.1588 +        case 'TD':
  1.1589 +          div.innerHTML = '<table><tbody><tr><td>' +  html.stripScripts() + '</td></tr></tbody></table>';
  1.1590 +          depth = 4;
  1.1591 +      }
  1.1592 +      $A(element.childNodes).each(function(node){
  1.1593 +        element.removeChild(node)
  1.1594 +      });
  1.1595 +      depth.times(function(){ div = div.firstChild });
  1.1596 +
  1.1597 +      $A(div.childNodes).each(
  1.1598 +        function(node){ element.appendChild(node) });
  1.1599 +    } else {
  1.1600 +      element.innerHTML = html.stripScripts();
  1.1601 +    }
  1.1602 +    setTimeout(function() {html.evalScripts()}, 10);
  1.1603 +    return element;
  1.1604 +  }
  1.1605 +};
  1.1606 +
  1.1607 +Object.extend(Element, Element.Methods);
  1.1608 +
  1.1609 +var _nativeExtensions = false;
  1.1610 +
  1.1611 +if(/Konqueror|Safari|KHTML/.test(navigator.userAgent))
  1.1612 +  ['', 'Form', 'Input', 'TextArea', 'Select'].each(function(tag) {
  1.1613 +    var className = 'HTML' + tag + 'Element';
  1.1614 +    if(window[className]) return;
  1.1615 +    var klass = window[className] = {};
  1.1616 +    klass.prototype = document.createElement(tag ? tag.toLowerCase() : 'div').__proto__;
  1.1617 +  });
  1.1618 +
  1.1619 +Element.addMethods = function(methods) {
  1.1620 +  Object.extend(Element.Methods, methods || {});
  1.1621 +
  1.1622 +  function copy(methods, destination, onlyIfAbsent) {
  1.1623 +    onlyIfAbsent = onlyIfAbsent || false;
  1.1624 +    var cache = Element.extend.cache;
  1.1625 +    for (var property in methods) {
  1.1626 +      var value = methods[property];
  1.1627 +      if (!onlyIfAbsent || !(property in destination))
  1.1628 +        destination[property] = cache.findOrStore(value);
  1.1629 +    }
  1.1630 +  }
  1.1631 +
  1.1632 +  if (typeof HTMLElement != 'undefined') {
  1.1633 +    copy(Element.Methods, HTMLElement.prototype);
  1.1634 +    copy(Element.Methods.Simulated, HTMLElement.prototype, true);
  1.1635 +    copy(Form.Methods, HTMLFormElement.prototype);
  1.1636 +    [HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement].each(function(klass) {
  1.1637 +      copy(Form.Element.Methods, klass.prototype);
  1.1638 +    });
  1.1639 +    _nativeExtensions = true;
  1.1640 +  }
  1.1641 +}
  1.1642 +
  1.1643 +var Toggle = new Object();
  1.1644 +Toggle.display = Element.toggle;
  1.1645 +
  1.1646 +/*--------------------------------------------------------------------------*/
  1.1647 +
  1.1648 +Abstract.Insertion = function(adjacency) {
  1.1649 +  this.adjacency = adjacency;
  1.1650 +}
  1.1651 +
  1.1652 +Abstract.Insertion.prototype = {
  1.1653 +  initialize: function(element, content) {
  1.1654 +    this.element = $(element);
  1.1655 +    this.content = content.stripScripts();
  1.1656 +
  1.1657 +    if (this.adjacency && this.element.insertAdjacentHTML) {
  1.1658 +      try {
  1.1659 +        this.element.insertAdjacentHTML(this.adjacency, this.content);
  1.1660 +      } catch (e) {
  1.1661 +        var tagName = this.element.tagName.toUpperCase();
  1.1662 +        if (['TBODY', 'TR'].include(tagName)) {
  1.1663 +          this.insertContent(this.contentFromAnonymousTable());
  1.1664 +        } else {
  1.1665 +          throw e;
  1.1666 +        }
  1.1667 +      }
  1.1668 +    } else {
  1.1669 +      this.range = this.element.ownerDocument.createRange();
  1.1670 +      if (this.initializeRange) this.initializeRange();
  1.1671 +      this.insertContent([this.range.createContextualFragment(this.content)]);
  1.1672 +    }
  1.1673 +
  1.1674 +    setTimeout(function() {content.evalScripts()}, 10);
  1.1675 +  },
  1.1676 +
  1.1677 +  contentFromAnonymousTable: function() {
  1.1678 +    var div = document.createElement('div');
  1.1679 +    div.innerHTML = '<table><tbody>' + this.content + '</tbody></table>';
  1.1680 +    return $A(div.childNodes[0].childNodes[0].childNodes);
  1.1681 +  }
  1.1682 +}
  1.1683 +
  1.1684 +var Insertion = new Object();
  1.1685 +
  1.1686 +Insertion.Before = Class.create();
  1.1687 +Insertion.Before.prototype = Object.extend(new Abstract.Insertion('beforeBegin'), {
  1.1688 +  initializeRange: function() {
  1.1689 +    this.range.setStartBefore(this.element);
  1.1690 +  },
  1.1691 +
  1.1692 +  insertContent: function(fragments) {
  1.1693 +    fragments.each((function(fragment) {
  1.1694 +      this.element.parentNode.insertBefore(fragment, this.element);
  1.1695 +    }).bind(this));
  1.1696 +  }
  1.1697 +});
  1.1698 +
  1.1699 +Insertion.Top = Class.create();
  1.1700 +Insertion.Top.prototype = Object.extend(new Abstract.Insertion('afterBegin'), {
  1.1701 +  initializeRange: function() {
  1.1702 +    this.range.selectNodeContents(this.element);
  1.1703 +    this.range.collapse(true);
  1.1704 +  },
  1.1705 +
  1.1706 +  insertContent: function(fragments) {
  1.1707 +    fragments.reverse(false).each((function(fragment) {
  1.1708 +      this.element.insertBefore(fragment, this.element.firstChild);
  1.1709 +    }).bind(this));
  1.1710 +  }
  1.1711 +});
  1.1712 +
  1.1713 +Insertion.Bottom = Class.create();
  1.1714 +Insertion.Bottom.prototype = Object.extend(new Abstract.Insertion('beforeEnd'), {
  1.1715 +  initializeRange: function() {
  1.1716 +    this.range.selectNodeContents(this.element);
  1.1717 +    this.range.collapse(this.element);
  1.1718 +  },
  1.1719 +
  1.1720 +  insertContent: function(fragments) {
  1.1721 +    fragments.each((function(fragment) {
  1.1722 +      this.element.appendChild(fragment);
  1.1723 +    }).bind(this));
  1.1724 +  }
  1.1725 +});
  1.1726 +
  1.1727 +Insertion.After = Class.create();
  1.1728 +Insertion.After.prototype = Object.extend(new Abstract.Insertion('afterEnd'), {
  1.1729 +  initializeRange: function() {
  1.1730 +    this.range.setStartAfter(this.element);
  1.1731 +  },
  1.1732 +
  1.1733 +  insertContent: function(fragments) {
  1.1734 +    fragments.each((function(fragment) {
  1.1735 +      this.element.parentNode.insertBefore(fragment,
  1.1736 +        this.element.nextSibling);
  1.1737 +    }).bind(this));
  1.1738 +  }
  1.1739 +});
  1.1740 +
  1.1741 +/*--------------------------------------------------------------------------*/
  1.1742 +
  1.1743 +Element.ClassNames = Class.create();
  1.1744 +Element.ClassNames.prototype = {
  1.1745 +  initialize: function(element) {
  1.1746 +    this.element = $(element);
  1.1747 +  },
  1.1748 +
  1.1749 +  _each: function(iterator) {
  1.1750 +    this.element.className.split(/\s+/).select(function(name) {
  1.1751 +      return name.length > 0;
  1.1752 +    })._each(iterator);
  1.1753 +  },
  1.1754 +
  1.1755 +  set: function(className) {
  1.1756 +    this.element.className = className;
  1.1757 +  },
  1.1758 +
  1.1759 +  add: function(classNameToAdd) {
  1.1760 +    if (this.include(classNameToAdd)) return;
  1.1761 +    this.set($A(this).concat(classNameToAdd).join(' '));
  1.1762 +  },
  1.1763 +
  1.1764 +  remove: function(classNameToRemove) {
  1.1765 +    if (!this.include(classNameToRemove)) return;
  1.1766 +    this.set($A(this).without(classNameToRemove).join(' '));
  1.1767 +  },
  1.1768 +
  1.1769 +  toString: function() {
  1.1770 +    return $A(this).join(' ');
  1.1771 +  }
  1.1772 +};
  1.1773 +
  1.1774 +Object.extend(Element.ClassNames.prototype, Enumerable);
  1.1775 +var Selector = Class.create();
  1.1776 +Selector.prototype = {
  1.1777 +  initialize: function(expression) {
  1.1778 +    this.params = {classNames: []};
  1.1779 +    this.expression = expression.toString().strip();
  1.1780 +    this.parseExpression();
  1.1781 +    this.compileMatcher();
  1.1782 +  },
  1.1783 +
  1.1784 +  parseExpression: function() {
  1.1785 +    function abort(message) { throw 'Parse error in selector: ' + message; }
  1.1786 +
  1.1787 +    if (this.expression == '')  abort('empty expression');
  1.1788 +
  1.1789 +    var params = this.params, expr = this.expression, match, modifier, clause, rest;
  1.1790 +    while (match = expr.match(/^(.*)\[([a-z0-9_:-]+?)(?:([~\|!]?=)(?:"([^"]*)"|([^\]\s]*)))?\]$/i)) {
  1.1791 +      params.attributes = params.attributes || [];
  1.1792 +      params.attributes.push({name: match[2], operator: match[3], value: match[4] || match[5] || ''});
  1.1793 +      expr = match[1];
  1.1794 +    }
  1.1795 +
  1.1796 +    if (expr == '*') return this.params.wildcard = true;
  1.1797 +
  1.1798 +    while (match = expr.match(/^([^a-z0-9_-])?([a-z0-9_-]+)(.*)/i)) {
  1.1799 +      modifier = match[1], clause = match[2], rest = match[3];
  1.1800 +      switch (modifier) {
  1.1801 +        case '#':       params.id = clause; break;
  1.1802 +        case '.':       params.classNames.push(clause); break;
  1.1803 +        case '':
  1.1804 +        case undefined: params.tagName = clause.toUpperCase(); break;
  1.1805 +        default:        abort(expr.inspect());
  1.1806 +      }
  1.1807 +      expr = rest;
  1.1808 +    }
  1.1809 +
  1.1810 +    if (expr.length > 0) abort(expr.inspect());
  1.1811 +  },
  1.1812 +
  1.1813 +  buildMatchExpression: function() {
  1.1814 +    var params = this.params, conditions = [], clause;
  1.1815 +
  1.1816 +    if (params.wildcard)
  1.1817 +      conditions.push('true');
  1.1818 +    if (clause = params.id)
  1.1819 +      conditions.push('element.readAttribute("id") == ' + clause.inspect());
  1.1820 +    if (clause = params.tagName)
  1.1821 +      conditions.push('element.tagName.toUpperCase() == ' + clause.inspect());
  1.1822 +    if ((clause = params.classNames).length > 0)
  1.1823 +      for (var i = 0, length = clause.length; i < length; i++)
  1.1824 +        conditions.push('element.hasClassName(' + clause[i].inspect() + ')');
  1.1825 +    if (clause = params.attributes) {
  1.1826 +      clause.each(function(attribute) {
  1.1827 +        var value = 'element.readAttribute(' + attribute.name.inspect() + ')';
  1.1828 +        var splitValueBy = function(delimiter) {
  1.1829 +          return value + ' && ' + value + '.split(' + delimiter.inspect() + ')';
  1.1830 +        }
  1.1831 +
  1.1832 +        switch (attribute.operator) {
  1.1833 +          case '=':       conditions.push(value + ' == ' + attribute.value.inspect()); break;
  1.1834 +          case '~=':      conditions.push(splitValueBy(' ') + '.include(' + attribute.value.inspect() + ')'); break;
  1.1835 +          case '|=':      conditions.push(
  1.1836 +                            splitValueBy('-') + '.first().toUpperCase() == ' + attribute.value.toUpperCase().inspect()
  1.1837 +                          ); break;
  1.1838 +          case '!=':      conditions.push(value + ' != ' + attribute.value.inspect()); break;
  1.1839 +          case '':
  1.1840 +          case undefined: conditions.push('element.hasAttribute(' + attribute.name.inspect() + ')'); break;
  1.1841 +          default:        throw 'Unknown operator ' + attribute.operator + ' in selector';
  1.1842 +        }
  1.1843 +      });
  1.1844 +    }
  1.1845 +
  1.1846 +    return conditions.join(' && ');
  1.1847 +  },
  1.1848 +
  1.1849 +  compileMatcher: function() {
  1.1850 +    this.match = new Function('element', 'if (!element.tagName) return false; \
  1.1851 +      element = $(element); \
  1.1852 +      return ' + this.buildMatchExpression());
  1.1853 +  },
  1.1854 +
  1.1855 +  findElements: function(scope) {
  1.1856 +    var element;
  1.1857 +
  1.1858 +    if (element = $(this.params.id))
  1.1859 +      if (this.match(element))
  1.1860 +        if (!scope || Element.childOf(element, scope))
  1.1861 +          return [element];
  1.1862 +
  1.1863 +    scope = (scope || document).getElementsByTagName(this.params.tagName || '*');
  1.1864 +
  1.1865 +    var results = [];
  1.1866 +    for (var i = 0, length = scope.length; i < length; i++)
  1.1867 +      if (this.match(element = scope[i]))
  1.1868 +        results.push(Element.extend(element));
  1.1869 +
  1.1870 +    return results;
  1.1871 +  },
  1.1872 +
  1.1873 +  toString: function() {
  1.1874 +    return this.expression;
  1.1875 +  }
  1.1876 +}
  1.1877 +
  1.1878 +Object.extend(Selector, {
  1.1879 +  matchElements: function(elements, expression) {
  1.1880 +    var selector = new Selector(expression);
  1.1881 +    return elements.select(selector.match.bind(selector)).map(Element.extend);
  1.1882 +  },
  1.1883 +
  1.1884 +  findElement: function(elements, expression, index) {
  1.1885 +    if (typeof expression == 'number') index = expression, expression = false;
  1.1886 +    return Selector.matchElements(elements, expression || '*')[index || 0];
  1.1887 +  },
  1.1888 +
  1.1889 +  findChildElements: function(element, expressions) {
  1.1890 +    return expressions.map(function(expression) {
  1.1891 +      return expression.match(/[^\s"]+(?:"[^"]*"[^\s"]+)*/g).inject([null], function(results, expr) {
  1.1892 +        var selector = new Selector(expr);
  1.1893 +        return results.inject([], function(elements, result) {
  1.1894 +          return elements.concat(selector.findElements(result || element));
  1.1895 +        });
  1.1896 +      });
  1.1897 +    }).flatten();
  1.1898 +  }
  1.1899 +});
  1.1900 +
  1.1901 +function $$() {
  1.1902 +  return Selector.findChildElements(document, $A(arguments));
  1.1903 +}
  1.1904 +var Form = {
  1.1905 +  reset: function(form) {
  1.1906 +    $(form).reset();
  1.1907 +    return form;
  1.1908 +  },
  1.1909 +
  1.1910 +  serializeElements: function(elements, getHash) {
  1.1911 +    var data = elements.inject({}, function(result, element) {
  1.1912 +      if (!element.disabled && element.name) {
  1.1913 +        var key = element.name, value = $(element).getValue();
  1.1914 +        if (value != undefined) {
  1.1915 +          if (result[key]) {
  1.1916 +            if (result[key].constructor != Array) result[key] = [result[key]];
  1.1917 +            result[key].push(value);
  1.1918 +          }
  1.1919 +          else result[key] = value;
  1.1920 +        }
  1.1921 +      }
  1.1922 +      return result;
  1.1923 +    });
  1.1924 +
  1.1925 +    return getHash ? data : Hash.toQueryString(data);
  1.1926 +  }
  1.1927 +};
  1.1928 +
  1.1929 +Form.Methods = {
  1.1930 +  serialize: function(form, getHash) {
  1.1931 +    return Form.serializeElements(Form.getElements(form), getHash);
  1.1932 +  },
  1.1933 +
  1.1934 +  getElements: function(form) {
  1.1935 +    return $A($(form).getElementsByTagName('*')).inject([],
  1.1936 +      function(elements, child) {
  1.1937 +        if (Form.Element.Serializers[child.tagName.toLowerCase()])
  1.1938 +          elements.push(Element.extend(child));
  1.1939 +        return elements;
  1.1940 +      }
  1.1941 +    );
  1.1942 +  },
  1.1943 +
  1.1944 +  getInputs: function(form, typeName, name) {
  1.1945 +    form = $(form);
  1.1946 +    var inputs = form.getElementsByTagName('input');
  1.1947 +
  1.1948 +    if (!typeName && !name) return $A(inputs).map(Element.extend);
  1.1949 +
  1.1950 +    for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) {
  1.1951 +      var input = inputs[i];
  1.1952 +      if ((typeName && input.type != typeName) || (name && input.name != name))
  1.1953 +        continue;
  1.1954 +      matchingInputs.push(Element.extend(input));
  1.1955 +    }
  1.1956 +
  1.1957 +    return matchingInputs;
  1.1958 +  },
  1.1959 +
  1.1960 +  disable: function(form) {
  1.1961 +    form = $(form);
  1.1962 +    form.getElements().each(function(element) {
  1.1963 +      element.blur();
  1.1964 +      element.disabled = 'true';
  1.1965 +    });
  1.1966 +    return form;
  1.1967 +  },
  1.1968 +
  1.1969 +  enable: function(form) {
  1.1970 +    form = $(form);
  1.1971 +    form.getElements().each(function(element) {
  1.1972 +      element.disabled = '';
  1.1973 +    });
  1.1974 +    return form;
  1.1975 +  },
  1.1976 +
  1.1977 +  findFirstElement: function(form) {
  1.1978 +    return $(form).getElements().find(function(element) {
  1.1979 +      return element.type != 'hidden' && !element.disabled &&
  1.1980 +        ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
  1.1981 +    });
  1.1982 +  },
  1.1983 +
  1.1984 +  focusFirstElement: function(form) {
  1.1985 +    form = $(form);
  1.1986 +    form.findFirstElement().activate();
  1.1987 +    return form;
  1.1988 +  }
  1.1989 +}
  1.1990 +
  1.1991 +Object.extend(Form, Form.Methods);
  1.1992 +
  1.1993 +/*--------------------------------------------------------------------------*/
  1.1994 +
  1.1995 +Form.Element = {
  1.1996 +  focus: function(element) {
  1.1997 +    $(element).focus();
  1.1998 +    return element;
  1.1999 +  },
  1.2000 +
  1.2001 +  select: function(element) {
  1.2002 +    $(element).select();
  1.2003 +    return element;
  1.2004 +  }
  1.2005 +}
  1.2006 +
  1.2007 +Form.Element.Methods = {
  1.2008 +  serialize: function(element) {
  1.2009 +    element = $(element);
  1.2010 +    if (!element.disabled && element.name) {
  1.2011 +      var value = element.getValue();
  1.2012 +      if (value != undefined) {
  1.2013 +        var pair = {};
  1.2014 +        pair[element.name] = value;
  1.2015 +        return Hash.toQueryString(pair);
  1.2016 +      }
  1.2017 +    }
  1.2018 +    return '';
  1.2019 +  },
  1.2020 +
  1.2021 +  getValue: function(element) {
  1.2022 +    element = $(element);
  1.2023 +    var method = element.tagName.toLowerCase();
  1.2024 +    return Form.Element.Serializers[method](element);
  1.2025 +  },
  1.2026 +
  1.2027 +  clear: function(element) {
  1.2028 +    $(element).value = '';
  1.2029 +    return element;
  1.2030 +  },
  1.2031 +
  1.2032 +  present: function(element) {
  1.2033 +    return $(element).value != '';
  1.2034 +  },
  1.2035 +
  1.2036 +  activate: function(element) {
  1.2037 +    element = $(element);
  1.2038 +    element.focus();
  1.2039 +    if (element.select && ( element.tagName.toLowerCase() != 'input' ||
  1.2040 +      !['button', 'reset', 'submit'].include(element.type) ) )
  1.2041 +      element.select();
  1.2042 +    return element;
  1.2043 +  },
  1.2044 +
  1.2045 +  disable: function(element) {
  1.2046 +    element = $(element);
  1.2047 +    element.disabled = true;
  1.2048 +    return element;
  1.2049 +  },
  1.2050 +
  1.2051 +  enable: function(element) {
  1.2052 +    element = $(element);
  1.2053 +    element.blur();
  1.2054 +    element.disabled = false;
  1.2055 +    return element;
  1.2056 +  }
  1.2057 +}
  1.2058 +
  1.2059 +Object.extend(Form.Element, Form.Element.Methods);
  1.2060 +var Field = Form.Element;
  1.2061 +var $F = Form.Element.getValue;
  1.2062 +
  1.2063 +/*--------------------------------------------------------------------------*/
  1.2064 +
  1.2065 +Form.Element.Serializers = {
  1.2066 +  input: function(element) {
  1.2067 +    switch (element.type.toLowerCase()) {
  1.2068 +      case 'checkbox':
  1.2069 +      case 'radio':
  1.2070 +        return Form.Element.Serializers.inputSelector(element);
  1.2071 +      default:
  1.2072 +        return Form.Element.Serializers.textarea(element);
  1.2073 +    }
  1.2074 +  },
  1.2075 +
  1.2076 +  inputSelector: function(element) {
  1.2077 +    return element.checked ? element.value : null;
  1.2078 +  },
  1.2079 +
  1.2080 +  textarea: function(element) {
  1.2081 +    return element.value;
  1.2082 +  },
  1.2083 +
  1.2084 +  select: function(element) {
  1.2085 +    return this[element.type == 'select-one' ?
  1.2086 +      'selectOne' : 'selectMany'](element);
  1.2087 +  },
  1.2088 +
  1.2089 +  selectOne: function(element) {
  1.2090 +    var index = element.selectedIndex;
  1.2091 +    return index >= 0 ? this.optionValue(element.options[index]) : null;
  1.2092 +  },
  1.2093 +
  1.2094 +  selectMany: function(element) {
  1.2095 +    var values, length = element.length;
  1.2096 +    if (!length) return null;
  1.2097 +
  1.2098 +    for (var i = 0, values = []; i < length; i++) {
  1.2099 +      var opt = element.options[i];
  1.2100 +      if (opt.selected) values.push(this.optionValue(opt));
  1.2101 +    }
  1.2102 +    return values;
  1.2103 +  },
  1.2104 +
  1.2105 +  optionValue: function(opt) {
  1.2106 +    // extend element because hasAttribute may not be native
  1.2107 +    return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text;
  1.2108 +  }
  1.2109 +}
  1.2110 +
  1.2111 +/*--------------------------------------------------------------------------*/
  1.2112 +
  1.2113 +Abstract.TimedObserver = function() {}
  1.2114 +Abstract.TimedObserver.prototype = {
  1.2115 +  initialize: function(element, frequency, callback) {
  1.2116 +    this.frequency = frequency;
  1.2117 +    this.element   = $(element);
  1.2118 +    this.callback  = callback;
  1.2119 +
  1.2120 +    this.lastValue = this.getValue();
  1.2121 +    this.registerCallback();
  1.2122 +  },
  1.2123 +
  1.2124 +  registerCallback: function() {
  1.2125 +    setInterval(this.onTimerEvent.bind(this), this.frequency * 1000);
  1.2126 +  },
  1.2127 +
  1.2128 +  onTimerEvent: function() {
  1.2129 +    var value = this.getValue();
  1.2130 +    var changed = ('string' == typeof this.lastValue && 'string' == typeof value
  1.2131 +      ? this.lastValue != value : String(this.lastValue) != String(value));
  1.2132 +    if (changed) {
  1.2133 +      this.callback(this.element, value);
  1.2134 +      this.lastValue = value;
  1.2135 +    }
  1.2136 +  }
  1.2137 +}
  1.2138 +
  1.2139 +Form.Element.Observer = Class.create();
  1.2140 +Form.Element.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  1.2141 +  getValue: function() {
  1.2142 +    return Form.Element.getValue(this.element);
  1.2143 +  }
  1.2144 +});
  1.2145 +
  1.2146 +Form.Observer = Class.create();
  1.2147 +Form.Observer.prototype = Object.extend(new Abstract.TimedObserver(), {
  1.2148 +  getValue: function() {
  1.2149 +    return Form.serialize(this.element);
  1.2150 +  }
  1.2151 +});
  1.2152 +
  1.2153 +/*--------------------------------------------------------------------------*/
  1.2154 +
  1.2155 +Abstract.EventObserver = function() {}
  1.2156 +Abstract.EventObserver.prototype = {
  1.2157 +  initialize: function(element, callback) {
  1.2158 +    this.element  = $(element);
  1.2159 +    this.callback = callback;
  1.2160 +
  1.2161 +    this.lastValue = this.getValue();
  1.2162 +    if (this.element.tagName.toLowerCase() == 'form')
  1.2163 +      this.registerFormCallbacks();
  1.2164 +    else
  1.2165 +      this.registerCallback(this.element);
  1.2166 +  },
  1.2167 +
  1.2168 +  onElementEvent: function() {
  1.2169 +    var value = this.getValue();
  1.2170 +    if (this.lastValue != value) {
  1.2171 +      this.callback(this.element, value);
  1.2172 +      this.lastValue = value;
  1.2173 +    }
  1.2174 +  },
  1.2175 +
  1.2176 +  registerFormCallbacks: function() {
  1.2177 +    Form.getElements(this.element).each(this.registerCallback.bind(this));
  1.2178 +  },
  1.2179 +
  1.2180 +  registerCallback: function(element) {
  1.2181 +    if (element.type) {
  1.2182 +      switch (element.type.toLowerCase()) {
  1.2183 +        case 'checkbox':
  1.2184 +        case 'radio':
  1.2185 +          Event.observe(element, 'click', this.onElementEvent.bind(this));
  1.2186 +          break;
  1.2187 +        default:
  1.2188 +          Event.observe(element, 'change', this.onElementEvent.bind(this));
  1.2189 +          break;
  1.2190 +      }
  1.2191 +    }
  1.2192 +  }
  1.2193 +}
  1.2194 +
  1.2195 +Form.Element.EventObserver = Class.create();
  1.2196 +Form.Element.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  1.2197 +  getValue: function() {
  1.2198 +    return Form.Element.getValue(this.element);
  1.2199 +  }
  1.2200 +});
  1.2201 +
  1.2202 +Form.EventObserver = Class.create();
  1.2203 +Form.EventObserver.prototype = Object.extend(new Abstract.EventObserver(), {
  1.2204 +  getValue: function() {
  1.2205 +    return Form.serialize(this.element);
  1.2206 +  }
  1.2207 +});
  1.2208 +if (!window.Event) {
  1.2209 +  var Event = new Object();
  1.2210 +}
  1.2211 +
  1.2212 +Object.extend(Event, {
  1.2213 +  KEY_BACKSPACE: 8,
  1.2214 +  KEY_TAB:       9,
  1.2215 +  KEY_RETURN:   13,
  1.2216 +  KEY_ESC:      27,
  1.2217 +  KEY_LEFT:     37,
  1.2218 +  KEY_UP:       38,
  1.2219 +  KEY_RIGHT:    39,
  1.2220 +  KEY_DOWN:     40,
  1.2221 +  KEY_DELETE:   46,
  1.2222 +  KEY_HOME:     36,
  1.2223 +  KEY_END:      35,
  1.2224 +  KEY_PAGEUP:   33,
  1.2225 +  KEY_PAGEDOWN: 34,
  1.2226 +
  1.2227 +  element: function(event) {
  1.2228 +    return event.target || event.srcElement;
  1.2229 +  },
  1.2230 +
  1.2231 +  isLeftClick: function(event) {
  1.2232 +    return (((event.which) && (event.which == 1)) ||
  1.2233 +            ((event.button) && (event.button == 1)));
  1.2234 +  },
  1.2235 +
  1.2236 +  pointerX: function(event) {
  1.2237 +    return event.pageX || (event.clientX +
  1.2238 +      (document.documentElement.scrollLeft || document.body.scrollLeft));
  1.2239 +  },
  1.2240 +
  1.2241 +  pointerY: function(event) {
  1.2242 +    return event.pageY || (event.clientY +
  1.2243 +      (document.documentElement.scrollTop || document.body.scrollTop));
  1.2244 +  },
  1.2245 +
  1.2246 +  stop: function(event) {
  1.2247 +    if (event.preventDefault) {
  1.2248 +      event.preventDefault();
  1.2249 +      event.stopPropagation();
  1.2250 +    } else {
  1.2251 +      event.returnValue = false;
  1.2252 +      event.cancelBubble = true;
  1.2253 +    }
  1.2254 +  },
  1.2255 +
  1.2256 +  // find the first node with the given tagName, starting from the
  1.2257 +  // node the event was triggered on; traverses the DOM upwards
  1.2258 +  findElement: function(event, tagName) {
  1.2259 +    var element = Event.element(event);
  1.2260 +    while (element.parentNode && (!element.tagName ||
  1.2261 +        (element.tagName.toUpperCase() != tagName.toUpperCase())))
  1.2262 +      element = element.parentNode;
  1.2263 +    return element;
  1.2264 +  },
  1.2265 +
  1.2266 +  observers: false,
  1.2267 +
  1.2268 +  _observeAndCache: function(element, name, observer, useCapture) {
  1.2269 +    if (!this.observers) this.observers = [];
  1.2270 +    if (element.addEventListener) {
  1.2271 +      this.observers.push([element, name, observer, useCapture]);
  1.2272 +      element.addEventListener(name, observer, useCapture);
  1.2273 +    } else if (element.attachEvent) {
  1.2274 +      this.observers.push([element, name, observer, useCapture]);
  1.2275 +      element.attachEvent('on' + name, observer);
  1.2276 +    }
  1.2277 +  },
  1.2278 +
  1.2279 +  unloadCache: function() {
  1.2280 +    if (!Event.observers) return;
  1.2281 +    for (var i = 0, length = Event.observers.length; i < length; i++) {
  1.2282 +      Event.stopObserving.apply(this, Event.observers[i]);
  1.2283 +      Event.observers[i][0] = null;
  1.2284 +    }
  1.2285 +    Event.observers = false;
  1.2286 +  },
  1.2287 +
  1.2288 +  observe: function(element, name, observer, useCapture) {
  1.2289 +    element = $(element);
  1.2290 +    useCapture = useCapture || false;
  1.2291 +
  1.2292 +    if (name == 'keypress' &&
  1.2293 +        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
  1.2294 +        || element.attachEvent))
  1.2295 +      name = 'keydown';
  1.2296 +
  1.2297 +    Event._observeAndCache(element, name, observer, useCapture);
  1.2298 +  },
  1.2299 +
  1.2300 +  stopObserving: function(element, name, observer, useCapture) {
  1.2301 +    element = $(element);
  1.2302 +    useCapture = useCapture || false;
  1.2303 +
  1.2304 +    if (name == 'keypress' &&
  1.2305 +        (navigator.appVersion.match(/Konqueror|Safari|KHTML/)
  1.2306 +        || element.detachEvent))
  1.2307 +      name = 'keydown';
  1.2308 +
  1.2309 +    if (element.removeEventListener) {
  1.2310 +      element.removeEventListener(name, observer, useCapture);
  1.2311 +    } else if (element.detachEvent) {
  1.2312 +      try {
  1.2313 +        element.detachEvent('on' + name, observer);
  1.2314 +      } catch (e) {}
  1.2315 +    }
  1.2316 +  }
  1.2317 +});
  1.2318 +
  1.2319 +/* prevent memory leaks in IE */
  1.2320 +if (navigator.appVersion.match(/\bMSIE\b/))
  1.2321 +  Event.observe(window, 'unload', Event.unloadCache, false);
  1.2322 +var Position = {
  1.2323 +  // set to true if needed, warning: firefox performance problems
  1.2324 +  // NOT neeeded for page scrolling, only if draggable contained in
  1.2325 +  // scrollable elements
  1.2326 +  includeScrollOffsets: false,
  1.2327 +
  1.2328 +  // must be called before calling withinIncludingScrolloffset, every time the
  1.2329 +  // page is scrolled
  1.2330 +  prepare: function() {
  1.2331 +    this.deltaX =  window.pageXOffset
  1.2332 +                || document.documentElement.scrollLeft
  1.2333 +                || document.body.scrollLeft
  1.2334 +                || 0;
  1.2335 +    this.deltaY =  window.pageYOffset
  1.2336 +                || document.documentElement.scrollTop
  1.2337 +                || document.body.scrollTop
  1.2338 +                || 0;
  1.2339 +  },
  1.2340 +
  1.2341 +  realOffset: function(element) {
  1.2342 +    var valueT = 0, valueL = 0;
  1.2343 +    do {
  1.2344 +      valueT += element.scrollTop  || 0;
  1.2345 +      valueL += element.scrollLeft || 0;
  1.2346 +      element = element.parentNode;
  1.2347 +    } while (element);
  1.2348 +    return [valueL, valueT];
  1.2349 +  },
  1.2350 +
  1.2351 +  cumulativeOffset: function(element) {
  1.2352 +    var valueT = 0, valueL = 0;
  1.2353 +    do {
  1.2354 +      valueT += element.offsetTop  || 0;
  1.2355 +      valueL += element.offsetLeft || 0;
  1.2356 +      element = element.offsetParent;
  1.2357 +    } while (element);
  1.2358 +    return [valueL, valueT];
  1.2359 +  },
  1.2360 +
  1.2361 +  positionedOffset: function(element) {
  1.2362 +    var valueT = 0, valueL = 0;
  1.2363 +    do {
  1.2364 +      valueT += element.offsetTop  || 0;
  1.2365 +      valueL += element.offsetLeft || 0;
  1.2366 +      element = element.offsetParent;
  1.2367 +      if (element) {
  1.2368 +        if(element.tagName=='BODY') break;
  1.2369 +        var p = Element.getStyle(element, 'position');
  1.2370 +        if (p == 'relative' || p == 'absolute') break;
  1.2371 +      }
  1.2372 +    } while (element);
  1.2373 +    return [valueL, valueT];
  1.2374 +  },
  1.2375 +
  1.2376 +  offsetParent: function(element) {
  1.2377 +    if (element.offsetParent) return element.offsetParent;
  1.2378 +    if (element == document.body) return element;
  1.2379 +
  1.2380 +    while ((element = element.parentNode) && element != document.body)
  1.2381 +      if (Element.getStyle(element, 'position') != 'static')
  1.2382 +        return element;
  1.2383 +
  1.2384 +    return document.body;
  1.2385 +  },
  1.2386 +
  1.2387 +  // caches x/y coordinate pair to use with overlap
  1.2388 +  within: function(element, x, y) {
  1.2389 +    if (this.includeScrollOffsets)
  1.2390 +      return this.withinIncludingScrolloffsets(element, x, y);
  1.2391 +    this.xcomp = x;
  1.2392 +    this.ycomp = y;
  1.2393 +    this.offset = this.cumulativeOffset(element);
  1.2394 +
  1.2395 +    return (y >= this.offset[1] &&
  1.2396 +            y <  this.offset[1] + element.offsetHeight &&
  1.2397 +            x >= this.offset[0] &&
  1.2398 +            x <  this.offset[0] + element.offsetWidth);
  1.2399 +  },
  1.2400 +
  1.2401 +  withinIncludingScrolloffsets: function(element, x, y) {
  1.2402 +    var offsetcache = this.realOffset(element);
  1.2403 +
  1.2404 +    this.xcomp = x + offsetcache[0] - this.deltaX;
  1.2405 +    this.ycomp = y + offsetcache[1] - this.deltaY;
  1.2406 +    this.offset = this.cumulativeOffset(element);
  1.2407 +
  1.2408 +    return (this.ycomp >= this.offset[1] &&
  1.2409 +            this.ycomp <  this.offset[1] + element.offsetHeight &&
  1.2410 +            this.xcomp >= this.offset[0] &&
  1.2411 +            this.xcomp <  this.offset[0] + element.offsetWidth);
  1.2412 +  },
  1.2413 +
  1.2414 +  // within must be called directly before
  1.2415 +  overlap: function(mode, element) {
  1.2416 +    if (!mode) return 0;
  1.2417 +    if (mode == 'vertical')
  1.2418 +      return ((this.offset[1] + element.offsetHeight) - this.ycomp) /
  1.2419 +        element.offsetHeight;
  1.2420 +    if (mode == 'horizontal')
  1.2421 +      return ((this.offset[0] + element.offsetWidth) - this.xcomp) /
  1.2422 +        element.offsetWidth;
  1.2423 +  },
  1.2424 +
  1.2425 +  page: function(forElement) {
  1.2426 +    var valueT = 0, valueL = 0;
  1.2427 +
  1.2428 +    var element = forElement;
  1.2429 +    do {
  1.2430 +      valueT += element.offsetTop  || 0;
  1.2431 +      valueL += element.offsetLeft || 0;
  1.2432 +
  1.2433 +      // Safari fix
  1.2434 +      if (element.offsetParent==document.body)
  1.2435 +        if (Element.getStyle(element,'position')=='absolute') break;
  1.2436 +
  1.2437 +    } while (element = element.offsetParent);
  1.2438 +
  1.2439 +    element = forElement;
  1.2440 +    do {
  1.2441 +      if (!window.opera || element.tagName=='BODY') {
  1.2442 +        valueT -= element.scrollTop  || 0;
  1.2443 +        valueL -= element.scrollLeft || 0;
  1.2444 +      }
  1.2445 +    } while (element = element.parentNode);
  1.2446 +
  1.2447 +    return [valueL, valueT];
  1.2448 +  },
  1.2449 +
  1.2450 +  clone: function(source, target) {
  1.2451 +    var options = Object.extend({
  1.2452 +      setLeft:    true,
  1.2453 +      setTop:     true,
  1.2454 +      setWidth:   true,
  1.2455 +      setHeight:  true,
  1.2456 +      offsetTop:  0,
  1.2457 +      offsetLeft: 0
  1.2458 +    }, arguments[2] || {})
  1.2459 +
  1.2460 +    // find page position of source
  1.2461 +    source = $(source);
  1.2462 +    var p = Position.page(source);
  1.2463 +
  1.2464 +    // find coordinate system to use
  1.2465 +    target = $(target);
  1.2466 +    var delta = [0, 0];
  1.2467 +    var parent = null;
  1.2468 +    // delta [0,0] will do fine with position: fixed elements,
  1.2469 +    // position:absolute needs offsetParent deltas
  1.2470 +    if (Element.getStyle(target,'position') == 'absolute') {
  1.2471 +      parent = Position.offsetParent(target);
  1.2472 +      delta = Position.page(parent);
  1.2473 +    }
  1.2474 +
  1.2475 +    // correct by body offsets (fixes Safari)
  1.2476 +    if (parent == document.body) {
  1.2477 +      delta[0] -= document.body.offsetLeft;
  1.2478 +      delta[1] -= document.body.offsetTop;
  1.2479 +    }
  1.2480 +
  1.2481 +    // set position
  1.2482 +    if(options.setLeft)   target.style.left  = (p[0] - delta[0] + options.offsetLeft) + 'px';
  1.2483 +    if(options.setTop)    target.style.top   = (p[1] - delta[1] + options.offsetTop) + 'px';
  1.2484 +    if(options.setWidth)  target.style.width = source.offsetWidth + 'px';
  1.2485 +    if(options.setHeight) target.style.height = source.offsetHeight + 'px';
  1.2486 +  },
  1.2487 +
  1.2488 +  absolutize: function(element) {
  1.2489 +    element = $(element);
  1.2490 +    if (element.style.position == 'absolute') return;
  1.2491 +    Position.prepare();
  1.2492 +
  1.2493 +    var offsets = Position.positionedOffset(element);
  1.2494 +    var top     = offsets[1];
  1.2495 +    var left    = offsets[0];
  1.2496 +    var width   = element.clientWidth;
  1.2497 +    var height  = element.clientHeight;
  1.2498 +
  1.2499 +    element._originalLeft   = left - parseFloat(element.style.left  || 0);
  1.2500 +    element._originalTop    = top  - parseFloat(element.style.top || 0);
  1.2501 +    element._originalWidth  = element.style.width;
  1.2502 +    element._originalHeight = element.style.height;
  1.2503 +
  1.2504 +    element.style.position = 'absolute';
  1.2505 +    element.style.top    = top + 'px';
  1.2506 +    element.style.left   = left + 'px';
  1.2507 +    element.style.width  = width + 'px';
  1.2508 +    element.style.height = height + 'px';
  1.2509 +  },
  1.2510 +
  1.2511 +  relativize: function(element) {
  1.2512 +    element = $(element);
  1.2513 +    if (element.style.position == 'relative') return;
  1.2514 +    Position.prepare();
  1.2515 +
  1.2516 +    element.style.position = 'relative';
  1.2517 +    var top  = parseFloat(element.style.top  || 0) - (element._originalTop || 0);
  1.2518 +    var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0);
  1.2519 +
  1.2520 +    element.style.top    = top + 'px';
  1.2521 +    element.style.left   = left + 'px';
  1.2522 +    element.style.height = element._originalHeight;
  1.2523 +    element.style.width  = element._originalWidth;
  1.2524 +  }
  1.2525 +}
  1.2526 +
  1.2527 +// Safari returns margins on body which is incorrect if the child is absolutely
  1.2528 +// positioned.  For performance reasons, redefine Position.cumulativeOffset for
  1.2529 +// KHTML/WebKit only.
  1.2530 +if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) {
  1.2531 +  Position.cumulativeOffset = function(element) {
  1.2532 +    var valueT = 0, valueL = 0;
  1.2533 +    do {
  1.2534 +      valueT += element.offsetTop  || 0;
  1.2535 +      valueL += element.offsetLeft || 0;
  1.2536 +      if (element.offsetParent == document.body)
  1.2537 +        if (Element.getStyle(element, 'position') == 'absolute') break;
  1.2538 +
  1.2539 +      element = element.offsetParent;
  1.2540 +    } while (element);
  1.2541 +
  1.2542 +    return [valueL, valueT];
  1.2543 +  }
  1.2544 +}
  1.2545 +
  1.2546 +Element.addMethods();
  1.2547 +
  1.2548 +
  1.2549 +// ------------------------------------------------------------------------
  1.2550 +// ------------------------------------------------------------------------
  1.2551 +
  1.2552 +// The rest of this file is the actual ray tracer written by Adam
  1.2553 +// Burmister. It's a concatenation of the following files:
  1.2554 +//
  1.2555 +//   flog/color.js
  1.2556 +//   flog/light.js
  1.2557 +//   flog/vector.js
  1.2558 +//   flog/ray.js
  1.2559 +//   flog/scene.js
  1.2560 +//   flog/material/basematerial.js
  1.2561 +//   flog/material/solid.js
  1.2562 +//   flog/material/chessboard.js
  1.2563 +//   flog/shape/baseshape.js
  1.2564 +//   flog/shape/sphere.js
  1.2565 +//   flog/shape/plane.js
  1.2566 +//   flog/intersectioninfo.js
  1.2567 +//   flog/camera.js
  1.2568 +//   flog/background.js
  1.2569 +//   flog/engine.js
  1.2570 +
  1.2571 +
  1.2572 +/* Fake a Flog.* namespace */
  1.2573 +if(typeof(Flog) == 'undefined') var Flog = {};
  1.2574 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
  1.2575 +
  1.2576 +Flog.RayTracer.Color = Class.create();
  1.2577 +
  1.2578 +Flog.RayTracer.Color.prototype = {
  1.2579 +    red : 0.0,
  1.2580 +    green : 0.0,
  1.2581 +    blue : 0.0,
  1.2582 +
  1.2583 +    initialize : function(r, g, b) {
  1.2584 +        if(!r) r = 0.0;
  1.2585 +        if(!g) g = 0.0;
  1.2586 +        if(!b) b = 0.0;
  1.2587 +
  1.2588 +        this.red = r;
  1.2589 +        this.green = g;
  1.2590 +        this.blue = b;
  1.2591 +    },
  1.2592 +
  1.2593 +    add : function(c1, c2){
  1.2594 +        var result = new Flog.RayTracer.Color(0,0,0);
  1.2595 +
  1.2596 +        result.red = c1.red + c2.red;
  1.2597 +        result.green = c1.green + c2.green;
  1.2598 +        result.blue = c1.blue + c2.blue;
  1.2599 +
  1.2600 +        return result;
  1.2601 +    },
  1.2602 +
  1.2603 +    addScalar: function(c1, s){
  1.2604 +        var result = new Flog.RayTracer.Color(0,0,0);
  1.2605 +
  1.2606 +        result.red = c1.red + s;
  1.2607 +        result.green = c1.green + s;
  1.2608 +        result.blue = c1.blue + s;
  1.2609 +
  1.2610 +        result.limit();
  1.2611 +
  1.2612 +        return result;
  1.2613 +    },
  1.2614 +
  1.2615 +    subtract: function(c1, c2){
  1.2616 +        var result = new Flog.RayTracer.Color(0,0,0);
  1.2617 +
  1.2618 +        result.red = c1.red - c2.red;
  1.2619 +        result.green = c1.green - c2.green;
  1.2620 +        result.blue = c1.blue - c2.blue;
  1.2621 +
  1.2622 +        return result;
  1.2623 +    },
  1.2624 +
  1.2625 +    multiply : function(c1, c2) {
  1.2626 +        var result = new Flog.RayTracer.Color(0,0,0);
  1.2627 +
  1.2628 +        result.red = c1.red * c2.red;
  1.2629 +        result.green = c1.green * c2.green;
  1.2630 +        result.blue = c1.blue * c2.blue;
  1.2631 +
  1.2632 +        return result;
  1.2633 +    },
  1.2634 +
  1.2635 +    multiplyScalar : function(c1, f) {
  1.2636 +        var result = new Flog.RayTracer.Color(0,0,0);
  1.2637 +
  1.2638 +        result.red = c1.red * f;
  1.2639 +        result.green = c1.green * f;
  1.2640 +        result.blue = c1.blue * f;
  1.2641 +
  1.2642 +        return result;
  1.2643 +    },
  1.2644 +
  1.2645 +    divideFactor : function(c1, f) {
  1.2646 +        var result = new Flog.RayTracer.Color(0,0,0);
  1.2647 +
  1.2648 +        result.red = c1.red / f;
  1.2649 +        result.green = c1.green / f;
  1.2650 +        result.blue = c1.blue / f;
  1.2651 +
  1.2652 +        return result;
  1.2653 +    },
  1.2654 +
  1.2655 +    limit: function(){
  1.2656 +        this.red = (this.red > 0.0) ? ( (this.red > 1.0) ? 1.0 : this.red ) : 0.0;
  1.2657 +        this.green = (this.green > 0.0) ? ( (this.green > 1.0) ? 1.0 : this.green ) : 0.0;
  1.2658 +        this.blue = (this.blue > 0.0) ? ( (this.blue > 1.0) ? 1.0 : this.blue ) : 0.0;
  1.2659 +    },
  1.2660 +
  1.2661 +    distance : function(color) {
  1.2662 +        var d = Math.abs(this.red - color.red) + Math.abs(this.green - color.green) + Math.abs(this.blue - color.blue);
  1.2663 +        return d;
  1.2664 +    },
  1.2665 +
  1.2666 +    blend: function(c1, c2, w){
  1.2667 +        var result = new Flog.RayTracer.Color(0,0,0);
  1.2668 +        result = Flog.RayTracer.Color.prototype.add(
  1.2669 +                    Flog.RayTracer.Color.prototype.multiplyScalar(c1, 1 - w),
  1.2670 +                    Flog.RayTracer.Color.prototype.multiplyScalar(c2, w)
  1.2671 +                  );
  1.2672 +        return result;
  1.2673 +    },
  1.2674 +
  1.2675 +    toString : function () {
  1.2676 +        var r = Math.floor(this.red*255);
  1.2677 +        var g = Math.floor(this.green*255);
  1.2678 +        var b = Math.floor(this.blue*255);
  1.2679 +
  1.2680 +        return "rgb("+ r +","+ g +","+ b +")";
  1.2681 +    }
  1.2682 +}
  1.2683 +/* Fake a Flog.* namespace */
  1.2684 +if(typeof(Flog) == 'undefined') var Flog = {};
  1.2685 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
  1.2686 +
  1.2687 +Flog.RayTracer.Light = Class.create();
  1.2688 +
  1.2689 +Flog.RayTracer.Light.prototype = {
  1.2690 +    position: null,
  1.2691 +    color: null,
  1.2692 +    intensity: 10.0,
  1.2693 +
  1.2694 +    initialize : function(pos, color, intensity) {
  1.2695 +        this.position = pos;
  1.2696 +        this.color = color;
  1.2697 +        this.intensity = (intensity ? intensity : 10.0);
  1.2698 +    },
  1.2699 +
  1.2700 +    getIntensity: function(distance){
  1.2701 +        if(distance >= intensity) return 0;
  1.2702 +
  1.2703 +        return Math.pow((intensity - distance) / strength, 0.2);
  1.2704 +    },
  1.2705 +
  1.2706 +    toString : function () {
  1.2707 +        return 'Light [' + this.position.x + ',' + this.position.y + ',' + this.position.z + ']';
  1.2708 +    }
  1.2709 +}
  1.2710 +/* Fake a Flog.* namespace */
  1.2711 +if(typeof(Flog) == 'undefined') var Flog = {};
  1.2712 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
  1.2713 +
  1.2714 +Flog.RayTracer.Vector = Class.create();
  1.2715 +
  1.2716 +Flog.RayTracer.Vector.prototype = {
  1.2717 +    x : 0.0,
  1.2718 +    y : 0.0,
  1.2719 +    z : 0.0,
  1.2720 +
  1.2721 +    initialize : function(x, y, z) {
  1.2722 +        this.x = (x ? x : 0);
  1.2723 +        this.y = (y ? y : 0);
  1.2724 +        this.z = (z ? z : 0);
  1.2725 +    },
  1.2726 +
  1.2727 +    copy: function(vector){
  1.2728 +        this.x = vector.x;
  1.2729 +        this.y = vector.y;
  1.2730 +        this.z = vector.z;
  1.2731 +    },
  1.2732 +
  1.2733 +    normalize : function() {
  1.2734 +        var m = this.magnitude();
  1.2735 +        return new Flog.RayTracer.Vector(this.x / m, this.y / m, this.z / m);
  1.2736 +    },
  1.2737 +
  1.2738 +    magnitude : function() {
  1.2739 +        return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z));
  1.2740 +    },
  1.2741 +
  1.2742 +    cross : function(w) {
  1.2743 +        return new Flog.RayTracer.Vector(
  1.2744 +                                            -this.z * w.y + this.y * w.z,
  1.2745 +                                           this.z * w.x - this.x * w.z,
  1.2746 +                                          -this.y * w.x + this.x * w.y);
  1.2747 +    },
  1.2748 +
  1.2749 +    dot : function(w) {
  1.2750 +        return this.x * w.x + this.y * w.y + this.z * w.z;
  1.2751 +    },
  1.2752 +
  1.2753 +    add : function(v, w) {
  1.2754 +        return new Flog.RayTracer.Vector(w.x + v.x, w.y + v.y, w.z + v.z);
  1.2755 +    },
  1.2756 +
  1.2757 +    subtract : function(v, w) {
  1.2758 +        if(!w || !v) throw 'Vectors must be defined [' + v + ',' + w + ']';
  1.2759 +        return new Flog.RayTracer.Vector(v.x - w.x, v.y - w.y, v.z - w.z);
  1.2760 +    },
  1.2761 +
  1.2762 +    multiplyVector : function(v, w) {
  1.2763 +        return new Flog.RayTracer.Vector(v.x * w.x, v.y * w.y, v.z * w.z);
  1.2764 +    },
  1.2765 +
  1.2766 +    multiplyScalar : function(v, w) {
  1.2767 +        return new Flog.RayTracer.Vector(v.x * w, v.y * w, v.z * w);
  1.2768 +    },
  1.2769 +
  1.2770 +    toString : function () {
  1.2771 +        return 'Vector [' + this.x + ',' + this.y + ',' + this.z + ']';
  1.2772 +    }
  1.2773 +}
  1.2774 +/* Fake a Flog.* namespace */
  1.2775 +if(typeof(Flog) == 'undefined') var Flog = {};
  1.2776 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
  1.2777 +
  1.2778 +Flog.RayTracer.Ray = Class.create();
  1.2779 +
  1.2780 +Flog.RayTracer.Ray.prototype = {
  1.2781 +    position : null,
  1.2782 +    direction : null,
  1.2783 +    initialize : function(pos, dir) {
  1.2784 +        this.position = pos;
  1.2785 +        this.direction = dir;
  1.2786 +    },
  1.2787 +
  1.2788 +    toString : function () {
  1.2789 +        return 'Ray [' + this.position + ',' + this.direction + ']';
  1.2790 +    }
  1.2791 +}
  1.2792 +/* Fake a Flog.* namespace */
  1.2793 +if(typeof(Flog) == 'undefined') var Flog = {};
  1.2794 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
  1.2795 +
  1.2796 +Flog.RayTracer.Scene = Class.create();
  1.2797 +
  1.2798 +Flog.RayTracer.Scene.prototype = {
  1.2799 +    camera : null,
  1.2800 +    shapes : [],
  1.2801 +    lights : [],
  1.2802 +    background : null,
  1.2803 +
  1.2804 +    initialize : function() {
  1.2805 +        this.camera = new Flog.RayTracer.Camera(
  1.2806 +            new Flog.RayTracer.Vector(0,0,-5),
  1.2807 +            new Flog.RayTracer.Vector(0,0,1),
  1.2808 +            new Flog.RayTracer.Vector(0,1,0)
  1.2809 +        );
  1.2810 +        this.shapes = new Array();
  1.2811 +        this.lights = new Array();
  1.2812 +        this.background = new Flog.RayTracer.Background(new Flog.RayTracer.Color(0,0,0.5), 0.2);
  1.2813 +    }
  1.2814 +}
  1.2815 +/* Fake a Flog.* namespace */
  1.2816 +if(typeof(Flog) == 'undefined') var Flog = {};
  1.2817 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
  1.2818 +if(typeof(Flog.RayTracer.Material) == 'undefined') Flog.RayTracer.Material = {};
  1.2819 +
  1.2820 +Flog.RayTracer.Material.BaseMaterial = Class.create();
  1.2821 +
  1.2822 +Flog.RayTracer.Material.BaseMaterial.prototype = {
  1.2823 +
  1.2824 +    gloss: 2.0,             // [0...infinity] 0 = matt
  1.2825 +    transparency: 0.0,      // 0=opaque
  1.2826 +    reflection: 0.0,        // [0...infinity] 0 = no reflection
  1.2827 +    refraction: 0.50,
  1.2828 +    hasTexture: false,
  1.2829 +
  1.2830 +    initialize : function() {
  1.2831 +
  1.2832 +    },
  1.2833 +
  1.2834 +    getColor: function(u, v){
  1.2835 +
  1.2836 +    },
  1.2837 +
  1.2838 +    wrapUp: function(t){
  1.2839 +        t = t % 2.0;
  1.2840 +        if(t < -1) t += 2.0;
  1.2841 +        if(t >= 1) t -= 2.0;
  1.2842 +        return t;
  1.2843 +    },
  1.2844 +
  1.2845 +    toString : function () {
  1.2846 +        return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
  1.2847 +    }
  1.2848 +}
  1.2849 +/* Fake a Flog.* namespace */
  1.2850 +if(typeof(Flog) == 'undefined') var Flog = {};
  1.2851 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
  1.2852 +
  1.2853 +Flog.RayTracer.Material.Solid = Class.create();
  1.2854 +
  1.2855 +Flog.RayTracer.Material.Solid.prototype = Object.extend(
  1.2856 +    new Flog.RayTracer.Material.BaseMaterial(), {
  1.2857 +        initialize : function(color, reflection, refraction, transparency, gloss) {
  1.2858 +            this.color = color;
  1.2859 +            this.reflection = reflection;
  1.2860 +            this.transparency = transparency;
  1.2861 +            this.gloss = gloss;
  1.2862 +            this.hasTexture = false;
  1.2863 +        },
  1.2864 +
  1.2865 +        getColor: function(u, v){
  1.2866 +            return this.color;
  1.2867 +        },
  1.2868 +
  1.2869 +        toString : function () {
  1.2870 +            return 'SolidMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
  1.2871 +        }
  1.2872 +    }
  1.2873 +);
  1.2874 +/* Fake a Flog.* namespace */
  1.2875 +if(typeof(Flog) == 'undefined') var Flog = {};
  1.2876 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
  1.2877 +
  1.2878 +Flog.RayTracer.Material.Chessboard = Class.create();
  1.2879 +
  1.2880 +Flog.RayTracer.Material.Chessboard.prototype = Object.extend(
  1.2881 +    new Flog.RayTracer.Material.BaseMaterial(), {
  1.2882 +        colorEven: null,
  1.2883 +        colorOdd: null,
  1.2884 +        density: 0.5,
  1.2885 +
  1.2886 +        initialize : function(colorEven, colorOdd, reflection, transparency, gloss, density) {
  1.2887 +            this.colorEven = colorEven;
  1.2888 +            this.colorOdd = colorOdd;
  1.2889 +            this.reflection = reflection;
  1.2890 +            this.transparency = transparency;
  1.2891 +            this.gloss = gloss;
  1.2892 +            this.density = density;
  1.2893 +            this.hasTexture = true;
  1.2894 +        },
  1.2895 +
  1.2896 +        getColor: function(u, v){
  1.2897 +            var t = this.wrapUp(u * this.density) * this.wrapUp(v * this.density);
  1.2898 +
  1.2899 +            if(t < 0.0)
  1.2900 +                return this.colorEven;
  1.2901 +            else
  1.2902 +                return this.colorOdd;
  1.2903 +        },
  1.2904 +
  1.2905 +        toString : function () {
  1.2906 +            return 'ChessMaterial [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
  1.2907 +        }
  1.2908 +    }
  1.2909 +);
  1.2910 +/* Fake a Flog.* namespace */
  1.2911 +if(typeof(Flog) == 'undefined') var Flog = {};
  1.2912 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
  1.2913 +if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
  1.2914 +
  1.2915 +Flog.RayTracer.Shape.BaseShape = Class.create();
  1.2916 +
  1.2917 +Flog.RayTracer.Shape.BaseShape.prototype = {
  1.2918 +    position: null,
  1.2919 +    material: null,
  1.2920 +
  1.2921 +    initialize : function() {
  1.2922 +        this.position = new Vector(0,0,0);
  1.2923 +        this.material = new Flog.RayTracer.Material.SolidMaterial(
  1.2924 +            new Flog.RayTracer.Color(1,0,1),
  1.2925 +            0,
  1.2926 +            0,
  1.2927 +            0
  1.2928 +        );
  1.2929 +    },
  1.2930 +
  1.2931 +    toString : function () {
  1.2932 +        return 'Material [gloss=' + this.gloss + ', transparency=' + this.transparency + ', hasTexture=' + this.hasTexture +']';
  1.2933 +    }
  1.2934 +}
  1.2935 +/* Fake a Flog.* namespace */
  1.2936 +if(typeof(Flog) == 'undefined') var Flog = {};
  1.2937 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
  1.2938 +if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
  1.2939 +
  1.2940 +Flog.RayTracer.Shape.Sphere = Class.create();
  1.2941 +
  1.2942 +Flog.RayTracer.Shape.Sphere.prototype = {
  1.2943 +    initialize : function(pos, radius, material) {
  1.2944 +        this.radius = radius;
  1.2945 +        this.position = pos;
  1.2946 +        this.material = material;
  1.2947 +    },
  1.2948 +
  1.2949 +    intersect: function(ray){
  1.2950 +        var info = new Flog.RayTracer.IntersectionInfo();
  1.2951 +        info.shape = this;
  1.2952 +
  1.2953 +        var dst = Flog.RayTracer.Vector.prototype.subtract(ray.position, this.position);
  1.2954 +
  1.2955 +        var B = dst.dot(ray.direction);
  1.2956 +        var C = dst.dot(dst) - (this.radius * this.radius);
  1.2957 +        var D = (B * B) - C;
  1.2958 +
  1.2959 +        if(D > 0){ // intersection!
  1.2960 +            info.isHit = true;
  1.2961 +            info.distance = (-B) - Math.sqrt(D);
  1.2962 +            info.position = Flog.RayTracer.Vector.prototype.add(
  1.2963 +                                                ray.position,
  1.2964 +                                                Flog.RayTracer.Vector.prototype.multiplyScalar(
  1.2965 +                                                    ray.direction,
  1.2966 +                                                    info.distance
  1.2967 +                                                )
  1.2968 +                                            );
  1.2969 +            info.normal = Flog.RayTracer.Vector.prototype.subtract(
  1.2970 +                                            info.position,
  1.2971 +                                            this.position
  1.2972 +                                        ).normalize();
  1.2973 +
  1.2974 +            info.color = this.material.getColor(0,0);
  1.2975 +        } else {
  1.2976 +            info.isHit = false;
  1.2977 +        }
  1.2978 +        return info;
  1.2979 +    },
  1.2980 +
  1.2981 +    toString : function () {
  1.2982 +        return 'Sphere [position=' + this.position + ', radius=' + this.radius + ']';
  1.2983 +    }
  1.2984 +}
  1.2985 +/* Fake a Flog.* namespace */
  1.2986 +if(typeof(Flog) == 'undefined') var Flog = {};
  1.2987 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
  1.2988 +if(typeof(Flog.RayTracer.Shape) == 'undefined') Flog.RayTracer.Shape = {};
  1.2989 +
  1.2990 +Flog.RayTracer.Shape.Plane = Class.create();
  1.2991 +
  1.2992 +Flog.RayTracer.Shape.Plane.prototype = {
  1.2993 +    d: 0.0,
  1.2994 +
  1.2995 +    initialize : function(pos, d, material) {
  1.2996 +        this.position = pos;
  1.2997 +        this.d = d;
  1.2998 +        this.material = material;
  1.2999 +    },
  1.3000 +
  1.3001 +    intersect: function(ray){
  1.3002 +        var info = new Flog.RayTracer.IntersectionInfo();
  1.3003 +
  1.3004 +        var Vd = this.position.dot(ray.direction);
  1.3005 +        if(Vd == 0) return info; // no intersection
  1.3006 +
  1.3007 +        var t = -(this.position.dot(ray.position) + this.d) / Vd;
  1.3008 +        if(t <= 0) return info;
  1.3009 +
  1.3010 +        info.shape = this;
  1.3011 +        info.isHit = true;
  1.3012 +        info.position = Flog.RayTracer.Vector.prototype.add(
  1.3013 +                                            ray.position,
  1.3014 +                                            Flog.RayTracer.Vector.prototype.multiplyScalar(
  1.3015 +                                                ray.direction,
  1.3016 +                                                t
  1.3017 +                                            )
  1.3018 +                                        );
  1.3019 +        info.normal = this.position;
  1.3020 +        info.distance = t;
  1.3021 +
  1.3022 +        if(this.material.hasTexture){
  1.3023 +            var vU = new Flog.RayTracer.Vector(this.position.y, this.position.z, -this.position.x);
  1.3024 +            var vV = vU.cross(this.position);
  1.3025 +            var u = info.position.dot(vU);
  1.3026 +            var v = info.position.dot(vV);
  1.3027 +            info.color = this.material.getColor(u,v);
  1.3028 +        } else {
  1.3029 +            info.color = this.material.getColor(0,0);
  1.3030 +        }
  1.3031 +
  1.3032 +        return info;
  1.3033 +    },
  1.3034 +
  1.3035 +    toString : function () {
  1.3036 +        return 'Plane [' + this.position + ', d=' + this.d + ']';
  1.3037 +    }
  1.3038 +}
  1.3039 +/* Fake a Flog.* namespace */
  1.3040 +if(typeof(Flog) == 'undefined') var Flog = {};
  1.3041 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
  1.3042 +
  1.3043 +Flog.RayTracer.IntersectionInfo = Class.create();
  1.3044 +
  1.3045 +Flog.RayTracer.IntersectionInfo.prototype = {
  1.3046 +    isHit: false,
  1.3047 +    hitCount: 0,
  1.3048 +    shape: null,
  1.3049 +    position: null,
  1.3050 +    normal: null,
  1.3051 +    color: null,
  1.3052 +    distance: null,
  1.3053 +
  1.3054 +    initialize : function() {
  1.3055 +        this.color = new Flog.RayTracer.Color(0,0,0);
  1.3056 +    },
  1.3057 +
  1.3058 +    toString : function () {
  1.3059 +        return 'Intersection [' + this.position + ']';
  1.3060 +    }
  1.3061 +}
  1.3062 +/* Fake a Flog.* namespace */
  1.3063 +if(typeof(Flog) == 'undefined') var Flog = {};
  1.3064 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
  1.3065 +
  1.3066 +Flog.RayTracer.Camera = Class.create();
  1.3067 +
  1.3068 +Flog.RayTracer.Camera.prototype = {
  1.3069 +    position: null,
  1.3070 +    lookAt: null,
  1.3071 +    equator: null,
  1.3072 +    up: null,
  1.3073 +    screen: null,
  1.3074 +
  1.3075 +    initialize : function(pos, lookAt, up) {
  1.3076 +        this.position = pos;
  1.3077 +        this.lookAt = lookAt;
  1.3078 +        this.up = up;
  1.3079 +        this.equator = lookAt.normalize().cross(this.up);
  1.3080 +        this.screen = Flog.RayTracer.Vector.prototype.add(this.position, this.lookAt);
  1.3081 +    },
  1.3082 +
  1.3083 +    getRay: function(vx, vy){
  1.3084 +        var pos = Flog.RayTracer.Vector.prototype.subtract(
  1.3085 +            this.screen,
  1.3086 +            Flog.RayTracer.Vector.prototype.subtract(
  1.3087 +                Flog.RayTracer.Vector.prototype.multiplyScalar(this.equator, vx),
  1.3088 +                Flog.RayTracer.Vector.prototype.multiplyScalar(this.up, vy)
  1.3089 +            )
  1.3090 +        );
  1.3091 +        pos.y = pos.y * -1;
  1.3092 +        var dir = Flog.RayTracer.Vector.prototype.subtract(
  1.3093 +            pos,
  1.3094 +            this.position
  1.3095 +        );
  1.3096 +
  1.3097 +        var ray = new Flog.RayTracer.Ray(pos, dir.normalize());
  1.3098 +
  1.3099 +        return ray;
  1.3100 +    },
  1.3101 +
  1.3102 +    toString : function () {
  1.3103 +        return 'Ray []';
  1.3104 +    }
  1.3105 +}
  1.3106 +/* Fake a Flog.* namespace */
  1.3107 +if(typeof(Flog) == 'undefined') var Flog = {};
  1.3108 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
  1.3109 +
  1.3110 +Flog.RayTracer.Background = Class.create();
  1.3111 +
  1.3112 +Flog.RayTracer.Background.prototype = {
  1.3113 +    color : null,
  1.3114 +    ambience : 0.0,
  1.3115 +
  1.3116 +    initialize : function(color, ambience) {
  1.3117 +        this.color = color;
  1.3118 +        this.ambience = ambience;
  1.3119 +    }
  1.3120 +}
  1.3121 +/* Fake a Flog.* namespace */
  1.3122 +if(typeof(Flog) == 'undefined') var Flog = {};
  1.3123 +if(typeof(Flog.RayTracer) == 'undefined') Flog.RayTracer = {};
  1.3124 +
  1.3125 +Flog.RayTracer.Engine = Class.create();
  1.3126 +
  1.3127 +Flog.RayTracer.Engine.prototype = {
  1.3128 +    canvas: null, /* 2d context we can render to */
  1.3129 +
  1.3130 +    initialize: function(options){
  1.3131 +        this.options = Object.extend({
  1.3132 +                canvasHeight: 100,
  1.3133 +                canvasWidth: 100,
  1.3134 +                pixelWidth: 2,
  1.3135 +                pixelHeight: 2,
  1.3136 +                renderDiffuse: false,
  1.3137 +                renderShadows: false,
  1.3138 +                renderHighlights: false,
  1.3139 +                renderReflections: false,
  1.3140 +                rayDepth: 2
  1.3141 +            }, options || {});
  1.3142 +
  1.3143 +        this.options.canvasHeight /= this.options.pixelHeight;
  1.3144 +        this.options.canvasWidth /= this.options.pixelWidth;
  1.3145 +
  1.3146 +        /* TODO: dynamically include other scripts */
  1.3147 +    },
  1.3148 +
  1.3149 +    setPixel: function(x, y, color){
  1.3150 +        var pxW, pxH;
  1.3151 +        pxW = this.options.pixelWidth;
  1.3152 +        pxH = this.options.pixelHeight;
  1.3153 +
  1.3154 +        if (this.canvas) {
  1.3155 +          this.canvas.fillStyle = color.toString();
  1.3156 +          this.canvas.fillRect (x * pxW, y * pxH, pxW, pxH);
  1.3157 +        } else {
  1.3158 +          // print(x * pxW, y * pxH, pxW, pxH);
  1.3159 +        }
  1.3160 +    },
  1.3161 +
  1.3162 +    renderScene: function(scene, canvas){
  1.3163 +        /* Get canvas */
  1.3164 +        if (canvas) {
  1.3165 +          this.canvas = canvas.getContext("2d");
  1.3166 +        } else {
  1.3167 +          this.canvas = null;
  1.3168 +        }
  1.3169 +
  1.3170 +        var canvasHeight = this.options.canvasHeight;
  1.3171 +        var canvasWidth = this.options.canvasWidth;
  1.3172 +
  1.3173 +        for(var y=0; y < canvasHeight; y++){
  1.3174 +            for(var x=0; x < canvasWidth; x++){
  1.3175 +                var yp = y * 1.0 / canvasHeight * 2 - 1;
  1.3176 +          		var xp = x * 1.0 / canvasWidth * 2 - 1;
  1.3177 +
  1.3178 +          		var ray = scene.camera.getRay(xp, yp);
  1.3179 +
  1.3180 +          		var color = this.getPixelColor(ray, scene);
  1.3181 +
  1.3182 +            	this.setPixel(x, y, color);
  1.3183 +            }
  1.3184 +        }
  1.3185 +    },
  1.3186 +
  1.3187 +    getPixelColor: function(ray, scene){
  1.3188 +        var info = this.testIntersection(ray, scene, null);
  1.3189 +        if(info.isHit){
  1.3190 +            var color = this.rayTrace(info, ray, scene, 0);
  1.3191 +            return color;
  1.3192 +        }
  1.3193 +        return scene.background.color;
  1.3194 +    },
  1.3195 +
  1.3196 +    testIntersection: function(ray, scene, exclude){
  1.3197 +        var hits = 0;
  1.3198 +        var best = new Flog.RayTracer.IntersectionInfo();
  1.3199 +        best.distance = 2000;
  1.3200 +
  1.3201 +        for(var i=0; i<scene.shapes.length; i++){
  1.3202 +            var shape = scene.shapes[i];
  1.3203 +
  1.3204 +            if(shape != exclude){
  1.3205 +                var info = shape.intersect(ray);
  1.3206 +                if(info.isHit && info.distance >= 0 && info.distance < best.distance){
  1.3207 +                    best = info;
  1.3208 +                    hits++;
  1.3209 +                }
  1.3210 +            }
  1.3211 +        }
  1.3212 +        best.hitCount = hits;
  1.3213 +        return best;
  1.3214 +    },
  1.3215 +
  1.3216 +    getReflectionRay: function(P,N,V){
  1.3217 +        var c1 = -N.dot(V);
  1.3218 +        var R1 = Flog.RayTracer.Vector.prototype.add(
  1.3219 +            Flog.RayTracer.Vector.prototype.multiplyScalar(N, 2*c1),
  1.3220 +            V
  1.3221 +        );
  1.3222 +        return new Flog.RayTracer.Ray(P, R1);
  1.3223 +    },
  1.3224 +
  1.3225 +    rayTrace: function(info, ray, scene, depth){
  1.3226 +        // Calc ambient
  1.3227 +        var color = Flog.RayTracer.Color.prototype.multiplyScalar(info.color, scene.background.ambience);
  1.3228 +        var oldColor = color;
  1.3229 +        var shininess = Math.pow(10, info.shape.material.gloss + 1);
  1.3230 +
  1.3231 +        for(var i=0; i<scene.lights.length; i++){
  1.3232 +            var light = scene.lights[i];
  1.3233 +
  1.3234 +            // Calc diffuse lighting
  1.3235 +            var v = Flog.RayTracer.Vector.prototype.subtract(
  1.3236 +                                light.position,
  1.3237 +                                info.position
  1.3238 +                            ).normalize();
  1.3239 +
  1.3240 +            if(this.options.renderDiffuse){
  1.3241 +                var L = v.dot(info.normal);
  1.3242 +                if(L > 0.0){
  1.3243 +                    color = Flog.RayTracer.Color.prototype.add(
  1.3244 +                                        color,
  1.3245 +                                        Flog.RayTracer.Color.prototype.multiply(
  1.3246 +                                            info.color,
  1.3247 +                                            Flog.RayTracer.Color.prototype.multiplyScalar(
  1.3248 +                                                light.color,
  1.3249 +                                                L
  1.3250 +                                            )
  1.3251 +                                        )
  1.3252 +                                    );
  1.3253 +                }
  1.3254 +            }
  1.3255 +
  1.3256 +            // The greater the depth the more accurate the colours, but
  1.3257 +            // this is exponentially (!) expensive
  1.3258 +            if(depth <= this.options.rayDepth){
  1.3259 +          // calculate reflection ray
  1.3260 +          if(this.options.renderReflections && info.shape.material.reflection > 0)
  1.3261 +          {
  1.3262 +              var reflectionRay = this.getReflectionRay(info.position, info.normal, ray.direction);
  1.3263 +              var refl = this.testIntersection(reflectionRay, scene, info.shape);
  1.3264 +
  1.3265 +              if (refl.isHit && refl.distance > 0){
  1.3266 +                  refl.color = this.rayTrace(refl, reflectionRay, scene, depth + 1);
  1.3267 +              } else {
  1.3268 +                  refl.color = scene.background.color;
  1.3269 +                        }
  1.3270 +
  1.3271 +                  color = Flog.RayTracer.Color.prototype.blend(
  1.3272 +                    color,
  1.3273 +                    refl.color,
  1.3274 +                    info.shape.material.reflection
  1.3275 +                  );
  1.3276 +          }
  1.3277 +
  1.3278 +                // Refraction
  1.3279 +                /* TODO */
  1.3280 +            }
  1.3281 +
  1.3282 +            /* Render shadows and highlights */
  1.3283 +
  1.3284 +            var shadowInfo = new Flog.RayTracer.IntersectionInfo();
  1.3285 +
  1.3286 +            if(this.options.renderShadows){
  1.3287 +                var shadowRay = new Flog.RayTracer.Ray(info.position, v);
  1.3288 +
  1.3289 +                shadowInfo = this.testIntersection(shadowRay, scene, info.shape);
  1.3290 +                if(shadowInfo.isHit && shadowInfo.shape != info.shape /*&& shadowInfo.shape.type != 'PLANE'*/){
  1.3291 +                    var vA = Flog.RayTracer.Color.prototype.multiplyScalar(color, 0.5);
  1.3292 +                    var dB = (0.5 * Math.pow(shadowInfo.shape.material.transparency, 0.5));
  1.3293 +                    color = Flog.RayTracer.Color.prototype.addScalar(vA,dB);
  1.3294 +                }
  1.3295 +            }
  1.3296 +
  1.3297 +      // Phong specular highlights
  1.3298 +      if(this.options.renderHighlights && !shadowInfo.isHit && info.shape.material.gloss > 0){
  1.3299 +        var Lv = Flog.RayTracer.Vector.prototype.subtract(
  1.3300 +                            info.shape.position,
  1.3301 +                            light.position
  1.3302 +                        ).normalize();
  1.3303 +
  1.3304 +        var E = Flog.RayTracer.Vector.prototype.subtract(
  1.3305 +                            scene.camera.position,
  1.3306 +                            info.shape.position
  1.3307 +                        ).normalize();
  1.3308 +
  1.3309 +        var H = Flog.RayTracer.Vector.prototype.subtract(
  1.3310 +                            E,
  1.3311 +                            Lv
  1.3312 +                        ).normalize();
  1.3313 +
  1.3314 +        var glossWeight = Math.pow(Math.max(info.normal.dot(H), 0), shininess);
  1.3315 +        color = Flog.RayTracer.Color.prototype.add(
  1.3316 +                            Flog.RayTracer.Color.prototype.multiplyScalar(light.color, glossWeight),
  1.3317 +                            color
  1.3318 +                        );
  1.3319 +      }
  1.3320 +        }
  1.3321 +        color.limit();
  1.3322 +        return color;
  1.3323 +    }
  1.3324 +};
  1.3325 +
  1.3326 +
  1.3327 +function renderScene(){
  1.3328 +    var scene = new Flog.RayTracer.Scene();
  1.3329 +
  1.3330 +    scene.camera = new Flog.RayTracer.Camera(
  1.3331 +                        new Flog.RayTracer.Vector(0, 0, -15),
  1.3332 +                        new Flog.RayTracer.Vector(-0.2, 0, 5),
  1.3333 +                        new Flog.RayTracer.Vector(0, 1, 0)
  1.3334 +                    );
  1.3335 +
  1.3336 +    scene.background = new Flog.RayTracer.Background(
  1.3337 +                                new Flog.RayTracer.Color(0.5, 0.5, 0.5),
  1.3338 +                                0.4
  1.3339 +                            );
  1.3340 +
  1.3341 +    var sphere = new Flog.RayTracer.Shape.Sphere(
  1.3342 +        new Flog.RayTracer.Vector(-1.5, 1.5, 2),
  1.3343 +        1.5,
  1.3344 +        new Flog.RayTracer.Material.Solid(
  1.3345 +            new Flog.RayTracer.Color(0,0.5,0.5),
  1.3346 +            0.3,
  1.3347 +            0.0,
  1.3348 +            0.0,
  1.3349 +            2.0
  1.3350 +        )
  1.3351 +    );
  1.3352 +
  1.3353 +    var sphere1 = new Flog.RayTracer.Shape.Sphere(
  1.3354 +        new Flog.RayTracer.Vector(1, 0.25, 1),
  1.3355 +        0.5,
  1.3356 +        new Flog.RayTracer.Material.Solid(
  1.3357 +            new Flog.RayTracer.Color(0.9,0.9,0.9),
  1.3358 +            0.1,
  1.3359 +            0.0,
  1.3360 +            0.0,
  1.3361 +            1.5
  1.3362 +        )
  1.3363 +    );
  1.3364 +
  1.3365 +    var plane = new Flog.RayTracer.Shape.Plane(
  1.3366 +                                new Flog.RayTracer.Vector(0.1, 0.9, -0.5).normalize(),
  1.3367 +                                1.2,
  1.3368 +                                new Flog.RayTracer.Material.Chessboard(
  1.3369 +                                    new Flog.RayTracer.Color(1,1,1),
  1.3370 +                                    new Flog.RayTracer.Color(0,0,0),
  1.3371 +                                    0.2,
  1.3372 +                                    0.0,
  1.3373 +                                    1.0,
  1.3374 +                                    0.7
  1.3375 +                                )
  1.3376 +                            );
  1.3377 +
  1.3378 +    scene.shapes.push(plane);
  1.3379 +    scene.shapes.push(sphere);
  1.3380 +    scene.shapes.push(sphere1);
  1.3381 +
  1.3382 +    var light = new Flog.RayTracer.Light(
  1.3383 +        new Flog.RayTracer.Vector(5, 10, -1),
  1.3384 +        new Flog.RayTracer.Color(0.8, 0.8, 0.8)
  1.3385 +    );
  1.3386 +
  1.3387 +    var light1 = new Flog.RayTracer.Light(
  1.3388 +        new Flog.RayTracer.Vector(-3, 5, -15),
  1.3389 +        new Flog.RayTracer.Color(0.8, 0.8, 0.8),
  1.3390 +        100
  1.3391 +    );
  1.3392 +
  1.3393 +    scene.lights.push(light);
  1.3394 +    scene.lights.push(light1);
  1.3395 +
  1.3396 +    var imageWidth = 100; // $F('imageWidth');
  1.3397 +    var imageHeight = 100; // $F('imageHeight');
  1.3398 +    var pixelSize = "5,5".split(','); //  $F('pixelSize').split(',');
  1.3399 +    var renderDiffuse = true; // $F('renderDiffuse');
  1.3400 +    var renderShadows = true; // $F('renderShadows');
  1.3401 +    var renderHighlights = true; // $F('renderHighlights');
  1.3402 +    var renderReflections = true; // $F('renderReflections');
  1.3403 +    var rayDepth = 2;//$F('rayDepth');
  1.3404 +
  1.3405 +    var raytracer = new Flog.RayTracer.Engine(
  1.3406 +        {
  1.3407 +            canvasWidth: imageWidth,
  1.3408 +            canvasHeight: imageHeight,
  1.3409 +            pixelWidth: pixelSize[0],
  1.3410 +            pixelHeight: pixelSize[1],
  1.3411 +            "renderDiffuse": renderDiffuse,
  1.3412 +            "renderHighlights": renderHighlights,
  1.3413 +            "renderShadows": renderShadows,
  1.3414 +            "renderReflections": renderReflections,
  1.3415 +            "rayDepth": rayDepth
  1.3416 +        }
  1.3417 +    );
  1.3418 +
  1.3419 +    raytracer.renderScene(scene, null, 0);
  1.3420 +}
  1.3421 +

mercurial