* New AJAX-based wholist refresh
[citadel.git] / webcit / static / rico.js
1 /* openrico.org rico.js */
2  
3 /**
4   *
5   *  Copyright 2005 Sabre Airline Solutions
6   *
7   *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
8   *  file except in compliance with the License. You may obtain a copy of the License at
9   *
10   *         http://www.apache.org/licenses/LICENSE-2.0
11   *
12   *  Unless required by applicable law or agreed to in writing, software distributed under the
13   *  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
14   *  either express or implied. See the License for the specific language governing permissions
15   *  and limitations under the License.
16   **/
17
18 // rico.js --------------------
19
20 var Rico = {
21   Version: '1.1-beta'
22 }
23
24 Rico.ArrayExtensions = new Array();
25
26 if (Object.prototype.extend) {
27    // in prototype.js...
28    Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend;
29 }
30
31 if (Array.prototype.push) {
32    // in prototype.js...
33    Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.push;
34 }
35
36 if (!Array.prototype.remove) {
37    Array.prototype.remove = function(dx) {
38       if( isNaN(dx) || dx > this.length )
39          return false;
40       for( var i=0,n=0; i<this.length; i++ )
41          if( i != dx )
42             this[n++]=this[i];
43       this.length-=1;
44    };
45   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.remove;
46 }
47
48 if (!Array.prototype.removeItem) {
49    Array.prototype.removeItem = function(item) {
50       for ( var i = 0 ; i < this.length ; i++ )
51          if ( this[i] == item ) {
52             this.remove(i);
53             break;
54          }
55    };
56   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.removeItem;
57 }
58
59 if (!Array.prototype.indices) {
60    Array.prototype.indices = function() {
61       var indexArray = new Array();
62       for ( index in this ) {
63          var ignoreThis = false;
64          for ( var i = 0 ; i < Rico.ArrayExtensions.length ; i++ ) {
65             if ( this[index] == Rico.ArrayExtensions[i] ) {
66                ignoreThis = true;
67                break;
68             }
69          }
70          if ( !ignoreThis )
71             indexArray[ indexArray.length ] = index;
72       }
73       return indexArray;
74    }
75   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.indices;
76 }
77
78 // Create the loadXML method and xml getter for Mozilla
79 if ( window.DOMParser &&
80           window.XMLSerializer &&
81           window.Node && Node.prototype && Node.prototype.__defineGetter__ ) {
82
83    if (!Document.prototype.loadXML) {
84       Document.prototype.loadXML = function (s) {
85          var doc2 = (new DOMParser()).parseFromString(s, "text/xml");
86          while (this.hasChildNodes())
87             this.removeChild(this.lastChild);
88
89          for (var i = 0; i < doc2.childNodes.length; i++) {
90             this.appendChild(this.importNode(doc2.childNodes[i], true));
91          }
92       };
93         }
94
95         Document.prototype.__defineGetter__( "xml",
96            function () {
97                    return (new XMLSerializer()).serializeToString(this);
98            }
99          );
100 }
101
102 document.getElementsByTagAndClassName = function(tagName, className) {
103   if ( tagName == null )
104      tagName = '*';
105
106   var children = document.getElementsByTagName(tagName) || document.all;
107   var elements = new Array();
108
109   if ( className == null )
110     return children;
111
112   for (var i = 0; i < children.length; i++) {
113     var child = children[i];
114     var classNames = child.className.split(' ');
115     for (var j = 0; j < classNames.length; j++) {
116       if (classNames[j] == className) {
117         elements.push(child);
118         break;
119       }
120     }
121   }
122
123   return elements;
124 }
125  
126  
127 // ricoAccordion.js --------------------
128  
129
130 Rico.Accordion = Class.create();
131
132 Rico.Accordion.prototype = {
133
134    initialize: function(container, options) {
135       this.container            = $(container);
136       this.lastExpandedTab      = null;
137       this.accordionTabs        = new Array();
138       this.setOptions(options);
139       this._attachBehaviors();
140
141       this.container.style.borderBottom = '1px solid ' + this.options.borderColor;
142
143       // set the initial visual state...
144       for ( var i=1 ; i < this.accordionTabs.length ; i++ )
145       {
146          this.accordionTabs[i].collapse();
147          this.accordionTabs[i].content.style.display = 'none';
148       }
149       this.lastExpandedTab = this.accordionTabs[0];
150       this.lastExpandedTab.content.style.height = this.options.panelHeight + "px";
151       this.lastExpandedTab.showExpanded();
152       this.lastExpandedTab.titleBar.style.fontWeight = this.options.expandedFontWeight;
153    },
154
155    setOptions: function(options) {
156       this.options = {
157          expandedBg          : '#63699c',
158          hoverBg             : '#63699c',
159          collapsedBg         : '#6b79a5',
160          expandedTextColor   : '#ffffff',
161          expandedFontWeight  : 'bold',
162          hoverTextColor      : '#ffffff',
163          collapsedTextColor  : '#ced7ef',
164          collapsedFontWeight : 'normal',
165          hoverTextColor      : '#ffffff',
166          borderColor         : '#1f669b',
167          panelHeight         : 200,
168          onHideTab           : null,
169          onShowTab           : null
170       }.extend(options || {});
171    },
172
173    showTabByIndex: function( anIndex, animate ) {
174       var doAnimate = arguments.length == 1 ? true : animate;
175       this.showTab( this.accordionTabs[anIndex], doAnimate );
176    },
177
178    showTab: function( accordionTab, animate ) {
179
180       var doAnimate = arguments.length == 1 ? true : animate;
181
182       if ( this.options.onHideTab )
183          this.options.onHideTab(this.lastExpandedTab);
184
185       this.lastExpandedTab.showCollapsed(); 
186       var accordion = this;
187       var lastExpandedTab = this.lastExpandedTab;
188
189       this.lastExpandedTab.content.style.height = (this.options.panelHeight - 1) + 'px';
190       accordionTab.content.style.display = '';
191
192       accordionTab.titleBar.style.fontWeight = this.options.expandedFontWeight;
193
194       if ( doAnimate ) {
195          new Effect.AccordionSize( this.lastExpandedTab.content,
196                                    accordionTab.content,
197                                    1,
198                                    this.options.panelHeight,
199                                    100, 10,
200                                    { complete: function() {accordion.showTabDone(lastExpandedTab)} } );
201          this.lastExpandedTab = accordionTab;
202       }
203       else {
204          this.lastExpandedTab.content.style.height = "1px";
205          accordionTab.content.style.height = this.options.panelHeight + "px";
206          this.lastExpandedTab = accordionTab;
207          this.showTabDone(lastExpandedTab);
208       }
209    },
210
211    showTabDone: function(collapsedTab) {
212       collapsedTab.content.style.display = 'none';
213       this.lastExpandedTab.showExpanded();
214       if ( this.options.onShowTab )
215          this.options.onShowTab(this.lastExpandedTab);
216    },
217
218    _attachBehaviors: function() {
219       var panels = this._getDirectChildrenByTag(this.container, 'DIV');
220       for ( var i = 0 ; i < panels.length ; i++ ) {
221
222          var tabChildren = this._getDirectChildrenByTag(panels[i],'DIV');
223          if ( tabChildren.length != 2 )
224             continue; // unexpected
225
226          var tabTitleBar   = tabChildren[0];
227          var tabContentBox = tabChildren[1];
228          this.accordionTabs.push( new Rico.Accordion.Tab(this,tabTitleBar,tabContentBox) );
229       }
230    },
231
232    _getDirectChildrenByTag: function(e, tagName) {
233       var kids = new Array();
234       var allKids = e.childNodes;
235       for( var i = 0 ; i < allKids.length ; i++ )
236          if ( allKids[i] && allKids[i].tagName && allKids[i].tagName == tagName )
237             kids.push(allKids[i]);
238       return kids;
239    }
240
241 };
242
243 Rico.Accordion.Tab = Class.create();
244
245 Rico.Accordion.Tab.prototype = {
246
247    initialize: function(accordion, titleBar, content) {
248       this.accordion = accordion;
249       this.titleBar  = titleBar;
250       this.content   = content;
251       this._attachBehaviors();
252    },
253
254    collapse: function() {
255       this.showCollapsed();
256       this.content.style.height = "1px";
257    },
258
259    showCollapsed: function() {
260       this.expanded = false;
261       this.titleBar.style.backgroundColor = this.accordion.options.collapsedBg;
262       this.titleBar.style.color           = this.accordion.options.collapsedTextColor;
263       this.titleBar.style.fontWeight      = this.accordion.options.collapsedFontWeight;
264       this.content.style.overflow = "hidden";
265    },
266
267    showExpanded: function() {
268       this.expanded = true;
269       this.titleBar.style.backgroundColor = this.accordion.options.expandedBg;
270       this.titleBar.style.color           = this.accordion.options.expandedTextColor;
271       this.content.style.overflow         = "visible";
272    },
273
274    titleBarClicked: function(e) {
275       if ( this.accordion.lastExpandedTab == this )
276          return;
277       this.accordion.showTab(this);
278    },
279
280    hover: function(e) {
281                 this.titleBar.style.backgroundColor = this.accordion.options.hoverBg;
282                 this.titleBar.style.color           = this.accordion.options.hoverTextColor;
283    },
284
285    unhover: function(e) {
286       if ( this.expanded ) {
287          this.titleBar.style.backgroundColor = this.accordion.options.expandedBg;
288          this.titleBar.style.color           = this.accordion.options.expandedTextColor;
289       }
290       else {
291          this.titleBar.style.backgroundColor = this.accordion.options.collapsedBg;
292          this.titleBar.style.color           = this.accordion.options.collapsedTextColor;
293       }
294    },
295
296    _attachBehaviors: function() {
297       this.content.style.border = "1px solid " + this.accordion.options.borderColor;
298       this.content.style.borderTopWidth    = "0px";
299       this.content.style.borderBottomWidth = "0px";
300       this.content.style.margin            = "0px";
301
302       this.titleBar.onclick     = this.titleBarClicked.bindAsEventListener(this);
303       this.titleBar.onmouseover = this.hover.bindAsEventListener(this);
304       this.titleBar.onmouseout  = this.unhover.bindAsEventListener(this);
305    }
306
307 };
308  
309  
310 // ricoAjaxEngine.js --------------------
311  
312
313 Rico.AjaxEngine = Class.create();
314
315 Rico.AjaxEngine.prototype = {
316
317    initialize: function() {
318       this.ajaxElements = new Array();
319       this.ajaxObjects  = new Array();
320       this.requestURLS  = new Array();
321    },
322
323    registerAjaxElement: function( anId, anElement ) {
324       if ( arguments.length == 1 )
325          anElement = $(anId);
326       this.ajaxElements[anId] = anElement;
327    },
328
329    registerAjaxObject: function( anId, anObject ) {
330       this.ajaxObjects[anId] = anObject;
331    },
332
333    registerRequest: function (requestLogicalName, requestURL) {
334       this.requestURLS[requestLogicalName] = requestURL;
335    },
336
337    sendRequest: function(requestName) {
338       var requestURL = this.requestURLS[requestName];
339       if ( requestURL == null )
340          return;
341
342       var queryString = "";
343       if ( arguments.length > 1 )
344          queryString = this._createQueryString(arguments, 1);
345
346       new Ajax.Request(requestURL, this._requestOptions(queryString));
347    },
348
349    sendRequestWithData: function(requestName, xmlDocument) {
350       var requestURL = this.requestURLS[requestName];
351       if ( requestURL == null )
352          return;
353
354       var queryString = "";
355       if ( arguments.length > 2 )
356          queryString = this._createQueryString(arguments, 2);
357
358       new Ajax.Request(requestURL + "?" + queryString, this._requestOptions(null,xmlDocument));
359    },
360
361    sendRequestAndUpdate: function(requestName,container,options) {
362       var requestURL = this.requestURLS[requestName];
363       if ( requestURL == null )
364          return;
365
366       var queryString = "";
367       if ( arguments.length > 3 )
368          queryString = this._createQueryString(arguments, 3);
369
370       var updaterOptions = this._requestOptions(queryString);
371       updaterOptions.onComplete = null;
372       updaterOptions.extend(options);
373
374       new Ajax.Updater(container, requestURL, updaterOptions);
375    },
376
377    sendRequestWithDataAndUpdate: function(requestName,xmlDocument,container,options) {
378       var requestURL = this.requestURLS[requestName];
379       if ( requestURL == null )
380          return;
381
382       var queryString = "";
383       if ( arguments.length > 4 )
384          queryString = this._createQueryString(arguments, 4);
385
386
387       var updaterOptions = this._requestOptions(queryString,xmlDocument);
388       updaterOptions.onComplete = null;
389       updaterOptions.extend(options);
390
391       new Ajax.Updater(container, requestURL + "?" + queryString, updaterOptions);
392    },
393
394    // Private -- not part of intended engine API --------------------------------------------------------------------
395
396    _requestOptions: function(queryString,xmlDoc) {
397       var self = this;
398
399       var requestHeaders = ['X-Rico-Version', Rico.Version ];
400       var sendMethod = "post"
401       if ( arguments[1] )
402          requestHeaders.push( 'Content-type', 'text/xml' );
403       else
404          sendMethod = "get";
405
406       return { requestHeaders: requestHeaders,
407                parameters:     queryString,
408                postBody:       arguments[1] ? xmlDoc : null,
409                method:         sendMethod,
410                onComplete:     self._onRequestComplete.bind(self) };
411    },
412
413    _createQueryString: function( theArgs, offset ) {
414       var queryString = ""
415       for ( var i = offset ; i < theArgs.length ; i++ ) {
416           if ( i != offset )
417             queryString += "&";
418
419           var anArg = theArgs[i];
420
421           if ( anArg.name != undefined && anArg.value != undefined ) {
422             queryString += anArg.name +  "=" + escape(anArg.value);
423           }
424           else {
425              var ePos  = anArg.indexOf('=');
426              var argName  = anArg.substring( 0, ePos );
427              var argValue = anArg.substring( ePos + 1 );
428              queryString += argName + "=" + escape(argValue);
429           }
430       }
431
432       return queryString;
433    },
434
435    _onRequestComplete : function(request) {
436
437       //!!TODO: error handling infrastructure?? 
438       if (request.status != 200)
439         return;
440
441       var response = request.responseXML.getElementsByTagName("ajax-response");
442       if (response == null || response.length != 1)
443          return;
444       this._processAjaxResponse( response[0].childNodes );
445    },
446
447    _processAjaxResponse: function( xmlResponseElements ) {
448       for ( var i = 0 ; i < xmlResponseElements.length ; i++ ) {
449          var responseElement = xmlResponseElements[i];
450
451          // only process nodes of type element.....
452          if ( responseElement.nodeType != 1 )
453             continue;
454
455          var responseType = responseElement.getAttribute("type");
456          var responseId   = responseElement.getAttribute("id");
457
458          if ( responseType == "object" )
459             this._processAjaxObjectUpdate( this.ajaxObjects[ responseId ], responseElement );
460          else if ( responseType == "element" )
461             this._processAjaxElementUpdate( this.ajaxElements[ responseId ], responseElement );
462          else
463             alert('unrecognized AjaxResponse type : ' + responseType );
464       }
465    },
466
467    _processAjaxObjectUpdate: function( ajaxObject, responseElement ) {
468       ajaxObject.ajaxUpdate( responseElement );
469    },
470
471    _processAjaxElementUpdate: function( ajaxElement, responseElement ) {
472       if ( responseElement.xml != undefined )
473          this._processAjaxElementUpdateIE( ajaxElement, responseElement );
474       else
475          this._processAjaxElementUpdateMozilla( ajaxElement, responseElement );
476    },
477
478    _processAjaxElementUpdateIE: function( ajaxElement, responseElement ) {
479       var newHTML = "";
480       for ( var i = 0 ; i < responseElement.childNodes.length ; i++ )
481          newHTML += responseElement.childNodes[i].xml;
482
483       ajaxElement.innerHTML = newHTML;
484    },
485
486    _processAjaxElementUpdateMozilla: function( ajaxElement, responseElement ) {
487       var xmlSerializer = new XMLSerializer();
488       var newHTML = "";
489       for ( var i = 0 ; i < responseElement.childNodes.length ; i++ )
490          newHTML += xmlSerializer.serializeToString(responseElement.childNodes[i]);
491
492       ajaxElement.innerHTML = newHTML;
493    }
494 }
495
496 var ajaxEngine = new Rico.AjaxEngine(); 
497  
498 // ricoColor.js --------------------
499  
500 Rico.Color = Class.create();
501
502 Rico.Color.prototype = {
503
504    initialize: function(red, green, blue) {
505       this.rgb = { r: red, g : green, b : blue };
506    },
507
508    setRed: function(r) {
509       this.rgb.r = r;
510    },
511
512    setGreen: function(g) {
513       this.rgb.g = g;
514    },
515
516    setBlue: function(b) {
517       this.rgb.b = b;
518    },
519
520    setHue: function(h) {
521
522       // get an HSB model, and set the new hue...
523       var hsb = this.asHSB();
524       hsb.h = h;
525
526       // convert back to RGB...
527       this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
528    },
529
530    setSaturation: function(s) {
531       // get an HSB model, and set the new hue...
532       var hsb = this.asHSB();
533       hsb.s = s;
534
535       // convert back to RGB and set values...
536       this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
537    },
538
539    setBrightness: function(b) {
540       // get an HSB model, and set the new hue...
541       var hsb = this.asHSB();
542       hsb.b = b;
543
544       // convert back to RGB and set values...
545       this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b );
546    },
547
548    darken: function(percent) {
549       var hsb  = this.asHSB();
550       this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0));
551    },
552
553    brighten: function(percent) {
554       var hsb  = this.asHSB();
555       this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1));
556    },
557
558    blend: function(other) {
559       this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
560       this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
561       this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
562    },
563
564    isBright: function() {
565       var hsb = this.asHSB();
566       return this.asHSB().b > 0.5;
567    },
568
569    isDark: function() {
570       return ! this.isBright();
571    },
572
573    asRGB: function() {
574       return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
575    },
576
577    asHex: function() {
578       return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
579    },
580
581    asHSB: function() {
582       return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
583    },
584
585    toString: function() {
586       return this.asHex();
587    }
588
589 };
590
591 Rico.Color.createFromHex = function(hexCode) {
592
593    if ( hexCode.indexOf('#') == 0 )
594       hexCode = hexCode.substring(1);
595    var red   = hexCode.substring(0,2);
596    var green = hexCode.substring(2,4);
597    var blue  = hexCode.substring(4,6);
598    return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
599 }
600
601 /**
602  * Factory method for creating a color from the background of
603  * an HTML element.
604  */
605 Rico.Color.createColorFromBackground = function(elem) {
606
607    var actualColor = RicoUtil.getElementsComputedStyle($(elem), "backgroundColor", "background-color");
608
609    if ( actualColor == "transparent" && elem.parent )
610       return Rico.Color.createColorFromBackground(elem.parent);
611
612    if ( actualColor == null )
613       return new Rico.Color(255,255,255);
614
615    if ( actualColor.indexOf("rgb(") == 0 ) {
616       var colors = actualColor.substring(4, actualColor.length - 1 );
617       var colorArray = colors.split(",");
618       return new Rico.Color( parseInt( colorArray[0] ),
619                             parseInt( colorArray[1] ),
620                             parseInt( colorArray[2] )  );
621
622    }
623    else if ( actualColor.indexOf("#") == 0 ) {
624       var redPart   = parseInt(actualColor.substring(1,3), 16);
625       var greenPart = parseInt(actualColor.substring(3,5), 16);
626       var bluePart  = parseInt(actualColor.substring(5), 16);
627       return new Rico.Color( redPart, greenPart, bluePart );
628    }
629    else
630       return new Rico.Color(255,255,255);
631 }
632
633 Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {
634
635    var red   = 0;
636         var green = 0;
637         var blue  = 0;
638
639    if (saturation == 0) {
640       red = parseInt(brightness * 255.0 + 0.5);
641            green = red;
642            blue = red;
643         }
644         else {
645       var h = (hue - Math.floor(hue)) * 6.0;
646       var f = h - Math.floor(h);
647       var p = brightness * (1.0 - saturation);
648       var q = brightness * (1.0 - saturation * f);
649       var t = brightness * (1.0 - (saturation * (1.0 - f)));
650
651       switch (parseInt(h)) {
652          case 0:
653             red   = (brightness * 255.0 + 0.5);
654             green = (t * 255.0 + 0.5);
655             blue  = (p * 255.0 + 0.5);
656             break;
657          case 1:
658             red   = (q * 255.0 + 0.5);
659             green = (brightness * 255.0 + 0.5);
660             blue  = (p * 255.0 + 0.5);
661             break;
662          case 2:
663             red   = (p * 255.0 + 0.5);
664             green = (brightness * 255.0 + 0.5);
665             blue  = (t * 255.0 + 0.5);
666             break;
667          case 3:
668             red   = (p * 255.0 + 0.5);
669             green = (q * 255.0 + 0.5);
670             blue  = (brightness * 255.0 + 0.5);
671             break;
672          case 4:
673             red   = (t * 255.0 + 0.5);
674             green = (p * 255.0 + 0.5);
675             blue  = (brightness * 255.0 + 0.5);
676             break;
677           case 5:
678             red   = (brightness * 255.0 + 0.5);
679             green = (p * 255.0 + 0.5);
680             blue  = (q * 255.0 + 0.5);
681             break;
682             }
683         }
684
685    return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) };
686 }
687
688 Rico.Color.RGBtoHSB = function(r, g, b) {
689
690    var hue;
691    var saturaton;
692    var brightness;
693
694    var cmax = (r > g) ? r : g;
695    if (b > cmax)
696       cmax = b;
697
698    var cmin = (r < g) ? r : g;
699    if (b < cmin)
700       cmin = b;
701
702    brightness = cmax / 255.0;
703    if (cmax != 0)
704       saturation = (cmax - cmin)/cmax;
705    else
706       saturation = 0;
707
708    if (saturation == 0)
709       hue = 0;
710    else {
711       var redc   = (cmax - r)/(cmax - cmin);
712         var greenc = (cmax - g)/(cmax - cmin);
713         var bluec  = (cmax - b)/(cmax - cmin);
714
715         if (r == cmax)
716            hue = bluec - greenc;
717         else if (g == cmax)
718            hue = 2.0 + redc - bluec;
719       else
720            hue = 4.0 + greenc - redc;
721
722         hue = hue / 6.0;
723         if (hue < 0)
724            hue = hue + 1.0;
725    }
726
727    return { h : hue, s : saturation, b : brightness };
728
729  
730 // ricoCorner.js --------------------
731  
732
733 Rico.Corner = {
734
735    round: function(e, options) {
736       var e = $(e);
737       this._setOptions(options);
738
739       var color = this.options.color;
740       if ( this.options.color == "fromElement" )
741          color = this._background(e);
742
743       var bgColor = this.options.bgColor;
744       if ( this.options.bgColor == "fromParent" )
745          bgColor = this._background(e.offsetParent);
746
747       this._roundCornersImpl(e, color, bgColor);
748    },
749
750    _roundCornersImpl: function(e, color, bgColor) {
751       if(this.options.border)
752          this._renderBorder(e,bgColor);
753       if(this._isTopRounded())
754          this._roundTopCorners(e,color,bgColor);
755       if(this._isBottomRounded())
756          this._roundBottomCorners(e,color,bgColor);
757    },
758
759    _renderBorder: function(el,bgColor) {
760       var borderValue = "1px solid " + this._borderColor(bgColor);
761       var borderL = "border-left: "  + borderValue;
762       var borderR = "border-right: " + borderValue;
763       var style   = "style='" + borderL + ";" + borderR +  "'";
764       el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>"
765    },
766
767    _roundTopCorners: function(el, color, bgColor) {
768       var corner = this._createCorner(bgColor);
769       for(var i=0 ; i < this.options.numSlices ; i++ )
770          corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));
771       el.style.paddingTop = 0;
772       el.insertBefore(corner,el.firstChild);
773    },
774
775    _roundBottomCorners: function(el, color, bgColor) {
776       var corner = this._createCorner(bgColor);
777       for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- )
778          corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));
779       el.style.paddingBottom = 0;
780       el.appendChild(corner);
781    },
782
783    _createCorner: function(bgColor) {
784       var corner = document.createElement("div");
785       corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
786       return corner;
787    },
788
789    _createCornerSlice: function(color,bgColor, n, position) {
790       var slice = document.createElement("span");
791
792       var inStyle = slice.style;
793       inStyle.backgroundColor = color;
794       inStyle.display  = "block";
795       inStyle.height   = "1px";
796       inStyle.overflow = "hidden";
797       inStyle.fontSize = "1px";
798
799       var borderColor = this._borderColor(color,bgColor);
800       if ( this.options.border && n == 0 ) {
801          inStyle.borderTopStyle    = "solid";
802          inStyle.borderTopWidth    = "1px";
803          inStyle.borderLeftWidth   = "0px";
804          inStyle.borderRightWidth  = "0px";
805          inStyle.borderBottomWidth = "0px";
806          inStyle.height            = "0px"; // assumes css compliant box model
807          inStyle.borderColor       = borderColor;
808       }
809       else if(borderColor) {
810          inStyle.borderColor = borderColor;
811          inStyle.borderStyle = "solid";
812          inStyle.borderWidth = "0px 1px";
813       }
814
815       if ( !this.options.compact && (n == (this.options.numSlices-1)) )
816          inStyle.height = "2px";
817
818       this._setMargin(slice, n, position);
819       this._setBorder(slice, n, position);
820
821       return slice;
822    },
823
824    _setOptions: function(options) {
825       this.options = {
826          corners : "all",
827          color   : "fromElement",
828          bgColor : "fromParent",
829          blend   : true,
830          border  : false,
831          compact : false
832       }.extend(options || {});
833
834       this.options.numSlices = this.options.compact ? 2 : 4;
835       if ( this._isTransparent() )
836          this.options.blend = false;
837    },
838
839    _whichSideTop: function() {
840       if ( this._hasString(this.options.corners, "all", "top") )
841          return "";
842
843       if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 )
844          return "";
845
846       if (this.options.corners.indexOf("tl") >= 0)
847          return "left";
848       else if (this.options.corners.indexOf("tr") >= 0)
849           return "right";
850       return "";
851    },
852
853    _whichSideBottom: function() {
854       if ( this._hasString(this.options.corners, "all", "bottom") )
855          return "";
856
857       if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 )
858          return "";
859
860       if(this.options.corners.indexOf("bl") >=0)
861          return "left";
862       else if(this.options.corners.indexOf("br")>=0)
863          return "right";
864       return "";
865    },
866
867    _borderColor : function(color,bgColor) {
868       if ( color == "transparent" )
869          return bgColor;
870       else if ( this.options.border )
871          return this.options.border;
872       else if ( this.options.blend )
873          return this._blend( bgColor, color );
874       else
875          return "";
876    },
877
878
879    _setMargin: function(el, n, corners) {
880       var marginSize = this._marginSize(n);
881       var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
882
883       if ( whichSide == "left" ) {
884          el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px";
885       }
886       else if ( whichSide == "right" ) {
887          el.style.marginRight = marginSize + "px"; el.style.marginLeft  = "0px";
888       }
889       else {
890          el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px";
891       }
892    },
893
894    _setBorder: function(el,n,corners) {
895       var borderSize = this._borderSize(n);
896       var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
897
898       if ( whichSide == "left" ) {
899          el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px";
900       }
901       else if ( whichSide == "right" ) {
902          el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth  = "0px";
903       }
904       else {
905          el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
906       }
907    },
908
909    _marginSize: function(n) {
910       if ( this._isTransparent() )
911          return 0;
912
913       var marginSizes          = [ 5, 3, 2, 1 ];
914       var blendedMarginSizes   = [ 3, 2, 1, 0 ];
915       var compactMarginSizes   = [ 2, 1 ];
916       var smBlendedMarginSizes = [ 1, 0 ];
917
918       if ( this.options.compact && this.options.blend )
919          return smBlendedMarginSizes[n];
920       else if ( this.options.compact )
921          return compactMarginSizes[n];
922       else if ( this.options.blend )
923          return blendedMarginSizes[n];
924       else
925          return marginSizes[n];
926    },
927
928    _borderSize: function(n) {
929       var transparentBorderSizes = [ 5, 3, 2, 1 ];
930       var blendedBorderSizes     = [ 2, 1, 1, 1 ];
931       var compactBorderSizes     = [ 1, 0 ];
932       var actualBorderSizes      = [ 0, 2, 0, 0 ];
933
934       if ( this.options.compact && (this.options.blend || this._isTransparent()) )
935          return 1;
936       else if ( this.options.compact )
937          return compactBorderSizes[n];
938       else if ( this.options.blend )
939          return blendedBorderSizes[n];
940       else if ( this.options.border )
941          return actualBorderSizes[n];
942       else if ( this._isTransparent() )
943          return transparentBorderSizes[n];
944       return 0;
945    },
946
947    _hasString: function(str) { for(var i=1 ; i<arguments.length ; i++) if (str.indexOf(arguments[i]) >= 0) return true; return false; },
948    _blend: function(c1, c2) { var cc1 = Rico.Color.createFromHex(c1); cc1.blend(Rico.Color.createFromHex(c2)); return cc1; },
949    _background: function(el) { try { return Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } },
950    _isTransparent: function() { return this.options.color == "transparent"; },
951    _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); },
952    _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); },
953    _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; }
954
955  
956 // ricoDragAndDrop.js --------------------
957  
958 Rico.DragAndDrop = Class.create();
959
960 Rico.DragAndDrop.prototype = {
961
962    initialize: function() {
963       this.dropZones                = new Array();
964       this.draggables               = new Array();
965       this.currentDragObjects       = new Array();
966       this.dragElement              = null;
967       this.lastSelectedDraggable    = null;
968       this.currentDragObjectVisible = false;
969       this.interestedInMotionEvents = false;
970    },
971
972    registerDropZone: function(aDropZone) {
973       this.dropZones[ this.dropZones.length ] = aDropZone;
974    },
975
976    deregisterDropZone: function(aDropZone) {
977       var newDropZones = new Array();
978       var j = 0;
979       for ( var i = 0 ; i < this.dropZones.length ; i++ ) {
980          if ( this.dropZones[i] != aDropZone )
981             newDropZones[j++] = this.dropZones[i];
982       }
983
984       this.dropZones = newDropZones;
985    },
986
987    clearDropZones: function() {
988       this.dropZones = new Array();
989    },
990
991    registerDraggable: function( aDraggable ) {
992       this.draggables[ this.draggables.length ] = aDraggable;
993       this._addMouseDownHandler( aDraggable );
994    },
995
996    clearSelection: function() {
997       for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
998          this.currentDragObjects[i].deselect();
999       this.currentDragObjects = new Array();
1000       this.lastSelectedDraggable = null;
1001    },
1002
1003    hasSelection: function() {
1004       return this.currentDragObjects.length > 0;
1005    },
1006
1007    setStartDragFromElement: function( e, mouseDownElement ) {
1008       this.origPos = RicoUtil.toDocumentPosition(mouseDownElement);
1009       this.startx = e.screenX - this.origPos.x
1010       this.starty = e.screenY - this.origPos.y
1011       //this.startComponentX = e.layerX ? e.layerX : e.offsetX;
1012       //this.startComponentY = e.layerY ? e.layerY : e.offsetY;
1013       //this.adjustedForDraggableSize = false;
1014
1015       this.interestedInMotionEvents = this.hasSelection();
1016       this._terminateEvent(e);
1017    },
1018
1019    updateSelection: function( draggable, extendSelection ) {
1020       if ( ! extendSelection )
1021          this.clearSelection();
1022
1023       if ( draggable.isSelected() ) {
1024          this.currentDragObjects.removeItem(draggable);
1025          draggable.deselect();
1026          if ( draggable == this.lastSelectedDraggable )
1027             this.lastSelectedDraggable = null;
1028       }
1029       else {
1030          this.currentDragObjects[ this.currentDragObjects.length ] = draggable;
1031          draggable.select();
1032          this.lastSelectedDraggable = draggable;
1033       }
1034    },
1035
1036    _mouseDownHandler: function(e) {
1037       if ( arguments.length == 0 )
1038          e = event;
1039
1040       // if not button 1 ignore it...
1041       var nsEvent = e.which != undefined;
1042       if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
1043          return;
1044
1045       var eventTarget     = e.target ? e.target : e.srcElement;
1046       var draggableObject = eventTarget.draggable;
1047
1048       this.updateSelection( draggableObject, e.ctrlKey );
1049
1050       // clear the drop zones postion cache...
1051       if ( this.hasSelection() )
1052          for ( var i = 0 ; i < this.dropZones.length ; i++ )
1053             this.dropZones[i].clearPositionCache();
1054
1055       this.setStartDragFromElement( e, draggableObject.getMouseDownHTMLElement() );
1056    },
1057
1058
1059    _mouseMoveHandler: function(e) {
1060       var nsEvent = e.which != undefined;
1061       if ( !this.interestedInMotionEvents ) {
1062          this._terminateEvent(e);
1063          return;
1064       }
1065
1066       if ( ! this.hasSelection() )
1067          return;
1068
1069       if ( ! this.currentDragObjectVisible )
1070          this._startDrag(e);
1071
1072       if ( !this.activatedDropZones )
1073          this._activateRegisteredDropZones();
1074
1075       //if ( !this.adjustedForDraggableSize )
1076       //   this._adjustForDraggableSize(e);
1077
1078       this._updateDraggableLocation(e);
1079       this._updateDropZonesHover(e);
1080
1081       this._terminateEvent(e);
1082    },
1083
1084    _makeDraggableObjectVisible: function(e)
1085    {
1086       if ( !this.hasSelection() )
1087          return;
1088
1089       var dragElement;
1090       if ( this.currentDragObjects.length > 1 )
1091          dragElement = this.currentDragObjects[0].getMultiObjectDragGUI(this.currentDragObjects);
1092       else
1093          dragElement = this.currentDragObjects[0].getSingleObjectDragGUI();
1094
1095       // go ahead and absolute position it...
1096       if ( RicoUtil.getElementsComputedStyle(dragElement, "position")  != "absolute" )
1097          dragElement.style.position = "absolute";
1098
1099       // need to parent him into the document...
1100       if ( dragElement.parentNode == null || dragElement.parentNode.nodeType == 11 )
1101          document.body.appendChild(dragElement);
1102
1103       this.dragElement = dragElement;
1104       this._updateDraggableLocation(e);
1105
1106       this.currentDragObjectVisible = true;
1107    },
1108
1109    /**
1110    _adjustForDraggableSize: function(e) {
1111       var dragElementWidth  = this.dragElement.offsetWidth;
1112       var dragElementHeight = this.dragElement.offsetHeight;
1113       if ( this.startComponentX > dragElementWidth )
1114          this.startx -= this.startComponentX - dragElementWidth + 2;
1115       if ( e.offsetY ) {
1116          if ( this.startComponentY > dragElementHeight )
1117             this.starty -= this.startComponentY - dragElementHeight + 2;
1118       }
1119       this.adjustedForDraggableSize = true;
1120    },
1121    **/
1122
1123    _updateDraggableLocation: function(e) {
1124       var dragObjectStyle = this.dragElement.style;
1125       dragObjectStyle.left = (e.screenX - this.startx) + "px"
1126       dragObjectStyle.top  = (e.screenY - this.starty) + "px";
1127    },
1128
1129    _updateDropZonesHover: function(e) {
1130       var n = this.dropZones.length;
1131       for ( var i = 0 ; i < n ; i++ ) {
1132          if ( ! this._mousePointInDropZone( e, this.dropZones[i] ) )
1133             this.dropZones[i].hideHover();
1134       }
1135
1136       for ( var i = 0 ; i < n ; i++ ) {
1137          if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
1138             if ( this.dropZones[i].canAccept(this.currentDragObjects) )
1139                this.dropZones[i].showHover();
1140          }
1141       }
1142    },
1143
1144    _startDrag: function(e) {
1145       for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
1146          this.currentDragObjects[i].startDrag();
1147
1148       this._makeDraggableObjectVisible(e);
1149    },
1150
1151    _mouseUpHandler: function(e) {
1152       if ( ! this.hasSelection() )
1153          return;
1154
1155       var nsEvent = e.which != undefined;
1156       if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
1157          return;
1158
1159       this.interestedInMotionEvents = false;
1160
1161       if ( this.dragElement == null ) {
1162          this._terminateEvent(e);
1163          return;
1164       }
1165
1166       if ( this._placeDraggableInDropZone(e) )
1167          this._completeDropOperation(e);
1168       else {
1169          this._terminateEvent(e);
1170          new Effect.Position( this.dragElement,
1171                               this.origPos.x,
1172                               this.origPos.y,
1173                               200,
1174                               20,
1175                               { complete : this._doCancelDragProcessing.bind(this) } );
1176       }
1177    },
1178
1179    _completeDropOperation: function(e) {
1180       if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() ) {
1181          if ( this.dragElement.parentNode != null )
1182             this.dragElement.parentNode.removeChild(this.dragElement);
1183       }
1184
1185       this._deactivateRegisteredDropZones();
1186       this._endDrag();
1187       this.clearSelection();
1188       this.dragElement = null;
1189       this.currentDragObjectVisible = false;
1190       this._terminateEvent(e);
1191    },
1192
1193    _doCancelDragProcessing: function() {
1194       this._cancelDrag();
1195
1196       if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() ) {
1197          if ( this.dragElement.parentNode != null ) {
1198             this.dragElement.parentNode.removeChild(this.dragElement);
1199          }
1200       }
1201
1202       this._deactivateRegisteredDropZones();
1203       this.dragElement = null;
1204       this.currentDragObjectVisible = false;
1205    },
1206
1207    _placeDraggableInDropZone: function(e) {
1208       var foundDropZone = false;
1209       var n = this.dropZones.length;
1210       for ( var i = 0 ; i < n ; i++ ) {
1211          if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
1212             if ( this.dropZones[i].canAccept(this.currentDragObjects) ) {
1213                this.dropZones[i].hideHover();
1214                this.dropZones[i].accept(this.currentDragObjects);
1215                foundDropZone = true;
1216                break;
1217             }
1218          }
1219       }
1220
1221       return foundDropZone;
1222    },
1223
1224    _cancelDrag: function() {
1225       for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
1226          this.currentDragObjects[i].cancelDrag();
1227    },
1228
1229    _endDrag: function() {
1230       for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
1231          this.currentDragObjects[i].endDrag();
1232    },
1233
1234    _mousePointInDropZone: function( e, dropZone ) {
1235
1236       var absoluteRect = dropZone.getAbsoluteRect();
1237
1238       return e.clientX  > absoluteRect.left  &&
1239              e.clientX  < absoluteRect.right &&
1240              e.clientY  > absoluteRect.top   &&
1241              e.clientY  < absoluteRect.bottom;
1242    },
1243
1244    _addMouseDownHandler: function( aDraggable )
1245    {
1246       var htmlElement = aDraggable.getMouseDownHTMLElement();
1247       if ( htmlElement != null ) {
1248          htmlElement.draggable = aDraggable;
1249          this._addMouseDownEvent( htmlElement );
1250       }
1251    },
1252
1253    _activateRegisteredDropZones: function() {
1254       var n = this.dropZones.length;
1255       for ( var i = 0 ; i < n ; i++ ) {
1256          var dropZone = this.dropZones[i];
1257          if ( dropZone.canAccept(this.currentDragObjects) )
1258             dropZone.activate();
1259       }
1260
1261       this.activatedDropZones = true;
1262    },
1263
1264    _deactivateRegisteredDropZones: function() {
1265       var n = this.dropZones.length;
1266       for ( var i = 0 ; i < n ; i++ )
1267          this.dropZones[i].deactivate();
1268       this.activatedDropZones = false;
1269    },
1270
1271    _addMouseDownEvent: function( htmlElement ) {
1272       if ( typeof document.implementation != "undefined" &&
1273          document.implementation.hasFeature("HTML",   "1.0") &&
1274          document.implementation.hasFeature("Events", "2.0") &&
1275          document.implementation.hasFeature("CSS",    "2.0") ) {
1276          htmlElement.addEventListener("mousedown", this._mouseDownHandler.bindAsEventListener(this), false);
1277       }
1278       else {
1279          htmlElement.attachEvent( "onmousedown", this._mouseDownHandler.bindAsEventListener(this) );
1280       }
1281    },
1282
1283    _terminateEvent: function(e) {
1284       if ( e.stopPropagation != undefined )
1285          e.stopPropagation();
1286       else if ( e.cancelBubble != undefined )
1287          e.cancelBubble = true;
1288
1289       if ( e.preventDefault != undefined )
1290          e.preventDefault();
1291       else
1292          e.returnValue = false;
1293    },
1294
1295    initializeEventHandlers: function() {
1296       if ( typeof document.implementation != "undefined" &&
1297          document.implementation.hasFeature("HTML",   "1.0") &&
1298          document.implementation.hasFeature("Events", "2.0") &&
1299          document.implementation.hasFeature("CSS",    "2.0") ) {
1300          document.addEventListener("mouseup",   this._mouseUpHandler.bindAsEventListener(this),  false);
1301          document.addEventListener("mousemove", this._mouseMoveHandler.bindAsEventListener(this), false);
1302       }
1303       else {
1304          document.attachEvent( "onmouseup",   this._mouseUpHandler.bindAsEventListener(this) );
1305          document.attachEvent( "onmousemove", this._mouseMoveHandler.bindAsEventListener(this) );
1306       }
1307    }
1308 }
1309
1310 var dndMgr = new Rico.DragAndDrop();
1311 dndMgr.initializeEventHandlers(); 
1312  
1313 // ricoDraggable.js --------------------
1314  
1315 Rico.Draggable = Class.create();
1316
1317 Rico.Draggable.prototype = {
1318
1319    initialize: function( type, htmlElement ) {
1320       this.type          = type;
1321       this.htmlElement   = $(htmlElement);
1322       this.selected      = false;
1323    },
1324
1325    /**
1326     *   Returns the HTML element that should have a mouse down event
1327     *   added to it in order to initiate a drag operation
1328     *
1329     **/
1330    getMouseDownHTMLElement: function() {
1331       return this.htmlElement;
1332    },
1333
1334    select: function() {
1335       this.selected = true;
1336
1337       if ( this.showingSelected )
1338          return;
1339
1340       var htmlElement = this.getMouseDownHTMLElement();
1341
1342       var color = Rico.Color.createColorFromBackground(htmlElement);
1343       color.isBright() ? color.darken(0.033) : color.brighten(0.033);
1344
1345       this.saveBackground = RicoUtil.getElementsComputedStyle(htmlElement, "backgroundColor", "background-color");
1346       htmlElement.style.backgroundColor = color.asHex();
1347       this.showingSelected = true;
1348    },
1349
1350    deselect: function() {
1351       this.selected = false;
1352       if ( !this.showingSelected )
1353          return;
1354
1355       var htmlElement = this.getMouseDownHTMLElement();
1356
1357       htmlElement.style.backgroundColor = this.saveBackground;
1358       this.showingSelected = false;
1359    },
1360
1361    isSelected: function() {
1362       return this.selected;
1363    },
1364
1365    startDrag: function() {
1366    },
1367
1368    cancelDrag: function() {
1369    },
1370
1371    endDrag: function() {
1372    },
1373
1374    getSingleObjectDragGUI: function() {
1375       return this.htmlElement;
1376    },
1377
1378    getMultiObjectDragGUI: function( draggables ) {
1379       return this.htmlElement;
1380    },
1381
1382    getDroppedGUI: function() {
1383       return this.htmlElement;
1384    },
1385
1386    toString: function() {
1387       return this.type + ":" + this.htmlElement + ":";
1388    }
1389
1390
1391  
1392 // ricoDropzone.js --------------------
1393  
1394 Rico.Dropzone = Class.create();
1395
1396 Rico.Dropzone.prototype = {
1397
1398    initialize: function( htmlElement ) {
1399       this.htmlElement  = $(htmlElement);
1400       this.absoluteRect = null;
1401    },
1402
1403    getHTMLElement: function() {
1404       return this.htmlElement;
1405    },
1406
1407    clearPositionCache: function() {
1408       this.absoluteRect = null;
1409    },
1410
1411    getAbsoluteRect: function() {
1412       if ( this.absoluteRect == null ) {
1413          var htmlElement = this.getHTMLElement();
1414          var pos = RicoUtil.toViewportPosition(htmlElement);
1415
1416          this.absoluteRect = {
1417             top:    pos.y,
1418             left:   pos.x,
1419             bottom: pos.y + htmlElement.offsetHeight,
1420             right:  pos.x + htmlElement.offsetWidth
1421          };
1422       }
1423       return this.absoluteRect;
1424    },
1425
1426    activate: function() {
1427       var htmlElement = this.getHTMLElement();
1428       if (htmlElement == null  || this.showingActive)
1429          return;
1430
1431       this.showingActive = true;
1432       this.saveBackgroundColor = htmlElement.style.backgroundColor;
1433
1434       var fallbackColor = "#ffea84";
1435       var currentColor = Rico.Color.createColorFromBackground(htmlElement);
1436       if ( currentColor == null )
1437          htmlElement.style.backgroundColor = fallbackColor;
1438       else {
1439          currentColor.isBright() ? currentColor.darken(0.2) : currentColor.brighten(0.2);
1440          htmlElement.style.backgroundColor = currentColor.asHex();
1441       }
1442    },
1443
1444    deactivate: function() {
1445       var htmlElement = this.getHTMLElement();
1446       if (htmlElement == null || !this.showingActive)
1447          return;
1448
1449       htmlElement.style.backgroundColor = this.saveBackgroundColor;
1450       this.showingActive = false;
1451       this.saveBackgroundColor = null;
1452    },
1453
1454    showHover: function() {
1455       var htmlElement = this.getHTMLElement();
1456       if ( htmlElement == null || this.showingHover )
1457          return;
1458
1459       this.saveBorderWidth = htmlElement.style.borderWidth;
1460       this.saveBorderStyle = htmlElement.style.borderStyle;
1461       this.saveBorderColor = htmlElement.style.borderColor;
1462
1463       this.showingHover = true;
1464       htmlElement.style.borderWidth = "1px";
1465       htmlElement.style.borderStyle = "solid";
1466       //htmlElement.style.borderColor = "#ff9900";
1467       htmlElement.style.borderColor = "#ffff00";
1468    },
1469
1470    hideHover: function() {
1471       var htmlElement = this.getHTMLElement();
1472       if ( htmlElement == null || !this.showingHover )
1473          return;
1474
1475       htmlElement.style.borderWidth = this.saveBorderWidth;
1476       htmlElement.style.borderStyle = this.saveBorderStyle;
1477       htmlElement.style.borderColor = this.saveBorderColor;
1478       this.showingHover = false;
1479    },
1480
1481    canAccept: function(draggableObjects) {
1482       return true;
1483    },
1484
1485    accept: function(draggableObjects) {
1486       var htmlElement = this.getHTMLElement();
1487       if ( htmlElement == null )
1488          return;
1489
1490       n = draggableObjects.length;
1491       for ( var i = 0 ; i < n ; i++ )
1492       {
1493          var theGUI = draggableObjects[i].getDroppedGUI();
1494          if ( RicoUtil.getElementsComputedStyle( theGUI, "position" ) == "absolute" )
1495          {
1496             theGUI.style.position = "static";
1497             theGUI.style.top = "";
1498             theGUI.style.top = "";
1499          }
1500          htmlElement.appendChild(theGUI);
1501       }
1502    }
1503
1504  
1505 // ricoEffects.js --------------------
1506  
1507
1508 Effect.SizeAndPosition = Class.create();
1509 Effect.SizeAndPosition.prototype = {
1510
1511    initialize: function(element, x, y, w, h, duration, steps, options) {
1512       this.element = $(element);
1513       this.x = x;
1514       this.y = y;
1515       this.w = w;
1516       this.h = h;
1517       this.duration = duration;
1518       this.steps    = steps;
1519       this.options  = arguments[7] || {};
1520
1521       this.sizeAndPosition();
1522    },
1523
1524    sizeAndPosition: function() {
1525       if (this.isFinished()) {
1526          if(this.options.complete) this.options.complete(this);
1527          return;
1528       }
1529
1530       if (this.timer)
1531          clearTimeout(this.timer);
1532
1533       var stepDuration = Math.round(this.duration/this.steps) ;
1534
1535       // Get original values: x,y = top left corner;  w,h = width height
1536       var currentX = this.element.offsetLeft;
1537       var currentY = this.element.offsetTop;
1538       var currentW = this.element.offsetWidth;
1539       var currentH = this.element.offsetHeight;
1540
1541       // If values not set, or zero, we do not modify them, and take original as final as well
1542       this.x = (this.x) ? this.x : currentX;
1543       this.y = (this.y) ? this.y : currentY;
1544       this.w = (this.w) ? this.w : currentW;
1545       this.h = (this.h) ? this.h : currentH;
1546
1547       // how much do we need to modify our values for each step?
1548       var difX = this.steps >  0 ? (this.x - currentX)/this.steps : 0;
1549       var difY = this.steps >  0 ? (this.y - currentY)/this.steps : 0;
1550       var difW = this.steps >  0 ? (this.w - currentW)/this.steps : 0;
1551       var difH = this.steps >  0 ? (this.h - currentH)/this.steps : 0;
1552
1553       this.moveBy(difX, difY);
1554       this.resizeBy(difW, difH);
1555
1556       this.duration -= stepDuration;
1557       this.steps--;
1558
1559       this.timer = setTimeout(this.sizeAndPosition.bind(this), stepDuration);
1560    },
1561
1562    isFinished: function() {
1563       return this.steps <= 0;
1564    },
1565
1566    moveBy: function( difX, difY ) {
1567       var currentLeft = this.element.offsetLeft;
1568       var currentTop  = this.element.offsetTop;
1569       var intDifX     = parseInt(difX);
1570       var intDifY     = parseInt(difY);
1571
1572       var style = this.element.style;
1573       if ( intDifX != 0 )
1574          style.left = (currentLeft + intDifX) + "px";
1575       if ( intDifY != 0 )
1576          style.top  = (currentTop + intDifY) + "px";
1577    },
1578
1579    resizeBy: function( difW, difH ) {
1580       var currentWidth  = this.element.offsetWidth;
1581       var currentHeight = this.element.offsetHeight;
1582       var intDifW       = parseInt(difW);
1583       var intDifH       = parseInt(difH);
1584
1585       var style = this.element.style;
1586       if ( intDifW != 0 )
1587          style.width   = (currentWidth  + intDifW) + "px";
1588       if ( intDifH != 0 )
1589          style.height  = (currentHeight + intDifH) + "px";
1590    }
1591 }
1592
1593 Effect.Size = Class.create();
1594 Effect.Size.prototype = {
1595
1596    initialize: function(element, w, h, duration, steps, options) {
1597       new Effect.SizeAndPosition(element, null, null, w, h, duration, steps, options);
1598   }
1599 }
1600
1601 Effect.Position = Class.create();
1602 Effect.Position.prototype = {
1603
1604    initialize: function(element, x, y, duration, steps, options) {
1605       new Effect.SizeAndPosition(element, x, y, null, null, duration, steps, options);
1606   }
1607 }
1608
1609 Effect.Round = Class.create();
1610 Effect.Round.prototype = {
1611
1612    initialize: function(tagName, className, options) {
1613       var elements = document.getElementsByTagAndClassName(tagName,className);
1614       for ( var i = 0 ; i < elements.length ; i++ )
1615          Rico.Corner.round( elements[i], options );
1616    }
1617 };
1618
1619 Effect.FadeTo = Class.create();
1620 Effect.FadeTo.prototype = {
1621
1622    initialize: function( element, opacity, duration, steps, options) {
1623       this.element  = $(element);
1624       this.opacity  = opacity;
1625       this.duration = duration;
1626       this.steps    = steps;
1627       this.options  = arguments[4] || {};
1628       this.fadeTo();
1629    },
1630
1631    fadeTo: function() {
1632       if (this.isFinished()) {
1633          if(this.options.complete) this.options.complete(this);
1634          return;
1635       }
1636
1637       if (this.timer)
1638          clearTimeout(this.timer);
1639
1640       var stepDuration = Math.round(this.duration/this.steps) ;
1641       var currentOpacity = this.getElementOpacity();
1642       var delta = this.steps > 0 ? (this.opacity - currentOpacity)/this.steps : 0;
1643
1644       this.changeOpacityBy(delta);
1645       this.duration -= stepDuration;
1646       this.steps--;
1647
1648       this.timer = setTimeout(this.fadeTo.bind(this), stepDuration);
1649    },
1650
1651    changeOpacityBy: function(v) {
1652       var currentOpacity = this.getElementOpacity();
1653       var newOpacity = Math.max(0, Math.min(currentOpacity+v, 1));
1654       this.element.ricoOpacity = newOpacity;
1655
1656       this.element.style.filter = "alpha(opacity:"+Math.round(newOpacity*100)+")";
1657       this.element.style.opacity = newOpacity; /*//*/;
1658    },
1659
1660    isFinished: function() {
1661       return this.steps <= 0;
1662    },
1663
1664    getElementOpacity: function() {
1665       if ( this.element.ricoOpacity == undefined ) {
1666          var opacity;
1667          if ( this.element.currentStyle ) {
1668             opacity = this.element.currentStyle.opacity;
1669          }
1670          else if ( document.defaultView.getComputedStyle != undefined ) {
1671             var computedStyle = document.defaultView.getComputedStyle;
1672             opacity = computedStyle(this.element, null).getPropertyValue('opacity');
1673          }
1674
1675          this.element.ricoOpacity = opacity != undefined ? opacity : 1.0;
1676       }
1677
1678       return parseFloat(this.element.ricoOpacity);
1679    }
1680 }
1681
1682 Effect.AccordionSize = Class.create();
1683
1684 Effect.AccordionSize.prototype = {
1685
1686    initialize: function(e1, e2, start, end, duration, steps, options) {
1687       this.e1       = $(e1);
1688       this.e2       = $(e2);
1689       this.start    = start;
1690       this.end      = end;
1691       this.duration = duration;
1692       this.steps    = steps;
1693       this.options  = arguments[6] || {};
1694
1695       this.accordionSize();
1696    },
1697
1698    accordionSize: function() {
1699
1700       if (this.isFinished()) {
1701          // just in case there are round errors or such...
1702          this.e1.style.height = this.start + "px";
1703          this.e2.style.height = this.end + "px";
1704
1705          if(this.options.complete)
1706             this.options.complete(this);
1707          return;
1708       }
1709
1710       if (this.timer)
1711          clearTimeout(this.timer);
1712
1713       var stepDuration = Math.round(this.duration/this.steps) ;
1714
1715       var diff = this.steps > 0 ? (parseInt(this.e1.offsetHeight) - this.start)/this.steps : 0;
1716       this.resizeBy(diff);
1717
1718       this.duration -= stepDuration;
1719       this.steps--;
1720
1721       this.timer = setTimeout(this.accordionSize.bind(this), stepDuration);
1722    },
1723
1724    isFinished: function() {
1725       return this.steps <= 0;
1726    },
1727
1728    resizeBy: function(diff) {
1729       var h1Height = this.e1.offsetHeight;
1730       var h2Height = this.e2.offsetHeight;
1731       var intDiff = parseInt(diff);
1732       if ( diff != 0 ) {
1733          this.e1.style.height = (h1Height - intDiff) + "px";
1734          this.e2.style.height = (h2Height + intDiff) + "px";
1735       }
1736    }
1737
1738 };
1739  
1740  
1741 // ricoLiveGrid.js --------------------
1742  
1743
1744 // Rico.LiveGridMetaData -----------------------------------------------------
1745
1746 Rico.LiveGridMetaData = Class.create();
1747
1748 Rico.LiveGridMetaData.prototype = {
1749
1750    initialize: function( pageSize, totalRows, options ) {
1751       this.pageSize  = pageSize;
1752       this.totalRows = totalRows;
1753       this.setOptions(options);
1754       this.scrollArrowHeight = 16;
1755    },
1756
1757    setOptions: function(options) {
1758       this.options = {
1759          largeBufferSize    : 7.0,   // 7 pages
1760          smallBufferSize    : 1.0,   // 1 page
1761          nearLimitFactor    : 0.2    // 20% of buffer
1762       }.extend(options || {});
1763    },
1764
1765    getPageSize: function() {
1766       return this.pageSize;
1767    },
1768
1769    getTotalRows: function() {
1770       return this.totalRows;
1771    },
1772
1773    setTotalRows: function(n) {
1774       this.totalRows = n;
1775    },
1776
1777    getLargeBufferSize: function() {
1778       return parseInt(this.options.largeBufferSize * this.pageSize);
1779    },
1780
1781    getSmallBufferSize: function() {
1782       return parseInt(this.options.smallBufferSize * this.pageSize);
1783    },
1784
1785    getLimitTolerance: function() {
1786       return parseInt(this.getLargeBufferSize() * this.options.nearLimitFactor);
1787    },
1788
1789         getBufferSize: function(isFull) {
1790                 return isFull ? this.getLargeBufferSize() : this.getSmallBufferSize();
1791         }
1792 };
1793
1794 // Rico.LiveGridScroller -----------------------------------------------------
1795
1796 Rico.LiveGridScroller = Class.create();
1797
1798 Rico.LiveGridScroller.prototype = {
1799
1800    initialize: function(liveGrid) {
1801       this.isIE = navigator.userAgent.toLowerCase().indexOf("msie") >= 0;
1802       this.liveGrid = liveGrid;
1803       this.metaData = liveGrid.metaData;
1804       this.createScrollBar();
1805       this.scrollTimeout = null;
1806       //this.sizeIEHeaderHack();
1807       this.lastScrollPos = 0;
1808    },
1809
1810    isUnPlugged: function() {
1811       return this.scrollerDiv.onscroll == null;
1812    },
1813
1814    plugin: function() {
1815       this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this);
1816    },
1817
1818    unplug: function() {
1819       this.scrollerDiv.onscroll = null;
1820    },
1821
1822    sizeIEHeaderHack: function() {
1823       if ( !this.isIE ) return;
1824       var headerTable = $(this.liveGrid.tableId + "_header");
1825       if ( headerTable )
1826          headerTable.rows[0].cells[0].style.width =
1827             (headerTable.rows[0].cells[0].offsetWidth + 1) + "px";
1828    },
1829
1830    createScrollBar: function() {
1831       var table = this.liveGrid.table;
1832       var visibleHeight = table.offsetHeight;
1833
1834       // create the outer div...
1835       this.scrollerDiv  = document.createElement("div");
1836       var scrollerStyle = this.scrollerDiv.style;
1837       scrollerStyle.borderRight = "1px solid #ababab"; // hard coded color!!!
1838       scrollerStyle.position    = "relative";
1839       scrollerStyle.left        = this.isIE ? "-6px" : "-3px";
1840       scrollerStyle.width       = "19px";
1841       scrollerStyle.height      = visibleHeight + "px";
1842       scrollerStyle.overflow    = "auto";
1843
1844       // create the inner div...
1845       this.heightDiv = document.createElement("div");
1846       this.heightDiv.style.width  = "1px";
1847       this.heightDiv.style.height = parseInt(visibleHeight *
1848                         this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px" ;
1849       this.lineHeight =  visibleHeight/this.metaData.getPageSize();
1850
1851       this.scrollerDiv.appendChild(this.heightDiv);
1852       this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this);
1853       table.parentNode.insertBefore( this.scrollerDiv, table.nextSibling );
1854    },
1855
1856    updateSize: function() {
1857       var table = this.liveGrid.table;
1858       var visibleHeight = table.offsetHeight;
1859       this.heightDiv.style.height = parseInt(visibleHeight *
1860                                   this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px";
1861    },
1862
1863    adjustScrollTop: function() {
1864         this.unplug();
1865         var rem = this.scrollerDiv.scrollTop % this.lineHeight;
1866         if (rem != 0) {
1867          if (this.lastScrollPos < this.scrollerDiv.scrollTop)
1868             this.scrollerDiv.scrollTop = this.scrollerDiv.scrollTop + this.lineHeight -rem;
1869          else
1870             this.scrollerDiv.scrollTop = this.scrollerDiv.scrollTop - rem;
1871       }
1872       this.lastScrollPos = this.scrollerDiv.scrollTop;
1873         this.plugin();
1874    },
1875
1876    handleScroll: function() {
1877       if ( this.scrollTimeout )
1878          clearTimeout( this.scrollTimeout );
1879
1880            //this.adjustScrollTop();
1881            var contentOffset = parseInt(this.scrollerDiv.scrollTop *
1882                                    this.metaData.getTotalRows() / this.heightDiv.offsetHeight);
1883       this.liveGrid.requestContentRefresh(contentOffset);
1884       if ( this.metaData.options.onscroll )
1885          this.metaData.options.onscroll( contentOffset, this.metaData );
1886
1887       this.scrollTimeout = setTimeout( this.scrollIdle.bind(this), 1200 );
1888    },
1889
1890    scrollIdle: function() {
1891       if ( this.metaData.options.onscrollidle )
1892          this.metaData.options.onscrollidle();
1893    }
1894 };
1895
1896 // Rico.LiveGridBuffer -----------------------------------------------------
1897
1898 Rico.LiveGridBuffer = Class.create();
1899
1900 Rico.LiveGridBuffer.prototype = {
1901
1902    initialize: function(metaData) {
1903       this.startPos = 0;
1904       this.size     = 0;
1905       this.metaData = metaData;
1906       this.rows     = new Array();
1907       this.updateInProgress = false;
1908    },
1909
1910    update: function(ajaxResponse,start) {
1911
1912       this.startPos = start;
1913       this.rows = new Array();
1914
1915       var rowsElement = ajaxResponse.getElementsByTagName('rows')[0];
1916       this.updateUI = rowsElement.getAttribute("update_ui") == "true"
1917       var trs = rowsElement.getElementsByTagName("tr");
1918       for ( var i=0 ; i < trs.length; i++ ) {
1919          var row = this.rows[i] = new Array();
1920          var cells = trs[i].getElementsByTagName("td");
1921          for ( var j=0; j < cells.length ; j++ ) {
1922             var cell = cells[j];
1923             var convertSpaces = cell.getAttribute("convert_spaces") == "true";
1924             var cellContent = cell.text != undefined ? cell.text : cell.textContent;
1925             row[j] = convertSpaces ? this.convertSpaces(cellContent) : cellContent;
1926          }
1927       }
1928       this.size = trs.length;
1929    },
1930
1931         isFullP: function() {
1932            return this.metaData.pageSize != this.size;
1933         },
1934
1935         isClose: function(start) {
1936                 return (start < this.startPos + this.size + (this.size/2)) &&
1937              (start + this.size + (this.size/2) > this.startPos)
1938         },
1939
1940    isInRange: function(start, count) {
1941       return (start < this.startPos + this.size) && (start + count > this.startPos)
1942    },
1943
1944    isFullyInRange: function(position) {
1945       return (position >= this.startPos) && (position+this.metaData.getPageSize()) <= (this.startPos + this.size)
1946    },
1947
1948    isNearingTopLimit: function(position) {
1949       return position - this.startPos < this.metaData.getLimitTolerance();
1950    },
1951
1952    isNearingBottomLimit: function(position) {
1953       var myEnd     = position + this.metaData.getPageSize();
1954       var bufferEnd = this.startPos + this.size;
1955       return bufferEnd - myEnd < this.metaData.getLimitTolerance();
1956    },
1957
1958    isAtTop: function() {
1959       return this.startPos == 0;
1960    },
1961
1962    isAtBottom: function() {
1963       return this.startPos + this.size == this.metaData.getTotalRows();
1964    },
1965
1966    isNearingLimit: function(position) {
1967       return ( !this.isAtTop()    && this.isNearingTopLimit(position)) ||
1968              ( !this.isAtBottom() && this.isNearingBottomLimit(position) )
1969    },
1970
1971    getRows: function(start, count) {
1972       var begPos = start - this.startPos
1973       var endPos = begPos + count
1974
1975       // er? need more data...
1976       if ( endPos > this.size )
1977          endPos = this.size
1978
1979       var results = new Array()
1980       var index = 0;
1981       for ( var i=begPos ; i < endPos; i++ ) {
1982          results[index++] = this.rows[i]
1983       }
1984       return results
1985    },
1986
1987    convertSpaces: function(s) {
1988       return s.split(" ").join("&nbsp;");
1989    }
1990
1991 };
1992
1993 Rico.LiveGridRequest = Class.create();
1994 Rico.LiveGridRequest.prototype = {
1995    initialize: function( requestOffset, options ) {
1996                 this.requestOffset = requestOffset;
1997    }
1998 };
1999
2000 // Rico.LiveGrid -----------------------------------------------------
2001
2002 Rico.LiveGrid = Class.create();
2003
2004 Rico.LiveGrid.prototype = {
2005
2006    initialize: function( tableId, visibleRows, totalRows, url, options ) {
2007
2008       if ( options == null )
2009          options = {};
2010
2011       this.tableId     = tableId;
2012       this.table       = $(tableId);
2013       this.metaData    = new Rico.LiveGridMetaData(visibleRows, totalRows, options);
2014       this.buffer      = new Rico.LiveGridBuffer(this.metaData);
2015       this.scroller    = new Rico.LiveGridScroller(this);
2016
2017       this.lastDisplayedStartPos = 0;
2018       this.timeoutHander         = null;
2019       this.additionalParms       = options.requestParameters || [];
2020
2021       this.processingRequest = null;
2022       this.unprocessedRequest = null;
2023
2024       this.initAjax(url);
2025       if ( options.prefetchBuffer )
2026          this.fetchBuffer(0, true);
2027    },
2028
2029    setRequestParams: function() {
2030       this.additionalParms = [];
2031       for ( var i=0 ; i < arguments.length ; i++ )
2032          this.additionalParms[i] = arguments[i];
2033    },
2034
2035    setTotalRows: function( newTotalRows ) {
2036       this.metaData.setTotalRows(newTotalRows);
2037       this.scroller.updateSize();
2038    },
2039
2040    initAjax: function(url) {
2041       ajaxEngine.registerRequest( this.tableId + '_request', url );
2042       ajaxEngine.registerAjaxObject( this.tableId + '_updater', this );
2043    },
2044
2045    invokeAjax: function() {
2046    },
2047
2048    largeBufferWindowStart: function(offset) {
2049       val = offset - ( (.5 * this.metaData.getLargeBufferSize()) - (.5 * this.metaData.getPageSize()) )
2050       return Math.max(parseInt(val), 0);
2051    },
2052
2053    handleTimedOut: function() {
2054       //server did not respond in 4 seconds... assume that there could have been
2055       //an error or something, and allow requests to be processed again...
2056       this.processingRequest = null;
2057    },
2058
2059    fetchBuffer: function(offset, fullBufferp) {
2060       if (this.processingRequest) {
2061           this.unprocessedRequest = new Rico.LiveGridRequest(offset);
2062            return;
2063       }
2064
2065       var fetchSize = this.metaData.getBufferSize(fullBufferp);
2066       bufferStartPos = Math.max(0,fullBufferp ? this.largeBufferWindowStart(offset) : offset);
2067
2068       this.processingRequest = new Rico.LiveGridRequest(offset);
2069       this.processingRequest.bufferOffset = bufferStartPos;
2070
2071       var callParms = [];
2072       callParms.push(this.tableId + '_request');
2073       callParms.push('id='        + this.tableId);
2074       callParms.push('page_size=' + fetchSize);
2075       callParms.push('offset='    + bufferStartPos);
2076
2077       for( var i=0 ; i < this.additionalParms.length ; i++ )
2078          callParms.push(this.additionalParms[i]);
2079
2080       ajaxEngine.sendRequest.apply( ajaxEngine, callParms );
2081       this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), 4000 );
2082    },
2083
2084    requestContentRefresh: function(contentOffset) {
2085       if ( this.buffer && this.buffer.isFullyInRange(contentOffset) ) {
2086          this.updateContent(contentOffset);
2087          if (this.buffer.isNearingLimit(contentOffset))
2088             this.fetchBuffer(contentOffset, true);
2089       }
2090       else if (this.buffer && this.buffer.isClose(contentOffset))
2091          this.fetchBuffer(contentOffset, true);
2092       else
2093               this.fetchBuffer(contentOffset, false);
2094    },
2095
2096    ajaxUpdate: function(ajaxResponse) {
2097       try {
2098          clearTimeout( this.timeoutHandler );
2099          this.buffer = new Rico.LiveGridBuffer(this.metaData);
2100          this.buffer.update(ajaxResponse,this.processingRequest.bufferOffset);
2101          if (this.unprocessedRequest == null) {
2102             offset = this.processingRequest.requestOffset;
2103             this.updateContent (offset);
2104          }
2105          this.processingRequest = null;
2106          if (this.unprocessedRequest != null) {
2107             this.requestContentRefresh(this.unprocessedRequest.requestOffset);
2108             this.unprocessedRequest = null
2109          }
2110       }
2111       catch(err) {
2112       }
2113    },
2114
2115    updateContent: function( offset ) {
2116       this.replaceCellContents(this.buffer, offset);
2117    },
2118
2119    replaceCellContents: function(buffer, startPos) {
2120       if (startPos == this.lastDisplayedStartPos){
2121          return;
2122       }
2123
2124       this.lastDisplayedStartPos = startPos
2125       var rows = buffer.getRows(startPos, this.metaData.getPageSize());
2126       for (var i=0; i < rows.length; i++) {
2127          var row = rows[i];
2128          for (var j=0; j < row.length; j++) {
2129             this.table.rows[i].cells[j].innerHTML = rows[i][j]
2130          }
2131       }
2132    }
2133 }; 
2134  
2135 // ricoUtil.js --------------------
2136  
2137
2138 var RicoUtil = {
2139
2140    getElementsComputedStyle: function ( htmlElement, cssProperty, mozillaEquivalentCSS) {
2141       if ( arguments.length == 2 )
2142          mozillaEquivalentCSS = cssProperty;
2143
2144       var el = $(htmlElement);
2145       if ( el.currentStyle )
2146          return el.currentStyle[cssProperty];
2147       else
2148          return document.defaultView.getComputedStyle(el, null).getPropertyValue(mozillaEquivalentCSS);
2149    },
2150
2151    createXmlDocument : function() {
2152       if (document.implementation && document.implementation.createDocument) {
2153          var doc = document.implementation.createDocument("", "", null);
2154
2155          if (doc.readyState == null) {
2156             doc.readyState = 1;
2157             doc.addEventListener("load", function () {
2158                doc.readyState = 4;
2159                if (typeof doc.onreadystatechange == "function")
2160                   doc.onreadystatechange();
2161             }, false);
2162          }
2163
2164          return doc;
2165       }
2166
2167       if (window.ActiveXObject)
2168           return Try.these(
2169             function() { return new ActiveXObject('MSXML2.DomDocument')   },
2170             function() { return new ActiveXObject('Microsoft.DomDocument')},
2171             function() { return new ActiveXObject('MSXML.DomDocument')    },
2172             function() { return new ActiveXObject('MSXML3.DomDocument')   }
2173           ) || false;
2174
2175       return null;
2176    },
2177
2178    toViewportPosition: function(element) {
2179       return this._toAbsolute(element,true);
2180    },
2181
2182    toDocumentPosition: function(element) {
2183       return this._toAbsolute(element,false);
2184    },
2185
2186    /**
2187     *  Compute the elements position in terms of the window viewport
2188     *  so that it can be compared to the position of the mouse (dnd)
2189     *  This is additions of all the offsetTop,offsetLeft values up the
2190     *  offsetParent hierarchy, ...taking into account any scrollTop,
2191     *  scrollLeft values along the way...
2192     *
2193     * IE has a bug reporting a correct offsetLeft of elements within a
2194     * a relatively positioned parent!!!
2195     **/
2196    _toAbsolute: function(element,accountForDocScroll) {
2197
2198       if ( navigator.userAgent.toLowerCase().indexOf("msie") == -1 )
2199          return this._toAbsoluteMozilla(element,accountForDocScroll);
2200
2201       var x = 0;
2202       var y = 0;
2203       var parent = element;
2204       while ( parent ) {
2205
2206          var borderXOffset = 0;
2207          var borderYOffset = 0;
2208          if ( parent != element ) {
2209             var borderXOffset = parseInt(this.getElementsComputedStyle(parent, "borderLeftWidth" ));
2210             var borderYOffset = parseInt(this.getElementsComputedStyle(parent, "borderTopWidth" ));
2211             borderXOffset = isNaN(borderXOffset) ? 0 : borderXOffset;
2212             borderYOffset = isNaN(borderYOffset) ? 0 : borderYOffset;
2213          }
2214
2215          x += parent.offsetLeft - parent.scrollLeft + borderXOffset;
2216          y += parent.offsetTop - parent.scrollTop + borderYOffset;
2217          parent = parent.offsetParent;
2218       }
2219
2220       if ( accountForDocScroll ) {
2221          x -= this.docScrollLeft();
2222          y -= this.docScrollTop();
2223       }
2224
2225       return { x:x, y:y };
2226    },
2227
2228    /**
2229     *  Mozilla did not report all of the parents up the hierarchy via the
2230     *  offsetParent property that IE did.  So for the calculation of the
2231     *  offsets we use the offsetParent property, but for the calculation of
2232     *  the scrollTop/scrollLeft adjustments we navigate up via the parentNode
2233     *  property instead so as to get the scroll offsets...
2234     *
2235     **/
2236    _toAbsoluteMozilla: function(element,accountForDocScroll) {
2237       var x = 0;
2238       var y = 0;
2239       var parent = element;
2240       while ( parent ) {
2241          x += parent.offsetLeft;
2242          y += parent.offsetTop;
2243          parent = parent.offsetParent;
2244       }
2245
2246       parent = element;
2247       while ( parent &&
2248               parent != document.body &&
2249               parent != document.documentElement ) {
2250          if ( parent.scrollLeft  )
2251             x -= parent.scrollLeft;
2252          if ( parent.scrollTop )
2253             y -= parent.scrollTop;
2254          parent = parent.parentNode;
2255       }
2256
2257       if ( accountForDocScroll ) {
2258          x -= this.docScrollLeft();
2259          y -= this.docScrollTop();
2260       }
2261
2262       return { x:x, y:y };
2263    },
2264
2265    docScrollLeft: function() {
2266       if ( window.pageXOffset )
2267          return window.pageXOffset;
2268       else if ( document.documentElement && document.documentElement.scrollLeft )
2269          return document.documentElement.scrollLeft;
2270       else if ( document.body )
2271          return document.body.scrollLeft;
2272       else
2273          return 0;
2274    },
2275
2276    docScrollTop: function() {
2277       if ( window.pageYOffset )
2278          return window.pageYOffset;
2279       else if ( document.documentElement && document.documentElement.scrollTop )
2280          return document.documentElement.scrollTop;
2281       else if ( document.body )
2282          return document.body.scrollTop;
2283       else
2284          return 0;
2285    }
2286
2287 };