ViewVC Help
View File | Revision Log | Show Annotations | Root Listing
root/cvsroot/COMP/WEBCONDDB/php_CondDB/js/rico.js
Revision: 1.1
Committed: Fri Jun 29 07:47:26 2007 UTC (17 years, 10 months ago) by kdziedzi
Content type: application/javascript
Branch: MAIN
CVS Tags: V01-01-02, V01-01-01, V1_01_00, V01-01-00, V1_00_01, HEAD
Log Message:
Introducing new order in project

File Contents

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