3 * Copyright 2005 Sabre Airline Solutions
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
15 //-------------------- ricoDragAndDrop.js
16 Rico.DragAndDrop = Class.create();
18 Rico.DragAndDrop.prototype = {
20 initialize: function() {
21 this.dropZones = new Array();
22 this.draggables = new Array();
23 this.currentDragObjects = new Array();
24 this.dragElement = null;
25 this.lastSelectedDraggable = null;
26 this.currentDragObjectVisible = false;
27 this.interestedInMotionEvents = false;
28 this._mouseDown = this._mouseDownHandler.bindAsEventListener(this);
29 this._mouseMove = this._mouseMoveHandler.bindAsEventListener(this);
30 this._mouseUp = this._mouseUpHandler.bindAsEventListener(this);
33 registerDropZone: function(aDropZone) {
34 this.dropZones[ this.dropZones.length ] = aDropZone;
37 deregisterDropZone: function(aDropZone) {
38 var newDropZones = new Array();
40 for ( var i = 0 ; i < this.dropZones.length ; i++ ) {
41 if ( this.dropZones[i] != aDropZone )
42 newDropZones[j++] = this.dropZones[i];
45 this.dropZones = newDropZones;
48 clearDropZones: function() {
49 this.dropZones = new Array();
52 registerDraggable: function( aDraggable ) {
53 this.draggables[ this.draggables.length ] = aDraggable;
54 this._addMouseDownHandler( aDraggable );
57 clearSelection: function() {
58 for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
59 this.currentDragObjects[i].deselect();
60 this.currentDragObjects = new Array();
61 this.lastSelectedDraggable = null;
64 hasSelection: function() {
65 return this.currentDragObjects.length > 0;
68 setStartDragFromElement: function( e, mouseDownElement ) {
69 this.origPos = RicoUtil.toDocumentPosition(mouseDownElement);
70 this.startx = e.screenX - this.origPos.x
71 this.starty = e.screenY - this.origPos.y
72 //this.startComponentX = e.layerX ? e.layerX : e.offsetX;
73 //this.startComponentY = e.layerY ? e.layerY : e.offsetY;
74 //this.adjustedForDraggableSize = false;
76 this.interestedInMotionEvents = this.hasSelection();
77 this._terminateEvent(e);
80 updateSelection: function( draggable, extendSelection ) {
81 if ( ! extendSelection )
82 this.clearSelection();
84 if ( draggable.isSelected() ) {
85 this.currentDragObjects.removeItem(draggable);
87 if ( draggable == this.lastSelectedDraggable )
88 this.lastSelectedDraggable = null;
91 this.currentDragObjects[ this.currentDragObjects.length ] = draggable;
93 this.lastSelectedDraggable = draggable;
97 _mouseDownHandler: function(e) {
98 if ( arguments.length == 0 )
101 // if not button 1 ignore it...
102 var nsEvent = e.which != undefined;
103 if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
106 var eventTarget = e.target ? e.target : e.srcElement;
107 var draggableObject = eventTarget.draggable;
109 var candidate = eventTarget;
110 while (draggableObject == null && candidate.parentNode) {
111 candidate = candidate.parentNode;
112 draggableObject = candidate.draggable;
115 if ( draggableObject == null )
118 this.updateSelection( draggableObject, e.ctrlKey );
120 // clear the drop zones postion cache...
121 if ( this.hasSelection() )
122 for ( var i = 0 ; i < this.dropZones.length ; i++ )
123 this.dropZones[i].clearPositionCache();
125 this.setStartDragFromElement( e, draggableObject.getMouseDownHTMLElement() );
129 _mouseMoveHandler: function(e) {
130 var nsEvent = e.which != undefined;
131 if ( !this.interestedInMotionEvents ) {
132 //this._terminateEvent(e);
136 if ( ! this.hasSelection() )
139 if ( ! this.currentDragObjectVisible )
142 if ( !this.activatedDropZones )
143 this._activateRegisteredDropZones();
145 //if ( !this.adjustedForDraggableSize )
146 // this._adjustForDraggableSize(e);
148 this._updateDraggableLocation(e);
149 this._updateDropZonesHover(e);
151 this._terminateEvent(e);
154 _makeDraggableObjectVisible: function(e)
156 if ( !this.hasSelection() )
160 if ( this.currentDragObjects.length > 1 )
161 dragElement = this.currentDragObjects[0].getMultiObjectDragGUI(this.currentDragObjects);
163 dragElement = this.currentDragObjects[0].getSingleObjectDragGUI();
165 // go ahead and absolute position it...
166 if ( RicoUtil.getElementsComputedStyle(dragElement, "position") != "absolute" )
167 /* if (Element.getStyle(dragElement,'position')=='absolute')*/
168 dragElement.style.position = "absolute";
170 // need to parent him into the document...
171 if ( dragElement.parentNode == null || dragElement.parentNode.nodeType == 11 )
172 document.body.appendChild(dragElement);
174 this.dragElement = dragElement;
175 this._updateDraggableLocation(e);
177 this.currentDragObjectVisible = true;
181 _adjustForDraggableSize: function(e) {
182 var dragElementWidth = this.dragElement.offsetWidth;
183 var dragElementHeight = this.dragElement.offsetHeight;
184 if ( this.startComponentX > dragElementWidth )
185 this.startx -= this.startComponentX - dragElementWidth + 2;
187 if ( this.startComponentY > dragElementHeight )
188 this.starty -= this.startComponentY - dragElementHeight + 2;
190 this.adjustedForDraggableSize = true;
194 _leftOffset: function(e) {
195 return e.offsetX ? document.body.scrollLeft : 0
198 _topOffset: function(e) {
199 return e.offsetY ? document.body.scrollTop:0
203 _updateDraggableLocation: function(e) {
204 var dragObjectStyle = this.dragElement.style;
205 dragObjectStyle.left = (e.screenX + this._leftOffset(e) - this.startx) + "px"
206 dragObjectStyle.top = (e.screenY + this._topOffset(e) - this.starty) + "px";
209 _updateDropZonesHover: function(e) {
210 var n = this.dropZones.length;
211 for ( var i = 0 ; i < n ; i++ ) {
212 if ( ! this._mousePointInDropZone( e, this.dropZones[i] ) )
213 this.dropZones[i].hideHover();
216 for ( var i = 0 ; i < n ; i++ ) {
217 if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
218 if ( this.dropZones[i].canAccept(this.currentDragObjects) )
219 this.dropZones[i].showHover();
224 _startDrag: function(e) {
225 for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
226 this.currentDragObjects[i].startDrag();
228 this._makeDraggableObjectVisible(e);
231 _mouseUpHandler: function(e) {
232 if ( ! this.hasSelection() )
235 var nsEvent = e.which != undefined;
236 if ( (nsEvent && e.which != 1) || (!nsEvent && e.button != 1))
239 this.interestedInMotionEvents = false;
241 if ( this.dragElement == null ) {
242 this._terminateEvent(e);
246 if ( this._placeDraggableInDropZone(e) )
247 this._completeDropOperation(e);
249 this._terminateEvent(e);
250 Rico.animate(new Rico.Effect.Position( this.dragElement, this.origPos.x, this.origPos.y),
253 onFinish : this._doCancelDragProcessing.bind(this) } );
256 Event.stopObserving(document.body, "mousemove", this._mouseMove);
257 Event.stopObserving(document.body, "mouseup", this._mouseUp);
260 _retTrue: function () {
264 _completeDropOperation: function(e) {
265 if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() ) {
266 if ( this.dragElement.parentNode != null )
267 this.dragElement.parentNode.removeChild(this.dragElement);
270 this._deactivateRegisteredDropZones();
272 this.clearSelection();
273 this.dragElement = null;
274 this.currentDragObjectVisible = false;
275 this._terminateEvent(e);
278 _doCancelDragProcessing: function() {
281 if ( this.dragElement != this.currentDragObjects[0].getMouseDownHTMLElement() && this.dragElement)
282 if ( this.dragElement.parentNode != null )
283 this.dragElement.parentNode.removeChild(this.dragElement);
286 this._deactivateRegisteredDropZones();
287 this.dragElement = null;
288 this.currentDragObjectVisible = false;
291 _placeDraggableInDropZone: function(e) {
292 var foundDropZone = false;
293 var n = this.dropZones.length;
294 for ( var i = 0 ; i < n ; i++ ) {
295 if ( this._mousePointInDropZone( e, this.dropZones[i] ) ) {
296 if ( this.dropZones[i].canAccept(this.currentDragObjects) ) {
297 this.dropZones[i].hideHover();
298 this.dropZones[i].accept(this.currentDragObjects);
299 foundDropZone = true;
305 return foundDropZone;
308 _cancelDrag: function() {
309 for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
310 this.currentDragObjects[i].cancelDrag();
313 _endDrag: function() {
314 for ( var i = 0 ; i < this.currentDragObjects.length ; i++ )
315 this.currentDragObjects[i].endDrag();
318 _mousePointInDropZone: function( e, dropZone ) {
320 var absoluteRect = dropZone.getAbsoluteRect();
322 return e.clientX > absoluteRect.left + this._leftOffset(e) &&
323 e.clientX < absoluteRect.right + this._leftOffset(e) &&
324 e.clientY > absoluteRect.top + this._topOffset(e) &&
325 e.clientY < absoluteRect.bottom + this._topOffset(e);
328 _addMouseDownHandler: function( aDraggable )
330 htmlElement = aDraggable.getMouseDownHTMLElement();
331 if ( htmlElement != null ) {
332 htmlElement.draggable = aDraggable;
333 Event.observe(htmlElement , "mousedown", this._onmousedown.bindAsEventListener(this));
334 Event.observe(htmlElement, "mousedown", this._mouseDown);
338 _activateRegisteredDropZones: function() {
339 var n = this.dropZones.length;
340 for ( var i = 0 ; i < n ; i++ ) {
341 var dropZone = this.dropZones[i];
342 if ( dropZone.canAccept(this.currentDragObjects) )
346 this.activatedDropZones = true;
349 _deactivateRegisteredDropZones: function() {
350 var n = this.dropZones.length;
351 for ( var i = 0 ; i < n ; i++ )
352 this.dropZones[i].deactivate();
353 this.activatedDropZones = false;
356 _onmousedown: function () {
357 Event.observe(document.body, "mousemove", this._mouseMove);
358 Event.observe(document.body, "mouseup", this._mouseUp);
361 _terminateEvent: function(e) {
362 if ( e.stopPropagation != undefined )
364 else if ( e.cancelBubble != undefined )
365 e.cancelBubble = true;
367 if ( e.preventDefault != undefined )
370 e.returnValue = false;
374 initializeEventHandlers: function() {
375 if ( typeof document.implementation != "undefined" &&
376 document.implementation.hasFeature("HTML", "1.0") &&
377 document.implementation.hasFeature("Events", "2.0") &&
378 document.implementation.hasFeature("CSS", "2.0") ) {
379 document.addEventListener("mouseup", this._mouseUpHandler.bindAsEventListener(this), false);
380 document.addEventListener("mousemove", this._mouseMoveHandler.bindAsEventListener(this), false);
383 document.attachEvent( "onmouseup", this._mouseUpHandler.bindAsEventListener(this) );
384 document.attachEvent( "onmousemove", this._mouseMoveHandler.bindAsEventListener(this) );
389 var dndMgr = new Rico.DragAndDrop();
390 dndMgr.initializeEventHandlers();
393 //-------------------- ricoDraggable.js
394 Rico.Draggable = Class.create();
396 Rico.Draggable.prototype = {
398 initialize: function( type, htmlElement ) {
400 this.htmlElement = $(htmlElement);
401 this.selected = false;
405 * Returns the HTML element that should have a mouse down event
406 * added to it in order to initiate a drag operation
409 getMouseDownHTMLElement: function() {
410 return this.htmlElement;
414 this.selected = true;
416 if ( this.showingSelected )
419 var htmlElement = this.getMouseDownHTMLElement();
421 var color = Rico.Color.createColorFromBackground(htmlElement);
422 color.isBright() ? color.darken(0.033) : color.brighten(0.033);
424 this.saveBackground = RicoUtil.getElementsComputedStyle(htmlElement, "backgroundColor", "background-color");
425 // this.saveBackground = Element.getStyle(htmlElement,'backgroundColor') || Element.getStyle(htmlElement,'background-color')
426 htmlElement.style.backgroundColor = color.asHex();
427 this.showingSelected = true;
430 deselect: function() {
431 this.selected = false;
432 if ( !this.showingSelected )
435 var htmlElement = this.getMouseDownHTMLElement();
437 htmlElement.style.backgroundColor = this.saveBackground;
438 this.showingSelected = false;
441 isSelected: function() {
442 return this.selected;
445 startDrag: function() {
448 cancelDrag: function() {
451 endDrag: function() {
454 getSingleObjectDragGUI: function() {
455 return this.htmlElement;
458 getMultiObjectDragGUI: function( draggables ) {
459 return this.htmlElement;
462 getDroppedGUI: function() {
463 return this.htmlElement;
466 toString: function() {
467 return this.type + ":" + this.htmlElement + ":";
473 //-------------------- ricoDropzone.js
474 Rico.Dropzone = Class.create();
476 Rico.Dropzone.prototype = {
478 initialize: function( htmlElement ) {
479 this.htmlElement = $(htmlElement);
480 this.absoluteRect = null;
483 getHTMLElement: function() {
484 return this.htmlElement;
487 clearPositionCache: function() {
488 this.absoluteRect = null;
491 getAbsoluteRect: function() {
492 if ( this.absoluteRect == null ) {
493 var htmlElement = this.getHTMLElement();
494 var pos = RicoUtil.toViewportPosition(htmlElement);
496 this.absoluteRect = {
499 bottom: pos.y + htmlElement.offsetHeight,
500 right: pos.x + htmlElement.offsetWidth
503 return this.absoluteRect;
506 activate: function() {
507 var htmlElement = this.getHTMLElement();
508 if (htmlElement == null || this.showingActive)
511 this.showingActive = true;
512 this.saveBackgroundColor = htmlElement.style.backgroundColor;
514 var fallbackColor = "#ffea84";
515 var currentColor = Rico.Color.createColorFromBackground(htmlElement);
516 if ( currentColor == null )
517 htmlElement.style.backgroundColor = fallbackColor;
519 currentColor.isBright() ? currentColor.darken(0.2) : currentColor.brighten(0.2);
520 htmlElement.style.backgroundColor = currentColor.asHex();
524 deactivate: function() {
525 var htmlElement = this.getHTMLElement();
526 if (htmlElement == null || !this.showingActive)
529 htmlElement.style.backgroundColor = this.saveBackgroundColor;
530 this.showingActive = false;
531 this.saveBackgroundColor = null;
534 showHover: function() {
535 var htmlElement = this.getHTMLElement();
536 if ( htmlElement == null || this.showingHover )
539 this.saveBorderWidth = htmlElement.style.borderWidth;
540 this.saveBorderStyle = htmlElement.style.borderStyle;
541 this.saveBorderColor = htmlElement.style.borderColor;
543 this.showingHover = true;
544 htmlElement.style.borderWidth = "1px";
545 htmlElement.style.borderStyle = "solid";
546 //htmlElement.style.borderColor = "#ff9900";
547 htmlElement.style.borderColor = "#ffff00";
550 hideHover: function() {
551 var htmlElement = this.getHTMLElement();
552 if ( htmlElement == null || !this.showingHover )
555 htmlElement.style.borderWidth = this.saveBorderWidth;
556 htmlElement.style.borderStyle = this.saveBorderStyle;
557 htmlElement.style.borderColor = this.saveBorderColor;
558 this.showingHover = false;
561 canAccept: function(draggableObjects) {
565 accept: function(draggableObjects) {
566 var htmlElement = this.getHTMLElement();
567 if ( htmlElement == null )
570 n = draggableObjects.length;
571 for ( var i = 0 ; i < n ; i++ )
573 var theGUI = draggableObjects[i].getDroppedGUI();
574 /* if (Element.getStyle(theGUI,'position')=='absolute')*/
575 if ( RicoUtil.getElementsComputedStyle( theGUI, "position" ) == "absolute" )
577 theGUI.style.position = "static";
578 theGUI.style.top = "";
579 theGUI.style.top = "";
581 htmlElement.appendChild(theGUI);
586 RicoUtil = Object.extend(RicoUtil, {
587 getElementsComputedStyle: function ( htmlElement, cssProperty, mozillaEquivalentCSS) {
588 if ( arguments.length == 2 )
589 mozillaEquivalentCSS = cssProperty;
591 var el = $(htmlElement);
592 if ( el.currentStyle )
593 return el.currentStyle[cssProperty];
595 return document.defaultView.getComputedStyle(el, null).getPropertyValue(mozillaEquivalentCSS);
598 createXmlDocument : function() {
599 if (document.implementation && document.implementation.createDocument) {
600 var doc = document.implementation.createDocument("", "", null);
602 if (doc.readyState == null) {
604 doc.addEventListener("load", function () {
606 if (typeof doc.onreadystatechange == "function")
607 doc.onreadystatechange();
614 if (window.ActiveXObject)
616 function() { return new ActiveXObject('MSXML2.DomDocument') },
617 function() { return new ActiveXObject('Microsoft.DomDocument')},
618 function() { return new ActiveXObject('MSXML.DomDocument') },
619 function() { return new ActiveXObject('MSXML3.DomDocument') }
625 getContentAsString: function( parentNode ) {
626 return parentNode.xml != undefined ?
627 this._getContentAsStringIE(parentNode) :
628 this._getContentAsStringMozilla(parentNode);
631 _getContentAsStringIE: function(parentNode) {
633 for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
634 var n = parentNode.childNodes[i];
635 if (n.nodeType == 4) {
636 contentStr += n.nodeValue;
645 _getContentAsStringMozilla: function(parentNode) {
646 var xmlSerializer = new XMLSerializer();
648 for ( var i = 0 ; i < parentNode.childNodes.length ; i++ ) {
649 var n = parentNode.childNodes[i];
650 if (n.nodeType == 4) { // CDATA node
651 contentStr += n.nodeValue;
654 contentStr += xmlSerializer.serializeToString(n);
660 toViewportPosition: function(element) {
661 return this._toAbsolute(element,true);
664 toDocumentPosition: function(element) {
665 return this._toAbsolute(element,false);
669 * Compute the elements position in terms of the window viewport
670 * so that it can be compared to the position of the mouse (dnd)
671 * This is additions of all the offsetTop,offsetLeft values up the
672 * offsetParent hierarchy, ...taking into account any scrollTop,
673 * scrollLeft values along the way...
675 * IE has a bug reporting a correct offsetLeft of elements within a
676 * a relatively positioned parent!!!
678 _toAbsolute: function(element,accountForDocScroll) {
680 if ( navigator.userAgent.toLowerCase().indexOf("msie") == -1 )
681 return this._toAbsoluteMozilla(element,accountForDocScroll);
685 var parent = element;
688 var borderXOffset = 0;
689 var borderYOffset = 0;
690 if ( parent != element ) {
691 var borderXOffset = parseInt(this.getElementsComputedStyle(parent, "borderLeftWidth" ));
692 var borderYOffset = parseInt(this.getElementsComputedStyle(parent, "borderTopWidth" ));
693 borderXOffset = isNaN(borderXOffset) ? 0 : borderXOffset;
694 borderYOffset = isNaN(borderYOffset) ? 0 : borderYOffset;
697 x += parent.offsetLeft - parent.scrollLeft + borderXOffset;
698 y += parent.offsetTop - parent.scrollTop + borderYOffset;
699 parent = parent.offsetParent;
702 if ( accountForDocScroll ) {
703 x -= this.docScrollLeft();
704 y -= this.docScrollTop();
711 * Mozilla did not report all of the parents up the hierarchy via the
712 * offsetParent property that IE did. So for the calculation of the
713 * offsets we use the offsetParent property, but for the calculation of
714 * the scrollTop/scrollLeft adjustments we navigate up via the parentNode
715 * property instead so as to get the scroll offsets...
718 _toAbsoluteMozilla: function(element,accountForDocScroll) {
721 var parent = element;
723 x += parent.offsetLeft;
724 y += parent.offsetTop;
725 parent = parent.offsetParent;
730 parent != document.body &&
731 parent != document.documentElement ) {
732 if ( parent.scrollLeft )
733 x -= parent.scrollLeft;
734 if ( parent.scrollTop )
735 y -= parent.scrollTop;
736 parent = parent.parentNode;
739 if ( accountForDocScroll ) {
740 x -= this.docScrollLeft();
741 y -= this.docScrollTop();
747 docScrollLeft: function() {
748 if ( window.pageXOffset )
749 return window.pageXOffset;
750 else if ( document.documentElement && document.documentElement.scrollLeft )
751 return document.documentElement.scrollLeft;
752 else if ( document.body )
753 return document.body.scrollLeft;
758 docScrollTop: function() {
759 if ( window.pageYOffset )
760 return window.pageYOffset;
761 else if ( document.documentElement && document.documentElement.scrollTop )
762 return document.documentElement.scrollTop;
763 else if ( document.body )
764 return document.body.scrollTop;
770 Rico.includeLoaded('ricoDragDrop.js');