Statistics
| Revision:

root / web-presentation / trunk / eneraptor-web-presentation / web-app / js / prototype / rico.js @ 86

History | View | Annotate | Download (82.4 KB)

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
18
var Rico = {
19
  Version: '1.1-beta2'
20
}
21

    
22
Rico.ArrayExtensions = new Array();
23

    
24
if (Object.prototype.extend) {
25
   // in prototype.js...
26
   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Object.prototype.extend;
27
}
28

    
29
if (Array.prototype.push) {
30
   // in prototype.js...
31
   Rico.ArrayExtensions[ Rico.ArrayExtensions.length ] = Array.prototype.push;
32
}
33

    
34
if (!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

    
46
if (!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

    
57
if (!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
77
if ( 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

    
100
document.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

    
127
Rico.Accordion = Class.create();
128

    
129
Rico.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

    
240
Rico.Accordion.Tab = Class.create();
241

    
242
Rico.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

    
309
Rico.AjaxEngine = Class.create();
310

    
311
Rico.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

    
498
var ajaxEngine = new Rico.AjaxEngine();
499

    
500

    
501
//-------------------- ricoColor.js
502
Rico.Color = Class.create();
503

    
504
Rico.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

    
593
Rico.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
 */
607
Rico.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

    
635
Rico.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

    
690
Rico.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

    
735
Rico.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
960
Rico.DragAndDrop = Class.create();
961

    
962
Rico.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
1326
Rico.Draggable = Class.create();
1327

    
1328
Rico.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
1405
Rico.Dropzone = Class.create();
1406

    
1407
Rico.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
 **/
1524
if ( window.Effect == undefined )
1525
   Effect = {};
1526

    
1527
Effect.SizeAndPosition = Class.create();
1528
Effect.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

    
1612
Effect.Size = Class.create();
1613
Effect.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

    
1620
Effect.Position = Class.create();
1621
Effect.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

    
1628
Effect.Round = Class.create();
1629
Effect.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

    
1638
Effect.FadeTo = Class.create();
1639
Effect.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

    
1701
Effect.AccordionSize = Class.create();
1702

    
1703
Effect.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

    
1764
Rico.LiveGridMetaData = Class.create();
1765

    
1766
Rico.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

    
1806
Rico.LiveGridScroller = Class.create();
1807

    
1808
Rico.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

    
1905
Rico.LiveGridBuffer = Class.create();
1906

    
1907
Rico.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 --------------------------------------------------
2078
Rico.GridViewPort = Class.create();
2079

    
2080
Rico.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

    
2171
Rico.LiveGridRequest = Class.create();
2172
Rico.LiveGridRequest.prototype = {
2173
   initialize: function( requestOffset, options ) {
2174
      this.requestOffset = requestOffset;
2175
   }
2176
};
2177

    
2178
// Rico.LiveGrid -----------------------------------------------------
2179

    
2180
Rico.LiveGrid = Class.create();
2181

    
2182
Rico.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
2325
Rico.LiveGridSort = Class.create();
2326

    
2327
Rico.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

    
2475
Rico.TableColumn = Class.create();
2476

    
2477
Rico.TableColumn.UNSORTED  = 0;
2478
Rico.TableColumn.SORT_ASC  = "ASC";
2479
Rico.TableColumn.SORT_DESC = "DESC";
2480

    
2481
Rico.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

    
2521
var 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
};