source: branches/util-apps/DomainUtil/web-app/js/prototype/rico.js @ 766

Last change on this file since 766 was 766, checked in by gav, 13 years ago

Add branches/util-apps/DomainUtil application.

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