source: ether_statistics/web/resources/js/windows_js_1.3/samples/rico/rico_change.js @ 569

Last change on this file since 569 was 569, checked in by vmipsl, 12 years ago

Nouveau projet

File size: 90.8 KB
Line 
1/**
2  *
3  *  Copyright 2005 Sabre Airline Solutions
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
6  *  file except in compliance with the License. You may obtain a copy of the License at
7  *
8  *         http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *  Unless required by applicable law or agreed to in writing, software distributed under the
11  *  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
12  *  either express or implied. See the License for the specific language governing permissions
13  *  and limitations under the License.
14  **/
15
16
17//-------------------- rico.js
18var Rico = {
19  Version: '1.1.2',
20  prototypeVersion: parseFloat(Prototype.Version.split(".")[0] + "." + Prototype.Version.split(".")[1])
21}
22
23if((typeof Prototype=='undefined') || Rico.prototypeVersion < 1.3)
24      throw("Rico requires the Prototype JavaScript framework >= 1.3");
25
26Rico.ArrayExtensions = new Array();
27
28if (Object.prototype.extend) {
29   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend;
30}else{
31  Object.prototype.extend = function(object) {
32    return Object.extend.apply(this, [this, object]);
33  }
34  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend;
35}
36
37if (Array.prototype.push) {
38   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.push;
39}
40
41if (!Array.prototype.remove) {
42   Array.prototype.remove = function(dx) {
43      if( isNaN(dx) || dx > this.length )
44         return false;
45      for( var i=0,n=0; i<this.length; i++ )
46         if( i != dx )
47            this[n++]=this[i];
48      this.length-=1;
49   };
50  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.remove;
51}
52
53if (!Array.prototype.removeItem) {
54   Array.prototype.removeItem = function(item) {
55      for ( var i = 0 ; i < this.length ; i++ )
56         if ( this[i] == item ) {
57            this.remove(i);
58            break;
59         }
60   };
61  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.removeItem;
62}
63
64if (!Array.prototype.indices) {
65   Array.prototype.indices = function() {
66      var indexArray = new Array();
67      for ( index in this ) {
68         var ignoreThis = false;
69         for ( var i = 0 ; i < Rico.ArrayExtensions.length ; i++ ) {
70            if ( this[index] == Rico.ArrayExtensions[i] ) {
71               ignoreThis = true;
72               break;
73            }
74         }
75         if ( !ignoreThis )
76            indexArray[ indexArray.length ] = index;
77      }
78      return indexArray;
79   }
80  Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.indices;
81}
82
83// Create the loadXML method and xml getter for Mozilla
84if ( window.DOMParser &&
85          window.XMLSerializer &&
86          window.Node && Node.prototype && Node.prototype.__defineGetter__ ) {
87
88   if (!Document.prototype.loadXML) {
89      Document.prototype.loadXML = function (s) {
90         var doc2 = (new DOMParser()).parseFromString(s, "text/xml");
91         while (this.hasChildNodes())
92            this.removeChild(this.lastChild);
93
94         for (var i = 0; i < doc2.childNodes.length; i++) {
95            this.appendChild(this.importNode(doc2.childNodes[i], true));
96         }
97      };
98        }
99
100        Document.prototype.__defineGetter__( "xml",
101           function () {
102                   return (new XMLSerializer()).serializeToString(this);
103           }
104         );
105}
106
107document.getElementsByTagAndClassName = function(tagName, className) {
108  if ( tagName == null )
109     tagName = '*';
110
111  var children = document.getElementsByTagName(tagName) || document.all;
112  var elements = new Array();
113
114  if ( className == null )
115    return children;
116
117  for (var i = 0; i < children.length; i++) {
118    var child = children[i];
119    var classNames = child.className.split(' ');
120    for (var j = 0; j < classNames.length; j++) {
121      if (classNames[j] == className) {
122        elements.push(child);
123        break;
124      }
125    }
126  }
127
128  return elements;
129}
130
131
132//-------------------- ricoAccordion.js
133Rico.Accordion = Class.create();
134
135Rico.Accordion.prototype = {
136
137   initialize: function(container, options) {
138      this.container            = $(container);
139      this.lastExpandedTab      = null;
140      this.accordionTabs        = new Array();
141      this.setOptions(options);
142      this._attachBehaviors();
143      if(!container) return;
144
145      this.container.style.borderBottom = '1px solid ' + this.options.borderColor;
146      // validate onloadShowTab
147       if (this.options.onLoadShowTab >= this.accordionTabs.length)
148        this.options.onLoadShowTab = 0;
149
150      // set the initial visual state...
151      for ( var i=0 ; i < this.accordionTabs.length ; i++ )
152      {
153        if (i != this.options.onLoadShowTab){
154         this.accordionTabs[i].collapse();
155         this.accordionTabs[i].content.style.display = 'none';
156        }
157      }
158      this.lastExpandedTab = this.accordionTabs[this.options.onLoadShowTab];
159      if (this.options.panelHeight == 'auto'){
160          var tabToCheck = (this.options.onloadShowTab === 0)? 1 : 0;
161          var titleBarSize = parseInt(RicoUtil.getElementsComputedStyle(this.accordionTabs[tabToCheck].titleBar, 'height'));
162          if (isNaN(titleBarSize))
163            titleBarSize = this.accordionTabs[tabToCheck].titleBar.offsetHeight;
164         
165          var totalTitleBarSize = this.accordionTabs.length * titleBarSize;
166          var parentHeight = parseInt(RicoUtil.getElementsComputedStyle(this.container.parentNode, 'height'));
167          if (isNaN(parentHeight))
168            parentHeight = this.container.parentNode.offsetHeight;
169         
170          this.options.panelHeight = parentHeight - totalTitleBarSize-2;
171      }
172     
173      this.lastExpandedTab.content.style.height = this.options.panelHeight + "px";
174      this.lastExpandedTab.showExpanded();
175      this.lastExpandedTab.titleBar.style.fontWeight = this.options.expandedFontWeight;
176
177   },
178
179   setOptions: function(options) {
180      this.options = {
181         expandedBg          : '#545985',
182         hoverBg             : '#63699c',
183         collapsedBg         : '#6b79a5',
184         expandedTextColor   : '#ffffff',
185         expandedFontWeight  : 'bold',
186         hoverTextColor      : '#ffffff',
187         collapsedTextColor  : '#ced7ef',
188         collapsedFontWeight : 'normal',
189         hoverTextColor      : '#ffffff',
190         borderColor         : '#ffffff',
191         panelHeight         : 200,
192         onHideTab           : null,
193         onShowTab           : null,
194         onLoadShowTab       : 0
195      }
196      Object.extend(this.options, options || {});
197   },
198
199   showTabByIndex: function( anIndex, animate ) {
200      var doAnimate = arguments.length == 1 ? true : animate;
201      this.showTab( this.accordionTabs[anIndex], doAnimate );
202   },
203
204   showTab: function( accordionTab, animate ) {
205     if ( this.lastExpandedTab == accordionTab )
206        return;
207
208      var doAnimate = arguments.length == 1 ? true : animate;
209
210      if ( this.options.onHideTab )
211         this.options.onHideTab(this.lastExpandedTab);
212
213      this.lastExpandedTab.showCollapsed(); 
214      var accordion = this;
215      var lastExpandedTab = this.lastExpandedTab;
216
217      this.lastExpandedTab.content.style.height = (this.options.panelHeight - 1) + 'px';
218      accordionTab.content.style.display = '';
219
220      accordionTab.titleBar.style.fontWeight = this.options.expandedFontWeight;
221
222      if ( doAnimate ) {
223         new Rico.Effect.AccordionSize( this.lastExpandedTab.content,
224                                   accordionTab.content,
225                                   1,
226                                   this.options.panelHeight,
227                                   100, 10,
228                                   { complete: function() {accordion.showTabDone(lastExpandedTab)} } );
229         this.lastExpandedTab = accordionTab;
230      }
231      else {
232         this.lastExpandedTab.content.style.height = "1px";
233         accordionTab.content.style.height = this.options.panelHeight + "px";
234         this.lastExpandedTab = accordionTab;
235         this.showTabDone(lastExpandedTab);
236      }
237   },
238
239   showTabDone: function(collapsedTab) {
240      collapsedTab.content.style.display = 'none';
241      this.lastExpandedTab.showExpanded();
242      if ( this.options.onShowTab )
243         this.options.onShowTab(this.lastExpandedTab);
244   },
245
246   _attachBehaviors: function() {
247      var panels = this._getDirectChildrenByTag(this.container, 'DIV');
248      for ( var i = 0 ; i < panels.length ; i++ ) {
249
250         var tabChildren = this._getDirectChildrenByTag(panels[i],'DIV');
251         if ( tabChildren.length != 2 )
252            continue; // unexpected
253
254         var tabTitleBar   = tabChildren[0];
255         var tabContentBox = tabChildren[1];
256         this.accordionTabs.push( new Rico.Accordion.Tab(this,tabTitleBar,tabContentBox) );
257      }
258   },
259
260   _getDirectChildrenByTag: function(e, tagName) {
261      var kids = new Array();
262      var allKids = e.childNodes;
263      for( var i = 0 ; i < allKids.length ; i++ )
264         if ( allKids[i] && allKids[i].tagName && allKids[i].tagName == tagName )
265            kids.push(allKids[i]);
266      return kids;
267   }
268
269};
270
271Rico.Accordion.Tab = Class.create();
272
273Rico.Accordion.Tab.prototype = {
274
275   initialize: function(accordion, titleBar, content) {
276      this.accordion = accordion;
277      this.titleBar  = titleBar;
278      this.content   = content;
279      this._attachBehaviors();
280   },
281
282   collapse: function() {
283      this.showCollapsed();
284      this.content.style.height = "1px";
285   },
286
287   showCollapsed: function() {
288      this.expanded = false;
289      this.titleBar.style.backgroundColor = this.accordion.options.collapsedBg;
290      this.titleBar.style.color           = this.accordion.options.collapsedTextColor;
291      this.titleBar.style.fontWeight      = this.accordion.options.collapsedFontWeight;
292      this.content.style.overflow = "hidden";
293   },
294
295   showExpanded: function() {
296      this.expanded = true;
297      this.titleBar.style.backgroundColor = this.accordion.options.expandedBg;
298      this.titleBar.style.color           = this.accordion.options.expandedTextColor;
299      this.content.style.overflow         = "auto";
300   },
301
302   titleBarClicked: function(e) {
303      if ( this.accordion.lastExpandedTab == this )
304         return;
305      this.accordion.showTab(this);
306   },
307
308   hover: function(e) {
309                this.titleBar.style.backgroundColor = this.accordion.options.hoverBg;
310                this.titleBar.style.color           = this.accordion.options.hoverTextColor;
311   },
312
313   unhover: function(e) {
314      if ( this.expanded ) {
315         this.titleBar.style.backgroundColor = this.accordion.options.expandedBg;
316         this.titleBar.style.color           = this.accordion.options.expandedTextColor;
317      }
318      else {
319         this.titleBar.style.backgroundColor = this.accordion.options.collapsedBg;
320         this.titleBar.style.color           = this.accordion.options.collapsedTextColor;
321      }
322   },
323
324   _attachBehaviors: function() {
325      this.content.style.border = "1px solid " + this.accordion.options.borderColor;
326      this.content.style.borderTopWidth    = "0px";
327      this.content.style.borderBottomWidth = "0px";
328      this.content.style.margin            = "0px";
329
330      this.titleBar.onclick     = this.titleBarClicked.bindAsEventListener(this);
331      this.titleBar.onmouseover = this.hover.bindAsEventListener(this);
332      this.titleBar.onmouseout  = this.unhover.bindAsEventListener(this);
333   }
334
335};
336
337
338//-------------------- ricoAjaxEngine.js
339Rico.AjaxEngine = Class.create();
340
341Rico.AjaxEngine.prototype = {
342
343   initialize: function() {
344      this.ajaxElements = new Array();
345      this.ajaxObjects  = new Array();
346      this.requestURLS  = new Array();
347      this.options = {};
348   },
349
350   registerAjaxElement: function( anId, anElement ) {
351      if ( !anElement )
352         anElement = $(anId);
353      this.ajaxElements[anId] = anElement;
354   },
355
356   registerAjaxObject: function( anId, anObject ) {
357      this.ajaxObjects[anId] = anObject;
358   },
359
360   registerRequest: function (requestLogicalName, requestURL) {
361      this.requestURLS[requestLogicalName] = requestURL;
362   },
363
364   sendRequest: function(requestName, options) {
365      // Allow for backwards Compatibility
366      if ( arguments.length >= 2 )
367       if (typeof arguments[1] == 'string')
368         options = {parameters: this._createQueryString(arguments, 1)};
369      this.sendRequestWithData(requestName, null, options);
370   },
371
372   sendRequestWithData: function(requestName, xmlDocument, options) {
373      var requestURL = this.requestURLS[requestName];
374      if ( requestURL == null )
375         return;
376
377      // Allow for backwards Compatibility
378      if ( arguments.length >= 3 )
379        if (typeof arguments[2] == 'string')
380          options.parameters = this._createQueryString(arguments, 2);
381
382      new Ajax.Request(requestURL, this._requestOptions(options,xmlDocument));
383   },
384
385   sendRequestAndUpdate: function(requestName,container,options) {
386      // Allow for backwards Compatibility
387      if ( arguments.length >= 3 )
388        if (typeof arguments[2] == 'string')
389          options.parameters = this._createQueryString(arguments, 2);
390
391      this.sendRequestWithDataAndUpdate(requestName, null, container, options);
392   },
393
394   sendRequestWithDataAndUpdate: function(requestName,xmlDocument,container,options) {
395      var requestURL = this.requestURLS[requestName];
396      if ( requestURL == null )
397         return;
398
399      // Allow for backwards Compatibility
400      if ( arguments.length >= 4 )
401        if (typeof arguments[3] == 'string')
402          options.parameters = this._createQueryString(arguments, 3);
403
404      var updaterOptions = this._requestOptions(options,xmlDocument);
405
406      new Ajax.Updater(container, requestURL, updaterOptions);
407   },
408
409   // Private -- not part of intended engine API --------------------------------------------------------------------
410
411   _requestOptions: function(options,xmlDoc) {
412      var requestHeaders = ['X-Rico-Version', Rico.Version ];
413      var sendMethod = 'post';
414      if ( xmlDoc == null )
415        if (Rico.prototypeVersion < 1.4)
416        requestHeaders.push( 'Content-type', 'text/xml' );
417      else
418          sendMethod = 'get';
419      (!options) ? options = {} : '';
420
421      if (!options._RicoOptionsProcessed){
422      // Check and keep any user onComplete functions
423        if (options.onComplete)
424             options.onRicoComplete = options.onComplete;
425        // Fix onComplete
426        if (options.overrideOnComplete)
427          options.onComplete = options.overrideOnComplete;
428        else
429          options.onComplete = this._onRequestComplete.bind(this);
430        options._RicoOptionsProcessed = true;
431      }
432
433     // Set the default options and extend with any user options
434     this.options = {
435                     requestHeaders: requestHeaders,
436                     parameters:     options.parameters,
437                     postBody:       xmlDoc,
438                     method:         sendMethod,
439                     onComplete:     options.onComplete
440                    };
441     // Set any user options:
442     Object.extend(this.options, options);
443     return this.options;
444   },
445
446   _createQueryString: function( theArgs, offset ) {
447      var queryString = ""
448      for ( var i = offset ; i < theArgs.length ; i++ ) {
449          if ( i != offset )
450            queryString += "&";
451
452          var anArg = theArgs[i];
453
454          if ( anArg.name != undefined && anArg.value != undefined ) {
455            queryString += anArg.name +  "=" + escape(anArg.value);
456          }
457          else {
458             var ePos  = anArg.indexOf('=');
459             var argName  = anArg.substring( 0, ePos );
460             var argValue = anArg.substring( ePos + 1 );
461             queryString += argName + "=" + escape(argValue);
462          }
463      }
464      return queryString;
465   },
466
467   _onRequestComplete : function(request) {
468      if(!request)
469          return;
470      // User can set an onFailure option - which will be called by prototype
471      if (request.status != 200)
472        return;
473
474      var response = request.responseXML.getElementsByTagName("ajax-response");
475      if (response == null || response.length != 1)
476         return;
477      this._processAjaxResponse( response[0].childNodes );
478     
479      // Check if user has set a onComplete function
480      var onRicoComplete = this.options.onRicoComplete;
481      if (onRicoComplete != null)
482          onRicoComplete();
483   },
484
485   _processAjaxResponse: function( xmlResponseElements ) {
486      for ( var i = 0 ; i < xmlResponseElements.length ; i++ ) {
487         var responseElement = xmlResponseElements[i];
488
489         // only process nodes of type element.....
490         if ( responseElement.nodeType != 1 )
491            continue;
492
493         var responseType = responseElement.getAttribute("type");
494         var responseId   = responseElement.getAttribute("id");
495
496         if ( responseType == "object" )
497            this._processAjaxObjectUpdate( this.ajaxObjects[ responseId ], responseElement );
498         else if ( responseType == "element" )
499            this._processAjaxElementUpdate( this.ajaxElements[ responseId ], responseElement );
500         else
501            alert('unrecognized AjaxResponse type : ' + responseType );
502      }
503   },
504
505   _processAjaxObjectUpdate: function( ajaxObject, responseElement ) {
506      ajaxObject.ajaxUpdate( responseElement );
507   },
508
509   _processAjaxElementUpdate: function( ajaxElement, responseElement ) {
510      ajaxElement.innerHTML = RicoUtil.getContentAsString(responseElement);
511   }
512
513}
514
515var ajaxEngine = new Rico.AjaxEngine();
516
517
518//-------------------- ricoColor.js
519Rico.Color = Class.create();
520
521Rico.Color.prototype = {
522
523   initialize: function(red, green, blue) {
524      this.rgb = { r: red, g : green, b : blue };
525   },
526
527   setRed: function(r) {
528      this.rgb.r = r;
529   },
530
531   setGreen: function(g) {
532      this.rgb.g = g;
533   },
534
535   setBlue: function(b) {
536      this.rgb.b = b;
537   },
538
539   setHue: function(h) {
540
541      // get an HSB model, and set the new hue...
542      var hsb = this.asHSB();
543      hsb.h = h;
544
545      // convert back to RGB...
546      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
547   },
548
549   setSaturation: function(s) {
550      // get an HSB model, and set the new hue...
551      var hsb = this.asHSB();
552      hsb.s = s;
553
554      // convert back to RGB and set values...
555      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, hsb.b);
556   },
557
558   setBrightness: function(b) {
559      // get an HSB model, and set the new hue...
560      var hsb = this.asHSB();
561      hsb.b = b;
562
563      // convert back to RGB and set values...
564      this.rgb = Rico.Color.HSBtoRGB( hsb.h, hsb.s, hsb.b );
565   },
566
567   darken: function(percent) {
568      var hsb  = this.asHSB();
569      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.max(hsb.b - percent,0));
570   },
571
572   brighten: function(percent) {
573      var hsb  = this.asHSB();
574      this.rgb = Rico.Color.HSBtoRGB(hsb.h, hsb.s, Math.min(hsb.b + percent,1));
575   },
576
577   blend: function(other) {
578      this.rgb.r = Math.floor((this.rgb.r + other.rgb.r)/2);
579      this.rgb.g = Math.floor((this.rgb.g + other.rgb.g)/2);
580      this.rgb.b = Math.floor((this.rgb.b + other.rgb.b)/2);
581   },
582
583   isBright: function() {
584      var hsb = this.asHSB();
585      return this.asHSB().b > 0.5;
586   },
587
588   isDark: function() {
589      return ! this.isBright();
590   },
591
592   asRGB: function() {
593      return "rgb(" + this.rgb.r + "," + this.rgb.g + "," + this.rgb.b + ")";
594   },
595
596   asHex: function() {
597      return "#" + this.rgb.r.toColorPart() + this.rgb.g.toColorPart() + this.rgb.b.toColorPart();
598   },
599
600   asHSB: function() {
601      return Rico.Color.RGBtoHSB(this.rgb.r, this.rgb.g, this.rgb.b);
602   },
603
604   toString: function() {
605      return this.asHex();
606   }
607
608};
609
610Rico.Color.createFromHex = function(hexCode) {
611  if(hexCode.length==4) {
612    var shortHexCode = hexCode; 
613    var hexCode = '#';
614    for(var i=1;i<4;i++) hexCode += (shortHexCode.charAt(i) + 
615shortHexCode.charAt(i));
616  }
617   if ( hexCode.indexOf('#') == 0 )
618      hexCode = hexCode.substring(1);
619   var red   = hexCode.substring(0,2);
620   var green = hexCode.substring(2,4);
621   var blue  = hexCode.substring(4,6);
622   return new Rico.Color( parseInt(red,16), parseInt(green,16), parseInt(blue,16) );
623}
624
625/**
626 * Factory method for creating a color from the background of
627 * an HTML element.
628 */
629Rico.Color.createColorFromBackground = function(elem) {
630
631   var actualColor = RicoUtil.getElementsComputedStyle($(elem), "backgroundColor", "background-color");
632
633   if ( actualColor == "transparent" && elem.parentNode )
634      return Rico.Color.createColorFromBackground(elem.parentNode);
635
636   if ( actualColor == null )
637      return new Rico.Color(255,255,255);
638
639   if ( actualColor.indexOf("rgb(") == 0 ) {
640      var colors = actualColor.substring(4, actualColor.length - 1 );
641      var colorArray = colors.split(",");
642      return new Rico.Color( parseInt( colorArray[0] ),
643                            parseInt( colorArray[1] ),
644                            parseInt( colorArray[2] )  );
645
646   }
647   else if ( actualColor.indexOf("#") == 0 ) {
648      return Rico.Color.createFromHex(actualColor);
649   }
650   else
651      return new Rico.Color(255,255,255);
652}
653
654Rico.Color.HSBtoRGB = function(hue, saturation, brightness) {
655
656   var red   = 0;
657        var green = 0;
658        var blue  = 0;
659
660   if (saturation == 0) {
661      red = parseInt(brightness * 255.0 + 0.5);
662           green = red;
663           blue = red;
664        }
665        else {
666      var h = (hue - Math.floor(hue)) * 6.0;
667      var f = h - Math.floor(h);
668      var p = brightness * (1.0 - saturation);
669      var q = brightness * (1.0 - saturation * f);
670      var t = brightness * (1.0 - (saturation * (1.0 - f)));
671
672      switch (parseInt(h)) {
673         case 0:
674            red   = (brightness * 255.0 + 0.5);
675            green = (t * 255.0 + 0.5);
676            blue  = (p * 255.0 + 0.5);
677            break;
678         case 1:
679            red   = (q * 255.0 + 0.5);
680            green = (brightness * 255.0 + 0.5);
681            blue  = (p * 255.0 + 0.5);
682            break;
683         case 2:
684            red   = (p * 255.0 + 0.5);
685            green = (brightness * 255.0 + 0.5);
686            blue  = (t * 255.0 + 0.5);
687            break;
688         case 3:
689            red   = (p * 255.0 + 0.5);
690            green = (q * 255.0 + 0.5);
691            blue  = (brightness * 255.0 + 0.5);
692            break;
693         case 4:
694            red   = (t * 255.0 + 0.5);
695            green = (p * 255.0 + 0.5);
696            blue  = (brightness * 255.0 + 0.5);
697            break;
698          case 5:
699            red   = (brightness * 255.0 + 0.5);
700            green = (p * 255.0 + 0.5);
701            blue  = (q * 255.0 + 0.5);
702            break;
703            }
704        }
705
706   return { r : parseInt(red), g : parseInt(green) , b : parseInt(blue) };
707}
708
709Rico.Color.RGBtoHSB = function(r, g, b) {
710
711   var hue;
712   var saturation;
713   var brightness;
714
715   var cmax = (r > g) ? r : g;
716   if (b > cmax)
717      cmax = b;
718
719   var cmin = (r < g) ? r : g;
720   if (b < cmin)
721      cmin = b;
722
723   brightness = cmax / 255.0;
724   if (cmax != 0)
725      saturation = (cmax - cmin)/cmax;
726   else
727      saturation = 0;
728
729   if (saturation == 0)
730      hue = 0;
731   else {
732      var redc   = (cmax - r)/(cmax - cmin);
733        var greenc = (cmax - g)/(cmax - cmin);
734        var bluec  = (cmax - b)/(cmax - cmin);
735
736        if (r == cmax)
737           hue = bluec - greenc;
738        else if (g == cmax)
739           hue = 2.0 + redc - bluec;
740      else
741           hue = 4.0 + greenc - redc;
742
743        hue = hue / 6.0;
744        if (hue < 0)
745           hue = hue + 1.0;
746   }
747
748   return { h : hue, s : saturation, b : brightness };
749}
750
751
752//-------------------- ricoCorner.js
753Rico.Corner = {
754
755   round: function(e, options) {
756      var e = $(e);
757      this._setOptions(options);
758
759      var color = this.options.color;
760      if ( this.options.color == "fromElement" )
761         color = this._background(e);
762
763      var bgColor = this.options.bgColor;
764      if ( this.options.bgColor == "fromParent" )
765         bgColor = this._background(e.offsetParent);
766
767      this._roundCornersImpl(e, color, bgColor);
768   },
769
770   _roundCornersImpl: function(e, color, bgColor) {
771      if(this.options.border)
772         this._renderBorder(e,bgColor);
773      if(this._isTopRounded())
774         this._roundTopCorners(e,color,bgColor);
775      if(this._isBottomRounded())
776         this._roundBottomCorners(e,color,bgColor);
777   },
778
779   _renderBorder: function(el,bgColor) {
780      var borderValue = "1px solid " + this._borderColor(bgColor);
781      var borderL = "border-left: "  + borderValue;
782      var borderR = "border-right: " + borderValue;
783      var style   = "style='" + borderL + ";" + borderR +  "'";
784      el.innerHTML = "<div " + style + ">" + el.innerHTML + "</div>"
785   },
786
787   _roundTopCorners: function(el, color, bgColor) {
788      var corner = this._createCorner(bgColor);
789      for(var i=0 ; i < this.options.numSlices ; i++ )
790         corner.appendChild(this._createCornerSlice(color,bgColor,i,"top"));
791      el.style.paddingTop = 0;
792      el.insertBefore(corner,el.firstChild);
793   },
794
795   _roundBottomCorners: function(el, color, bgColor) {
796      var corner = this._createCorner(bgColor);
797      for(var i=(this.options.numSlices-1) ; i >= 0 ; i-- )
798         corner.appendChild(this._createCornerSlice(color,bgColor,i,"bottom"));
799      el.style.paddingBottom = 0;
800      el.appendChild(corner);
801   },
802
803   _createCorner: function(bgColor) {
804      var corner = document.createElement("div");
805      corner.style.backgroundColor = (this._isTransparent() ? "transparent" : bgColor);
806      return corner;
807   },
808
809   _createCornerSlice: function(color,bgColor, n, position) {
810      var slice = document.createElement("span");
811
812      var inStyle = slice.style;
813      inStyle.backgroundColor = color;
814      inStyle.display  = "block";
815      inStyle.height   = "1px";
816      inStyle.overflow = "hidden";
817      inStyle.fontSize = "1px";
818
819      var borderColor = this._borderColor(color,bgColor);
820      if ( this.options.border && n == 0 ) {
821         inStyle.borderTopStyle    = "solid";
822         inStyle.borderTopWidth    = "1px";
823         inStyle.borderLeftWidth   = "0px";
824         inStyle.borderRightWidth  = "0px";
825         inStyle.borderBottomWidth = "0px";
826         inStyle.height            = "0px"; // assumes css compliant box model
827         inStyle.borderColor       = borderColor;
828      }
829      else if(borderColor) {
830         inStyle.borderColor = borderColor;
831         inStyle.borderStyle = "solid";
832         inStyle.borderWidth = "0px 1px";
833      }
834
835      if ( !this.options.compact && (n == (this.options.numSlices-1)) )
836         inStyle.height = "2px";
837
838      this._setMargin(slice, n, position);
839      this._setBorder(slice, n, position);
840      return slice;
841   },
842
843   _setOptions: function(options) {
844      this.options = {
845         corners : "all",
846         color   : "fromElement",
847         bgColor : "fromParent",
848         blend   : true,
849         border  : false,
850         compact : false
851      }
852      Object.extend(this.options, options || {});
853
854      this.options.numSlices = this.options.compact ? 2 : 4;
855      if ( this._isTransparent() )
856         this.options.blend = false;
857   },
858
859   _whichSideTop: function() {
860      if ( this._hasString(this.options.corners, "all", "top") )
861         return "";
862
863      if ( this.options.corners.indexOf("tl") >= 0 && this.options.corners.indexOf("tr") >= 0 )
864         return "";
865
866      if (this.options.corners.indexOf("tl") >= 0)
867         return "left";
868      else if (this.options.corners.indexOf("tr") >= 0)
869          return "right";
870      return "";
871   },
872
873   _whichSideBottom: function() {
874      if ( this._hasString(this.options.corners, "all", "bottom") )
875         return "";
876
877      if ( this.options.corners.indexOf("bl")>=0 && this.options.corners.indexOf("br")>=0 )
878         return "";
879
880      if(this.options.corners.indexOf("bl") >=0)
881         return "left";
882      else if(this.options.corners.indexOf("br")>=0)
883         return "right";
884      return "";
885   },
886
887   _borderColor : function(color,bgColor) {
888      if ( color == "transparent" )
889         return bgColor;
890      else if ( this.options.border )
891         return this.options.border;
892      else if ( this.options.blend )
893         return this._blend( bgColor, color );
894      else
895         return "";
896   },
897
898
899   _setMargin: function(el, n, corners) {
900      var marginSize = this._marginSize(n);
901      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
902
903      if ( whichSide == "left" ) {
904         el.style.marginLeft = marginSize + "px"; el.style.marginRight = "0px";
905      }
906      else if ( whichSide == "right" ) {
907         el.style.marginRight = marginSize + "px"; el.style.marginLeft  = "0px";
908      }
909      else {
910         el.style.marginLeft = marginSize + "px"; el.style.marginRight = marginSize + "px";
911      }
912   },
913
914   _setBorder: function(el,n,corners) {
915      var borderSize = this._borderSize(n);
916      var whichSide = corners == "top" ? this._whichSideTop() : this._whichSideBottom();
917      if ( whichSide == "left" ) {
918         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = "0px";
919      }
920      else if ( whichSide == "right" ) {
921         el.style.borderRightWidth = borderSize + "px"; el.style.borderLeftWidth  = "0px";
922      }
923      else {
924         el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
925      }
926      if (this.options.border != false)
927        el.style.borderLeftWidth = borderSize + "px"; el.style.borderRightWidth = borderSize + "px";
928   },
929
930   _marginSize: function(n) {
931      if ( this._isTransparent() )
932         return 0;
933
934      var marginSizes          = [ 5, 3, 2, 1 ];
935      var blendedMarginSizes   = [ 3, 2, 1, 0 ];
936      var compactMarginSizes   = [ 2, 1 ];
937      var smBlendedMarginSizes = [ 1, 0 ];
938
939      if ( this.options.compact && this.options.blend )
940         return smBlendedMarginSizes[n];
941      else if ( this.options.compact )
942         return compactMarginSizes[n];
943      else if ( this.options.blend )
944         return blendedMarginSizes[n];
945      else
946         return marginSizes[n];
947   },
948
949   _borderSize: function(n) {
950      var transparentBorderSizes = [ 5, 3, 2, 1 ];
951      var blendedBorderSizes     = [ 2, 1, 1, 1 ];
952      var compactBorderSizes     = [ 1, 0 ];
953      var actualBorderSizes      = [ 0, 2, 0, 0 ];
954
955      if ( this.options.compact && (this.options.blend || this._isTransparent()) )
956         return 1;
957      else if ( this.options.compact )
958         return compactBorderSizes[n];
959      else if ( this.options.blend )
960         return blendedBorderSizes[n];
961      else if ( this.options.border )
962         return actualBorderSizes[n];
963      else if ( this._isTransparent() )
964         return transparentBorderSizes[n];
965      return 0;
966   },
967
968   _hasString: function(str) { for(var i=1 ; i<arguments.length ; i++) if (str.indexOf(arguments[i]) >= 0) return true; return false; },
969   _blend: function(c1, c2) { var cc1 = Rico.Color.createFromHex(c1); cc1.blend(Rico.Color.createFromHex(c2)); return cc1; },
970   _background: function(el) { try { return Rico.Color.createColorFromBackground(el).asHex(); } catch(err) { return "#ffffff"; } },
971   _isTransparent: function() { return this.options.color == "transparent"; },
972   _isTopRounded: function() { return this._hasString(this.options.corners, "all", "top", "tl", "tr"); },
973   _isBottomRounded: function() { return this._hasString(this.options.corners, "all", "bottom", "bl", "br"); },
974   _hasSingleTextChild: function(el) { return el.childNodes.length == 1 && el.childNodes[0].nodeType == 3; }
975}
976
977
978//-------------------- ricoDragAndDrop.js
979Rico.DragAndDrop = Class.create();
980
981Rico.DragAndDrop.prototype = {
982
983   initialize: function() {
984      this.dropZones                = new Array();
985      this.draggables               = new Array();
986      this.currentDragObjects       = new Array();
987      this.dragElement              = null;
988      this.lastSelectedDraggable    = null;
989      this.currentDragObjectVisible = false;
990      this.interestedInMotionEvents = false;
991      this._mouseDown = this._mouseDownHandler.bindAsEventListener(this);
992      this._mouseMove = this._mouseMoveHandler.bindAsEventListener(this);
993      this._mouseUp = this._mouseUpHandler.bindAsEventListener(this);
994   },
995
996   registerDropZone: function(aDropZone) {
997      this.dropZones[ this.dropZones.length ] = aDropZone;
998   },
999
1000   deregisterDropZone: function(aDropZone) {
1001      var newDropZones = new Array();
1002      var j = 0;
1003      for ( var i = 0 ; i < this.dropZones.length ; i++ ) {
1004         if ( this.dropZones[i] != aDropZone )
1005            newDropZones[j++] = this.dropZones[i];
1006      }
1007
1008      this.dropZones = newDropZones;
1009   },
1010
1011   clearDropZones: function() {
1012      this.dropZones = new Array();
1013   },
1014
1015   registerDraggable: function( aDraggable ) {
1016      this.draggables[ this.draggables.length ] = aDraggable;
1017      this._addMouseDownHandler( aDraggable );
1018   },
1019
1020   clearSelection: function() {
1021      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
1022         this.currentDragObjects[i].deselect();
1023      this.currentDragObjects = new Array();
1024      this.lastSelectedDraggable = null;
1025   },
1026
1027   hasSelection: function() {
1028      return this.currentDragObjects.length > 0;
1029   },
1030
1031   setStartDragFromElement: function( e, mouseDownElement ) {
1032      this.origPos = RicoUtil.toDocumentPosition(mouseDownElement);
1033      this.startx = e.screenX - this.origPos.x
1034      this.starty = e.screenY - this.origPos.y
1035      //this.startComponentX = e.layerX ? e.layerX : e.offsetX;
1036      //this.startComponentY = e.layerY ? e.layerY : e.offsetY;
1037      //this.adjustedForDraggableSize = false;
1038
1039      this.interestedInMotionEvents = this.hasSelection();
1040      this._terminateEvent(e);
1041   },
1042
1043   updateSelection: function( draggable, extendSelection ) {
1044      if ( ! extendSelection )
1045         this.clearSelection();
1046
1047      if ( draggable.isSelected() ) {
1048         this.currentDragObjects.removeItem(draggable);
1049         draggable.deselect();
1050         if ( draggable == this.lastSelectedDraggable )
1051            this.lastSelectedDraggable = null;
1052      }
1053      else {
1054         this.currentDragObjects[ this.currentDragObjects.length ] = draggable;
1055         draggable.select();
1056         this.lastSelectedDraggable = draggable;
1057      }
1058   },
1059
1060   _mouseDownHandler: function(e) {
1061      if ( arguments.length == 0 )
1062         e = event;
1063
1064      // if not button 1 ignore it...
1065      var nsEvent = e.which != undefined;
1066      if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
1067         return;
1068
1069      var eventTarget      = e.target ? e.target : e.srcElement;
1070      var draggableObject  = eventTarget.draggable;
1071
1072      var candidate = eventTarget;
1073      while (draggableObject == null && candidate.parentNode) {
1074         candidate = candidate.parentNode;
1075         draggableObject = candidate.draggable;
1076      }
1077   
1078      if ( draggableObject == null )
1079         return;
1080
1081      this.updateSelection( draggableObject, e.ctrlKey );
1082
1083      // clear the drop zones postion cache...
1084      if ( this.hasSelection() )
1085         for ( var i = 0 ; i < this.dropZones.length ; i++ )
1086            this.dropZones[i].clearPositionCache();
1087
1088      this.setStartDragFromElement( e, draggableObject.getMouseDownHTMLElement() );
1089   },
1090
1091
1092   _mouseMoveHandler: function(e) {
1093      var nsEvent = e.which != undefined;
1094      if ( !this.interestedInMotionEvents ) {
1095         //this._terminateEvent(e);
1096         return;
1097      }
1098
1099      if ( ! this.hasSelection() )
1100         return;
1101
1102      if ( ! this.currentDragObjectVisible )
1103         this._startDrag(e);
1104
1105      if ( !this.activatedDropZones )
1106         this._activateRegisteredDropZones();
1107
1108      //if ( !this.adjustedForDraggableSize )
1109      //   this._adjustForDraggableSize(e);
1110
1111      this._updateDraggableLocation(e);
1112      this._updateDropZonesHover(e);
1113
1114      this._terminateEvent(e);
1115   },
1116
1117   _makeDraggableObjectVisible: function(e)
1118   {
1119      if ( !this.hasSelection() )
1120         return;
1121
1122      var dragElement;
1123      if ( this.currentDragObjects.length > 1 )
1124         dragElement = this.currentDragObjects[0].getMultiObjectDragGUI(this.currentDragObjects);
1125      else
1126         dragElement = this.currentDragObjects[0].getSingleObjectDragGUI();
1127
1128      // go ahead and absolute position it...
1129      if ( RicoUtil.getElementsComputedStyle(dragElement, "position")  != "absolute" )
1130         dragElement.style.position = "absolute";
1131
1132      // need to parent him into the document...
1133      if ( dragElement.parentNode == null || dragElement.parentNode.nodeType == 11 )
1134         document.body.appendChild(dragElement);
1135
1136      this.dragElement = dragElement;
1137      this._updateDraggableLocation(e);
1138
1139      this.currentDragObjectVisible = true;
1140   },
1141
1142   /**
1143   _adjustForDraggableSize: function(e) {
1144      var dragElementWidth  = this.dragElement.offsetWidth;
1145      var dragElementHeight = this.dragElement.offsetHeight;
1146      if ( this.startComponentX > dragElementWidth )
1147         this.startx -= this.startComponentX - dragElementWidth + 2;
1148      if ( e.offsetY ) {
1149         if ( this.startComponentY > dragElementHeight )
1150            this.starty -= this.startComponentY - dragElementHeight + 2;
1151      }
1152      this.adjustedForDraggableSize = true;
1153   },
1154   **/
1155
1156   _leftOffset: function(e) {
1157           return e.offsetX ? document.body.scrollLeft : 0
1158        },
1159
1160   _topOffset: function(e) {
1161           return e.offsetY ? document.body.scrollTop:0
1162        },
1163
1164               
1165   _updateDraggableLocation: function(e) {
1166      var dragObjectStyle = this.dragElement.style;
1167      dragObjectStyle.left = (e.screenX + this._leftOffset(e) - this.startx) + "px"
1168      dragObjectStyle.top  = (e.screenY + this._topOffset(e) - this.starty) + "px";
1169   },
1170
1171   _updateDropZonesHover: function(e) {
1172      var n = this.dropZones.length;
1173      for ( var i = 0 ; i < n ; i++ ) {
1174         if ( ! this._mousePointInDropZone( e, this.dropZones[i] ) )
1175            this.dropZones[i].hideHover();
1176      }
1177
1178      for ( var i = 0 ; i < n ; i++ ) {
1179         if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
1180            if ( this.dropZones[i].canAccept(this.currentDragObjects) )
1181               this.dropZones[i].showHover();
1182         }
1183      }
1184   },
1185
1186   _startDrag: function(e) {
1187      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
1188         this.currentDragObjects[i].startDrag();
1189
1190      this._makeDraggableObjectVisible(e);
1191   },
1192
1193   _mouseUpHandler: function(e) {
1194      if ( ! this.hasSelection() )
1195         return;
1196
1197      var nsEvent = e.which != undefined;
1198      if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
1199         return;
1200
1201      this.interestedInMotionEvents = false;
1202
1203      if ( this.dragElement == null ) {
1204         this._terminateEvent(e);
1205         return;
1206      }
1207
1208      if ( this._placeDraggableInDropZone(e) )
1209         this._completeDropOperation(e);
1210      else {
1211         this._terminateEvent(e);
1212         new Rico.Effect.Position( this.dragElement,
1213                              this.origPos.x,
1214                              this.origPos.y,
1215                              200,
1216                              20,
1217                              { complete : this._doCancelDragProcessing.bind(this) } );
1218      }
1219
1220     Event.stopObserving(document.body, "mousemove", this._mouseMove);
1221     Event.stopObserving(document.body, "mouseup",  this._mouseUp);
1222   },
1223
1224   _retTrue: function () {
1225      return true;
1226   },
1227
1228   _completeDropOperation: function(e) {
1229      if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() ) {
1230         if ( this.dragElement.parentNode != null )
1231            this.dragElement.parentNode.removeChild(this.dragElement);
1232      }
1233
1234      this._deactivateRegisteredDropZones();
1235      this._endDrag();
1236      this.clearSelection();
1237      this.dragElement = null;
1238      this.currentDragObjectVisible = false;
1239      this._terminateEvent(e);
1240   },
1241
1242   _doCancelDragProcessing: function() {
1243      this._cancelDrag();
1244
1245        if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() && this.dragElement)
1246           if ( this.dragElement.parentNode != null )
1247              this.dragElement.parentNode.removeChild(this.dragElement);
1248
1249
1250      this._deactivateRegisteredDropZones();
1251      this.dragElement = null;
1252      this.currentDragObjectVisible = false;
1253   },
1254
1255   _placeDraggableInDropZone: function(e) {
1256      var foundDropZone = false;
1257      var n = this.dropZones.length;
1258      for ( var i = 0 ; i < n ; i++ ) {
1259         if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
1260            if ( this.dropZones[i].canAccept(this.currentDragObjects) ) {
1261               this.dropZones[i].hideHover();
1262               this.dropZones[i].accept(this.currentDragObjects);
1263               foundDropZone = true;
1264               break;
1265            }
1266         }
1267      }
1268
1269      return foundDropZone;
1270   },
1271
1272   _cancelDrag: function() {
1273      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
1274         this.currentDragObjects[i].cancelDrag();
1275   },
1276
1277   _endDrag: function() {
1278      for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
1279         this.currentDragObjects[i].endDrag();
1280   },
1281
1282   _mousePointInDropZone: function( e, dropZone ) {
1283
1284      var absoluteRect = dropZone.getAbsoluteRect();
1285
1286      return e.clientX  > absoluteRect.left + this._leftOffset(e) &&
1287             e.clientX  < absoluteRect.right + this._leftOffset(e) &&
1288             e.clientY  > absoluteRect.top + this._topOffset(e)   &&
1289             e.clientY  < absoluteRect.bottom + this._topOffset(e);
1290   },
1291
1292   _addMouseDownHandler: function( aDraggable )
1293   {
1294       htmlElement  = aDraggable.getMouseDownHTMLElement();
1295      if ( htmlElement  != null ) { 
1296         htmlElement.draggable = aDraggable;
1297         Event.observe(htmlElement , "mousedown", this._onmousedown.bindAsEventListener(this));
1298         Event.observe(htmlElement, "mousedown", this._mouseDown);
1299      }
1300   },
1301
1302   _activateRegisteredDropZones: function() {
1303      var n = this.dropZones.length;
1304      for ( var i = 0 ; i < n ; i++ ) {
1305         var dropZone = this.dropZones[i];
1306         if ( dropZone.canAccept(this.currentDragObjects) )
1307            dropZone.activate();
1308      }
1309
1310      this.activatedDropZones = true;
1311   },
1312
1313   _deactivateRegisteredDropZones: function() {
1314      var n = this.dropZones.length;
1315      for ( var i = 0 ; i < n ; i++ )
1316         this.dropZones[i].deactivate();
1317      this.activatedDropZones = false;
1318   },
1319
1320   _onmousedown: function () {
1321     Event.observe(document.body, "mousemove", this._mouseMove);
1322     Event.observe(document.body, "mouseup",  this._mouseUp);
1323   },
1324
1325   _terminateEvent: function(e) {
1326      if ( e.stopPropagation != undefined )
1327         e.stopPropagation();
1328      else if ( e.cancelBubble != undefined )
1329         e.cancelBubble = true;
1330
1331      if ( e.preventDefault != undefined )
1332         e.preventDefault();
1333      else
1334         e.returnValue = false;
1335   },
1336
1337
1338           initializeEventHandlers: function() {
1339              if ( typeof document.implementation != "undefined" &&
1340                 document.implementation.hasFeature("HTML",   "1.0") &&
1341                 document.implementation.hasFeature("Events", "2.0") &&
1342                 document.implementation.hasFeature("CSS",    "2.0") ) {
1343                 document.addEventListener("mouseup",   this._mouseUpHandler.bindAsEventListener(this),  false);
1344                 document.addEventListener("mousemove", this._mouseMoveHandler.bindAsEventListener(this), false);
1345              }
1346              else {
1347                 document.attachEvent( "onmouseup",   this._mouseUpHandler.bindAsEventListener(this) );
1348                 document.attachEvent( "onmousemove", this._mouseMoveHandler.bindAsEventListener(this) );
1349              }
1350           }
1351        }
1352
1353        var dndMgr = new Rico.DragAndDrop();
1354        dndMgr.initializeEventHandlers();
1355
1356
1357//-------------------- ricoDraggable.js
1358Rico.Draggable = Class.create();
1359
1360Rico.Draggable.prototype = {
1361
1362   initialize: function( type, htmlElement ) {
1363      this.type          = type;
1364      this.htmlElement   = $(htmlElement);
1365      this.selected      = false;
1366   },
1367
1368   /**
1369    *   Returns the HTML element that should have a mouse down event
1370    *   added to it in order to initiate a drag operation
1371    *
1372    **/
1373   getMouseDownHTMLElement: function() {
1374      return this.htmlElement;
1375   },
1376
1377   select: function() {
1378      this.selected = true;
1379
1380      if ( this.showingSelected )
1381         return;
1382
1383      var htmlElement = this.getMouseDownHTMLElement();
1384
1385      var color = Rico.Color.createColorFromBackground(htmlElement);
1386      color.isBright() ? color.darken(0.033) : color.brighten(0.033);
1387
1388      this.saveBackground = RicoUtil.getElementsComputedStyle(htmlElement, "backgroundColor", "background-color");
1389      htmlElement.style.backgroundColor = color.asHex();
1390      this.showingSelected = true;
1391   },
1392
1393   deselect: function() {
1394      this.selected = false;
1395      if ( !this.showingSelected )
1396         return;
1397
1398      var htmlElement = this.getMouseDownHTMLElement();
1399
1400      htmlElement.style.backgroundColor = this.saveBackground;
1401      this.showingSelected = false;
1402   },
1403
1404   isSelected: function() {
1405      return this.selected;
1406   },
1407
1408   startDrag: function() {
1409   },
1410
1411   cancelDrag: function() {
1412   },
1413
1414   endDrag: function() {
1415   },
1416
1417   getSingleObjectDragGUI: function() {
1418      return this.htmlElement;
1419   },
1420
1421   getMultiObjectDragGUI: function( draggables ) {
1422      return this.htmlElement;
1423   },
1424
1425   getDroppedGUI: function() {
1426      return this.htmlElement;
1427   },
1428
1429   toString: function() {
1430      return this.type + ":" + this.htmlElement + ":";
1431   }
1432
1433}
1434
1435
1436//-------------------- ricoDropzone.js
1437Rico.Dropzone = Class.create();
1438
1439Rico.Dropzone.prototype = {
1440
1441   initialize: function( htmlElement ) {
1442      this.htmlElement  = $(htmlElement);
1443      this.absoluteRect = null;
1444   },
1445
1446   getHTMLElement: function() {
1447      return this.htmlElement;
1448   },
1449
1450   clearPositionCache: function() {
1451      this.absoluteRect = null;
1452   },
1453
1454   getAbsoluteRect: function() {
1455      if ( this.absoluteRect == null ) {
1456         var htmlElement = this.getHTMLElement();
1457         var pos = RicoUtil.toViewportPosition(htmlElement);
1458
1459         this.absoluteRect = {
1460            top:    pos.y,
1461            left:   pos.x,
1462            bottom: pos.y + htmlElement.offsetHeight,
1463            right:  pos.x + htmlElement.offsetWidth
1464         };
1465      }
1466      return this.absoluteRect;
1467   },
1468
1469   activate: function() {
1470      var htmlElement = this.getHTMLElement();
1471      if (htmlElement == null  || this.showingActive)
1472         return;
1473
1474      this.showingActive = true;
1475      this.saveBackgroundColor = htmlElement.style.backgroundColor;
1476
1477      var fallbackColor = "#ffea84";
1478      var currentColor = Rico.Color.createColorFromBackground(htmlElement);
1479      if ( currentColor == null )
1480         htmlElement.style.backgroundColor = fallbackColor;
1481      else {
1482         currentColor.isBright() ? currentColor.darken(0.2) : currentColor.brighten(0.2);
1483         htmlElement.style.backgroundColor = currentColor.asHex();
1484      }
1485   },
1486
1487   deactivate: function() {
1488      var htmlElement = this.getHTMLElement();
1489      if (htmlElement == null || !this.showingActive)
1490         return;
1491
1492      htmlElement.style.backgroundColor = this.saveBackgroundColor;
1493      this.showingActive = false;
1494      this.saveBackgroundColor = null;
1495   },
1496
1497   showHover: function() {
1498      var htmlElement = this.getHTMLElement();
1499      if ( htmlElement == null || this.showingHover )
1500         return;
1501
1502      this.saveBorderWidth = htmlElement.style.borderWidth;
1503      this.saveBorderStyle = htmlElement.style.borderStyle;
1504      this.saveBorderColor = htmlElement.style.borderColor;
1505
1506      this.showingHover = true;
1507      htmlElement.style.borderWidth = "1px";
1508      htmlElement.style.borderStyle = "solid";
1509      //htmlElement.style.borderColor = "#ff9900";
1510      htmlElement.style.borderColor = "#ffff00";
1511   },
1512
1513   hideHover: function() {
1514      var htmlElement = this.getHTMLElement();
1515      if ( htmlElement == null || !this.showingHover )
1516         return;
1517
1518      htmlElement.style.borderWidth = this.saveBorderWidth;
1519      htmlElement.style.borderStyle = this.saveBorderStyle;
1520      htmlElement.style.borderColor = this.saveBorderColor;
1521      this.showingHover = false;
1522   },
1523
1524   canAccept: function(draggableObjects) {
1525      return true;
1526   },
1527
1528   accept: function(draggableObjects) {
1529      var htmlElement = this.getHTMLElement();
1530      if ( htmlElement == null )
1531         return;
1532
1533      n = draggableObjects.length;
1534      for ( var i = 0 ; i < n ; i++ )
1535      {
1536         var theGUI = draggableObjects[i].getDroppedGUI();
1537         if ( RicoUtil.getElementsComputedStyle( theGUI, "position" ) == "absolute" )
1538         {
1539            theGUI.style.position = "static";
1540            theGUI.style.top = "";
1541            theGUI.style.top = "";
1542         }
1543         htmlElement.appendChild(theGUI);
1544      }
1545   }
1546}
1547
1548
1549//-------------------- ricoEffects.js
1550
1551Rico.Effect = {};
1552
1553Rico.Effect.SizeAndPosition = Class.create();
1554Rico.Effect.SizeAndPosition.prototype = {
1555
1556   initialize: function(element, x, y, w, h, duration, steps, options) {
1557      this.element = $(element);
1558      this.x = x;
1559      this.y = y;
1560      this.w = w;
1561      this.h = h;
1562      this.duration = duration;
1563      this.steps    = steps;
1564      this.options  = arguments[7] || {};
1565
1566      this.sizeAndPosition();
1567   },
1568
1569   sizeAndPosition: function() {
1570      if (this.isFinished()) {
1571         if(this.options.complete) this.options.complete(this);
1572         return;
1573      }
1574
1575      if (this.timer)
1576         clearTimeout(this.timer);
1577
1578      var stepDuration = Math.round(this.duration/this.steps) ;
1579
1580      // Get original values: x,y = top left corner;  w,h = width height
1581      var currentX = this.element.offsetLeft;
1582      var currentY = this.element.offsetTop;
1583      var currentW = this.element.offsetWidth;
1584      var currentH = this.element.offsetHeight;
1585
1586      // If values not set, or zero, we do not modify them, and take original as final as well
1587      this.x = (this.x) ? this.x : currentX;
1588      this.y = (this.y) ? this.y : currentY;
1589      this.w = (this.w) ? this.w : currentW;
1590      this.h = (this.h) ? this.h : currentH;
1591
1592      // how much do we need to modify our values for each step?
1593      var difX = this.steps >  0 ? (this.x - currentX)/this.steps : 0;
1594      var difY = this.steps >  0 ? (this.y - currentY)/this.steps : 0;
1595      var difW = this.steps >  0 ? (this.w - currentW)/this.steps : 0;
1596      var difH = this.steps >  0 ? (this.h - currentH)/this.steps : 0;
1597
1598      this.moveBy(difX, difY);
1599      this.resizeBy(difW, difH);
1600
1601      this.duration -= stepDuration;
1602      this.steps--;
1603
1604      this.timer = setTimeout(this.sizeAndPosition.bind(this), stepDuration);
1605   },
1606
1607   isFinished: function() {
1608      return this.steps <= 0;
1609   },
1610
1611   moveBy: function( difX, difY ) {
1612      var currentLeft = this.element.offsetLeft;
1613      var currentTop  = this.element.offsetTop;
1614      var intDifX     = parseInt(difX);
1615      var intDifY     = parseInt(difY);
1616
1617      var style = this.element.style;
1618      if ( intDifX != 0 )
1619         style.left = (currentLeft + intDifX) + "px";
1620      if ( intDifY != 0 )
1621         style.top  = (currentTop + intDifY) + "px";
1622   },
1623
1624   resizeBy: function( difW, difH ) {
1625      var currentWidth  = this.element.offsetWidth;
1626      var currentHeight = this.element.offsetHeight;
1627      var intDifW       = parseInt(difW);
1628      var intDifH       = parseInt(difH);
1629
1630      var style = this.element.style;
1631      if ( intDifW != 0 )
1632         style.width   = (currentWidth  + intDifW) + "px";
1633      if ( intDifH != 0 )
1634         style.height  = (currentHeight + intDifH) + "px";
1635   }
1636}
1637
1638Rico.Effect.Size = Class.create();
1639Rico.Effect.Size.prototype = {
1640
1641   initialize: function(element, w, h, duration, steps, options) {
1642      new Rico.Effect.SizeAndPosition(element, null, null, w, h, duration, steps, options);
1643  }
1644}
1645
1646Rico.Effect.Position = Class.create();
1647Rico.Effect.Position.prototype = {
1648
1649   initialize: function(element, x, y, duration, steps, options) {
1650      new Rico.Effect.SizeAndPosition(element, x, y, null, null, duration, steps, options);
1651  }
1652}
1653
1654Rico.Effect.Round = Class.create();
1655Rico.Effect.Round.prototype = {
1656
1657   initialize: function(tagName, className, options) {
1658      var elements = document.getElementsByTagAndClassName(tagName,className);
1659      for ( var i = 0 ; i < elements.length ; i++ )
1660         Rico.Corner.round( elements[i], options );
1661   }
1662};
1663
1664Rico.Effect.FadeTo = Class.create();
1665Rico.Effect.FadeTo.prototype = {
1666
1667   initialize: function( element, opacity, duration, steps, options) {
1668      this.element  = $(element);
1669      this.opacity  = opacity;
1670      this.duration = duration;
1671      this.steps    = steps;
1672      this.options  = arguments[4] || {};
1673      this.fadeTo();
1674   },
1675
1676   fadeTo: function() {
1677      if (this.isFinished()) {
1678         if(this.options.complete) this.options.complete(this);
1679         return;
1680      }
1681
1682      if (this.timer)
1683         clearTimeout(this.timer);
1684
1685      var stepDuration = Math.round(this.duration/this.steps) ;
1686      var currentOpacity = this.getElementOpacity();
1687      var delta = this.steps > 0 ? (this.opacity - currentOpacity)/this.steps : 0;
1688
1689      this.changeOpacityBy(delta);
1690      this.duration -= stepDuration;
1691      this.steps--;
1692
1693      this.timer = setTimeout(this.fadeTo.bind(this), stepDuration);
1694   },
1695
1696   changeOpacityBy: function(v) {
1697      var currentOpacity = this.getElementOpacity();
1698      var newOpacity = Math.max(0, Math.min(currentOpacity+v, 1));
1699      this.element.ricoOpacity = newOpacity;
1700
1701      this.element.style.filter = "alpha(opacity:"+Math.round(newOpacity*100)+")";
1702      this.element.style.opacity = newOpacity; /*//*/;
1703   },
1704
1705   isFinished: function() {
1706      return this.steps <= 0;
1707   },
1708
1709   getElementOpacity: function() {
1710      if ( this.element.ricoOpacity == undefined ) {
1711         var opacity = RicoUtil.getElementsComputedStyle(this.element, 'opacity');
1712         this.element.ricoOpacity = opacity != undefined ? opacity : 1.0;
1713      }
1714      return parseFloat(this.element.ricoOpacity);
1715   }
1716}
1717
1718Rico.Effect.AccordionSize = Class.create();
1719
1720Rico.Effect.AccordionSize.prototype = {
1721
1722   initialize: function(e1, e2, start, end, duration, steps, options) {
1723      this.e1       = $(e1);
1724      this.e2       = $(e2);
1725      this.start    = start;
1726      this.end      = end;
1727      this.duration = duration;
1728      this.steps    = steps;
1729      this.options  = arguments[6] || {};
1730
1731      this.accordionSize();
1732   },
1733
1734   accordionSize: function() {
1735
1736      if (this.isFinished()) {
1737         // just in case there are round errors or such...
1738         this.e1.style.height = this.start + "px";
1739         this.e2.style.height = this.end + "px";
1740
1741         if(this.options.complete)
1742            this.options.complete(this);
1743         return;
1744      }
1745
1746      if (this.timer)
1747         clearTimeout(this.timer);
1748
1749      var stepDuration = Math.round(this.duration/this.steps) ;
1750
1751      var diff = this.steps > 0 ? (parseInt(this.e1.offsetHeight) - this.start)/this.steps : 0;
1752      this.resizeBy(diff);
1753
1754      this.duration -= stepDuration;
1755      this.steps--;
1756
1757      this.timer = setTimeout(this.accordionSize.bind(this), stepDuration);
1758   },
1759
1760   isFinished: function() {
1761      return this.steps <= 0;
1762   },
1763
1764   resizeBy: function(diff) {
1765      var h1Height = this.e1.offsetHeight;
1766      var h2Height = this.e2.offsetHeight;
1767      var intDiff = parseInt(diff);
1768      if ( diff != 0 ) {
1769         this.e1.style.height = (h1Height - intDiff) + "px";
1770         this.e2.style.height = (h2Height + intDiff) + "px";
1771      }
1772   }
1773
1774};
1775
1776
1777//-------------------- ricoLiveGrid.js
1778// Rico.LiveGridMetaData -----------------------------------------------------
1779
1780Rico.LiveGridMetaData = Class.create();
1781
1782Rico.LiveGridMetaData.prototype = {
1783
1784   initialize: function( pageSize, totalRows, columnCount, options ) {
1785      this.pageSize  = pageSize;
1786      this.totalRows = totalRows;
1787      this.setOptions(options);
1788      this.ArrowHeight = 16;
1789      this.columnCount = columnCount;
1790   },
1791
1792   setOptions: function(options) {
1793      this.options = {
1794         largeBufferSize    : 7.0,   // 7 pages
1795         nearLimitFactor    : 0.2    // 20% of buffer
1796      };
1797      Object.extend(this.options, options || {});
1798   },
1799
1800   getPageSize: function() {
1801      return this.pageSize;
1802   },
1803
1804   getTotalRows: function() {
1805      return this.totalRows;
1806   },
1807
1808   setTotalRows: function(n) {
1809      this.totalRows = n;
1810   },
1811
1812   getLargeBufferSize: function() {
1813      return parseInt(this.options.largeBufferSize * this.pageSize);
1814   },
1815
1816   getLimitTolerance: function() {
1817      return parseInt(this.getLargeBufferSize() * this.options.nearLimitFactor);
1818   }
1819};
1820
1821// Rico.LiveGridScroller -----------------------------------------------------
1822
1823Rico.LiveGridScroller = Class.create();
1824
1825Rico.LiveGridScroller.prototype = {
1826
1827   initialize: function(liveGrid, viewPort) {
1828      this.isIE = navigator.userAgent.toLowerCase().indexOf("msie") >= 0;
1829      this.liveGrid = liveGrid;
1830      this.metaData = liveGrid.metaData;
1831      this.createScrollBar();
1832      this.scrollTimeout = null;
1833      this.lastScrollPos = 0;
1834      this.viewPort = viewPort;
1835      this.rows = new Array();
1836   },
1837
1838   isUnPlugged: function() {
1839      return this.scrollerDiv.onscroll == null;
1840   },
1841
1842   plugin: function() {
1843      this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this);
1844   },
1845
1846   unplug: function() {
1847      this.scrollerDiv.onscroll = null;
1848   },
1849
1850   sizeIEHeaderHack: function() {
1851      if ( !this.isIE ) return;
1852      var headerTable = $(this.liveGrid.tableId + "_header");
1853      if ( headerTable )
1854         headerTable.rows[0].cells[0].style.width =
1855            (headerTable.rows[0].cells[0].offsetWidth + 1) + "px";
1856   },
1857
1858   createScrollBar: function() {
1859      var visibleHeight = this.liveGrid.viewPort.visibleHeight();
1860      // create the outer div...
1861      this.scrollerDiv  = document.createElement("div");
1862      var scrollerStyle = this.scrollerDiv.style;
1863      scrollerStyle.borderRight = this.liveGrid.options.scrollerBorderRight;
1864      scrollerStyle.position    = "relative";
1865      scrollerStyle.left        = this.isIE ? "-6px" : "-3px";
1866      scrollerStyle.width       = "19px";
1867      scrollerStyle.height      = visibleHeight + "px";
1868      scrollerStyle.overflow    = "auto";
1869
1870      // create the inner div...
1871      this.heightDiv = document.createElement("div");
1872      this.heightDiv.style.width  = "1px";
1873
1874      this.heightDiv.style.height = parseInt(visibleHeight *
1875                        this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px" ;
1876      this.scrollerDiv.appendChild(this.heightDiv);
1877      this.scrollerDiv.onscroll = this.handleScroll.bindAsEventListener(this);
1878
1879     var table = this.liveGrid.table;
1880     table.parentNode.parentNode.insertBefore( this.scrollerDiv, table.parentNode.nextSibling );
1881          var eventName = this.isIE ? "mousewheel" : "DOMMouseScroll";
1882          Event.observe(table, eventName, 
1883                        function(evt) {
1884                           if (evt.wheelDelta>=0 || evt.detail < 0) //wheel-up
1885                              this.scrollerDiv.scrollTop -= (2*this.viewPort.rowHeight);
1886                           else
1887                              this.scrollerDiv.scrollTop += (2*this.viewPort.rowHeight);
1888                           this.handleScroll(false);
1889                        }.bindAsEventListener(this), 
1890                        false);
1891     },
1892
1893   updateSize: function() {
1894      var table = this.liveGrid.table;
1895      var visibleHeight = this.viewPort.visibleHeight();
1896      this.heightDiv.style.height = parseInt(visibleHeight *
1897                                  this.metaData.getTotalRows()/this.metaData.getPageSize()) + "px";
1898   },
1899
1900   rowToPixel: function(rowOffset) {
1901      return (rowOffset / this.metaData.getTotalRows()) * this.heightDiv.offsetHeight
1902   },
1903   
1904   moveScroll: function(rowOffset) {
1905      this.scrollerDiv.scrollTop = this.rowToPixel(rowOffset);
1906      if ( this.metaData.options.onscroll )
1907         this.metaData.options.onscroll( this.liveGrid, rowOffset );
1908   },
1909
1910   handleScroll: function() {
1911     if ( this.scrollTimeout )
1912         clearTimeout( this.scrollTimeout );
1913
1914    var scrollDiff = this.lastScrollPos-this.scrollerDiv.scrollTop;
1915    if (scrollDiff != 0.00) {
1916       var r = this.scrollerDiv.scrollTop % this.viewPort.rowHeight;
1917       if (r != 0) {
1918          this.unplug();
1919          if ( scrollDiff < 0 ) {
1920             this.scrollerDiv.scrollTop += (this.viewPort.rowHeight-r);
1921          } else {
1922             this.scrollerDiv.scrollTop -= r;
1923          }
1924          this.plugin();
1925       }
1926    }
1927    var contentOffset = parseInt(this.scrollerDiv.scrollTop / this.viewPort.rowHeight);
1928    this.liveGrid.requestContentRefresh(contentOffset);
1929    this.viewPort.scrollTo(this.scrollerDiv.scrollTop);
1930
1931    if ( this.metaData.options.onscroll )
1932       this.metaData.options.onscroll( this.liveGrid, contentOffset );
1933
1934    this.scrollTimeout = setTimeout(this.scrollIdle.bind(this), 1200 );
1935    this.lastScrollPos = this.scrollerDiv.scrollTop;
1936
1937   },
1938
1939   scrollIdle: function() {
1940      if ( this.metaData.options.onscrollidle )
1941         this.metaData.options.onscrollidle();
1942   }
1943};
1944
1945// Rico.LiveGridBuffer -----------------------------------------------------
1946
1947Rico.LiveGridBuffer = Class.create();
1948
1949Rico.LiveGridBuffer.prototype = {
1950
1951   initialize: function(metaData, viewPort) {
1952      this.startPos = 0;
1953      this.size     = 0;
1954      this.metaData = metaData;
1955      this.rows     = new Array();
1956      this.updateInProgress = false;
1957      this.viewPort = viewPort;
1958      this.maxBufferSize = metaData.getLargeBufferSize() * 2;
1959      this.maxFetchSize = metaData.getLargeBufferSize();
1960      this.lastOffset = 0;
1961   },
1962
1963   getBlankRow: function() {
1964      if (!this.blankRow ) {
1965         this.blankRow = new Array();
1966         for ( var i=0; i < this.metaData.columnCount ; i++ ) 
1967            this.blankRow[i] = "&nbsp;";
1968     }
1969     return this.blankRow;
1970   },
1971
1972   loadRows: function(ajaxResponse) {
1973      var rowsElement = ajaxResponse.getElementsByTagName('rows')[0];
1974      this.updateUI = rowsElement.getAttribute("update_ui") == "true"
1975      var newRows = new Array()
1976      var trs = rowsElement.getElementsByTagName("tr");
1977      for ( var i=0 ; i < trs.length; i++ ) {
1978         var row = newRows[i] = new Array(); 
1979         var cells = trs[i].getElementsByTagName("td");
1980         for ( var j=0; j < cells.length ; j++ ) {
1981            var cell = cells[j];
1982            var convertSpaces = cell.getAttribute("convert_spaces") == "true";
1983            var cellContent = RicoUtil.getContentAsString(cell);
1984            row[j] = convertSpaces ? this.convertSpaces(cellContent) : cellContent;
1985            if (!row[j]) 
1986               row[j] = '&nbsp;';
1987         }
1988      }
1989      return newRows;
1990   },
1991     
1992   update: function(ajaxResponse, start) {
1993     var newRows = this.loadRows(ajaxResponse);
1994      if (this.rows.length == 0) { // initial load
1995         this.rows = newRows;
1996         this.size = this.rows.length;
1997         this.startPos = start;
1998         return;
1999      }
2000      if (start > this.startPos) { //appending
2001         if (this.startPos + this.rows.length < start) {
2002            this.rows =  newRows;
2003            this.startPos = start;//
2004         } else {
2005              this.rows = this.rows.concat( newRows.slice(0, newRows.length));
2006            if (this.rows.length > this.maxBufferSize) {
2007               var fullSize = this.rows.length;
2008               this.rows = this.rows.slice(this.rows.length - this.maxBufferSize, this.rows.length)
2009               this.startPos = this.startPos +  (fullSize - this.rows.length);
2010            }
2011         }
2012      } else { //prepending
2013         if (start + newRows.length < this.startPos) {
2014            this.rows =  newRows;
2015         } else {
2016            this.rows = newRows.slice(0, this.startPos).concat(this.rows);
2017            if (this.rows.length > this.maxBufferSize) 
2018               this.rows = this.rows.slice(0, this.maxBufferSize)
2019         }
2020         this.startPos =  start;
2021      }
2022      this.size = this.rows.length;
2023   },
2024   
2025   clear: function() {
2026      this.rows = new Array();
2027      this.startPos = 0;
2028      this.size = 0;
2029   },
2030
2031   isOverlapping: function(start, size) {
2032      return ((start < this.endPos()) && (this.startPos < start + size)) || (this.endPos() == 0)
2033   },
2034
2035   isInRange: function(position) {
2036      return (position >= this.startPos) && (position + this.metaData.getPageSize() <= this.endPos()); 
2037             //&& this.size()  != 0;
2038   },
2039
2040   isNearingTopLimit: function(position) {
2041      return position - this.startPos < this.metaData.getLimitTolerance();
2042   },
2043
2044   endPos: function() {
2045      return this.startPos + this.rows.length;
2046   },
2047   
2048   isNearingBottomLimit: function(position) {
2049      return this.endPos() - (position + this.metaData.getPageSize()) < this.metaData.getLimitTolerance();
2050   },
2051
2052   isAtTop: function() {
2053      return this.startPos == 0;
2054   },
2055
2056   isAtBottom: function() {
2057      return this.endPos() == this.metaData.getTotalRows();
2058   },
2059
2060   isNearingLimit: function(position) {
2061      return ( !this.isAtTop()    && this.isNearingTopLimit(position)) ||
2062             ( !this.isAtBottom() && this.isNearingBottomLimit(position) )
2063   },
2064
2065   getFetchSize: function(offset) {
2066      var adjustedOffset = this.getFetchOffset(offset);
2067      var adjustedSize = 0;
2068      if (adjustedOffset >= this.startPos) { //apending
2069         var endFetchOffset = this.maxFetchSize  + adjustedOffset;
2070         if (endFetchOffset > this.metaData.totalRows)
2071            endFetchOffset = this.metaData.totalRows;
2072         adjustedSize = endFetchOffset - adjustedOffset; 
2073                        if(adjustedOffset == 0 && adjustedSize < this.maxFetchSize){
2074                           adjustedSize = this.maxFetchSize;
2075                        }
2076      } else {//prepending
2077         var adjustedSize = this.startPos - adjustedOffset;
2078         if (adjustedSize > this.maxFetchSize)
2079            adjustedSize = this.maxFetchSize;
2080      }
2081      return adjustedSize;
2082   }, 
2083
2084   getFetchOffset: function(offset) {
2085      var adjustedOffset = offset;
2086      if (offset > this.startPos)  //apending
2087         adjustedOffset = (offset > this.endPos()) ? offset :  this.endPos(); 
2088      else { //prepending
2089         if (offset + this.maxFetchSize >= this.startPos) {
2090            var adjustedOffset = this.startPos - this.maxFetchSize;
2091            if (adjustedOffset < 0)
2092               adjustedOffset = 0;
2093         }
2094      }
2095      this.lastOffset = adjustedOffset;
2096      return adjustedOffset;
2097   },
2098
2099   getRows: function(start, count) {
2100      var begPos = start - this.startPos
2101      var endPos = begPos + count
2102
2103      // er? need more data...
2104      if ( endPos > this.size )
2105         endPos = this.size
2106
2107      var results = new Array()
2108      var index = 0;
2109      for ( var i=begPos ; i < endPos; i++ ) {
2110         results[index++] = this.rows[i]
2111      }
2112      return results
2113   },
2114
2115   convertSpaces: function(s) {
2116      return s.split(" ").join("&nbsp;");
2117   }
2118
2119};
2120
2121
2122//Rico.GridViewPort --------------------------------------------------
2123Rico.GridViewPort = Class.create();
2124
2125Rico.GridViewPort.prototype = {
2126
2127   initialize: function(table, rowHeight, visibleRows, buffer, liveGrid) {
2128      this.lastDisplayedStartPos = 0;
2129      this.div = table.parentNode;
2130      this.table = table
2131      this.rowHeight = rowHeight;
2132      this.div.style.height = (this.rowHeight * visibleRows) + "px";
2133      this.div.style.overflow = "hidden";
2134      this.buffer = buffer;
2135      this.liveGrid = liveGrid;
2136      this.visibleRows = visibleRows + 1;
2137      this.lastPixelOffset = 0;
2138      this.startPos = 0;
2139   },
2140
2141   populateRow: function(htmlRow, row) {
2142      for (var j=0; j < row.length; j++) {
2143         htmlRow.cells[j].innerHTML = row[j]
2144      }
2145   },
2146   
2147   bufferChanged: function() {
2148      this.refreshContents( parseInt(this.lastPixelOffset / this.rowHeight));
2149   },
2150   
2151   clearRows: function() {
2152      if (!this.isBlank) {
2153         this.liveGrid.table.className = this.liveGrid.options.loadingClass;
2154         for (var i=0; i < this.visibleRows; i++)
2155            this.populateRow(this.table.rows[i], this.buffer.getBlankRow());
2156         this.isBlank = true;
2157      }
2158   },
2159   
2160   clearContents: function() {   
2161      this.clearRows();
2162      this.scrollTo(0);
2163      this.startPos = 0;
2164      this.lastStartPos = -1;   
2165   },
2166   
2167   refreshContents: function(startPos) {
2168      if (startPos == this.lastRowPos && !this.isPartialBlank && !this.isBlank) {
2169         return;
2170      }
2171      if ((startPos + this.visibleRows < this.buffer.startPos) 
2172          || (this.buffer.startPos + this.buffer.size < startPos) 
2173          || (this.buffer.size == 0)) {
2174         this.clearRows();
2175         return;
2176      }
2177      this.isBlank = false;
2178      var viewPrecedesBuffer = this.buffer.startPos > startPos
2179      var contentStartPos = viewPrecedesBuffer ? this.buffer.startPos: startPos; 
2180      var contentEndPos = (this.buffer.startPos + this.buffer.size < startPos + this.visibleRows) 
2181                                 ? this.buffer.startPos + this.buffer.size
2182                                 : startPos + this.visibleRows;
2183      var rowSize = contentEndPos - contentStartPos;
2184      var rows = this.buffer.getRows(contentStartPos, rowSize ); 
2185      var blankSize = this.visibleRows - rowSize;
2186      var blankOffset = viewPrecedesBuffer ? 0: rowSize;
2187      var contentOffset = viewPrecedesBuffer ? blankSize: 0;
2188
2189      for (var i=0; i < rows.length; i++) {//initialize what we have
2190        this.populateRow(this.table.rows[i + contentOffset], rows[i]);
2191      }
2192      for (var i=0; i < blankSize; i++) {// blank out the rest
2193        this.populateRow(this.table.rows[i + blankOffset], this.buffer.getBlankRow());
2194      }
2195      this.isPartialBlank = blankSize > 0;
2196      this.lastRowPos = startPos;
2197
2198       this.liveGrid.table.className = this.liveGrid.options.tableClass;
2199       // Check if user has set a onRefreshComplete function
2200       var onRefreshComplete = this.liveGrid.options.onRefreshComplete;
2201       if (onRefreshComplete != null)
2202           onRefreshComplete();
2203   },
2204
2205   scrollTo: function(pixelOffset) {     
2206      if (this.lastPixelOffset == pixelOffset)
2207         return;
2208
2209      this.refreshContents(parseInt(pixelOffset / this.rowHeight))
2210      this.div.scrollTop = pixelOffset % this.rowHeight       
2211     
2212      this.lastPixelOffset = pixelOffset;
2213   },
2214   
2215   visibleHeight: function() {
2216      return parseInt(RicoUtil.getElementsComputedStyle(this.div, 'height'));
2217   }
2218
2219};
2220
2221
2222Rico.LiveGridRequest = Class.create();
2223Rico.LiveGridRequest.prototype = {
2224   initialize: function( requestOffset, options ) {
2225      this.requestOffset = requestOffset;
2226   }
2227};
2228
2229// Rico.LiveGrid -----------------------------------------------------
2230
2231Rico.LiveGrid = Class.create();
2232
2233Rico.LiveGrid.prototype = {
2234
2235   initialize: function( tableId, visibleRows, totalRows, url, options, ajaxOptions ) {
2236
2237     this.options = {
2238                tableClass:           $(tableId).className,
2239                loadingClass:         $(tableId).className,
2240                scrollerBorderRight: '1px solid #ababab',
2241                bufferTimeout:        20000,
2242                sortAscendImg:        'images/sort_asc.gif',
2243                sortDescendImg:       'images/sort_desc.gif',
2244                sortImageWidth:       9,
2245                sortImageHeight:      5,
2246                ajaxSortURLParms:     [],
2247                onRefreshComplete:    null,
2248                requestParameters:    null,
2249                inlineStyles:         true
2250                };
2251      Object.extend(this.options, options || {});
2252
2253      this.ajaxOptions = {parameters: null};
2254      Object.extend(this.ajaxOptions, ajaxOptions || {});
2255
2256      this.tableId     = tableId; 
2257      this.table       = $(tableId);
2258
2259      this.addLiveGridHtml();
2260
2261      var columnCount  = this.table.rows[0].cells.length;
2262      this.metaData    = new Rico.LiveGridMetaData(visibleRows, totalRows, columnCount, options);
2263      this.buffer      = new Rico.LiveGridBuffer(this.metaData);
2264
2265      var rowCount = this.table.rows.length;
2266      this.viewPort =  new Rico.GridViewPort(this.table, 
2267                                            this.table.offsetHeight/rowCount,
2268                                            visibleRows,
2269                                            this.buffer, this);
2270      this.scroller    = new Rico.LiveGridScroller(this,this.viewPort);
2271      this.options.sortHandler = this.sortHandler.bind(this);
2272
2273      if ( $(tableId + '_header') )
2274         this.sort = new Rico.LiveGridSort(tableId + '_header', this.options)
2275
2276      this.processingRequest = null;
2277      this.unprocessedRequest = null;
2278
2279      this.initAjax(url);
2280      if ( this.options.prefetchBuffer || this.options.prefetchOffset > 0) {
2281         var offset = 0;
2282         if (this.options.offset ) {
2283            offset = this.options.offset;           
2284            this.scroller.moveScroll(offset);
2285            this.viewPort.scrollTo(this.scroller.rowToPixel(offset));           
2286         }
2287         if (this.options.sortCol) {
2288             this.sortCol = options.sortCol;
2289             this.sortDir = options.sortDir;
2290         }
2291         this.requestContentRefresh(offset);
2292      }
2293   },
2294
2295   addLiveGridHtml: function() {
2296     // Check to see if need to create a header table.
2297     if (this.table.getElementsByTagName("thead").length > 0){
2298       // Create Table this.tableId+'_header'
2299       var tableHeader = this.table.cloneNode(true);
2300       tableHeader.setAttribute('id', this.tableId+'_header');
2301       tableHeader.setAttribute('class', this.table.className+'_header');
2302
2303       // Clean up and insert
2304       for( var i = 0; i < tableHeader.tBodies.length; i++ ) 
2305       tableHeader.removeChild(tableHeader.tBodies[i]);
2306       this.table.deleteTHead();
2307       this.table.parentNode.insertBefore(tableHeader,this.table);
2308     }
2309
2310    new Insertion.Before(this.table, "<div id='"+this.tableId+"_container'></div>");
2311    this.table.previousSibling.appendChild(this.table);
2312    new Insertion.Before(this.table,"<div id='"+this.tableId+"_viewport' style='float:left;'></div>");
2313    this.table.previousSibling.appendChild(this.table);
2314   },
2315
2316
2317   resetContents: function() {
2318      this.scroller.moveScroll(0);
2319      this.buffer.clear();
2320      this.viewPort.clearContents();
2321   },
2322   
2323   sortHandler: function(column) {
2324           if(!column) return ;
2325      this.sortCol = column.name;
2326      this.sortDir = column.currentSort;
2327
2328      this.resetContents();
2329      this.requestContentRefresh(0) 
2330   },
2331
2332   adjustRowSize: function() {
2333         
2334        },
2335       
2336   setTotalRows: function( newTotalRows ) {
2337      this.resetContents();
2338      this.metaData.setTotalRows(newTotalRows);
2339      this.scroller.updateSize();
2340   },
2341
2342   initAjax: function(url) {
2343      ajaxEngine.registerRequest( this.tableId + '_request', url );
2344      ajaxEngine.registerAjaxObject( this.tableId + '_updater', this );
2345   },
2346
2347   invokeAjax: function() {
2348   },
2349
2350   handleTimedOut: function() {
2351      //server did not respond in 4 seconds... assume that there could have been
2352      //an error or something, and allow requests to be processed again...
2353      this.processingRequest = null;
2354      this.processQueuedRequest();
2355   },
2356
2357   fetchBuffer: function(offset) {
2358      if ( this.buffer.isInRange(offset) &&
2359         !this.buffer.isNearingLimit(offset)) {
2360         return;
2361         }
2362      if (this.processingRequest) {
2363          this.unprocessedRequest = new Rico.LiveGridRequest(offset);
2364         return;
2365      }
2366      var bufferStartPos = this.buffer.getFetchOffset(offset);
2367      this.processingRequest = new Rico.LiveGridRequest(offset);
2368      this.processingRequest.bufferOffset = bufferStartPos;   
2369      var fetchSize = this.buffer.getFetchSize(offset);
2370      var partialLoaded = false;
2371     
2372      var queryString
2373      if (this.options.requestParameters)
2374         queryString = this._createQueryString(this.options.requestParameters, 0);
2375
2376        queryString = (queryString == null) ? '' : queryString+'&';
2377        queryString  = queryString+'id='+this.tableId+'&page_size='+fetchSize+'&offset='+bufferStartPos;
2378        if (this.sortCol)
2379            queryString = queryString+'&sort_col='+escape(this.sortCol)+'&sort_dir='+this.sortDir;
2380
2381        this.ajaxOptions.parameters = queryString;
2382
2383       ajaxEngine.sendRequest( this.tableId + '_request', this.ajaxOptions );
2384
2385       this.timeoutHandler = setTimeout( this.handleTimedOut.bind(this), this.options.bufferTimeout);
2386
2387   },
2388
2389   setRequestParams: function() {
2390      this.options.requestParameters = [];
2391      for ( var i=0 ; i < arguments.length ; i++ )
2392         this.options.requestParameters[i] = arguments[i];
2393   },
2394
2395   requestContentRefresh: function(contentOffset) {
2396      this.fetchBuffer(contentOffset);
2397   },
2398
2399   ajaxUpdate: function(ajaxResponse) {
2400      try {
2401         clearTimeout( this.timeoutHandler );
2402         this.buffer.update(ajaxResponse,this.processingRequest.bufferOffset);
2403         this.viewPort.bufferChanged();
2404      }
2405      catch(err) {}
2406      finally {this.processingRequest = null; }
2407      this.processQueuedRequest();
2408   },
2409
2410   _createQueryString: function( theArgs, offset ) {
2411      var queryString = ""
2412      if (!theArgs)
2413          return queryString;
2414
2415      for ( var i = offset ; i < theArgs.length ; i++ ) {
2416          if ( i != offset )
2417            queryString += "&";
2418
2419          var anArg = theArgs[i];
2420
2421          if ( anArg.name != undefined && anArg.value != undefined ) {
2422            queryString += anArg.name +  "=" + escape(anArg.value);
2423          }
2424          else {
2425             var ePos  = anArg.indexOf('=');
2426             var argName  = anArg.substring( 0, ePos );
2427             var argValue = anArg.substring( ePos + 1 );
2428             queryString += argName + "=" + escape(argValue);
2429          }
2430      }
2431      return queryString;
2432   },
2433
2434   processQueuedRequest: function() {
2435      if (this.unprocessedRequest != null) {
2436         this.requestContentRefresh(this.unprocessedRequest.requestOffset);
2437         this.unprocessedRequest = null
2438      }
2439   }
2440};
2441
2442
2443//-------------------- ricoLiveGridSort.js
2444Rico.LiveGridSort = Class.create();
2445
2446Rico.LiveGridSort.prototype = {
2447
2448   initialize: function(headerTableId, options) {
2449      this.headerTableId = headerTableId;
2450      this.headerTable   = $(headerTableId);
2451      this.options = options;
2452      this.setOptions();
2453      this.applySortBehavior();
2454
2455      if ( this.options.sortCol ) {
2456         this.setSortUI( this.options.sortCol, this.options.sortDir );
2457      }
2458   },
2459
2460   setSortUI: function( columnName, sortDirection ) {
2461      var cols = this.options.columns;
2462      for ( var i = 0 ; i < cols.length ; i++ ) {
2463         if ( cols[i].name == columnName ) {
2464            this.setColumnSort(i, sortDirection);
2465            break;
2466         }
2467      }
2468   },
2469
2470   setOptions: function() {
2471      // preload the images...
2472      new Image().src = this.options.sortAscendImg;
2473      new Image().src = this.options.sortDescendImg;
2474
2475      this.sort = this.options.sortHandler;
2476      if ( !this.options.columns )
2477         this.options.columns = this.introspectForColumnInfo();
2478      else {
2479         // allow client to pass { columns: [ ["a", true], ["b", false] ] }
2480         // and convert to an array of Rico.TableColumn objs...
2481         this.options.columns = this.convertToTableColumns(this.options.columns);
2482      }
2483   },
2484
2485   applySortBehavior: function() {
2486      var headerRow   = this.headerTable.rows[0];
2487      var headerCells = headerRow.cells;
2488      for ( var i = 0 ; i < headerCells.length ; i++ ) {
2489         this.addSortBehaviorToColumn( i, headerCells[i] );
2490      }
2491   },
2492
2493   addSortBehaviorToColumn: function( n, cell ) {
2494      if ( this.options.columns[n].isSortable() ) {
2495         cell.id            = this.headerTableId + '_' + n;
2496         cell.style.cursor  = 'pointer';
2497         cell.onclick       = this.headerCellClicked.bindAsEventListener(this);
2498         cell.innerHTML     = cell.innerHTML + '<span id="' + this.headerTableId + '_img_' + n + '">'
2499                           + '&nbsp;&nbsp;&nbsp;</span>';
2500      }
2501   },
2502
2503   // event handler....
2504   headerCellClicked: function(evt) {
2505      var eventTarget = evt.target ? evt.target : evt.srcElement;
2506      var cellId = eventTarget.id;
2507      var columnNumber = parseInt(cellId.substring( cellId.lastIndexOf('_') + 1 ));
2508      var sortedColumnIndex = this.getSortedColumnIndex();
2509      if ( sortedColumnIndex != -1 ) {
2510         if ( sortedColumnIndex != columnNumber ) {
2511            this.removeColumnSort(sortedColumnIndex);
2512            this.setColumnSort(columnNumber, Rico.TableColumn.SORT_ASC);
2513         }
2514         else
2515            this.toggleColumnSort(sortedColumnIndex);
2516      }
2517      else
2518         this.setColumnSort(columnNumber, Rico.TableColumn.SORT_ASC);
2519
2520      if (this.options.sortHandler) {
2521         this.options.sortHandler(this.options.columns[columnNumber]);
2522      }
2523   },
2524
2525   removeColumnSort: function(n) {
2526      this.options.columns[n].setUnsorted();
2527      this.setSortImage(n);
2528   },
2529
2530   setColumnSort: function(n, direction) {
2531        if(isNaN(n)) return ;
2532      this.options.columns[n].setSorted(direction);
2533      this.setSortImage(n);
2534   },
2535
2536   toggleColumnSort: function(n) {
2537      this.options.columns[n].toggleSort();
2538      this.setSortImage(n);
2539   },
2540
2541   setSortImage: function(n) {
2542      var sortDirection = this.options.columns[n].getSortDirection();
2543
2544      var sortImageSpan = $( this.headerTableId + '_img_' + n );
2545      if ( sortDirection == Rico.TableColumn.UNSORTED )
2546         sortImageSpan.innerHTML = '&nbsp;&nbsp;';
2547      else if ( sortDirection == Rico.TableColumn.SORT_ASC )
2548         sortImageSpan.innerHTML = '&nbsp;&nbsp;<img width="'  + this.options.sortImageWidth    + '" ' +
2549                                                     'height="'+ this.options.sortImageHeight   + '" ' +
2550                                                     'src="'   + this.options.sortAscendImg + '"/>';
2551      else if ( sortDirection == Rico.TableColumn.SORT_DESC )
2552         sortImageSpan.innerHTML = '&nbsp;&nbsp;<img width="'  + this.options.sortImageWidth    + '" ' +
2553                                                     'height="'+ this.options.sortImageHeight   + '" ' +
2554                                                     'src="'   + this.options.sortDescendImg + '"/>';
2555   },
2556
2557   getSortedColumnIndex: function() {
2558      var cols = this.options.columns;
2559      for ( var i = 0 ; i < cols.length ; i++ ) {
2560         if ( cols[i].isSorted() )
2561            return i;
2562      }
2563
2564      return -1;
2565   },
2566
2567   introspectForColumnInfo: function() {
2568      var columns = new Array();
2569      var headerRow   = this.headerTable.rows[0];
2570      var headerCells = headerRow.cells;
2571      for ( var i = 0 ; i < headerCells.length ; i++ )
2572         columns.push( new Rico.TableColumn( this.deriveColumnNameFromCell(headerCells[i],i), true ) );
2573      return columns;
2574   },
2575
2576   convertToTableColumns: function(cols) {
2577      var columns = new Array();
2578      for ( var i = 0 ; i < cols.length ; i++ )
2579         columns.push( new Rico.TableColumn( cols[i][0], cols[i][1] ) );
2580      return columns;
2581   },
2582
2583   deriveColumnNameFromCell: function(cell,columnNumber) {
2584      var cellContent = cell.innerText != undefined ? cell.innerText : cell.textContent;
2585      return cellContent ? cellContent.toLowerCase().split(' ').join('_') : "col_" + columnNumber;
2586   }
2587};
2588
2589Rico.TableColumn = Class.create();
2590
2591Rico.TableColumn.UNSORTED  = 0;
2592Rico.TableColumn.SORT_ASC  = "ASC";
2593Rico.TableColumn.SORT_DESC = "DESC";
2594
2595Rico.TableColumn.prototype = {
2596   initialize: function(name, sortable) {
2597      this.name        = name;
2598      this.sortable    = sortable;
2599      this.currentSort = Rico.TableColumn.UNSORTED;
2600   },
2601
2602   isSortable: function() {
2603      return this.sortable;
2604   },
2605
2606   isSorted: function() {
2607      return this.currentSort != Rico.TableColumn.UNSORTED;
2608   },
2609
2610   getSortDirection: function() {
2611      return this.currentSort;
2612   },
2613
2614   toggleSort: function() {
2615      if ( this.currentSort == Rico.TableColumn.UNSORTED || this.currentSort == Rico.TableColumn.SORT_DESC )
2616         this.currentSort = Rico.TableColumn.SORT_ASC;
2617      else if ( this.currentSort == Rico.TableColumn.SORT_ASC )
2618         this.currentSort = Rico.TableColumn.SORT_DESC;
2619   },
2620
2621   setUnsorted: function(direction) {
2622      this.setSorted(Rico.TableColumn.UNSORTED);
2623   },
2624
2625   setSorted: function(direction) {
2626      // direction must by one of Rico.TableColumn.UNSORTED, .SORT_ASC, or .SORT_DESC...
2627      this.currentSort = direction;
2628   }
2629
2630};
2631
2632
2633//-------------------- ricoUtil.js
2634var RicoUtil = {
2635
2636   getElementsComputedStyle: function ( htmlElement, cssProperty, mozillaEquivalentCSS) {
2637      if ( arguments.length == 2 )
2638         mozillaEquivalentCSS = cssProperty;
2639
2640      var el = $(htmlElement);
2641      if ( el.currentStyle )
2642         return el.currentStyle[cssProperty];
2643      else
2644         return document.defaultView.getComputedStyle(el, null).getPropertyValue(mozillaEquivalentCSS);
2645   },
2646
2647   createXmlDocument : function() {
2648      if (document.implementation && document.implementation.createDocument) {
2649         var doc = document.implementation.createDocument("", "", null);
2650
2651         if (doc.readyState == null) {
2652            doc.readyState = 1;
2653            doc.addEventListener("load", function () {
2654               doc.readyState = 4;
2655               if (typeof doc.onreadystatechange == "function")
2656                  doc.onreadystatechange();
2657            }, false);
2658         }
2659
2660         return doc;
2661      }
2662
2663      if (window.ActiveXObject)
2664          return Try.these(
2665            function() { return new ActiveXObject('MSXML2.DomDocument')   },
2666            function() { return new ActiveXObject('Microsoft.DomDocument')},
2667            function() { return new ActiveXObject('MSXML.DomDocument')    },
2668            function() { return new ActiveXObject('MSXML3.DomDocument')   }
2669          ) || false;
2670
2671      return null;
2672   },
2673
2674   getContentAsString: function( parentNode ) {
2675      return parentNode.xml != undefined ? 
2676         this._getContentAsStringIE(parentNode) :
2677         this._getContentAsStringMozilla(parentNode);
2678   },
2679
2680  _getContentAsStringIE: function(parentNode) {
2681     var contentStr = "";
2682     for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
2683         var n = parentNode.childNodes[i];
2684         if (n.nodeType == 4) {
2685             contentStr += n.nodeValue;
2686         }
2687         else {
2688           contentStr += n.xml;
2689       }
2690     }
2691     return contentStr;
2692  },
2693
2694  _getContentAsStringMozilla: function(parentNode) {
2695     var xmlSerializer = new XMLSerializer();
2696     var contentStr = "";
2697     for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
2698          var n = parentNode.childNodes[i];
2699          if (n.nodeType == 4) { // CDATA node
2700              contentStr += n.nodeValue;
2701          }
2702          else {
2703            contentStr += xmlSerializer.serializeToString(n);
2704        }
2705     }
2706     return contentStr;
2707  },
2708
2709   toViewportPosition: function(element) {
2710      return this._toAbsolute(element,true);
2711   },
2712
2713   toDocumentPosition: function(element) {
2714      return this._toAbsolute(element,false);
2715   },
2716
2717   /**
2718    *  Compute the elements position in terms of the window viewport
2719    *  so that it can be compared to the position of the mouse (dnd)
2720    *  This is additions of all the offsetTop,offsetLeft values up the
2721    *  offsetParent hierarchy, ...taking into account any scrollTop,
2722    *  scrollLeft values along the way...
2723    *
2724    * IE has a bug reporting a correct offsetLeft of elements within a
2725    * a relatively positioned parent!!!
2726    **/
2727   _toAbsolute: function(element,accountForDocScroll) {
2728
2729      if ( navigator.userAgent.toLowerCase().indexOf("msie") == -1 )
2730         return this._toAbsoluteMozilla(element,accountForDocScroll);
2731
2732      var x = 0;
2733      var y = 0;
2734      var parent = element;
2735      while ( parent ) {
2736
2737         var borderXOffset = 0;
2738         var borderYOffset = 0;
2739         if ( parent != element ) {
2740            var borderXOffset = parseInt(this.getElementsComputedStyle(parent, "borderLeftWidth" ));
2741            var borderYOffset = parseInt(this.getElementsComputedStyle(parent, "borderTopWidth" ));
2742            borderXOffset = isNaN(borderXOffset) ? 0 : borderXOffset;
2743            borderYOffset = isNaN(borderYOffset) ? 0 : borderYOffset;
2744         }
2745
2746         x += parent.offsetLeft - parent.scrollLeft + borderXOffset;
2747         y += parent.offsetTop - parent.scrollTop + borderYOffset;
2748         parent = parent.offsetParent;
2749      }
2750
2751      if ( accountForDocScroll ) {
2752         x -= this.docScrollLeft();
2753         y -= this.docScrollTop();
2754      }
2755
2756      return { x:x, y:y };
2757   },
2758
2759   /**
2760    *  Mozilla did not report all of the parents up the hierarchy via the
2761    *  offsetParent property that IE did.  So for the calculation of the
2762    *  offsets we use the offsetParent property, but for the calculation of
2763    *  the scrollTop/scrollLeft adjustments we navigate up via the parentNode
2764    *  property instead so as to get the scroll offsets...
2765    *
2766    **/
2767   _toAbsoluteMozilla: function(element,accountForDocScroll) {
2768      var x = 0;
2769      var y = 0;
2770      var parent = element;
2771      while ( parent ) {
2772         x += parent.offsetLeft;
2773         y += parent.offsetTop;
2774         parent = parent.offsetParent;
2775      }
2776
2777      parent = element;
2778      while ( parent &&
2779              parent != document.body &&
2780              parent != document.documentElement ) {
2781         if ( parent.scrollLeft  )
2782            x -= parent.scrollLeft;
2783         if ( parent.scrollTop )
2784            y -= parent.scrollTop;
2785         parent = parent.parentNode;
2786      }
2787
2788      if ( accountForDocScroll ) {
2789         x -= this.docScrollLeft();
2790         y -= this.docScrollTop();
2791      }
2792
2793      return { x:x, y:y };
2794   },
2795
2796   docScrollLeft: function() {
2797      if ( window.pageXOffset )
2798         return window.pageXOffset;
2799      else if ( document.documentElement && document.documentElement.scrollLeft )
2800         return document.documentElement.scrollLeft;
2801      else if ( document.body )
2802         return document.body.scrollLeft;
2803      else
2804         return 0;
2805   },
2806
2807   docScrollTop: function() {
2808      if ( window.pageYOffset )
2809         return window.pageYOffset;
2810      else if ( document.documentElement && document.documentElement.scrollTop )
2811         return document.documentElement.scrollTop;
2812      else if ( document.body )
2813         return document.body.scrollTop;
2814      else
2815         return 0;
2816   }
2817
2818};
Note: See TracBrowser for help on using the repository browser.