* New AJAX-based wholist refresh
authorArt Cancro <ajc@citadel.org>
Sat, 23 Jul 2005 04:18:41 +0000 (04:18 +0000)
committerArt Cancro <ajc@citadel.org>
Sat, 23 Jul 2005 04:18:41 +0000 (04:18 +0000)
webcit/ChangeLog
webcit/static/head.html
webcit/static/prototype.js [new file with mode: 0644]
webcit/static/rico.js [new file with mode: 0644]
webcit/webcit.c
webcit/webcit.h
webcit/who.c

index 0d6c9da0ca9066ec04da12e5da756dc7c098338f..a3d7c5114d2fdf76a36929259d7ada6415e3752d 100644 (file)
@@ -1,4 +1,7 @@
 $Log$
+Revision 619.32  2005/07/23 04:18:40  ajc
+* New AJAX-based wholist refresh
+
 Revision 619.31  2005/07/22 15:07:29  ajc
 * 'whobbs' is now 'who'
 * Refactored the wholist code to have its inner div output by a separate
@@ -2737,4 +2740,3 @@ Sun Dec  6 19:50:55 EST 1998 Art Cancro <ajc@uncnsrd.mt-kisco.ny.us>
 
 1998-12-03 Nathan Bryant <bryant@cs.usm.maine.edu>
        * webserver.c: warning fix
-
index 93b8ea3261c3c84785e6e37a0065fee8e5f48c7c..f4b0ac922d286119ad76e33843112839389099a5 100644 (file)
@@ -9,5 +9,7 @@
 <meta name="MSSmartTagsPreventParsing" content="TRUE" />
 <link href="/static/webcit.css" rel="stylesheet" type="text/css">
 <script type="text/javascript" src="static/wclib.js"></script>
+<script type="text/javascript" src="static/prototype.js"></script>
+<script type="text/javascript" src="static/rico.js"></script>
 </head>
 <body>
diff --git a/webcit/static/prototype.js b/webcit/static/prototype.js
new file mode 100644 (file)
index 0000000..006d603
--- /dev/null
@@ -0,0 +1,778 @@
+/*  Prototype: an object-oriented Javascript library, version 1.2.1
+ *  (c) 2005 Sam Stephenson <sam@conio.net>
+ *
+ *  THIS FILE IS AUTOMATICALLY GENERATED. When sending patches, please diff
+ *  against the source tree, available from the Prototype darcs repository. 
+ *
+ *  Prototype is freely distributable under the terms of an MIT-style license.
+ *
+ *  For details, see the Prototype web site: http://prototype.conio.net/
+ *
+/*--------------------------------------------------------------------------*/
+
+var Prototype = {
+  Version: '1.2.1'
+}
+
+var Class = {
+  create: function() {
+    return function() { 
+      this.initialize.apply(this, arguments);
+    }
+  }
+}
+
+var Abstract = new Object();
+
+Object.prototype.extend = function(object) {
+  for (property in object) {
+    this[property] = object[property];
+  }
+  return this;
+}
+
+Function.prototype.bind = function(object) {
+  var method = this;
+  return function() {
+    method.apply(object, arguments);
+  }
+}
+
+Function.prototype.bindAsEventListener = function(object) {
+  var method = this;
+  return function(event) {
+    method.call(object, event || window.event);
+  }
+}
+
+Number.prototype.toColorPart = function() {
+  var digits = this.toString(16);
+  if (this < 16) return '0' + digits;
+  return digits;
+}
+
+var Try = {
+  these: function() {
+    var returnValue;
+    
+    for (var i = 0; i < arguments.length; i++) {
+      var lambda = arguments[i];
+      try {
+        returnValue = lambda();
+        break;
+      } catch (e) {}
+    }
+    
+    return returnValue;
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var PeriodicalExecuter = Class.create();
+PeriodicalExecuter.prototype = {
+  initialize: function(callback, frequency) {
+    this.callback = callback;
+    this.frequency = frequency;
+    this.currentlyExecuting = false;
+    
+    this.registerCallback();
+  },
+  
+  registerCallback: function() {
+    setTimeout(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+  
+  onTimerEvent: function() {
+    if (!this.currentlyExecuting) {
+      try { 
+        this.currentlyExecuting = true;
+        this.callback(); 
+      } finally { 
+        this.currentlyExecuting = false;
+      }
+    }
+    
+    this.registerCallback();
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+function $() {
+  var elements = new Array();
+  
+  for (var i = 0; i < arguments.length; i++) {
+    var element = arguments[i];
+    if (typeof element == 'string')
+      element = document.getElementById(element);
+
+    if (arguments.length == 1) 
+      return element;
+      
+    elements.push(element);
+  }
+  
+  return elements;
+}
+
+/*--------------------------------------------------------------------------*/
+
+if (!Array.prototype.push) {
+  Array.prototype.push = function() {
+               var startLength = this.length;
+               for (var i = 0; i < arguments.length; i++)
+      this[startLength + i] = arguments[i];
+         return this.length;
+  }
+}
+
+if (!Function.prototype.apply) {
+  // Based on code from http://www.youngpup.net/
+  Function.prototype.apply = function(object, parameters) {
+    var parameterStrings = new Array();
+    if (!object)     object = window;
+    if (!parameters) parameters = new Array();
+    
+    for (var i = 0; i < parameters.length; i++)
+      parameterStrings[i] = 'x[' + i + ']';
+    
+    object.__apply__ = this;
+    var result = eval('obj.__apply__(' + 
+      parameterStrings[i].join(', ') + ')');
+    object.__apply__ = null;
+    
+    return result;
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Ajax = {
+  getTransport: function() {
+    return Try.these(
+      function() {return new ActiveXObject('Msxml2.XMLHTTP')},
+      function() {return new ActiveXObject('Microsoft.XMLHTTP')},
+      function() {return new XMLHttpRequest()}
+    ) || false;
+  },
+  
+  emptyFunction: function() {}
+}
+
+Ajax.Base = function() {};
+Ajax.Base.prototype = {
+  setOptions: function(options) {
+    this.options = {
+      method:       'post',
+      asynchronous: true,
+      parameters:   ''
+    }.extend(options || {});
+  }
+}
+
+Ajax.Request = Class.create();
+Ajax.Request.Events = 
+  ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
+
+Ajax.Request.prototype = (new Ajax.Base()).extend({
+  initialize: function(url, options) {
+    this.transport = Ajax.getTransport();
+    this.setOptions(options);
+  
+    try {
+      if (this.options.method == 'get')
+        url += '?' + this.options.parameters + '&_=';
+    
+      this.transport.open(this.options.method, url,
+        this.options.asynchronous);
+      
+      if (this.options.asynchronous) {
+        this.transport.onreadystatechange = this.onStateChange.bind(this);
+        setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
+      }
+              
+      this.transport.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
+      this.transport.setRequestHeader('X-Prototype-Version',
+        Prototype.Version);
+
+      if (this.options.method == 'post') {
+        this.transport.setRequestHeader('Connection', 'close');
+        this.transport.setRequestHeader('Content-type',
+          'application/x-www-form-urlencoded');
+      }
+
+      if (this.options.requestHeaders) {
+         for (var i=0; i< (this.options.requestHeaders.length-1);i+=2)
+            this.transport.setRequestHeader(this.options.requestHeaders[i],
+                                            this.options.requestHeaders[i+1]);
+      }
+
+      var sendData = this.options.postBody   ? this.options.postBody
+                   : this.options.parameters ? this.options.parameters + '&_'
+                   : null;
+
+      this.transport.send(this.options.method == 'post' ? sendData : null );
+                      
+    } catch (e) {
+    }    
+  },
+      
+  onStateChange: function() {
+    var readyState = this.transport.readyState;
+    if (readyState != 1)
+      this.respondToReadyState(this.transport.readyState);
+  },
+  
+  respondToReadyState: function(readyState) {
+    var event = Ajax.Request.Events[readyState];
+    (this.options['on' + event] || Ajax.emptyFunction)(this.transport);
+  }
+});
+
+Ajax.Updater = Class.create();
+Ajax.Updater.prototype = (new Ajax.Base()).extend({
+  initialize: function(container, url, options) {
+    this.container = $(container);
+    this.setOptions(options);
+  
+    if (this.options.asynchronous) {
+      this.onComplete = this.options.onComplete;
+      this.options.onComplete = this.updateContent.bind(this);
+    }
+
+    this.request = new Ajax.Request(url, this.options);
+    
+    if (!this.options.asynchronous)
+      this.updateContent();
+  },
+  
+  updateContent: function() {
+    if (this.request.transport.status == 200) {
+      if (this.options.insertion) {
+        new this.options.insertion(this.container,
+        this.request.transport.responseText);
+      } else {
+        this.container.innerHTML = this.request.transport.responseText;
+      }
+    }  
+
+    if (this.onComplete) {
+      setTimeout((function() {this.onComplete(
+        this.request.transport)}).bind(this), 10);
+    }
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+var Field = {
+  clear: function() {
+    for (var i = 0; i < arguments.length; i++)
+      $(arguments[i]).value = '';
+  },
+
+  focus: function(element) {
+    $(element).focus();
+  },
+  
+  present: function() {
+    for (var i = 0; i < arguments.length; i++)
+      if ($(arguments[i]).value == '') return false;
+    return true;
+  },
+  
+  select: function(element) {
+    $(element).select();
+  },
+   
+  activate: function(element) {
+    $(element).focus();
+    $(element).select();
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Form = {
+  serialize: function(form) {
+    var elements = Form.getElements($(form));
+    var queryComponents = new Array();
+    
+    for (var i = 0; i < elements.length; i++) {
+      var queryComponent = Form.Element.serialize(elements[i]);
+      if (queryComponent)
+        queryComponents.push(queryComponent);
+    }
+    
+    return queryComponents.join('&');
+  },
+  
+  getElements: function(form) {
+    form = $(form);
+    var elements = new Array();
+
+    for (tagName in Form.Element.Serializers) {
+      var tagElements = form.getElementsByTagName(tagName);
+      for (var j = 0; j < tagElements.length; j++)
+        elements.push(tagElements[j]);
+    }
+    return elements;
+  },
+  
+  disable: function(form) {
+    var elements = Form.getElements(form);
+    for (var i = 0; i < elements.length; i++) {
+      var element = elements[i];
+      element.blur();
+      element.disable = 'true';
+    }
+  },
+
+  focusFirstElement: function(form) {
+    form = $(form);
+    var elements = Form.getElements(form);
+    for (var i = 0; i < elements.length; i++) {
+      var element = elements[i];
+      if (element.type != 'hidden' && !element.disabled) {
+        Field.activate(element);
+        break;
+      }
+    }
+  },
+
+  reset: function(form) {
+    $(form).reset();
+  }
+}
+
+Form.Element = {
+  serialize: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    var parameter = Form.Element.Serializers[method](element);
+    
+    if (parameter)
+      return encodeURIComponent(parameter[0]) + '=' + 
+        encodeURIComponent(parameter[1]);                   
+  },
+  
+  getValue: function(element) {
+    element = $(element);
+    var method = element.tagName.toLowerCase();
+    var parameter = Form.Element.Serializers[method](element);
+    
+    if (parameter) 
+      return parameter[1];
+  }
+}
+
+Form.Element.Serializers = {
+  input: function(element) {
+    switch (element.type.toLowerCase()) {
+      case 'hidden':
+      case 'password':
+      case 'text':
+        return Form.Element.Serializers.textarea(element);
+      case 'checkbox':  
+      case 'radio':
+        return Form.Element.Serializers.inputSelector(element);
+    }
+    return false;
+  },
+
+  inputSelector: function(element) {
+    if (element.checked)
+      return [element.name, element.value];
+  },
+
+  textarea: function(element) {
+    return [element.name, element.value];
+  },
+
+  select: function(element) {
+    var index = element.selectedIndex;
+    var value = element.options[index].value || element.options[index].text;
+    return [element.name, (index >= 0) ? value : ''];
+  }
+}
+
+/*--------------------------------------------------------------------------*/
+
+var $F = Form.Element.getValue;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.TimedObserver = function() {}
+Abstract.TimedObserver.prototype = {
+  initialize: function(element, frequency, callback) {
+    this.frequency = frequency;
+    this.element   = $(element);
+    this.callback  = callback;
+    
+    this.lastValue = this.getValue();
+    this.registerCallback();
+  },
+  
+  registerCallback: function() {
+    setTimeout(this.onTimerEvent.bind(this), this.frequency * 1000);
+  },
+  
+  onTimerEvent: function() {
+    var value = this.getValue();
+    if (this.lastValue != value) {
+      this.callback(this.element, value);
+      this.lastValue = value;
+    }
+    
+    this.registerCallback();
+  }
+}
+
+Form.Element.Observer = Class.create();
+Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend({
+  getValue: function() {
+    return Form.Element.getValue(this.element);
+  }
+});
+
+Form.Observer = Class.create();
+Form.Observer.prototype = (new Abstract.TimedObserver()).extend({
+  getValue: function() {
+    return Form.serialize(this.element);
+  }
+});
+
+
+/*--------------------------------------------------------------------------*/
+
+document.getElementsByClassName = function(className) {
+  var children = document.getElementsByTagName('*') || document.all;
+  var elements = new Array();
+  
+  for (var i = 0; i < children.length; i++) {
+    var child = children[i];
+    var classNames = child.className.split(' ');
+    for (var j = 0; j < classNames.length; j++) {
+      if (classNames[j] == className) {
+        elements.push(child);
+        break;
+      }
+    }
+  }
+  
+  return elements;
+}
+
+/*--------------------------------------------------------------------------*/
+
+var Element = {
+  toggle: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      element.style.display = 
+        (element.style.display == 'none' ? '' : 'none');
+    }
+  },
+
+  hide: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      element.style.display = 'none';
+    }
+  },
+
+  show: function() {
+    for (var i = 0; i < arguments.length; i++) {
+      var element = $(arguments[i]);
+      element.style.display = '';
+    }
+  },
+
+  remove: function(element) {
+    element = $(element);
+    element.parentNode.removeChild(element);
+  },
+   
+  getHeight: function(element) {
+    element = $(element);
+    return element.offsetHeight; 
+  }
+}
+
+var Toggle = new Object();
+Toggle.display = Element.toggle;
+
+/*--------------------------------------------------------------------------*/
+
+Abstract.Insertion = function(adjacency) {
+  this.adjacency = adjacency;
+}
+
+Abstract.Insertion.prototype = {
+  initialize: function(element, content) {
+    this.element = $(element);
+    this.content = content;
+    
+    if (this.adjacency && this.element.insertAdjacentHTML) {
+      this.element.insertAdjacentHTML(this.adjacency, this.content);
+    } else {
+      this.range = this.element.ownerDocument.createRange();
+      if (this.initializeRange) this.initializeRange();
+      this.fragment = this.range.createContextualFragment(this.content);
+      this.insertContent();
+    }
+  }
+}
+
+var Insertion = new Object();
+
+Insertion.Before = Class.create();
+Insertion.Before.prototype = (new Abstract.Insertion('beforeBegin')).extend({
+  initializeRange: function() {
+    this.range.setStartBefore(this.element);
+  },
+  
+  insertContent: function() {
+    this.element.parentNode.insertBefore(this.fragment, this.element);
+  }
+});
+
+Insertion.Top = Class.create();
+Insertion.Top.prototype = (new Abstract.Insertion('afterBegin')).extend({
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(true);
+  },
+  
+  insertContent: function() {  
+    this.element.insertBefore(this.fragment, this.element.firstChild);
+  }
+});
+
+Insertion.Bottom = Class.create();
+Insertion.Bottom.prototype = (new Abstract.Insertion('beforeEnd')).extend({
+  initializeRange: function() {
+    this.range.selectNodeContents(this.element);
+    this.range.collapse(this.element);
+  },
+  
+  insertContent: function() {
+    this.element.appendChild(this.fragment);
+  }
+});
+
+Insertion.After = Class.create();
+Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend({
+  initializeRange: function() {
+    this.range.setStartAfter(this.element);
+  },
+  
+  insertContent: function() {
+    this.element.parentNode.insertBefore(this.fragment, 
+      this.element.nextSibling);
+  }
+});
+
+/*--------------------------------------------------------------------------*/
+
+var Effect = new Object();
+
+Effect.Highlight = Class.create();
+Effect.Highlight.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+    this.start  = 153;
+    this.finish = 255;
+    this.current = this.start;
+    this.fade();
+  },
+  
+  fade: function() {
+    if (this.isFinished()) return;
+    if (this.timer) clearTimeout(this.timer);
+    this.highlight(this.element, this.current);
+    this.current += 17;
+    this.timer = setTimeout(this.fade.bind(this), 250);
+  },
+  
+  isFinished: function() {
+    return this.current > this.finish;
+  },
+  
+  highlight: function(element, current) {
+    element.style.backgroundColor = "#ffff" + current.toColorPart();
+  }
+}
+
+
+Effect.Fade = Class.create();
+Effect.Fade.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+    this.start  = 100;
+    this.finish = 0;
+    this.current = this.start;
+    this.fade();
+  },
+  
+  fade: function() {
+    if (this.isFinished()) { this.element.style.display = 'none'; return; }
+    if (this.timer) clearTimeout(this.timer);
+    this.setOpacity(this.element, this.current);
+    this.current -= 10;
+    this.timer = setTimeout(this.fade.bind(this), 50);
+  },
+  
+  isFinished: function() {
+    return this.current <= this.finish;
+  },
+  
+  setOpacity: function(element, opacity) {
+    opacity = (opacity == 100) ? 99.999 : opacity;
+    element.style.filter = "alpha(opacity:"+opacity+")";
+    element.style.opacity = opacity/100 /*//*/;
+  }
+}
+
+Effect.Scale = Class.create();
+Effect.Scale.prototype = {
+  initialize: function(element, percent) {
+    this.element = $(element);
+    this.startScale    = 1.0;
+    this.startHeight   = this.element.offsetHeight;
+    this.startWidth    = this.element.offsetWidth;
+    this.currentHeight = this.startHeight;
+    this.currentWidth  = this.startWidth;
+    this.finishScale   = (percent/100) /*//*/;
+    if (this.element.style.fontSize=="") this.sizeEm = 1.0;
+    if (this.element.style.fontSize.indexOf("em")>0)
+       this.sizeEm      = parseFloat(this.element.style.fontSize);
+    if(this.element.effect_scale) {
+      clearTimeout(this.element.effect_scale.timer);
+      this.startScale  = this.element.effect_scale.currentScale;
+      this.startHeight = this.element.effect_scale.startHeight;
+      this.startWidth  = this.element.effect_scale.startWidth;
+      if(this.element.effect_scale.sizeEm) 
+        this.sizeEm    = this.element.effect_scale.sizeEm;      
+    }
+    this.element.effect_scale = this;
+    this.currentScale  = this.startScale;
+    this.factor        = this.finishScale - this.startScale;
+    this.options       = arguments[2] || {}; 
+    this.scale();
+  },
+  
+  scale: function() {
+    if (this.isFinished()) { 
+      this.setDimensions(this.element, this.startWidth*this.finishScale, this.startHeight*this.finishScale);
+      if(this.sizeEm) this.element.style.fontSize = this.sizeEm*this.finishScale + "em";
+      if(this.options.complete) this.options.complete(this);
+      return; 
+    }
+    if (this.timer) clearTimeout(this.timer);
+    if (this.options.step) this.options.step(this);
+    this.setDimensions(this.element, this.currentWidth, this.currentHeight);
+    if(this.sizeEm) this.element.style.fontSize = this.sizeEm*this.currentScale + "em";
+    this.currentScale += (this.factor/10) /*//*/;
+    this.currentWidth = this.startWidth * this.currentScale;
+    this.currentHeight = this.startHeight * this.currentScale;
+    this.timer = setTimeout(this.scale.bind(this), 50);
+  },
+  
+  isFinished: function() {
+    return (this.factor < 0) ? 
+      this.currentScale <= this.finishScale : this.currentScale >= this.finishScale;
+  },
+  
+  setDimensions: function(element, width, height) {
+    element.style.width = width + 'px';
+    element.style.height = height + 'px';
+  }
+}
+
+Effect.Squish = Class.create();
+Effect.Squish.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+    new Effect.Scale(this.element, 1, { complete: this.hide.bind(this) } );
+  },
+  hide: function() {
+    this.element.style.display = 'none';
+  } 
+}
+
+Effect.Puff = Class.create();
+Effect.Puff.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+    this.opacity = 100;
+    this.startTop  = this.element.top || this.element.offsetTop;
+    this.startLeft = this.element.left || this.element.offsetLeft;
+    new Effect.Scale(this.element, 200, { step: this.fade.bind(this), complete: this.hide.bind(this) } );
+  },
+  fade: function(effect) {
+    topd    = (((effect.currentScale)*effect.startHeight) - effect.startHeight)/2;
+    leftd   = (((effect.currentScale)*effect.startWidth) - effect.startWidth)/2;
+    this.element.style.position='absolute';
+    this.element.style.top = this.startTop-topd + "px";
+    this.element.style.left = this.startLeft-leftd + "px";
+    this.opacity -= 10;
+    this.setOpacity(this.element, this.opacity); 
+    if(navigator.appVersion.indexOf('AppleWebKit')>0) this.element.innerHTML += ''; //force redraw on safari
+  },
+  hide: function() {
+    this.element.style.display = 'none';
+  },
+  setOpacity: function(element, opacity) {
+    opacity = (opacity == 100) ? 99.999 : opacity;
+    element.style.filter = "alpha(opacity:"+opacity+")";
+    element.style.opacity = opacity/100 /*//*/;
+  }
+}
+
+Effect.Appear = Class.create();
+Effect.Appear.prototype = {
+  initialize: function(element) {
+    this.element = $(element);
+    this.start  = 0;
+    this.finish = 100;
+    this.current = this.start;
+    this.fade();
+  },
+  
+  fade: function() {
+    if (this.isFinished()) return;
+    if (this.timer) clearTimeout(this.timer);
+    this.setOpacity(this.element, this.current);
+    this.current += 10;
+    this.timer = setTimeout(this.fade.bind(this), 50);
+  },
+  
+  isFinished: function() {
+    return this.current > this.finish;
+  },
+  
+  setOpacity: function(element, opacity) {
+    opacity = (opacity == 100) ? 99.999 : opacity;
+    element.style.filter = "alpha(opacity:"+opacity+")";
+    element.style.opacity = opacity/100 /*//*/;
+    element.style.display = '';
+  }
+}
+
+Effect.ContentZoom = Class.create();
+Effect.ContentZoom.prototype = {
+  initialize: function(element, percent) {
+    this.element = $(element);
+    if (this.element.style.fontSize=="") this.sizeEm = 1.0;
+    if (this.element.style.fontSize.indexOf("em")>0)
+       this.sizeEm = parseFloat(this.element.style.fontSize);
+    if(this.element.effect_contentzoom) {
+      this.sizeEm = this.element.effect_contentzoom.sizeEm;
+    }
+    this.element.effect_contentzoom = this;
+    this.element.style.fontSize = this.sizeEm*(percent/100) + "em" /*//*/;
+    if(navigator.appVersion.indexOf('AppleWebKit')>0) { this.element.scrollTop -= 1; };
+  }
+}
diff --git a/webcit/static/rico.js b/webcit/static/rico.js
new file mode 100644 (file)
index 0000000..d17acb1
--- /dev/null
@@ -0,0 +1,2287 @@
+/* openrico.org rico.js */
+/**
+  *
+  *  Copyright 2005 Sabre Airline Solutions
+  *
+  *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
+  *  file except in compliance with the License. You may obtain a copy of the License at
+  *
+  *         http://www.apache.org/licenses/LICENSE-2.0
+  *
+  *  Unless required by applicable law or agreed to in writing, software distributed under the
+  *  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+  *  either express or implied. See the License for the specific language governing permissions
+  *  and limitations under the License.
+  **/
+
+// rico.js --------------------
+
+var Rico = {
+  Version: '1.1-beta'
+}
+
+Rico.ArrayExtensions = new Array();
+
+if (Object.prototype.extend) {
+   // in prototype.js...
+   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend;
+}
+
+if (Array.prototype.push) {
+   // in prototype.js...
+   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.push;
+}
+
+if (!Array.prototype.remove) {
+   Array.prototype.remove = function(dx) {
+      if( isNaN(dx) || dx > this.length )
+         return false;
+      for( var i=0,n=0; i<this.length; i++ )
+         if( i != dx )
+            this[n++]=this[i];
+      this.length-=1;
+   };
+  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.remove;
+}
+
+if (!Array.prototype.removeItem) {
+   Array.prototype.removeItem = function(item) {
+      for ( var i = 0 ; i < this.length ; i++ )
+         if ( this[i] == item ) {
+            this.remove(i);
+            break;
+         }
+   };
+  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.removeItem;
+}
+
+if (!Array.prototype.indices) {
+   Array.prototype.indices = function() {
+      var indexArray = new Array();
+      for ( index in this ) {
+         var ignoreThis = false;
+         for ( var i = 0 ; i < Rico.ArrayExtensions.length ; i++ ) {
+            if ( this[index] == Rico.ArrayExtensions[i] ) {
+               ignoreThis = true;
+               break;
+            }
+         }
+         if ( !ignoreThis )
+            indexArray[ indexArray.length ] = index;
+      }
+      return indexArray;
+   }
+  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.indices;
+}
+
+// Create the loadXML method and xml getter for Mozilla
+if ( window.DOMParser &&
+         window.XMLSerializer &&
+         window.Node && Node.prototype && Node.prototype.__defineGetter__ ) {
+
+   if (!Document.prototype.loadXML) {
+      Document.prototype.loadXML = function (s) {
+         var doc2 = (new DOMParser()).parseFromString(s, "text/xml");
+         while (this.hasChildNodes())
+            this.removeChild(this.lastChild);
+
+         for (var i = 0; i < doc2.childNodes.length; i++) {
+            this.appendChild(this.importNode(doc2.childNodes[i], true));
+         }
+      };
+       }
+
+       Document.prototype.__defineGetter__( "xml",
+          function () {
+                  return (new XMLSerializer()).serializeToString(this);
+          }
+        );
+}
+
+document.getElementsByTagAndClassName = function(tagName, className) {
+  if ( tagName == null )
+     tagName = '*';
+
+  var children = document.getElementsByTagName(tagName) || document.all;
+  var elements = new Array();
+
+  if ( className == null )
+    return children;
+
+  for (var i = 0; i < children.length; i++) {
+    var child = children[i];
+    var classNames = child.className.split(' ');
+    for (var j = 0; j < classNames.length; j++) {
+      if (classNames[j] == className) {
+        elements.push(child);
+        break;
+      }
+    }
+  }
+
+  return elements;
+}
+// ricoAccordion.js --------------------
+
+Rico.Accordion = Class.create();
+
+Rico.Accordion.prototype = {
+
+   initialize: function(container, options) {
+      this.container            = $(container);
+      this.lastExpandedTab      = null;
+      this.accordionTabs        = new Array();
+      this.setOptions(options);
+      this._attachBehaviors();
+
+      this.container.style.borderBottom = '1px solid ' + this.options.borderColor;
+
+      // set the initial visual state...
+      for ( var i=1 ; i < this.accordionTabs.length ; i++ )
+      {
+         this.accordionTabs[i].collapse();
+         this.accordionTabs[i].content.style.display = 'none';
+      }
+      this.lastExpandedTab = this.accordionTabs[0];
+      this.lastExpandedTab.content.style.height = this.options.panelHeight + "px";
+      this.lastExpandedTab.showExpanded();
+      this.lastExpandedTab.titleBar.style.fontWeight = this.options.expandedFontWeight;
+   },
+
+   setOptions: function(options) {
+      this.options = {
+         expandedBg          : '#63699c',
+         hoverBg             : '#63699c',
+         collapsedBg         : '#6b79a5',
+         expandedTextColor   : '#ffffff',
+         expandedFontWeight  : 'bold',
+         hoverTextColor      : '#ffffff',
+         collapsedTextColor  : '#ced7ef',
+         collapsedFontWeight : 'normal',
+         hoverTextColor      : '#ffffff',
+         borderColor         : '#1f669b',
+         panelHeight         : 200,
+         onHideTab           : null,
+         onShowTab           : null
+      }.extend(options || {});
+   },
+
+   showTabByIndex: function( anIndex, animate ) {
+      var doAnimate = arguments.length == 1 ? true : animate;
+      this.showTab( this.accordionTabs[anIndex], doAnimate );
+   },
+
+   showTab: function( accordionTab, animate ) {
+
+      var doAnimate = arguments.length == 1 ? true : animate;
+
+      if ( this.options.onHideTab )
+         this.options.onHideTab(this.lastExpandedTab);
+
+      this.lastExpandedTab.showCollapsed(); 
+      var accordion = this;
+      var lastExpandedTab = this.lastExpandedTab;
+
+      this.lastExpandedTab.content.style.height = (this.options.panelHeight - 1) + 'px';
+      accordionTab.content.style.display = '';
+
+      accordionTab.titleBar.style.fontWeight = this.options.expandedFontWeight;
+
+      if ( doAnimate ) {
+         new Effect.AccordionSize( this.lastExpandedTab.content,
+                                   accordionTab.content,
+                                   1,
+                                   this.options.panelHeight,
+                                   100, 10,
+                                   { complete: function() {accordion.showTabDone(lastExpandedTab)} } );
+         this.lastExpandedTab = accordionTab;
+      }
+      else {
+         this.lastExpandedTab.content.style.height = "1px";
+         accordionTab.content.style.height = this.options.panelHeight + "px";
+         this.lastExpandedTab = accordionTab;
+         this.showTabDone(lastExpandedTab);
+      }
+   },
+
+   showTabDone: function(collapsedTab) {
+      collapsedTab.content.style.display = 'none';
+      this.lastExpandedTab.showExpanded();
+      if ( this.options.onShowTab )
+         this.options.onShowTab(this.lastExpandedTab);
+   },
+
+   _attachBehaviors: function() {
+      var panels = this._getDirectChildrenByTag(this.container, 'DIV');
+      for ( var i = 0 ; i < panels.length ; i++ ) {
+
+         var tabChildren = this._getDirectChildrenByTag(panels[i],'DIV');
+         if ( tabChildren.length != 2 )
+            continue; // unexpected
+
+         var tabTitleBar   = tabChildren[0];
+         var tabContentBox = tabChildren[1];
+         this.accordionTabs.push( new Rico.Accordion.Tab(this,tabTitleBar,tabContentBox) );
+      }
+   },
+
+   _getDirectChildrenByTag: function(e, tagName) {
+      var kids = new Array();
+      var allKids = e.childNodes;
+      for( var i = 0 ; i < allKids.length ; i++ )
+         if ( allKids[i] && allKids[i].tagName && allKids[i].tagName == tagName )
+            kids.push(allKids[i]);
+      return kids;
+   }
+
+};
+
+Rico.Accordion.Tab = Class.create();
+
+Rico.Accordion.Tab.prototype = {
+
+   initialize: function(accordion, titleBar, content) {
+      this.accordion = accordion;
+      this.titleBar  = titleBar;
+      this.content   = content;
+      this._attachBehaviors();
+   },
+
+   collapse: function() {
+      this.showCollapsed();
+      this.content.style.height = "1px";
+   },
+
+   showCollapsed: function() {
+      this.expanded = false;
+      this.titleBar.style.backgroundColor = this.accordion.options.collapsedBg;
+      this.titleBar.style.color           = this.accordion.options.collapsedTextColor;
+      this.titleBar.style.fontWeight      = this.accordion.options.collapsedFontWeight;
+      this.content.style.overflow = "hidden";
+   },
+
+   showExpanded: function() {
+      this.expanded = true;
+      this.titleBar.style.backgroundColor = this.accordion.options.expandedBg;
+      this.titleBar.style.color           = this.accordion.options.expandedTextColor;
+      this.content.style.overflow         = "visible";
+   },
+
+   titleBarClicked: function(e) {
+      if ( this.accordion.lastExpandedTab == this )
+         return;
+      this.accordion.showTab(this);
+   },
+
+   hover: function(e) {
+               this.titleBar.style.backgroundColor = this.accordion.options.hoverBg;
+               this.titleBar.style.color           = this.accordion.options.hoverTextColor;
+   },
+
+   unhover: function(e) {
+      if ( this.expanded ) {
+         this.titleBar.style.backgroundColor = this.accordion.options.expandedBg;
+         this.titleBar.style.color           = this.accordion.options.expandedTextColor;
+      }
+      else {
+         this.titleBar.style.backgroundColor = this.accordion.options.collapsedBg;
+         this.titleBar.style.color           = this.accordion.options.collapsedTextColor;
+      }
+   },
+
+   _attachBehaviors: function() {
+      this.content.style.border = "1px solid " + this.accordion.options.borderColor;
+      this.content.style.borderTopWidth    = "0px";
+      this.content.style.borderBottomWidth = "0px";
+      this.content.style.margin            = "0px";
+
+      this.titleBar.onclick     = this.titleBarClicked.bindAsEventListener(this);
+      this.titleBar.onmouseover = this.hover.bindAsEventListener(this);
+      this.titleBar.onmouseout  = this.unhover.bindAsEventListener(this);
+   }
+
+};
+// ricoAjaxEngine.js --------------------
+
+Rico.AjaxEngine = Class.create();
+
+Rico.AjaxEngine.prototype = {
+
+   initialize: function() {
+      this.ajaxElements = new Array();
+      this.ajaxObjects  = new Array();
+      this.requestURLS  = new Array();
+   },
+
+   registerAjaxElement: function( anId, anElement ) {
+      if ( arguments.length == 1 )
+         anElement = $(anId);
+      this.ajaxElements[anId] = anElement;
+   },
+
+   registerAjaxObject: function( anId, anObject ) {
+      this.ajaxObjects[anId] = anObject;
+   },
+
+   registerRequest: function (requestLogicalName, requestURL) {
+      this.requestURLS[requestLogicalName] = requestURL;
+   },
+
+   sendRequest: function(requestName) {
+      var requestURL = this.requestURLS[requestName];
+      if ( requestURL == null )
+         return;
+
+      var queryString = "";
+      if ( arguments.length > 1 )
+         queryString = this._createQueryString(arguments, 1);
+
+      new Ajax.Request(requestURL, this._requestOptions(queryString));
+   },
+
+   sendRequestWithData: function(requestName, xmlDocument) {
+      var requestURL = this.requestURLS[requestName];
+      if ( requestURL == null )
+         return;
+
+      var queryString = "";
+      if ( arguments.length > 2 )
+         queryString = this._createQueryString(arguments, 2);
+
+      new Ajax.Request(requestURL + "?" + queryString, this._requestOptions(null,xmlDocument));
+   },
+
+   sendRequestAndUpdate: function(requestName,container,options) {
+      var requestURL = this.requestURLS[requestName];
+      if ( requestURL == null )
+         return;
+
+      var queryString = "";
+      if ( arguments.length > 3 )
+         queryString = this._createQueryString(arguments, 3);
+
+      var updaterOptions = this._requestOptions(queryString);
+      updaterOptions.onComplete = null;
+      updaterOptions.extend(options);
+
+      new Ajax.Updater(container, requestURL, updaterOptions);
+   },
+
+   sendRequestWithDataAndUpdate: function(requestName,xmlDocument,container,options) {
+      var requestURL = this.requestURLS[requestName];
+      if ( requestURL == null )
+         return;
+
+      var queryString = "";
+      if ( arguments.length > 4 )
+         queryString = this._createQueryString(arguments, 4);
+
+
+      var updaterOptions = this._requestOptions(queryString,xmlDocument);
+      updaterOptions.onComplete = null;
+      updaterOptions.extend(options);
+
+      new Ajax.Updater(container, requestURL + "?" + queryString, updaterOptions);
+   },
+
+   // Private -- not part of intended engine API --------------------------------------------------------------------
+
+   _requestOptions: function(queryString,xmlDoc) {
+      var self = this;
+
+      var requestHeaders = ['X-Rico-Version', Rico.Version ];
+      var sendMethod = "post"
+      if ( arguments[1] )
+         requestHeaders.push( 'Content-type', 'text/xml' );
+      else
+         sendMethod = "get";
+
+      return { requestHeaders: requestHeaders,
+               parameters:     queryString,
+               postBody:       arguments[1] ? xmlDoc : null,
+               method:         sendMethod,
+               onComplete:     self._onRequestComplete.bind(self) };
+   },
+
+   _createQueryString: function( theArgs, offset ) {
+      var queryString = ""
+      for ( var i = offset ; i < theArgs.length ; i++ ) {
+          if ( i != offset )
+            queryString += "&";
+
+          var anArg = theArgs[i];
+
+          if ( anArg.name != undefined && anArg.value != undefined ) {
+            queryString += anArg.name +  "=" + escape(anArg.value);
+          }
+          else {
+             var ePos  = anArg.indexOf('=');
+             var argName  = anArg.substring( 0, ePos );
+             var argValue = anArg.substring( ePos + 1 );
+             queryString += argName + "=" + escape(argValue);
+          }
+      }
+
+      return queryString;
+   },
+
+   _onRequestComplete : function(request) {
+
+      //!!TODO: error handling infrastructure?? 
+      if (request.status != 200)
+        return;
+
+      var response = request.responseXML.getElementsByTagName("ajax-response");
+      if (response == null || response.length != 1)
+         return;
+      this._processAjaxResponse( response[0].childNodes );
+   },
+
+   _processAjaxResponse: function( xmlResponseElements ) {
+      for ( var i = 0 ; i < xmlResponseElements.length ; i++ ) {
+         var responseElement = xmlResponseElements[i];
+
+         // only process nodes of type element.....
+         if ( responseElement.nodeType != 1 )
+            continue;
+
+         var responseType = responseElement.getAttribute("type");
+         var responseId   = responseElement.getAttribute("id");
+
+         if ( responseType == "object" )
+            this._processAjaxObjectUpdate( this.ajaxObjects[ responseId ], responseElement );
+         else if ( responseType == "element" )
+            this._processAjaxElementUpdate( this.ajaxElements[ responseId ], responseElement );
+         else
+            alert('unrecognized AjaxResponse type : ' + responseType );
+      }
+   },
+
+   _processAjaxObjectUpdate: function( ajaxObject, responseElement ) {
+      ajaxObject.ajaxUpdate( responseElement );
+   },
+
+   _processAjaxElementUpdate: function( ajaxElement, responseElement ) {
+      if ( responseElement.xml != undefined )
+         this._processAjaxElementUpdateIE( ajaxElement, responseElement );
+      else
+         this._processAjaxElementUpdateMozilla( ajaxElement, responseElement );
+   },
+
+   _processAjaxElementUpdateIE: function( ajaxElement, responseElement ) {
+      var newHTML = "";
+      for ( var i = 0 ; i < responseElement.childNodes.length ; i++ )
+         newHTML += responseElement.childNodes[i].xml;
+
+      ajaxElement.innerHTML = newHTML;
+   },
+
+   _processAjaxElementUpdateMozilla: function( ajaxElement, responseElement ) {
+      var xmlSerializer = new XMLSerializer();
+      var newHTML = "";
+      for ( var i = 0 ; i < responseElement.childNodes.length ; i++ )
+         newHTML += xmlSerializer.serializeToString(responseElement.childNodes[i]);
+
+      ajaxElement.innerHTML = newHTML;
+   }
+}
+
+var ajaxEngine = new Rico.AjaxEngine(); 
+// ricoColor.js --------------------
+Rico.Color = Class.create();
+
+Rico.Color.prototype = {
+
+   initialize: function(red, green, blue) {
+      this.rgb = { r: red, g : green, b : blue };
+   },
+
+   setRed: function(r) {
+      this.rgb.r = r;
+   },
+
+   setGreen: function(g) {
+      this.rgb.g = g;
+   },
+
+   setBlue: function(b) {
+      this.rgb.b = b;
+   },
+
+   setHue: function(h) {
+
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.h = h;
+
+      // convert back to RGB...
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+   },
+
+   setSaturation: function(s) {
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.s = s;
+
+      // convert back to RGB and set values...
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
+   },
+
+   setBrightness: function(b) {
+      // get an HSB model, and set the new hue...
+      var hsb = this.asHSB();
+      hsb.b = b;
+
+      // convert back to RGB and set values...
+      this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b );
+   },
+
+   darken: function(percent) {
+      var hsb  = this.asHSB();
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0));
+   },
+
+   brighten: function(percent) {
+      var hsb  = this.asHSB();
+      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1));
+   },
+
+   blend: function(other) {
+      this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
+      this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
+      this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
+   },
+
+   isBright: function() {
+      var hsb = this.asHSB();
+      return this.asHSB().b > 0.5;
+   },
+
+   isDark: function() {
+      return ! this.isBright();
+   },
+
+   asRGB: function() {
+      return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
+   },
+
+   asHex: function() {
+      return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
+   },
+
+   asHSB: function() {
+      return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
+   },
+
+   toString: function() {
+      return this.asHex();
+   }
+
+};
+
+Rico.Color.createFromHex = function(hexCode) {
+
+   if ( hexCode.indexOf('#') == 0 )
+      hexCode = hexCode.substring(1);
+   var red   = hexCode.substring(0,2);
+   var green = hexCode.substring(2,4);
+   var blue  = hexCode.substring(4,6);
+   return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
+}
+
+/**
+ * Factory method for creating a color from the background of
+ * an HTML element.
+ */
+Rico.Color.createColorFromBackground = function(elem) {
+
+   var actualColor = RicoUtil.getElementsComputedStyle($(elem), "backgroundColor", "background-color");
+
+   if ( actualColor == "transparent" && elem.parent )
+      return Rico.Color.createColorFromBackground(elem.parent);
+
+   if ( actualColor == null )
+      return new Rico.Color(255,255,255);
+
+   if ( actualColor.indexOf("rgb(") == 0 ) {
+      var colors = actualColor.substring(4, actualColor.length - 1 );
+      var colorArray = colors.split(",");
+      return new Rico.Color( parseInt( colorArray[0] ),
+                            parseInt( colorArray[1] ),
+                            parseInt( colorArray[2] )  );
+
+   }
+   else if ( actualColor.indexOf("#") == 0 ) {
+      var redPart   = parseInt(actualColor.substring(1,3), 16);
+      var greenPart = parseInt(actualColor.substring(3,5), 16);
+      var bluePart  = parseInt(actualColor.substring(5), 16);
+      return new Rico.Color( redPart, greenPart, bluePart );
+   }
+   else
+      return new Rico.Color(255,255,255);
+}
+
+Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {
+
+   var red   = 0;
+       var green = 0;
+       var blue  = 0;
+
+   if (saturation == 0) {
+      red = parseInt(brightness * 255.0 + 0.5);
+          green = red;
+          blue = red;
+       }
+       else {
+      var h = (hue - Math.floor(hue)) * 6.0;
+      var f = h - Math.floor(h);
+      var p = brightness * (1.0 - saturation);
+      var q = brightness * (1.0 - saturation * f);
+      var t = brightness * (1.0 - (saturation * (1.0 - f)));
+
+      switch (parseInt(h)) {
+         case 0:
+            red   = (brightness * 255.0 + 0.5);
+            green = (t * 255.0 + 0.5);
+            blue  = (p * 255.0 + 0.5);
+            break;
+         case 1:
+            red   = (q * 255.0 + 0.5);
+            green = (brightness * 255.0 + 0.5);
+            blue  = (p * 255.0 + 0.5);
+            break;
+         case 2:
+            red   = (p * 255.0 + 0.5);
+            green = (brightness * 255.0 + 0.5);
+            blue  = (t * 255.0 + 0.5);
+            break;
+         case 3:
+            red   = (p * 255.0 + 0.5);
+            green = (q * 255.0 + 0.5);
+            blue  = (brightness * 255.0 + 0.5);
+            break;
+         case 4:
+            red   = (t * 255.0 + 0.5);
+            green = (p * 255.0 + 0.5);
+            blue  = (brightness * 255.0 + 0.5);
+            break;
+          case 5:
+            red   = (brightness * 255.0 + 0.5);
+            green = (p * 255.0 + 0.5);
+            blue  = (q * 255.0 + 0.5);
+            break;
+           }
+       }
+
+   return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) };
+}
+
+Rico.Color.RGBtoHSB = function(r, g, b) {
+
+   var hue;
+   var saturaton;
+   var brightness;
+
+   var cmax = (r > g) ? r : g;
+   if (b > cmax)
+      cmax = b;
+
+   var cmin = (r < g) ? r : g;
+   if (b < cmin)
+      cmin = b;
+
+   brightness = cmax / 255.0;
+   if (cmax != 0)
+      saturation = (cmax - cmin)/cmax;
+   else
+      saturation = 0;
+
+   if (saturation == 0)
+      hue = 0;
+   else {
+      var redc   = (cmax - r)/(cmax - cmin);
+       var greenc = (cmax - g)/(cmax - cmin);
+       var bluec  = (cmax - b)/(cmax - cmin);
+
+       if (r == cmax)
+          hue = bluec - greenc;
+       else if (g == cmax)
+          hue = 2.0 + redc - bluec;
+      else
+          hue = 4.0 + greenc - redc;
+
+       hue = hue / 6.0;
+       if (hue < 0)
+          hue = hue + 1.0;
+   }
+
+   return { h : hue, s : saturation, b : brightness };
+} 
+// ricoCorner.js --------------------
+
+Rico.Corner = {
+
+   round: function(e, options) {
+      var e = $(e);
+      this._setOptions(options);
+
+      var color = this.options.color;
+      if ( this.options.color == "fromElement" )
+         color = this._background(e);
+
+      var bgColor = this.options.bgColor;
+      if ( this.options.bgColor == "fromParent" )
+         bgColor = this._background(e.offsetParent);
+
+      this._roundCornersImpl(e, color, bgColor);
+   },
+
+   _roundCornersImpl: function(e, color, bgColor) {
+      if(this.options.border)
+         this._renderBorder(e,bgColor);
+      if(this._isTopRounded())
+         this._roundTopCorners(e,color,bgColor);
+      if(this._isBottomRounded())
+         this._roundBottomCorners(e,color,bgColor);
+   },
+
+   _renderBorder: function(el,bgColor) {
+      var borderValue = "1px solid " + this._borderColor(bgColor);
+      var borderL = "border-left: "  + borderValue;
+      var borderR = "border-right: " + borderValue;
+      var style   = "style='" + borderL + ";" + borderR +  "'";
+      el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>"
+   },
+
+   _roundTopCorners: function(el, color, bgColor) {
+      var corner = this._createCorner(bgColor);
+      for(var i=0 ; i < this.options.numSlices ; i++ )
+         corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));
+      el.style.paddingTop = 0;
+      el.insertBefore(corner,el.firstChild);
+   },
+
+   _roundBottomCorners: function(el, color, bgColor) {
+      var corner = this._createCorner(bgColor);
+      for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- )
+         corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));
+      el.style.paddingBottom = 0;
+      el.appendChild(corner);
+   },
+
+   _createCorner: function(bgColor) {
+      var corner = document.createElement("div");
+      corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
+      return corner;
+   },
+
+   _createCornerSlice: function(color,bgColor, n, position) {
+      var slice = document.createElement("span");
+
+      var inStyle = slice.style;
+      inStyle.backgroundColor = color;
+      inStyle.display  = "block";
+      inStyle.height   = "1px";
+      inStyle.overflow = "hidden";
+      inStyle.fontSize = "1px";
+
+      var borderColor = this._borderColor(color,bgColor);
+      if ( this.options.border && n == 0 ) {
+         inStyle.borderTopStyle    = "solid";
+         inStyle.borderTopWidth    = "1px";
+         inStyle.borderLeftWidth   = "0px";
+         inStyle.borderRightWidth  = "0px";
+         inStyle.borderBottomWidth = "0px";
+         inStyle.height            = "0px"; // assumes css compliant box model
+         inStyle.borderColor       = borderColor;
+      }
+      else if(borderColor) {
+         inStyle.borderColor = borderColor;
+         inStyle.borderStyle = "solid";
+         inStyle.borderWidth = "0px 1px";
+      }
+
+      if ( !this.options.compact && (n == (this.options.numSlices-1)) )
+         inStyle.height = "2px";
+
+      this._setMargin(slice, n, position);
+      this._setBorder(slice, n, position);
+
+      return slice;
+   },
+
+   _setOptions: function(options) {
+      this.options = {
+         corners : "all",
+         color   : "fromElement",
+         bgColor : "fromParent",
+         blend   : true,
+         border  : false,
+         compact : false
+      }.extend(options || {});
+
+      this.options.numSlices = this.options.compact ? 2 : 4;
+      if ( this._isTransparent() )
+         this.options.blend = false;
+   },
+
+   _whichSideTop: function() {
+      if ( this._hasString(this.options.corners, "all", "top") )
+         return "";
+
+      if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 )
+         return "";
+
+      if (this.options.corners.indexOf("tl") >= 0)
+         return "left";
+      else if (this.options.corners.indexOf("tr") >= 0)
+          return "right";
+      return "";
+   },
+
+   _whichSideBottom: function() {
+      if ( this._hasString(this.options.corners, "all", "bottom") )
+         return "";
+
+      if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 )
+         return "";
+
+      if(this.options.corners.indexOf("bl") >=0)
+         return "left";
+      else if(this.options.corners.indexOf("br")>=0)
+         return "right";
+      return "";
+   },
+
+   _borderColor : function(color,bgColor) {
+      if ( color == "transparent" )
+         return bgColor;
+      else if ( this.options.border )
+         return this.options.border;
+      else if ( this.options.blend )
+         return this._blend( bgColor, color );
+      else
+         return "";
+   },
+
+
+   _setMargin: function(el, n, corners) {
+      var marginSize = this._marginSize(n);
+      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
+
+      if ( whichSide == "left" ) {
+         el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px";
+      }
+      else if ( whichSide == "right" ) {
+         el.style.marginRight = marginSize + "px"; el.style.marginLeft  = "0px";
+      }
+      else {
+         el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px";
+      }
+   },
+
+   _setBorder: function(el,n,corners) {
+      var borderSize = this._borderSize(n);
+      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
+
+      if ( whichSide == "left" ) {
+         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px";
+      }
+      else if ( whichSide == "right" ) {
+         el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth  = "0px";
+      }
+      else {
+         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
+      }
+   },
+
+   _marginSize: function(n) {
+      if ( this._isTransparent() )
+         return 0;
+
+      var marginSizes          = [ 5, 3, 2, 1 ];
+      var blendedMarginSizes   = [ 3, 2, 1, 0 ];
+      var compactMarginSizes   = [ 2, 1 ];
+      var smBlendedMarginSizes = [ 1, 0 ];
+
+      if ( this.options.compact && this.options.blend )
+         return smBlendedMarginSizes[n];
+      else if ( this.options.compact )
+         return compactMarginSizes[n];
+      else if ( this.options.blend )
+         return blendedMarginSizes[n];
+      else
+         return marginSizes[n];
+   },
+
+   _borderSize: function(n) {
+      var transparentBorderSizes = [ 5, 3, 2, 1 ];
+      var blendedBorderSizes     = [ 2, 1, 1, 1 ];
+      var compactBorderSizes     = [ 1, 0 ];
+      var actualBorderSizes      = [ 0, 2, 0, 0 ];
+
+      if ( this.options.compact && (this.options.blend || this._isTransparent()) )
+         return 1;
+      else if ( this.options.compact )
+         return compactBorderSizes[n];
+      else if ( this.options.blend )
+         return blendedBorderSizes[n];
+      else if ( this.options.border )
+         return actualBorderSizes[n];
+      else if ( this._isTransparent() )
+         return transparentBorderSizes[n];
+      return 0;
+   },
+
+   _hasString: function(str) { for(var i=1 ; i<arguments.length ; i++) if (str.indexOf(arguments[i]) >= 0) return true; return false; },
+   _blend: function(c1, c2) { var cc1 = Rico.Color.createFromHex(c1); cc1.blend(Rico.Color.createFromHex(c2)); return cc1; },
+   _background: function(el) { try { return Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } },
+   _isTransparent: function() { return this.options.color == "transparent"; },
+   _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); },
+   _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); },
+   _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; }
+} 
+// ricoDragAndDrop.js --------------------
+Rico.DragAndDrop = Class.create();
+
+Rico.DragAndDrop.prototype = {
+
+   initialize: function() {
+      this.dropZones                = new Array();
+      this.draggables               = new Array();
+      this.currentDragObjects       = new Array();
+      this.dragElement              = null;
+      this.lastSelectedDraggable    = null;
+      this.currentDragObjectVisible = false;
+      this.interestedInMotionEvents = false;
+   },
+
+   registerDropZone: function(aDropZone) {
+      this.dropZones[ this.dropZones.length ] = aDropZone;
+   },
+
+   deregisterDropZone: function(aDropZone) {
+      var newDropZones = new Array();
+      var j = 0;
+      for ( var i = 0 ; i < this.dropZones.length ; i++ ) {
+         if ( this.dropZones[i] != aDropZone )
+            newDropZones[j++] = this.dropZones[i];
+      }
+
+      this.dropZones = newDropZones;
+   },
+
+   clearDropZones: function() {
+      this.dropZones = new Array();
+   },
+
+   registerDraggable: function( aDraggable ) {
+      this.draggables[ this.draggables.length ] = aDraggable;
+      this._addMouseDownHandler( aDraggable );
+   },
+
+   clearSelection: function() {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].deselect();
+      this.currentDragObjects = new Array();
+      this.lastSelectedDraggable = null;
+   },
+
+   hasSelection: function() {
+      return this.currentDragObjects.length > 0;
+   },
+
+   setStartDragFromElement: function( e, mouseDownElement ) {
+      this.origPos = RicoUtil.toDocumentPosition(mouseDownElement);
+      this.startx = e.screenX - this.origPos.x
+      this.starty = e.screenY - this.origPos.y
+      //this.startComponentX = e.layerX ? e.layerX : e.offsetX;
+      //this.startComponentY = e.layerY ? e.layerY : e.offsetY;
+      //this.adjustedForDraggableSize = false;
+
+      this.interestedInMotionEvents = this.hasSelection();
+      this._terminateEvent(e);
+   },
+
+   updateSelection: function( draggable, extendSelection ) {
+      if ( ! extendSelection )
+         this.clearSelection();
+
+      if ( draggable.isSelected() ) {
+         this.currentDragObjects.removeItem(draggable);
+         draggable.deselect();
+         if ( draggable == this.lastSelectedDraggable )
+            this.lastSelectedDraggable = null;
+      }
+      else {
+         this.currentDragObjects[ this.currentDragObjects.length ] = draggable;
+         draggable.select();
+         this.lastSelectedDraggable = draggable;
+      }
+   },
+
+   _mouseDownHandler: function(e) {
+      if ( arguments.length == 0 )
+         e = event;
+
+      // if not button 1 ignore it...
+      var nsEvent = e.which != undefined;
+      if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
+         return;
+
+      var eventTarget     = e.target ? e.target : e.srcElement;
+      var draggableObject = eventTarget.draggable;
+
+      this.updateSelection( draggableObject, e.ctrlKey );
+
+      // clear the drop zones postion cache...
+      if ( this.hasSelection() )
+         for ( var i = 0 ; i < this.dropZones.length ; i++ )
+            this.dropZones[i].clearPositionCache();
+
+      this.setStartDragFromElement( e, draggableObject.getMouseDownHTMLElement() );
+   },
+
+
+   _mouseMoveHandler: function(e) {
+      var nsEvent = e.which != undefined;
+      if ( !this.interestedInMotionEvents ) {
+         this._terminateEvent(e);
+         return;
+      }
+
+      if ( ! this.hasSelection() )
+         return;
+
+      if ( ! this.currentDragObjectVisible )
+         this._startDrag(e);
+
+      if ( !this.activatedDropZones )
+         this._activateRegisteredDropZones();
+
+      //if ( !this.adjustedForDraggableSize )
+      //   this._adjustForDraggableSize(e);
+
+      this._updateDraggableLocation(e);
+      this._updateDropZonesHover(e);
+
+      this._terminateEvent(e);
+   },
+
+   _makeDraggableObjectVisible: function(e)
+   {
+      if ( !this.hasSelection() )
+         return;
+
+      var dragElement;
+      if ( this.currentDragObjects.length > 1 )
+         dragElement = this.currentDragObjects[0].getMultiObjectDragGUI(this.currentDragObjects);
+      else
+         dragElement = this.currentDragObjects[0].getSingleObjectDragGUI();
+
+      // go ahead and absolute position it...
+      if ( RicoUtil.getElementsComputedStyle(dragElement, "position")  != "absolute" )
+         dragElement.style.position = "absolute";
+
+      // need to parent him into the document...
+      if ( dragElement.parentNode == null || dragElement.parentNode.nodeType == 11 )
+         document.body.appendChild(dragElement);
+
+      this.dragElement = dragElement;
+      this._updateDraggableLocation(e);
+
+      this.currentDragObjectVisible = true;
+   },
+
+   /**
+   _adjustForDraggableSize: function(e) {
+      var dragElementWidth  = this.dragElement.offsetWidth;
+      var dragElementHeight = this.dragElement.offsetHeight;
+      if ( this.startComponentX > dragElementWidth )
+         this.startx -= this.startComponentX - dragElementWidth + 2;
+      if ( e.offsetY ) {
+         if ( this.startComponentY > dragElementHeight )
+            this.starty -= this.startComponentY - dragElementHeight + 2;
+      }
+      this.adjustedForDraggableSize = true;
+   },
+   **/
+
+   _updateDraggableLocation: function(e) {
+      var dragObjectStyle = this.dragElement.style;
+      dragObjectStyle.left = (e.screenX - this.startx) + "px"
+      dragObjectStyle.top  = (e.screenY - this.starty) + "px";
+   },
+
+   _updateDropZonesHover: function(e) {
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ ) {
+         if ( ! this._mousePointInDropZone( e, this.dropZones[i] ) )
+            this.dropZones[i].hideHover();
+      }
+
+      for ( var i = 0 ; i < n ; i++ ) {
+         if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
+            if ( this.dropZones[i].canAccept(this.currentDragObjects) )
+               this.dropZones[i].showHover();
+         }
+      }
+   },
+
+   _startDrag: function(e) {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].startDrag();
+
+      this._makeDraggableObjectVisible(e);
+   },
+
+   _mouseUpHandler: function(e) {
+      if ( ! this.hasSelection() )
+         return;
+
+      var nsEvent = e.which != undefined;
+      if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
+         return;
+
+      this.interestedInMotionEvents = false;
+
+      if ( this.dragElement == null ) {
+         this._terminateEvent(e);
+         return;
+      }
+
+      if ( this._placeDraggableInDropZone(e) )
+         this._completeDropOperation(e);
+      else {
+         this._terminateEvent(e);
+         new Effect.Position( this.dragElement,
+                              this.origPos.x,
+                              this.origPos.y,
+                              200,
+                              20,
+                              { complete : this._doCancelDragProcessing.bind(this) } );
+      }
+   },
+
+   _completeDropOperation: function(e) {
+      if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() ) {
+         if ( this.dragElement.parentNode != null )
+            this.dragElement.parentNode.removeChild(this.dragElement);
+      }
+
+      this._deactivateRegisteredDropZones();
+      this._endDrag();
+      this.clearSelection();
+      this.dragElement = null;
+      this.currentDragObjectVisible = false;
+      this._terminateEvent(e);
+   },
+
+   _doCancelDragProcessing: function() {
+      this._cancelDrag();
+
+      if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() ) {
+         if ( this.dragElement.parentNode != null ) {
+            this.dragElement.parentNode.removeChild(this.dragElement);
+         }
+      }
+
+      this._deactivateRegisteredDropZones();
+      this.dragElement = null;
+      this.currentDragObjectVisible = false;
+   },
+
+   _placeDraggableInDropZone: function(e) {
+      var foundDropZone = false;
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ ) {
+         if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
+            if ( this.dropZones[i].canAccept(this.currentDragObjects) ) {
+               this.dropZones[i].hideHover();
+               this.dropZones[i].accept(this.currentDragObjects);
+               foundDropZone = true;
+               break;
+            }
+         }
+      }
+
+      return foundDropZone;
+   },
+
+   _cancelDrag: function() {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].cancelDrag();
+   },
+
+   _endDrag: function() {
+      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
+         this.currentDragObjects[i].endDrag();
+   },
+
+   _mousePointInDropZone: function( e, dropZone ) {
+
+      var absoluteRect = dropZone.getAbsoluteRect();
+
+      return e.clientX  > absoluteRect.left  &&
+             e.clientX  < absoluteRect.right &&
+             e.clientY  > absoluteRect.top   &&
+             e.clientY  < absoluteRect.bottom;
+   },
+
+   _addMouseDownHandler: function( aDraggable )
+   {
+      var htmlElement = aDraggable.getMouseDownHTMLElement();
+      if ( htmlElement != null ) {
+         htmlElement.draggable = aDraggable;
+         this._addMouseDownEvent( htmlElement );
+      }
+   },
+
+   _activateRegisteredDropZones: function() {
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ ) {
+         var dropZone = this.dropZones[i];
+         if ( dropZone.canAccept(this.currentDragObjects) )
+            dropZone.activate();
+      }
+
+      this.activatedDropZones = true;
+   },
+
+   _deactivateRegisteredDropZones: function() {
+      var n = this.dropZones.length;
+      for ( var i = 0 ; i < n ; i++ )
+         this.dropZones[i].deactivate();
+      this.activatedDropZones = false;
+   },
+
+   _addMouseDownEvent: function( htmlElement ) {
+      if ( typeof document.implementation != "undefined" &&
+         document.implementation.hasFeature("HTML",   "1.0") &&
+         document.implementation.hasFeature("Events", "2.0") &&
+         document.implementation.hasFeature("CSS",    "2.0") ) {
+         htmlElement.addEventListener("mousedown", this._mouseDownHandler.bindAsEventListener(this), false);
+      }
+      else {
+         htmlElement.attachEvent( "onmousedown", this._mouseDownHandler.bindAsEventListener(this) );
+      }
+   },
+
+   _terminateEvent: function(e) {
+      if ( e.stopPropagation != undefined )
+         e.stopPropagation();
+      else if ( e.cancelBubble != undefined )
+         e.cancelBubble = true;
+
+      if ( e.preventDefault != undefined )
+         e.preventDefault();
+      else
+         e.returnValue = false;
+   },
+
+   initializeEventHandlers: function() {
+      if ( typeof document.implementation != "undefined" &&
+         document.implementation.hasFeature("HTML",   "1.0") &&
+         document.implementation.hasFeature("Events", "2.0") &&
+         document.implementation.hasFeature("CSS",    "2.0") ) {
+         document.addEventListener("mouseup",   this._mouseUpHandler.bindAsEventListener(this),  false);
+         document.addEventListener("mousemove", this._mouseMoveHandler.bindAsEventListener(this), false);
+      }
+      else {
+         document.attachEvent( "onmouseup",   this._mouseUpHandler.bindAsEventListener(this) );
+         document.attachEvent( "onmousemove", this._mouseMoveHandler.bindAsEventListener(this) );
+      }
+   }
+}
+
+var dndMgr = new Rico.DragAndDrop();
+dndMgr.initializeEventHandlers(); 
+// ricoDraggable.js --------------------
+Rico.Draggable = Class.create();
+
+Rico.Draggable.prototype = {
+
+   initialize: function( type, htmlElement ) {
+      this.type          = type;
+      this.htmlElement   = $(htmlElement);
+      this.selected      = false;
+   },
+
+   /**
+    *   Returns the HTML element that should have a mouse down event
+    *   added to it in order to initiate a drag operation
+    *
+    **/
+   getMouseDownHTMLElement: function() {
+      return this.htmlElement;
+   },
+
+   select: function() {
+      this.selected = true;
+
+      if ( this.showingSelected )
+         return;
+
+      var htmlElement = this.getMouseDownHTMLElement();
+
+      var color = Rico.Color.createColorFromBackground(htmlElement);
+      color.isBright() ? color.darken(0.033) : color.brighten(0.033);
+
+      this.saveBackground = RicoUtil.getElementsComputedStyle(htmlElement, "backgroundColor", "background-color");
+      htmlElement.style.backgroundColor = color.asHex();
+      this.showingSelected = true;
+   },
+
+   deselect: function() {
+      this.selected = false;
+      if ( !this.showingSelected )
+         return;
+
+      var htmlElement = this.getMouseDownHTMLElement();
+
+      htmlElement.style.backgroundColor = this.saveBackground;
+      this.showingSelected = false;
+   },
+
+   isSelected: function() {
+      return this.selected;
+   },
+
+   startDrag: function() {
+   },
+
+   cancelDrag: function() {
+   },
+
+   endDrag: function() {
+   },
+
+   getSingleObjectDragGUI: function() {
+      return this.htmlElement;
+   },
+
+   getMultiObjectDragGUI: function( draggables ) {
+      return this.htmlElement;
+   },
+
+   getDroppedGUI: function() {
+      return this.htmlElement;
+   },
+
+   toString: function() {
+      return this.type + ":" + this.htmlElement + ":";
+   }
+
+} 
+// ricoDropzone.js --------------------
+Rico.Dropzone = Class.create();
+
+Rico.Dropzone.prototype = {
+
+   initialize: function( htmlElement ) {
+      this.htmlElement  = $(htmlElement);
+      this.absoluteRect = null;
+   },
+
+   getHTMLElement: function() {
+      return this.htmlElement;
+   },
+
+   clearPositionCache: function() {
+      this.absoluteRect = null;
+   },
+
+   getAbsoluteRect: function() {
+      if ( this.absoluteRect == null ) {
+         var htmlElement = this.getHTMLElement();
+         var pos = RicoUtil.toViewportPosition(htmlElement);
+
+         this.absoluteRect = {
+            top:    pos.y,
+            left:   pos.x,
+            bottom: pos.y + htmlElement.offsetHeight,
+            right:  pos.x + htmlElement.offsetWidth
+         };
+      }
+      return this.absoluteRect;
+   },
+
+   activate: function() {
+      var htmlElement = this.getHTMLElement();
+      if (htmlElement == null  || this.showingActive)
+         return;
+
+      this.showingActive = true;
+      this.saveBackgroundColor = htmlElement.style.backgroundColor;
+
+      var fallbackColor = "#ffea84";
+      var currentColor = Rico.Color.createColorFromBackground(htmlElement);
+      if ( currentColor == null )
+         htmlElement.style.backgroundColor = fallbackColor;
+      else {
+         currentColor.isBright() ? currentColor.darken(0.2) : currentColor.brighten(0.2);
+         htmlElement.style.backgroundColor = currentColor.asHex();
+      }
+   },
+
+   deactivate: function() {
+      var htmlElement = this.getHTMLElement();
+      if (htmlElement == null || !this.showingActive)
+         return;
+
+      htmlElement.style.backgroundColor = this.saveBackgroundColor;
+      this.showingActive = false;
+      this.saveBackgroundColor = null;
+   },
+
+   showHover: function() {
+      var htmlElement = this.getHTMLElement();
+      if ( htmlElement == null || this.showingHover )
+         return;
+
+      this.saveBorderWidth = htmlElement.style.borderWidth;
+      this.saveBorderStyle = htmlElement.style.borderStyle;
+      this.saveBorderColor = htmlElement.style.borderColor;
+
+      this.showingHover = true;
+      htmlElement.style.borderWidth = "1px";
+      htmlElement.style.borderStyle = "solid";
+      //htmlElement.style.borderColor = "#ff9900";
+      htmlElement.style.borderColor = "#ffff00";
+   },
+
+   hideHover: function() {
+      var htmlElement = this.getHTMLElement();
+      if ( htmlElement == null || !this.showingHover )
+         return;
+
+      htmlElement.style.borderWidth = this.saveBorderWidth;
+      htmlElement.style.borderStyle = this.saveBorderStyle;
+      htmlElement.style.borderColor = this.saveBorderColor;
+      this.showingHover = false;
+   },
+
+   canAccept: function(draggableObjects) {
+      return true;
+   },
+
+   accept: function(draggableObjects) {
+      var htmlElement = this.getHTMLElement();
+      if ( htmlElement == null )
+         return;
+
+      n = draggableObjects.length;
+      for ( var i = 0 ; i < n ; i++ )
+      {
+         var theGUI = draggableObjects[i].getDroppedGUI();
+         if ( RicoUtil.getElementsComputedStyle( theGUI, "position" ) == "absolute" )
+         {
+            theGUI.style.position = "static";
+            theGUI.style.top = "";
+            theGUI.style.top = "";
+         }
+         htmlElement.appendChild(theGUI);
+      }
+   }
+} 
+// ricoEffects.js --------------------
+
+Effect.SizeAndPosition = Class.create();
+Effect.SizeAndPosition.prototype = {
+
+   initialize: function(element, x, y, w, h, duration, steps, options) {
+      this.element = $(element);
+      this.x = x;
+      this.y = y;
+      this.w = w;
+      this.h = h;
+      this.duration = duration;
+      this.steps    = steps;
+      this.options  = arguments[7] || {};
+
+      this.sizeAndPosition();
+   },
+
+   sizeAndPosition: function() {
+      if (this.isFinished()) {
+         if(this.options.complete) this.options.complete(this);
+         return;
+      }
+
+      if (this.timer)
+         clearTimeout(this.timer);
+
+      var stepDuration = Math.round(this.duration/this.steps) ;
+
+      // Get original values: x,y = top left corner;  w,h = width height
+      var currentX = this.element.offsetLeft;
+      var currentY = this.element.offsetTop;
+      var currentW = this.element.offsetWidth;
+      var currentH = this.element.offsetHeight;
+
+      // If values not set, or zero, we do not modify them, and take original as final as well
+      this.x = (this.x) ? this.x : currentX;
+      this.y = (this.y) ? this.y : currentY;
+      this.w = (this.w) ? this.w : currentW;
+      this.h = (this.h) ? this.h : currentH;
+
+      // how much do we need to modify our values for each step?
+      var difX = this.steps >  0 ? (this.x - currentX)/this.steps : 0;
+      var difY = this.steps >  0 ? (this.y - currentY)/this.steps : 0;
+      var difW = this.steps >  0 ? (this.w - currentW)/this.steps : 0;
+      var difH = this.steps >  0 ? (this.h - currentH)/this.steps : 0;
+
+      this.moveBy(difX, difY);
+      this.resizeBy(difW, difH);
+
+      this.duration -= stepDuration;
+      this.steps--;
+
+      this.timer = setTimeout(this.sizeAndPosition.bind(this), stepDuration);
+   },
+
+   isFinished: function() {
+      return this.steps <= 0;
+   },
+
+   moveBy: function( difX, difY ) {
+      var currentLeft = this.element.offsetLeft;
+      var currentTop  = this.element.offsetTop;
+      var intDifX     = parseInt(difX);
+      var intDifY     = parseInt(difY);
+
+      var style = this.element.style;
+      if ( intDifX != 0 )
+         style.left = (currentLeft + intDifX) + "px";
+      if ( intDifY != 0 )
+         style.top  = (currentTop + intDifY) + "px";
+   },
+
+   resizeBy: function( difW, difH ) {
+      var currentWidth  = this.element.offsetWidth;
+      var currentHeight = this.element.offsetHeight;
+      var intDifW       = parseInt(difW);
+      var intDifH       = parseInt(difH);
+
+      var style = this.element.style;
+      if ( intDifW != 0 )
+         style.width   = (currentWidth  + intDifW) + "px";
+      if ( intDifH != 0 )
+         style.height  = (currentHeight + intDifH) + "px";
+   }
+}
+
+Effect.Size = Class.create();
+Effect.Size.prototype = {
+
+   initialize: function(element, w, h, duration, steps, options) {
+      new Effect.SizeAndPosition(element, null, null, w, h, duration, steps, options);
+  }
+}
+
+Effect.Position = Class.create();
+Effect.Position.prototype = {
+
+   initialize: function(element, x, y, duration, steps, options) {
+      new Effect.SizeAndPosition(element, x, y, null, null, duration, steps, options);
+  }
+}
+
+Effect.Round = Class.create();
+Effect.Round.prototype = {
+
+   initialize: function(tagName, className, options) {
+      var elements = document.getElementsByTagAndClassName(tagName,className);
+      for ( var i = 0 ; i < elements.length ; i++ )
+         Rico.Corner.round( elements[i], options );
+   }
+};
+
+Effect.FadeTo = Class.create();
+Effect.FadeTo.prototype = {
+
+   initialize: function( element, opacity, duration, steps, options) {
+      this.element  = $(element);
+      this.opacity  = opacity;
+      this.duration = duration;
+      this.steps    = steps;
+      this.options  = arguments[4] || {};
+      this.fadeTo();
+   },
+
+   fadeTo: function() {
+      if (this.isFinished()) {
+         if(this.options.complete) this.options.complete(this);
+         return;
+      }
+
+      if (this.timer)
+         clearTimeout(this.timer);
+
+      var stepDuration = Math.round(this.duration/this.steps) ;
+      var currentOpacity = this.getElementOpacity();
+      var delta = this.steps > 0 ? (this.opacity - currentOpacity)/this.steps : 0;
+
+      this.changeOpacityBy(delta);
+      this.duration -= stepDuration;
+      this.steps--;
+
+      this.timer = setTimeout(this.fadeTo.bind(this), stepDuration);
+   },
+
+   changeOpacityBy: function(v) {
+      var currentOpacity = this.getElementOpacity();
+      var newOpacity = Math.max(0, Math.min(currentOpacity+v, 1));
+      this.element.ricoOpacity = newOpacity;
+
+      this.element.style.filter = "alpha(opacity:"+Math.round(newOpacity*100)+")";
+      this.element.style.opacity = newOpacity; /*//*/;
+   },
+
+   isFinished: function() {
+      return this.steps <= 0;
+   },
+
+   getElementOpacity: function() {
+      if ( this.element.ricoOpacity == undefined ) {
+         var opacity;
+         if ( this.element.currentStyle ) {
+            opacity = this.element.currentStyle.opacity;
+         }
+         else if ( document.defaultView.getComputedStyle != undefined ) {
+            var computedStyle = document.defaultView.getComputedStyle;
+            opacity = computedStyle(this.element, null).getPropertyValue('opacity');
+         }
+
+         this.element.ricoOpacity = opacity != undefined ? opacity : 1.0;
+      }
+
+      return parseFloat(this.element.ricoOpacity);
+   }
+}
+
+Effect.AccordionSize = Class.create();
+
+Effect.AccordionSize.prototype = {
+
+   initialize: function(e1, e2, start, end, duration, steps, options) {
+      this.e1       = $(e1);
+      this.e2       = $(e2);
+      this.start    = start;
+      this.end      = end;
+      this.duration = duration;
+      this.steps    = steps;
+      this.options  = arguments[6] || {};
+
+      this.accordionSize();
+   },
+
+   accordionSize: function() {
+
+      if (this.isFinished()) {
+         // just in case there are round errors or such...
+         this.e1.style.height = this.start + "px";
+         this.e2.style.height = this.end + "px";
+
+         if(this.options.complete)
+            this.options.complete(this);
+         return;
+      }
+
+      if (this.timer)
+         clearTimeout(this.timer);
+
+      var stepDuration = Math.round(this.duration/this.steps) ;
+
+      var diff = this.steps > 0 ? (parseInt(this.e1.offsetHeight) - this.start)/this.steps : 0;
+      this.resizeBy(diff);
+
+      this.duration -= stepDuration;
+      this.steps--;
+
+      this.timer = setTimeout(this.accordionSize.bind(this), stepDuration);
+   },
+
+   isFinished: function() {
+      return this.steps <= 0;
+   },
+
+   resizeBy: function(diff) {
+      var h1Height = this.e1.offsetHeight;
+      var h2Height = this.e2.offsetHeight;
+      var intDiff = parseInt(diff);
+      if ( diff != 0 ) {
+         this.e1.style.height = (h1Height - intDiff) + "px";
+         this.e2.style.height = (h2Height + intDiff) + "px";
+      }
+   }
+
+};
+// ricoLiveGrid.js --------------------
+
+// Rico.LiveGridMetaData -----------------------------------------------------
+
+Rico.LiveGridMetaData = Class.create();
+
+Rico.LiveGridMetaData.prototype = {
+
+   initialize: function( pageSize, totalRows, options ) {
+      this.pageSize  = pageSize;
+      this.totalRows = totalRows;
+      this.setOptions(options);
+      this.scrollArrowHeight = 16;
+   },
+
+   setOptions: function(options) {
+      this.options = {
+         largeBufferSize    : 7.0,   // 7 pages
+         smallBufferSize    : 1.0,   // 1 page
+         nearLimitFactor    : 0.2    // 20% of buffer
+      }.extend(options || {});
+   },
+
+   getPageSize: function() {
+      return this.pageSize;
+   },
+
+   getTotalRows: function() {
+      return this.totalRows;
+   },
+
+   setTotalRows: function(n) {
+      this.totalRows = n;
+   },
+
+   getLargeBufferSize: function() {
+      return parseInt(this.options.largeBufferSize * this.pageSize);
+   },
+
+   getSmallBufferSize: function() {
+      return parseInt(this.options.smallBufferSize * this.pageSize);
+   },
+
+   getLimitTolerance: function() {
+      return parseInt(this.getLargeBufferSize() * this.options.nearLimitFactor);
+   },
+
+       getBufferSize: function(isFull) {
+               return isFull ? this.getLargeBufferSize() : this.getSmallBufferSize();
+       }
+};
+
+// Rico.LiveGridScroller -----------------------------------------------------
+
+Rico.LiveGridScroller = Class.create();
+
+Rico.LiveGridScroller.prototype = {
+
+   initialize: function(liveGrid) {
+      this.isIE = navigator.userAgent.toLowerCase().indexOf("msie") >= 0;
+      this.liveGrid = liveGrid;
+      this.metaData = liveGrid.metaData;
+      this.createScrollBar();
+      this.scrollTimeout = null;
+      //this.sizeIEHeaderHack();
+      this.lastScrollPos = 0;
+   },
+
+   isUnPlugged: function() {
+      return this.scrollerDiv.onscroll == null;
+   },
+
+   plugin: function() {
+      this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this);
+   },
+
+   unplug: function() {
+      this.scrollerDiv.onscroll = null;
+   },
+
+   sizeIEHeaderHack: function() {
+      if ( !this.isIE ) return;
+      var headerTable = $(this.liveGrid.tableId + "_header");
+      if ( headerTable )
+         headerTable.rows[0].cells[0].style.width =
+            (headerTable.rows[0].cells[0].offsetWidth + 1) + "px";
+   },
+
+   createScrollBar: function() {
+      var table = this.liveGrid.table;
+      var visibleHeight = table.offsetHeight;
+
+      // create the outer div...
+      this.scrollerDiv  = document.createElement("div");
+      var scrollerStyle = this.scrollerDiv.style;
+      scrollerStyle.borderRight = "1px solid #ababab"; // hard coded color!!!
+      scrollerStyle.position    = "relative";
+      scrollerStyle.left        = this.isIE ? "-6px" : "-3px";
+      scrollerStyle.width       = "19px";
+      scrollerStyle.height      = visibleHeight + "px";
+      scrollerStyle.overflow    = "auto";
+
+      // create the inner div...
+      this.heightDiv = document.createElement("div");
+      this.heightDiv.style.width  = "1px";
+      this.heightDiv.style.height = parseInt(visibleHeight *
+                        this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px" ;
+      this.lineHeight =  visibleHeight/this.metaData.getPageSize();
+
+      this.scrollerDiv.appendChild(this.heightDiv);
+      this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this);
+      table.parentNode.insertBefore( this.scrollerDiv, table.nextSibling );
+   },
+
+   updateSize: function() {
+      var table = this.liveGrid.table;
+      var visibleHeight = table.offsetHeight;
+      this.heightDiv.style.height = parseInt(visibleHeight *
+                                  this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px";
+   },
+
+   adjustScrollTop: function() {
+       this.unplug();
+       var rem = this.scrollerDiv.scrollTop % this.lineHeight;
+       if (rem != 0) {
+         if (this.lastScrollPos < this.scrollerDiv.scrollTop)
+            this.scrollerDiv.scrollTop = this.scrollerDiv.scrollTop + this.lineHeight -rem;
+         else
+            this.scrollerDiv.scrollTop = this.scrollerDiv.scrollTop - rem;
+      }
+      this.lastScrollPos = this.scrollerDiv.scrollTop;
+       this.plugin();
+   },
+
+   handleScroll: function() {
+      if ( this.scrollTimeout )
+         clearTimeout( this.scrollTimeout );
+
+          //this.adjustScrollTop();
+          var contentOffset = parseInt(this.scrollerDiv.scrollTop *
+                                   this.metaData.getTotalRows() / this.heightDiv.offsetHeight);
+      this.liveGrid.requestContentRefresh(contentOffset);
+      if ( this.metaData.options.onscroll )
+         this.metaData.options.onscroll( contentOffset, this.metaData );
+
+      this.scrollTimeout = setTimeout( this.scrollIdle.bind(this), 1200 );
+   },
+
+   scrollIdle: function() {
+      if ( this.metaData.options.onscrollidle )
+         this.metaData.options.onscrollidle();
+   }
+};
+
+// Rico.LiveGridBuffer -----------------------------------------------------
+
+Rico.LiveGridBuffer = Class.create();
+
+Rico.LiveGridBuffer.prototype = {
+
+   initialize: function(metaData) {
+      this.startPos = 0;
+      this.size     = 0;
+      this.metaData = metaData;
+      this.rows     = new Array();
+      this.updateInProgress = false;
+   },
+
+   update: function(ajaxResponse,start) {
+
+      this.startPos = start;
+      this.rows = new Array();
+
+      var rowsElement = ajaxResponse.getElementsByTagName('rows')[0];
+      this.updateUI = rowsElement.getAttribute("update_ui") == "true"
+      var trs = rowsElement.getElementsByTagName("tr");
+      for ( var i=0 ; i < trs.length; i++ ) {
+         var row = this.rows[i] = new Array();
+         var cells = trs[i].getElementsByTagName("td");
+         for ( var j=0; j < cells.length ; j++ ) {
+            var cell = cells[j];
+            var convertSpaces = cell.getAttribute("convert_spaces") == "true";
+            var cellContent = cell.text != undefined ? cell.text : cell.textContent;
+            row[j] = convertSpaces ? this.convertSpaces(cellContent) : cellContent;
+         }
+      }
+      this.size = trs.length;
+   },
+
+       isFullP: function() {
+          return this.metaData.pageSize != this.size;
+       },
+
+       isClose: function(start) {
+               return (start < this.startPos + this.size + (this.size/2)) &&
+             (start + this.size + (this.size/2) > this.startPos)
+       },
+
+   isInRange: function(start, count) {
+      return (start < this.startPos + this.size) && (start + count > this.startPos)
+   },
+
+   isFullyInRange: function(position) {
+      return (position >= this.startPos) && (position+this.metaData.getPageSize()) <= (this.startPos + this.size)
+   },
+
+   isNearingTopLimit: function(position) {
+      return position - this.startPos < this.metaData.getLimitTolerance();
+   },
+
+   isNearingBottomLimit: function(position) {
+      var myEnd     = position + this.metaData.getPageSize();
+      var bufferEnd = this.startPos + this.size;
+      return bufferEnd - myEnd < this.metaData.getLimitTolerance();
+   },
+
+   isAtTop: function() {
+      return this.startPos == 0;
+   },
+
+   isAtBottom: function() {
+      return this.startPos + this.size == this.metaData.getTotalRows();
+   },
+
+   isNearingLimit: function(position) {
+      return ( !this.isAtTop()    && this.isNearingTopLimit(position)) ||
+             ( !this.isAtBottom() && this.isNearingBottomLimit(position) )
+   },
+
+   getRows: function(start, count) {
+      var begPos = start - this.startPos
+      var endPos = begPos + count
+
+      // er? need more data...
+      if ( endPos > this.size )
+         endPos = this.size
+
+      var results = new Array()
+      var index = 0;
+      for ( var i=begPos ; i < endPos; i++ ) {
+         results[index++] = this.rows[i]
+      }
+      return results
+   },
+
+   convertSpaces: function(s) {
+      return s.split(" ").join("&nbsp;");
+   }
+
+};
+
+Rico.LiveGridRequest = Class.create();
+Rico.LiveGridRequest.prototype = {
+   initialize: function( requestOffset, options ) {
+               this.requestOffset = requestOffset;
+   }
+};
+
+// Rico.LiveGrid -----------------------------------------------------
+
+Rico.LiveGrid = Class.create();
+
+Rico.LiveGrid.prototype = {
+
+   initialize: function( tableId, visibleRows, totalRows, url, options ) {
+
+      if ( options == null )
+         options = {};
+
+      this.tableId     = tableId;
+      this.table       = $(tableId);
+      this.metaData    = new Rico.LiveGridMetaData(visibleRows, totalRows, options);
+      this.buffer      = new Rico.LiveGridBuffer(this.metaData);
+      this.scroller    = new Rico.LiveGridScroller(this);
+
+      this.lastDisplayedStartPos = 0;
+      this.timeoutHander         = null;
+      this.additionalParms       = options.requestParameters || [];
+
+      this.processingRequest = null;
+      this.unprocessedRequest = null;
+
+      this.initAjax(url);
+      if ( options.prefetchBuffer )
+         this.fetchBuffer(0, true);
+   },
+
+   setRequestParams: function() {
+      this.additionalParms = [];
+      for ( var i=0 ; i < arguments.length ; i++ )
+         this.additionalParms[i] = arguments[i];
+   },
+
+   setTotalRows: function( newTotalRows ) {
+      this.metaData.setTotalRows(newTotalRows);
+      this.scroller.updateSize();
+   },
+
+   initAjax: function(url) {
+      ajaxEngine.registerRequest( this.tableId + '_request', url );
+      ajaxEngine.registerAjaxObject( this.tableId + '_updater', this );
+   },
+
+   invokeAjax: function() {
+   },
+
+   largeBufferWindowStart: function(offset) {
+      val = offset - ( (.5 * this.metaData.getLargeBufferSize()) - (.5 * this.metaData.getPageSize()) )
+      return Math.max(parseInt(val), 0);
+   },
+
+   handleTimedOut: function() {
+      //server did not respond in 4 seconds... assume that there could have been
+      //an error or something, and allow requests to be processed again...
+      this.processingRequest = null;
+   },
+
+   fetchBuffer: function(offset, fullBufferp) {
+      if (this.processingRequest) {
+         this.unprocessedRequest = new Rico.LiveGridRequest(offset);
+           return;
+      }
+
+      var fetchSize = this.metaData.getBufferSize(fullBufferp);
+      bufferStartPos = Math.max(0,fullBufferp ? this.largeBufferWindowStart(offset) : offset);
+
+      this.processingRequest = new Rico.LiveGridRequest(offset);
+      this.processingRequest.bufferOffset = bufferStartPos;
+
+      var callParms = [];
+      callParms.push(this.tableId + '_request');
+      callParms.push('id='        + this.tableId);
+      callParms.push('page_size=' + fetchSize);
+      callParms.push('offset='    + bufferStartPos);
+
+      for( var i=0 ; i < this.additionalParms.length ; i++ )
+         callParms.push(this.additionalParms[i]);
+
+      ajaxEngine.sendRequest.apply( ajaxEngine, callParms );
+      this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), 4000 );
+   },
+
+   requestContentRefresh: function(contentOffset) {
+      if ( this.buffer && this.buffer.isFullyInRange(contentOffset) ) {
+         this.updateContent(contentOffset);
+         if (this.buffer.isNearingLimit(contentOffset))
+            this.fetchBuffer(contentOffset, true);
+      }
+      else if (this.buffer && this.buffer.isClose(contentOffset))
+         this.fetchBuffer(contentOffset, true);
+      else
+             this.fetchBuffer(contentOffset, false);
+   },
+
+   ajaxUpdate: function(ajaxResponse) {
+      try {
+         clearTimeout( this.timeoutHandler );
+         this.buffer = new Rico.LiveGridBuffer(this.metaData);
+         this.buffer.update(ajaxResponse,this.processingRequest.bufferOffset);
+         if (this.unprocessedRequest == null) {
+            offset = this.processingRequest.requestOffset;
+            this.updateContent (offset);
+         }
+         this.processingRequest = null;
+         if (this.unprocessedRequest != null) {
+            this.requestContentRefresh(this.unprocessedRequest.requestOffset);
+            this.unprocessedRequest = null
+         }
+      }
+      catch(err) {
+      }
+   },
+
+   updateContent: function( offset ) {
+      this.replaceCellContents(this.buffer, offset);
+   },
+
+   replaceCellContents: function(buffer, startPos) {
+      if (startPos == this.lastDisplayedStartPos){
+         return;
+      }
+
+      this.lastDisplayedStartPos = startPos
+      var rows = buffer.getRows(startPos, this.metaData.getPageSize());
+      for (var i=0; i < rows.length; i++) {
+         var row = rows[i];
+         for (var j=0; j < row.length; j++) {
+            this.table.rows[i].cells[j].innerHTML = rows[i][j]
+         }
+      }
+   }
+}; 
+// ricoUtil.js --------------------
+
+var RicoUtil = {
+
+   getElementsComputedStyle: function ( htmlElement, cssProperty, mozillaEquivalentCSS) {
+      if ( arguments.length == 2 )
+         mozillaEquivalentCSS = cssProperty;
+
+      var el = $(htmlElement);
+      if ( el.currentStyle )
+         return el.currentStyle[cssProperty];
+      else
+         return document.defaultView.getComputedStyle(el, null).getPropertyValue(mozillaEquivalentCSS);
+   },
+
+   createXmlDocument : function() {
+      if (document.implementation && document.implementation.createDocument) {
+         var doc = document.implementation.createDocument("", "", null);
+
+         if (doc.readyState == null) {
+            doc.readyState = 1;
+            doc.addEventListener("load", function () {
+               doc.readyState = 4;
+               if (typeof doc.onreadystatechange == "function")
+                  doc.onreadystatechange();
+            }, false);
+         }
+
+         return doc;
+      }
+
+      if (window.ActiveXObject)
+          return Try.these(
+            function() { return new ActiveXObject('MSXML2.DomDocument')   },
+            function() { return new ActiveXObject('Microsoft.DomDocument')},
+            function() { return new ActiveXObject('MSXML.DomDocument')    },
+            function() { return new ActiveXObject('MSXML3.DomDocument')   }
+          ) || false;
+
+      return null;
+   },
+
+   toViewportPosition: function(element) {
+      return this._toAbsolute(element,true);
+   },
+
+   toDocumentPosition: function(element) {
+      return this._toAbsolute(element,false);
+   },
+
+   /**
+    *  Compute the elements position in terms of the window viewport
+    *  so that it can be compared to the position of the mouse (dnd)
+    *  This is additions of all the offsetTop,offsetLeft values up the
+    *  offsetParent hierarchy, ...taking into account any scrollTop,
+    *  scrollLeft values along the way...
+    *
+    * IE has a bug reporting a correct offsetLeft of elements within a
+    * a relatively positioned parent!!!
+    **/
+   _toAbsolute: function(element,accountForDocScroll) {
+
+      if ( navigator.userAgent.toLowerCase().indexOf("msie") == -1 )
+         return this._toAbsoluteMozilla(element,accountForDocScroll);
+
+      var x = 0;
+      var y = 0;
+      var parent = element;
+      while ( parent ) {
+
+         var borderXOffset = 0;
+         var borderYOffset = 0;
+         if ( parent != element ) {
+            var borderXOffset = parseInt(this.getElementsComputedStyle(parent, "borderLeftWidth" ));
+            var borderYOffset = parseInt(this.getElementsComputedStyle(parent, "borderTopWidth" ));
+            borderXOffset = isNaN(borderXOffset) ? 0 : borderXOffset;
+            borderYOffset = isNaN(borderYOffset) ? 0 : borderYOffset;
+         }
+
+         x += parent.offsetLeft - parent.scrollLeft + borderXOffset;
+         y += parent.offsetTop - parent.scrollTop + borderYOffset;
+         parent = parent.offsetParent;
+      }
+
+      if ( accountForDocScroll ) {
+         x -= this.docScrollLeft();
+         y -= this.docScrollTop();
+      }
+
+      return { x:x, y:y };
+   },
+
+   /**
+    *  Mozilla did not report all of the parents up the hierarchy via the
+    *  offsetParent property that IE did.  So for the calculation of the
+    *  offsets we use the offsetParent property, but for the calculation of
+    *  the scrollTop/scrollLeft adjustments we navigate up via the parentNode
+    *  property instead so as to get the scroll offsets...
+    *
+    **/
+   _toAbsoluteMozilla: function(element,accountForDocScroll) {
+      var x = 0;
+      var y = 0;
+      var parent = element;
+      while ( parent ) {
+         x += parent.offsetLeft;
+         y += parent.offsetTop;
+         parent = parent.offsetParent;
+      }
+
+      parent = element;
+      while ( parent &&
+              parent != document.body &&
+              parent != document.documentElement ) {
+         if ( parent.scrollLeft  )
+            x -= parent.scrollLeft;
+         if ( parent.scrollTop )
+            y -= parent.scrollTop;
+         parent = parent.parentNode;
+      }
+
+      if ( accountForDocScroll ) {
+         x -= this.docScrollLeft();
+         y -= this.docScrollTop();
+      }
+
+      return { x:x, y:y };
+   },
+
+   docScrollLeft: function() {
+      if ( window.pageXOffset )
+         return window.pageXOffset;
+      else if ( document.documentElement && document.documentElement.scrollLeft )
+         return document.documentElement.scrollLeft;
+      else if ( document.body )
+         return document.body.scrollLeft;
+      else
+         return 0;
+   },
+
+   docScrollTop: function() {
+      if ( window.pageYOffset )
+         return window.pageYOffset;
+      else if ( document.documentElement && document.documentElement.scrollTop )
+         return document.documentElement.scrollTop;
+      else if ( document.body )
+         return document.body.scrollTop;
+      else
+         return 0;
+   }
+
+};
\ No newline at end of file
index 1d6b7be49e2bc8705cfa441236ff1cd133e8117c..adb2021de0c74f6c79a36e4b1cb5140597d33bbc 100644 (file)
@@ -779,43 +779,6 @@ void display_success(char *successmessage)
 
 
 
-void extract_action(char *actbuf, char *cmdbuf)
-{
-       int i;
-
-       strcpy(actbuf, cmdbuf);
-
-       /*
-        * First strip out the http method
-        */
-       remove_token(actbuf, 0, ' ');
-       if (actbuf[0] == ' ') strcpy(actbuf, &actbuf[1]);
-       if (actbuf[0] == '/') strcpy(actbuf, &actbuf[1]);
-
-       /*
-        * Now kill invalid (for webcit) characters
-        */
-       for (i = 0; i < strlen(actbuf); ++i) {
-               if (actbuf[i] == ' ') {
-                       actbuf[i] = 0;
-                       i = 0;
-               }
-               if (actbuf[i] == '/') {
-                       actbuf[i] = 0;
-                       i = 0;
-               }
-               if (actbuf[i] == '?') {
-                       actbuf[i] = 0;
-                       i = 0;
-               }
-               if (actbuf[i] == '&') {
-                       actbuf[i] = 0;
-                       i = 0;
-               }
-       }
-}
-
-
 void upload_handler(char *name, char *filename, char *partnum, char *disp,
                        void *content, char *cbtype, char *cbcharset,
                        size_t length, char *encoding, void *userdata)
@@ -862,9 +825,12 @@ void upload_handler(char *name, char *filename, char *partnum, char *disp,
  */
 void session_loop(struct httprequest *req)
 {
-       char cmd[SIZ];
-       char method[SIZ];
-       char action[SIZ];
+       char cmd[1024];
+       char method[128];
+       char action[128];
+       char arg1[128];
+       char arg2[128];
+       char arg3[128];
        char buf[SIZ];
        int a, b;
        int ContentLength = 0;
@@ -907,7 +873,27 @@ void session_loop(struct httprequest *req)
        safestrncpy(cmd, hptr->line, sizeof cmd);
        hptr = hptr->next;
        extract_token(method, cmd, 0, ' ', sizeof method);
-       extract_action(action, cmd);
+
+       /* Figure out the action */
+       extract_token(action, cmd, 1, '/', sizeof action);
+       if (strstr(action, "?")) *strstr(action, "?") = 0;
+       if (strstr(action, "&")) *strstr(action, "&") = 0;
+       if (strstr(action, " ")) *strstr(action, " ") = 0;
+
+       extract_token(arg1, cmd, 2, '/', sizeof arg1);
+       if (strstr(arg1, "?")) *strstr(arg1, "?") = 0;
+       if (strstr(arg1, "&")) *strstr(arg1, "&") = 0;
+       if (strstr(arg1, " ")) *strstr(arg1, " ") = 0;
+
+       extract_token(arg2, cmd, 3, '/', sizeof arg2);
+       if (strstr(arg2, "?")) *strstr(arg2, "?") = 0;
+       if (strstr(arg2, "&")) *strstr(arg2, "&") = 0;
+       if (strstr(arg2, " ")) *strstr(arg2, " ") = 0;
+
+       extract_token(arg3, cmd, 4, '/', sizeof arg3);
+       if (strstr(arg3, "?")) *strstr(arg3, "?") = 0;
+       if (strstr(arg3, "&")) *strstr(arg3, "&") = 0;
+       if (strstr(arg3, " ")) *strstr(arg3, " ") = 0;
 
        while (hptr != NULL) {
                safestrncpy(buf, hptr->line, sizeof buf);
@@ -983,9 +969,10 @@ void session_loop(struct httprequest *req)
                }
        }
 
+
        /* Static content can be sent without connecting to Citadel. */
        if (!strcasecmp(action, "static")) {
-               safestrncpy(buf, &cmd[12], sizeof buf);
+               safestrncpy(buf, arg1, sizeof buf);
                for (a = 0; a < strlen(buf); ++a)
                        if (isspace(buf[a]))
                                buf[a] = 0;
@@ -1159,6 +1146,8 @@ void session_loop(struct httprequest *req)
                display_main_menu();
        } else if (!strcasecmp(action, "who")) {
                who();
+       } else if (!strcasecmp(action, "who_inner_html")) {
+               who_inner_html();
        } else if (!strcasecmp(action, "knrooms")) {
                knrooms();
        } else if (!strcasecmp(action, "gotonext")) {
index 1e761dd17d1f2f07f7f60bc162b6fcea6f2ee5a3..7901df4e0994d5c27eb34f9f9ca98eab774623d1 100644 (file)
@@ -294,6 +294,7 @@ int tcp_connectsock(char *, char *);
 void serv_getln(char *strbuf, int bufsize);
 void serv_puts(char *string);
 void who(void);
+void who_inner_html(void);
 void fmout(FILE *fp, char *align);
 void wDumpContent(int);
 void serv_printf(const char *format,...);
index d6fd0512ac6899f574636fca91575acd0bc347fe..8756d7d96aaf45d4557c180868b5c912a56debb9 100644 (file)
@@ -38,12 +38,12 @@ void who_inner_div(void) {
        time_t now;
        int bg = 0;
 
-       wprintf("<table border=0 cellspacing=0 width=100%% bgcolor=\"#FFFFFF\">"
+       wprintf("<table border=\"0\" cellspacing=\"0\" width=\"100%%\" bgcolor=\"#FFFFFF\">"
                "<tr>\n");
-       wprintf("<TH COLSPAN=3>&nbsp;</TH>\n");
-       wprintf("<TH>User Name</TH>\n");
-       wprintf("<TH>Room</TH>");
-       wprintf("<TH>From host</TH>\n</TR>\n");
+       wprintf("<th colspan=\"3\"> </th>\n");
+       wprintf("<th>User Name</th>\n");
+       wprintf("<th>Room</th>");
+       wprintf("<th>From host</th>\n</tr>\n");
 
        serv_puts("TIME");
        serv_getln(buf, sizeof buf);
@@ -67,7 +67,7 @@ void who_inner_div(void) {
                        last_activity = extract_long(buf, 5);
 
                        bg = 1 - bg;
-                       wprintf("<TR BGCOLOR=\"#%s\">",
+                       wprintf("<tr bgcolor=\"#%s\">",
                                (bg ? "DDDDDD" : "FFFFFF")
                        );
 
@@ -75,83 +75,114 @@ void who_inner_div(void) {
                        wprintf("<td>");
                        if ((WC->is_aide) &&
                            (sess != WC->ctdl_pid)) {
-                               wprintf(" <A HREF=\"/terminate_session&which_session=%d&session_owner=", sess);
-                               urlescputs(user);
+                               wprintf(" <a href=\"/terminate_session?which_session=%d", sess);
                                wprintf("\" onClick=\"return ConfirmKill();\" "
-                               ">[kill]</A>");
+                               ">[kill]</a>");
                        }
                        if (sess == WC->ctdl_pid) {
-                               wprintf(" <A HREF=\"/edit_me\" "
-                                       ">[edit]</A>");
+                               wprintf(" <a href=\"/edit_me\" "
+                                       ">[edit]</a>");
                        }
-                       wprintf("</TD>");
+                       wprintf("</td>");
 
                        /* (link to page this user) */
-                       wprintf("<TD><A HREF=\"/display_page?recp=");
+                       wprintf("<td><a href=\"/display_page?recp=");
                        urlescputs(user);
                        wprintf("\">"
-                               "<IMG ALIGN=MIDDLE "
-                               "SRC=\"/static/citadelchat_24x.gif\" "
-                               "ALT=\"(p)\""
-                               " BORDER=0></A>&nbsp;");
-                       wprintf("</TD>");
+                               "<img align=\"middle\" "
+                               "src=\"/static/citadelchat_24x.gif\" "
+                               "alt=\"(p)\""
+                               " border=\"0\" /></a> ");
+                       wprintf("</td>");
 
                        /* (idle flag) */
-                       wprintf("<TD>");
+                       wprintf("<td>");
                        if ((now - last_activity) > 900L) {
-                               wprintf("&nbsp;"
-                                       "<IMG ALIGN=MIDDLE "
-                                       "SRC=\"/static/inactiveuser_24x.gif\" "
-                                       "ALT=\"[idle]\" BORDER=0>");
+                               wprintf(" "
+                                       "<img align=\"middle\" "
+                                       "src=\"/static/inactiveuser_24x.gif\" "
+                                       "alt=\"[idle]\" border=\"0\" />");
                        }
                        else {
-                               wprintf("&nbsp;"
-                                       "<IMG ALIGN=MIDDLE "
-                                       "SRC=\"/static/activeuser_24x.gif\" "
-                                       "ALT=\"[active]\" BORDER=0>");
+                               wprintf(" "
+                                       "<img align=\"middle\" "
+                                       "src=\"/static/activeuser_24x.gif\" "
+                                       "alt=\"[active]\" border=\"0\" />");
                        }
-                       wprintf("</TD>\n\t<TD>");
+                       wprintf("</td>\n<td>");
 
 
 
                        /* username (link to user bio/photo page) */
-                       wprintf("<A HREF=\"/showuser&who=");
+                       wprintf("<a href=\"/showuser?who=");
                        urlescputs(user);
                        wprintf("\">");
                        escputs(user);
-                       wprintf("</A>");
+                       wprintf("</a>");
 
                        /* room */
-                       wprintf("</TD>\n\t<TD>");
+                       wprintf("</td>\n\t<td>");
                        escputs(room);
                        if (strlen(realroom) > 0) {
-                               wprintf("<br /><I>");
+                               wprintf("<br /><i>");
                                escputs(realroom);
-                               wprintf("</I>");
+                               wprintf("</i>");
                        }
-                       wprintf("</TD>\n\t<TD>");
+                       wprintf("</td>\n\t<td>");
 
                        /* hostname */
                        escputs(host);
                        if (strlen(realhost) > 0) {
-                               wprintf("<br /><I>");
+                               wprintf("<br /><i>");
                                escputs(realhost);
-                               wprintf("</I>");
+                               wprintf("</i>");
                        }
-                       wprintf("</TD>\n</TR>");
+                       wprintf("</td>\n</tr>");
                }
        }
-       wprintf("</TABLE>");
+       wprintf("</table>");
 }
 
 
+/*
+ * XML-encapsulated version of wholist inner html
+ */
+void who_inner_html(void) {
+       output_headers(0, 0, 0, 0, 0, 0, 0);
+
+       wprintf("Content-type: text/xml;charset=UTF-8\r\n"
+               "Server: %s\r\n"
+               "Connection: close\r\n"
+               "Pragma: no-cache\r\n"
+               "Cache-Control: no-store\r\n",
+               SERVER);
+       begin_burst();
+
+       wprintf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
+               "<ajax-response>\r\n"
+               "<response type=\"element\" id=\"fix_scrollbar_bug\">\r\n"
+       );
+
+       who_inner_div();
+
+       wprintf("</response>\r\n"
+               "</ajax-response>\r\n"
+               "\r\n"
+       );
+
+       wDumpContent(0);
+}
+
 
 /*
  * who is on?
  */
 void who(void)
 {
-       output_headers(1, 1, 2, 0, 1, 0, 0);
+       /*
+       output_headers(1, 1, 2, 0, 1, 0, 0); old refresh30 version
+       */
+       output_headers(1, 1, 2, 0, 0, 0, 0);
 
        wprintf("<script type=\"text/javascript\">\n"
                "function ConfirmKill() { \n"
@@ -162,8 +193,11 @@ void who(void)
 
        wprintf("<div id=\"banner\">\n");
        wprintf("<TABLE WIDTH=100%% BORDER=0 BGCOLOR=\"#444455\"><TR><TD>");
-       wprintf("<IMG SRC=\"/static/usermanag_48x.gif\" ALT=\" \" ALIGN=MIDDLE>");
-       wprintf("<SPAN CLASS=\"titlebar\">&nbsp;Users currently on ");
+       wprintf("<IMG SRC=\"/static/usermanag_48x.gif\" ALT=\" \" "
+               "ALIGN=MIDDLE "
+               ">");
+               /* "onLoad=\"javascript:bodyOnLoad()\" " */
+       wprintf("<SPAN CLASS=\"titlebar\"> Users currently on ");
        escputs(serv_info.serv_humannode);
        wprintf("</SPAN></TD><TD ALIGN=RIGHT>");
        offer_start_page();
@@ -172,14 +206,33 @@ void who(void)
 
        wprintf("<div id=\"content\">\n");
 
-       wprintf("<div id=\"fix_scrollbar_bug\">");
+       wprintf("<div style=\"display:inline\" id=\"fix_scrollbar_bug\">");
        who_inner_div();        /* Actual data handled by another function */
        wprintf("</div>\n");
 
-       wprintf("<div align=center>"
+       wprintf("<div id=\"instructions\" align=center>"
                "Click on a name to read user info.  Click on "
-               "<IMG ALIGN=MIDDLE SRC=\"/static/citadelchat_16x.gif\" ALT=\"(p)\" "
-               "BORDER=0> to send an instant message to that user.</div>\n");
+               "<IMG ALIGN=MIDDLE SRC=\"/static/citadelchat_16x.gif\" "
+               "ALT=\"(p)\" BORDER=0>"
+               " to send an instant message to that user.</div>\n");
+
+       /* JavaScript to make the ajax refresh happen:
+        * 1. Register the request 'getWholist' which calls the WebCit action 'who_inner_html'
+        * 2. Register the 'fix_scrollbar_bug' div as one we're interested in ajaxifying
+        * 3. setInterval to make the ajax refresh happen every 30 seconds.  The random number
+        *    in the request is there to prevent IE from caching the XML even though it's been
+        *    told not to.  Die, Microsoft, Die.
+        */
+       wprintf(
+"                                                                      \n"
+" <script type=\"text/javascript\">                                    \n"
+"      ajaxEngine.registerRequest('getWholist', 'who_inner_html');\n"
+"      ajaxEngine.registerAjaxElement('fix_scrollbar_bug');    \n"
+"      setInterval(\"ajaxEngine.sendRequest('getWholist', 'junk='+Math.random());\", 3000);    \n"
+"</script>\n"
+       );
+
+
        wDumpContent(1);
 }
 
@@ -256,10 +309,9 @@ void edit_me(void)
                        wprintf("<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Change user name\">");
                        wprintf("</TD>\n</TR>\n");
                }
-               wprintf("<TR><TD>&nbsp;</TD><TD>&nbsp;</TD><TD ALIGN=center>");
+               wprintf("<TR><TD> </TD><TD> </TD><TD ALIGN=center>");
                wprintf("<INPUT TYPE=\"submit\" NAME=\"sc\" VALUE=\"Cancel\">");
                wprintf("</TD></TR></TABLE>\n");
-
                wprintf("</FORM></CENTER>\n");
                wDumpContent(1);
        }