﻿// copyright 2006 Dennis Hall
// this copyright notice must stay intact at all times.
var jg_ihtm, jg_ie, jg_fast, jg_dom, jg_moz, jg_n4 = (document.layers && typeof document.classes != "undefined");
function chkDHTM(x, i) {
    x = document.body || null;
    jg_ie = x && typeof x.insertAdjacentHTML != "undefined";
    jg_dom = (x && !jg_ie && typeof x.appendChild != "undefined" && typeof document.createRange != "undefined" && typeof (i = document.createRange()).setStartBefore != "undefined" && typeof i.createContextualFragment != "undefined");
    jg_ihtm = !jg_ie && !jg_dom && x && typeof x.innerHTML != "undefined";
    jg_fast = jg_ie && document.all && !window.opera;
    jg_moz = jg_dom && typeof x.style.MozOpacity != "undefined";
}
function pntDoc() {
    this.wnd.document.write(jg_fast ? this.htmRpc() : this.htm);
    this.htm = "";
}
function pntCnvDom() {
    var x = this.wnd.document.createRange();
    x.setStartBefore(this.cnv);
    x = x.createContextualFragment(jg_fast ? this.htmRpc() : this.htm);
    if (this.cnv) {
        this.cnv.appendChild(x);
    }
    this.htm = "";
}
function pntCnvIe() {
    if (this.cnv) {
        this.cnv.insertAdjacentHTML("BeforeEnd", jg_fast ? this.htmRpc() : this.htm);
    }
    this.htm = "";
}
function pntCnvIhtm() {
    if (this.cnv) {
        this.cnv.innerHTML += this.htm;
    }
    this.htm = "";
}
function pntCnv() {
    this.htm = "";
}
function mkDiv(x, y, w, h, a) {
    if (!a) a = 1;
    //this.htm+="<div style=\"position:absolute;"+(  (jg_ie) ? "filter:alpha(opacity="+(a*100)+");zoom:1" : "opacity:"+a  )+";left:"+x+"px;top:"+y+"px;width:"+w+"px;height:"+h+"px;clip:rect(0,"+w+"px,"+h+"px,0);background-color:"+this.color+(!jg_moz?";overflow:hidden":"")+";\"></div>";
    this.htm += "<div style=\"position:absolute;left:" + x + "px;top:" + y + "px;width:" + w + "px;height:" + h + "px;clip:rect(0," + w + "px," + h + "px,0);background-color:" + this.color + (!jg_moz ? ";overflow:hidden" : "") + ";\"></div>";
}
function mkDivIe(x, y, w, h) {
    this.htm += "%%" + this.color + ";" + x + ";" + y + ";" + w + ";" + h + ";";
}
function mkDivPrt(x, y, w, h, a) {
    if (!a) a = 1;
    this.htm += "<div style=\"position:absolute;" + ((jg_ie) ? "filter:alpha(opacity=" + (a * 100) + ");zoom:1" : "opacity:" + a) + ";border-left:" + w + "px solid " + this.color + ";left:" + x + "px;top:" + y + "px;width:0px;height:" + h + "px;clip:rect(0," + w + "px," + h + "px,0);background-color:" + this.color + (!jg_moz ? ";overflow:hidden" : "") + ";\"></div>";
}
function mkLyr(x, y, w, h) {
    this.htm += "<layer " + "left=\"" + x + "\" " + "top=\"" + y + "\" " + "width=\"" + w + "\" " + "height=\"" + h + "\" " + "bgcolor=\"" + this.color + "\"></layer>\n";
}
var regex = /%%([^;]+);([^;]+);([^;]+);([^;]+);([^;]+);/g;
function htmRpc() {
    return this.htm.replace(regex, "<div style=\"overflow:hidden;position:absolute;background-color:" + "$1;left:$2;top:$3;width:$4;height:$5\"></div>\n");
}
function htmPrtRpc() {
    return this.htm.replace(regex, "<div style=\"overflow:hidden;position:absolute;background-color:" + "$1;left:$2;top:$3;width:$4;height:$5;border-left:$4px solid $1\"></div>\n");
}
function mkRect(x, y, w, h) {
    var s = this.stroke;
    this.mkDiv(x, y, w, s);
    this.mkDiv(x + w, y, s, h);
    this.mkDiv(x, y + h, w + s, s);
    this.mkDiv(x, y + s, s, h - s);
}
function jsgStroke() {
    this.DOTTED = -1;
}
var Stroke = new jsgStroke();
function jsGraphics(id, wnd) {
    this.setColor = new Function("arg", "this.color = arg.toLowerCase();");
    this.setStroke = function(x) {
        this.stroke = x;
        this.drawRect = mkRect;
    };
    this.setPrintable = function(arg) {
        this.printable = arg;
        if (jg_fast) {
            this.mkDiv = mkDivIe;
            this.htmRpc = arg ? htmPrtRpc : htmRpc;
        } else {
            this.mkDiv = jg_n4 ? mkLyr : arg ? mkDivPrt : mkDiv;
        }
    };
    this.setFont = function(fam, sz, sty) {
        this.ftFam = fam;
        this.ftSz = sz;
        this.ftSty = sty || Font.PLAIN;
    };
    this.fillRect = function(x, y, w, h, a) {
        this.mkDiv(x, y, w, h, a);
    };
    this.drawPolygon = function(x, y) {
        this.drawPolyline(x, y);
        this.drawLine(x[x.length - 1], y[x.length - 1], x[0], y[0]);
    };
    this.clear = function() {
        this.htm = "";
        if (this.cnv) {
            this.cnv.innerHTML = this.defhtm;
        }
    };
    this.setStroke(1);
    this.color = "#000000";
    this.htm = "";
    this.wnd = wnd || window;
    if (!(jg_ie || jg_dom || jg_ihtm)) {
        chkDHTM();
    }
    if (typeof id != "string" || !id) {
        this.paint = pntDoc;
    } else {
        this.cnv = document.all ? (this.wnd.document.all[id] || null) : document.getElementById ? (this.wnd.document.getElementById(id) || null) : null;
        this.defhtm = (this.cnv && this.cnv.innerHTML) ? this.cnv.innerHTML : "";
        this.paint = jg_dom ? pntCnvDom : jg_ie ? pntCnvIe : jg_ihtm ? pntCnvIhtm : pntCnv;
    }
    this.setPrintable(false);
}
function integer_compare(x, y) {
    return (x < y) ? -1 : ((x > y) * 1);
}

function drag_select(container_id, type_of_selectable, callback_function) {

    var me = this;
    me.keyModifier = null;
    me.selectionModifier = "";
    var _length = 0;
    var SHIFT = 16;
    var CTRL = 17;
    var ALT = 18;
    var previousSelections = [];

    //ox and oy are the origin x and y :: the point where we begin making the selection box.
    //mx and my are the mouse x and y
    var ox = 0, oy = 0, mx = 0, my = 0, jg, IE = document.all ? true : false, originset = false, selectables = [];
    if (!IE) document.captureEvents(Event.MOUSEMOVE);

    add_container(container_id, type_of_selectable, callback_function);

    document.body.onmouseup = dontTrackTheMouse;
    document.onkeydown = setKeyModifier;
    document.onkeyup = clearKeyModifier;

    //should be stored in an array of "selectables_containers"
    //.. so more than one group--defined by it's container--can be 'selectable'
    //var theOL=document.getElementById( container_id );

    //should CREATE, then append.. some tiny box
    var myCanvas = document.createElement('div');
    myCanvas.style.position = "absolute";
    myCanvas.style.top = "0";
    myCanvas.style.left = "0";
    myCanvas.style.width = "1px";
    myCanvas.style.height = "1px";
    myCanvas.style.fontsize = "1px";
    myCanvas.id = "myCanvas";
    document.body.appendChild(myCanvas);
    jg = new jsGraphics("myCanvas");

    //for ie7 (and maybe ie6 with some help)
    var img = document.createElement('img');
    img.addClassName('dragimage');
    img.src = "images/blue.png";
    img.style.position = "absolute";
    img.style.display = "none";
    document.body.appendChild(img);


    function add_container(container_id, type_of_selectable, callback_function) {
        var container = document.getElementById(container_id);
        container.onmousedown = trackthemouse;
        container.onselectstart = function() { return false; };
        //var elements = container.getElementsByTagName(type_of_selectable);
        var elements = $$('#' + container_id + ' ' + type_of_selectable);
        selectables[_length] = { elements: elements, elxys: getpoints(elements, _length), selectionMask: [], f: callback_function };
        _length++;
    }

    //gets the 4 xy coordinate pairs of the rectangle defined by the screen rendering of all the selectable element
    function getpoints(elements, s) {
        var points = [];
        var len = elements.length;
        (function loop(I) {
            if (I == len) return;
            var i = I;


            var el = elements[i];
            el.style.cursor = "default";
            el.onclick = function(e) {
                clicked(s, i, e);
                /*console.log(s + ', '+i);selectables[j].selectionMask[i] = true;selectables[j].f(this, true);*/
            };
            var xy = getxy(el);
            points[i] = [xy[0], xy[1], xy[0] + el.offsetWidth, xy[1] + el.offsetHeight];


            loop(++I);
        })(0);

        return points;
    }

    function clicked(s, si, e) {
        setSelectionModifier(e);
        setPreviousSelections();
        var elements = selectables[s].elements;
        var length = elements.length;
        for (var i = 0; i < length; i++) {
            isOverlapping = si == i;
            if (me.selectionModifier == "") {
                //console.log('no ctrl');
                selectables[s].selectionMask[i] = isOverlapping;
            } else {
                //console.log("yes ctrl");
                selectables[s].selectionMask[i] = (isOverlapping || previousSelections[s][i]) && !(isOverlapping && previousSelections[s][i]);
                //if(i==0) console.dir(previousSelections[s]);
            }
            selectables[s].f(elements[i], selectables[s].selectionMask[i]);
        }
    }

    function getxy(obj) {
        var curleft = 0, curtop = 0;
        if (obj.offsetParent) {
            curleft = obj.offsetLeft;
            curtop = obj.offsetTop;
            while (obj = obj.offsetParent) {
                curleft += obj.offsetLeft;
                curtop += obj.offsetTop;
            }
        }
        return [curleft, curtop];
    }

    function setSelectionModifier(e) {
        if (!e) var e = window.event;
        if (e && e.ctrlKey) {
            me.selectionModifier = "xor";
            return;
        }
        if (me.keyModifier == CTRL) {
            me.selectionModifier = "xor";
        } else if (me.keyModifier == SHIFT) {
            me.selectionModifier = "add";
        } else if (me.keyModifier == ALT) {
            me.selectionModifier = "remove";
        } else {
            me.selectionModifier = "";
        }
    }


    function trackthemouse() {
        setSelectionModifier();
        document.onmousemove = mouseMoveEvent;
        img.style.width = '1px';
        img.style.height = '1px';
        img.style.display = "";
        return false;
    }

    function dontTrackTheMouse() {
        document.onmousemove = null;
        originset = false;
        //img.style.display = "none";
        $$('.dragimage').each(function(el) { el.style.display = 'none'; });
        jg.clear();
    }

    function setKeyModifier(e) {
        //not checking if MORE THAN ONE key is held down!
        if (!e) var e = window.event;
        var code;
        if (e.keyCode) code = e.keyCode;
        else if (e.which) code = e.which;
        //console.log(code);
        me.keyModifier = code;
    }


    function clearKeyModifier() {
        me.keyModifier = null;
    }

    function setPreviousSelections() {
        var len = selectables.length;
        for (var s = 0; s < len; s++) {
            var len2 = selectables[s].elements.length;
            previousSelections[s] = [];
            for (var i = 0; i < len2; i++) {
                previousSelections[s][i] = selectables[s].selectionMask[i];
            }
        }
    }

    function mouseMoveEvent(e) {
        if (!e) var e = window.event;

        if (e && e.ctrlKey) {
            me.selectionModifier = "xor";
        }

        if (e.pageX || e.pageY) {
            mx = e.pageX;
            my = e.pageY;
        }
        else if (e.clientX || e.clientY) {
            mx = e.clientX + document.body.scrollLeft
				+ document.documentElement.scrollLeft;
            my = e.clientY + document.body.scrollTop
				+ document.documentElement.scrollTop;
        }

        if (!originset) {
            ox = mx;
            oy = my;
            originset = true;

            setPreviousSelections();

            //console.dir(previousSelections);
            //console.log('setting origin');

            //return false;
        }

        var dx = (mx > ox) ? mx - ox : ox - mx;
        var dy = (my > oy) ? my - oy : oy - my;

        //if the mouse has BARELY been moved, assume it was an accident while attempting a click
        if (dx + dy < 3) return;

        //ul = upperleft, lr = lowerright (corners)
        var ul = [], lr = [];

        // the next 20 lines or so could probably be simplified a little more.
        if (ox > mx) {
            ul[0] = mx;
            lr[0] = ox - mx;
        } else {
            ul[0] = ox;
            lr[0] = mx - ox;
        }
        if (oy > my) {
            ul[1] = my;
            lr[1] = oy - my;
        } else {
            ul[1] = oy;
            lr[1] = my - oy;
        }

        var x2 = ul[0];
        var y2 = ul[1];
        var x3 = x2 + lr[0];
        var y3 = y2 + lr[1];

        var len = selectables.length;
        for (var s = 0; s < len; s++) {

            var elxy = selectables[s].elxys;
            var length = elxy.length;
            var elements = selectables[s].elements;

            for (var i = 0; i < length; i++) {
                // if selection area (= x2,y2, x3,y3) overlaps selectables[i]...
                var isOverlapping = !((elxy[i][0] < x2 && elxy[i][2] < x2) || (elxy[i][0] > x3 && elxy[i][2] > x3) || (elxy[i][1] < y2 && elxy[i][3] < y2) || (elxy[i][1] > y3 && elxy[i][3] > y3));

                if (me.selectionModifier == "") {
                    //console.log('no ctrl');
                    selectables[s].selectionMask[i] = isOverlapping;
                } else {
                    //console.log("yes ctrl");
                    selectables[s].selectionMask[i] = (isOverlapping || previousSelections[s][i]) && !(isOverlapping && previousSelections[s][i]);
                    //if(i==0) console.dir(previousSelections[s]);
                }
                selectables[s].f(elements[i], selectables[s].selectionMask[i]);
            }


        }

        jg.clear();
        jg.setColor("#557799"); // blue
        jg.drawRect(ul[0], ul[1], lr[0], lr[1]);
        //if(!document.all) {
        //	jg.fillRect(++ul[0], ++ul[1], --lr[0], --lr[1], 0.3);
        //} else {
        img.style.top = ul[1] + 'px';
        img.style.left = ul[0] + 'px';
        img.style.width = dx + 'px';
        img.style.height = dy + 'px';
        //img.style.width = "100px";
        //img.style.height = "100px";
        //}
        jg.paint();
        return false;
    }

    //alert( selectables.length );
    //alert( selectables[0].elements[0].length );
    //alert( selectables[0].func );

}

Element.addMethods({
    disableSelection: function(element) {
        element.onselectstart = function() {
            return false;
        };
        element.unselectable = "on";
        element.style.MozUserSelect = "none";
        element.style.cursor = "default";
    }
});

Log = Class.create();
Log.prototype = {
    initialize: function(elId) {
        this._element = $(elId);
        this._isEnabled = true;
    },
    disable: function() {
        this._isEnabled = false;
    },
    enable: function() {
        this._isEnabled = true;
    },
    purge: function() {
        this._element.update('');
    },
    write: function(str) {
        if (log._isEnabled) {
            var newContent = this._element.innerHTML + '<br/>' + str;
            this._element.update(newContent);
        }
    }
}


Array.prototype.sortNumbers = function() {
    for (i = 0; i < this.length; i++) {
        for (j = i + 1; j < this.length; j++) {
            if (Number(this[i]) > Number(this[j])) {
                tempValue = this[j];
                this[j] = this[i];
                this[i] = tempValue;
            }
        }
    }
}

Hash.addMethods({
    length: function() {
        return this.keys().length;
    },
    empty: function() {
        this.each(function(pair) {
            this.unset(pair.key);
        } .bind(this));
    },
    last: function() {
        return this.values()[this.length() - 1];
    }
});

SelectionPaneHolder = new function() {
    this._selectionPanes = new Hash();
    this.register = function(SelectionPane) {
        this._selectionPanes.set(SelectionPane.getElementId(), SelectionPane);
    };
    this.resetPanes = function() {
        this._selectionPanes.values().each(function(pane) {
            pane.reset();
        })
    }
};



SelectionSortable = Class.create();
SelectionSortable.prototype = {
    initialize: function(elementId, options) {
        this._element = $(elementId);
        this._elementId = elementId;
        this._options = options;
    },
    onUpdate: function() { },
    create: function() {

        Sortable.onHover = function(element, dropon, overlap) {
            if (Element.isParent(dropon, element)) return;

            if (overlap > .33 && overlap < .66 && Sortable.options(dropon).tree) {
                return;
            } else if (overlap > 0.5) {

                if (element.next() != dropon) {
                    /**
                    dropon.setStyle({'margin':'0 0 0 20px'});
                    if(dropon.next(0)) {			
                    dropon.next(0).setStyle({'margin':'0'});
                    }
                    if(dropon.previous(0)) {
                    dropon.previous(0).setStyle({'margin':'0'});
                    }
                    */
                }


                Sortable.mark(dropon, 'before');
                if (dropon.previousSibling != element) {
                    var oldParentNode = element.parentNode;
                    element.style.visibility = "hidden"; // fix gecko rendering
                    dropon.parentNode.insertBefore(element, dropon);
                    if (dropon.parentNode != oldParentNode)
                        Sortable.options(oldParentNode).onChange(element);
                    Sortable.options(dropon.parentNode).onChange(element);
                }
            } else {
                Sortable.mark(dropon, 'after');
                /**
                if(dropon.next(0)) {			
                dropon.next(0).setStyle({'margin':'0'});
                }
                if(dropon.previous(0)) {
                dropon.previous(0).setStyle({'margin':'0'});
                }			
                dropon.setStyle({'margin':'0'});
                */
                var nextElement = dropon.nextSibling || null;
                if (nextElement != element) {
                    var oldParentNode = element.parentNode;
                    element.style.visibility = "hidden"; // fix gecko rendering
                    dropon.parentNode.insertBefore(element, nextElement);
                    if (dropon.parentNode != oldParentNode)
                        Sortable.options(oldParentNode).onChange(element);
                    Sortable.options(dropon.parentNode).onChange(element);
                }
            }
        };

        Sortable.create(this._elementId, options);

    }

}

var SelectableObject = Class.create();
SelectableObject.prototype = {

    initialize: function(elementId) {
        this._elementId = elementId;
        this._element = $(elementId);

        this._isSelected = false;
        this._isClicked = false;
        this._selectClassName = 'selection_pane_selected';
        this.setDimensions();
        this.setOriginalPosition();
        this._disableSelection();
    },
    getPosition: function() {
        return this._element.cumulativeOffset();
    },
    getDimensions: function() {
        return this._element.getDimensions();
    },
    setDimensions: function() {
        var dim = this._element.getDimensions();
        this._width = dim.width;
        this._height = dim.height;

    },
    setOriginalPosition: function() {
        var pos = this.getPosition();
        this._originalX = pos.left;
        this._originalY = pos.top;
        this._x1 = pos.left;
        this._y1 = pos.top;
        this._x2 = this._x1 + this._width;
        this._y2 = this._y1 + this._height;
    },
    getX1: function() {
        return this._x1;
    },
    getX2: function() {
        return this._x2;
    },
    getY1: function() {
        return this._y1;
    },
    getY2: function() {
        return this._y2;
    },
    getWidth: function() {
        return this._width;
    },
    getHeight: function() {
        return this._height;
    },
    select: function() {

        this._isSelected = true;
        this.onSelect();
    },
    click: function() {
        this._isClicked = true;
    },
    unselect: function() {
        this._isSelected = false;
        this._isClicked = false;
        this.onUnselect();
    },
    getElement: function() {
        return this._element;
    },
    onSelect: function() {
        this._element.addClassName(this._selectClassName);
    },
    onUnselect: function() {
        this._element.removeClassName(this._selectClassName);
    },
    isSelected: function() {
        return this._isSelected == true;
    },
    hasMoved: function() {
        var pos = this.getPosition();
        if (pos.left != this._originalX
           || pos.top != this._originalY) {
            return true;
        }
        return false;
    },
    isSelectedBySelectionTool: function(selectionTool) {
        if (selectionTool.getX1() <= this._x1
           && selectionTool.getY1() <= this._1
           && selectionTool.getX2() <= this._x2
           && selectionTool.getY2() <= this._y2) {
            return true;
        }
        return false;
    },

    _disableSelection: function() {
        this._element.disableSelection();
        this._element.descendants().each(function(el) {
            el.disableSelection();
        });
    }

}

var SelectionPanel = Class.create();

SelectionPanel.prototype = {

    initialize: function(paneElement, options) {
        this._element = $(paneElement);
        this._elementId = paneElement;
        this._descendants = this._element.descendants();
        this._selectableObjects = new Hash();
        this._selectedObjects = new Hash();
        this._options = options;
        this._drag_n_drop = true;
        this._multipleSelection = false; // options for multiple select on canvas with ctrl key
        if (typeof options == 'undefined' || options == null) this._options = {};

        this._options.selectable_class = (this._options.selectable_class) ? this._options.selectable_class : 'selection_pane_selectable';
        if (this._options.sortable_id) {
            this._sortableId = this._options.sortable_id;
        } else {
            this._sortableId = 'sortable';
        }
        if (this._options.drag_n_drop == false) {
            this._drag_n_drop = false;
        } else {
            this._drag_n_drop = true;
        }
        if (typeof this._options.onDrop != 'function') {
            this._options.onDrop = function() { };
        }
        this._ghosting = false;
        if (this._options.ghosting) {
            this._ghosting = this._options.ghosting;
        }
        this._selectedElements = new Hash();
        this._initSelectableElements();
        this._initSelectionTool();
        this._initEvents();
        this._createSingleSortable();


        SelectionPaneHolder.register(this);
    },
    _clearSelectedElements: function() {
        this._selectedElements.values().each(function(el) {
            el.removeClassName('selection_pane_selected');
        });
        this._selectedElements = new Hash(); // clear selected elements
        this._selectedObjects = new Hash();
    },
    reset: function() {
        this._destroySingleSortable();

        this._clearSelectedElements();
        this._initSelectableElements();
        this._initEvents();
        this._initSelectionTool();

        this._createSingleSortable();

    },
    getElementId: function() {
        return this._elementId;
    },
    _getSortableOptions: function() {
        change = function(element) { // element is the element clicked on
            element.addClassName('selection_pane_sortable_moving');

            this._selectedObjects = this.getSelectedElements();

            // Add a class to elements the user wants to move
            this.getSelectedElementsPositionOrder().values().each(function(el) {
                el.addClassName('selection_pane_sortable_moving_child');
            });
            if (!$('selection_pane_sortable_caption') && this._selectedObjects.length() > 0) {
                //var caption=new Element('div');
                //caption.addClassName('selection_pane_sortable_caption');
                //caption.writeAttribute('id','selection_pane_sortable_caption');
                //caption.update('+'+this._selectedObjects.length());
                //element.appendChild(caption);    
            }
        } .bind(this);

        update = function(element) { // element in that case is the sortable container that receives the dragged el

            var orderedEls = this.getSelectedElementsPositionOrder(); // this is the selection pane that receives the dropped el, not the origin
            var lastEl = null;

            // Drag and drop inserts element that is being dropped.
            // So we need to look at the order and insert after or before
            droppedEl = $$('.selection_pane_sortable_moving')[0];
            if (droppedEl) { // If no droppedEl, means it comes from another drag and drop


                prevSiblings = droppedEl.previousSiblings();
                nextSiblings = droppedEl.nextSiblings();
                after = true;
                if (prevSiblings.length == 0) {
                    insertEl = nextSiblings[0];
                    after = false;
                } else {
                    insertEl = prevSiblings[0];
                }
                if (after) {
                    insertEl.insert({ after: droppedEl });
                } else {
                    insertEl.insert({ before: droppedEl });
                }
                $$('.selection_pane_sortable_moving_child').reverse().each(function(el) {
                    droppedEl.insert({ after: el });
                    el.removeClassName('selection_pane_sortable_moving_child');
                });
                droppedEl.removeClassName('selection_pane_sortable_moving');
                //if($('selection_pane_sortable_caption')){
                //$('selection_pane_sortable_caption').remove();
                //}
                $$('#' + this._elementId + ' .selection_pane_selected').each(function(el) {
                    el.removeClassName('selection_pane_selected');
                });
                /**
                $(this._sortableId).childElements().each(function(el){
                el.setStyle({'margin':'0'});
                });
                */
                this._onDrop();
                SelectionPaneHolder.resetPanes();

            }
        } .bind(this);
        var containmentIds = new Array();
        containmentIds.push(this._sortableId);
        if (this._options.additional_sortable_ids) {
            containmentIds = containmentIds.concat(this._options.additional_sortable_ids);
        }
        return { ghosting: this._ghosting, dropOnEmpty: false, overlap: 'horizontal', containment: containmentIds, constraint: false,
            onUpdate: update, onChange: change, scrollSensitivity: 80
        };
    },
    _onDrop: function() {
        this._options.onDrop();

    },
    _updateSelectableObjectsPosition: function() {
        this._selectableObjects.each(function(pair) {
            obj = pair.value;
            obj.setOriginalPosition();
        } .bind(this));
    },
    getSelectedElementsPositionOrder: function() {
        var elementsByIdx = new Hash();
        //log.purge();
        this._descendants.each(function(el, idx) {

            if (this._selectedObjects.get(el.identify())) {
                elementsByIdx.set(idx, el);
            }
        } .bind(this));
        return elementsByIdx;
    },
    getSelectedElementIdsWithOrder: function() {
        var ids = new Array();
        this.getSelectedElementsPositionOrder().each(function(pair) {
            ids.push(pair.value.identify()); // since the value is an Element
        } .bind(this));
        return ids;
    },
    getSelectableObjectsWithOrder: function() {
        var orderedObjects = new Hash();
        this._element.descendants().each(function(el) {
            if (this._selectableObjects.get(el.identify())) {
                orderedObjects.set(el.identify(), this._selectableObjects.get(el.identify()));
            }
        } .bind(this));
        return orderedObjects;
    },
    getSelectableElementsIdsWithOrder: function() {
        return this.getSelectableObjectsWithOrder().keys();
    },
    _createSingleSortable: function() {
        if (this._drag_n_drop) {
            options = this._getSortableOptions();
            //Sortable.create(this._sortableId,options);

            var ss = new SelectionSortable(this._sortableId, options);
            ss.create();
        }

    },
    _destroySingleSortable: function() {
        Sortable.destroy(this._sortableId);
    },
    _initSelectableElements: function() {
        this._selectableObjects = new Hash();
        $$('#' + this._element.identify() + ' .' + this._options.selectable_class).each(function(el) {
            this._selectableObjects.set(el.identify(), new SelectableObject(el.identify()));
            Event.observe(el, 'mouseup', this.selectElement.bind(this));
            Event.observe(el, 'mousedown', this.initDrag.bind(this));
        } .bind(this));
        //this._updateSelectableObjectsPosition();
    },
    _initSelectionTool: function() {
        //this._selectionTool=new SelectionTool(this, this._options.selectable_class);
    },
    _activateSelectionTool: function(event) {
        //this._selectionTool.activate(event.pointerX(), event.pointerY());
    },
    _initEvents: function() {
        //Event.observe(document, 'keydown', this.activateMultiple.bind(this));
        //Event.observe(document, 'keyup', this.stopMultiple.bind(this));
        //Event.observe(document, 'mouseup', this._onDoneSelecting.bind(this));
        //Event.observe(this._element, 'mouseup', this._onDoneSelecting.bind(this));
        //Event.observe(this._element, 'mousedown', function(event) { this._activateSelectionTool(event); } .bind(this));
        //Event.observe(this._element, 'mousemove', function(event) { (this._selectionTool) ? this._selectionTool.draw(event.pointerX(), event.pointerY()) : null } .bind(this));
    },
    activateMultiple: function(event) {
        if (event.keyCode == 16) {
            //this._multipleSelection=true;
            //this._selectionTool.enableMultipleSelection();
        }
    },
    stopMultiple: function(event) {
        if (event.keyCode == 16) {
            //this._multipleSelection=false;
            //this._selectionTool.disableMultipleSelection();
        }
    },
    select: function(event) {
        if (event.element().hasClassName('selection_pane_selectable')) {
            //this.selectElement(event);
        } else {
            //this._selectionTool.create(event.pointerX(),event.pointerY());    
        }
    },
    addSelectedObject: function(obj) {
        this._selectedObjects.set(obj.getElement().identify(), obj);
        //log.write(this._selectedObjects.keys().length);
        obj.select();
    },
    removeSelectedObject: function(obj) {
        this._selectedObjects.unset(obj.getElement().identify());
        obj.unselect();
    },
    emptySelection: function() {
        this._selectedObjects.each(function(pair) {
            pair.value.unselect();
        });
        this._selectedObjects.empty();

    },
    _locateClickedElement: function(event) {
        selectedObj = null;
        if (this._selectableObjects.get(event.element().identify())) {
            selectedObj = this._selectableObjects.get(event.element().identify());
        } else {
            // find parent
            event.element().ancestors().each(function(el) {
                if (this._selectableObjects.get(el.identify())) {
                    selectedObj = this._selectableObjects.get(el.identify());
                }
            } .bind(this));
        }
        return selectedObj
    },
    selectElement: function(event) {
        var selectedObj = this._locateClickedElement(event);
        if (!selectedObj) return false;
        // if selected obj has moved, means it's being dropped on mouse up
        if (selectedObj.hasMoved()) {
            selectedObj.setOriginalPosition();
            return false;
        }

        if (!this._multipleSelection) {
            this._selectedObjects.empty();
            this._selectableObjects.each(function(pair) {
                selectableObj = pair.value;
                el = selectableObj.getElement();
                if (el.identify() != selectedObj.getElement().identify()) {
                    selectableObj.unselect();
                }
            } .bind(this));
        }
        if (selectedObj.isSelected()) {
            this.removeSelectedObject(selectedObj);
        } else {

        }
        this.addSelectedObject(selectedObj);

    },
    initDrag: function(event) {
        if (this._drag_n_drop) {
            if (selectedObj = this._locateClickedElement(event)) {
                selectedObj.click();
            }
        }
    },
    getSelectableObjects: function() {
        return this._selectableObjects;
    },

    getSelectedElements: function() {
        //return this._selectionTool.getSelectedElements();

        var retHash = new Hash();
        $$('#' + this._element.identify() + ' .selection_pane_selected').each(function(el) {
            retHash.set(el.identify(), el);
        } .bind(this));
        return retHash;
    },
    _onDoneSelecting: function() {

        //this._selectionTool.destroy();
    },
    getElement: function() {
        return this._element;
    },
    _clearSortableProxy: function(el) {
        //this._sortableProxyEl.update('');  
    },

    moveSelectedElements: function() {

    },
    onDragDrop: function() {
        // Get sortable container and look at its position in the list
        // where it's been dropped
        // take its children and position them at its spot
        // kill the drag and drop container
        //$('debug').update(this._beingDropped.identify());
        this._dropZone.descendants().each(function(el, idx) {
            if (el == this._beingDropped) {
                insertPos = idx;
                el.siblings()[idx - 1].insert({ after: el.innerHTML });
                throw $break;
            }
        } .bind(this));
        this._beingDropped.remove();

    }

}

var SelectionTool = Class.create();

SelectionTool.prototype = {

    initialize: function(canvas, selectableClass) {
        this._canvas = canvas;
        this._canvasEl = canvas.getElement();
        var canvasOffset = this._canvasEl.cumulativeOffset();
        this._canvasX = canvasOffset.left;
        this._canvasY = canvasOffset.top;
        var canvasDim = this._canvasEl.getDimensions();
        this._canvasHeight = canvasDim.height;
        this._canvasWidth = canvasDim.width;
        this._element = null;
        this._selectableClass = selectableClass;
        this._selectedElements = new Hash();
        this._selectMultiple = false;

    },
    activate: function(x, y) {
        this._canvas.emptySelection();
        this._originalX = x;
        this._originalY = y;
        this._height = 0;
        this._width = 0;
        if (this._element) {
            this._element.remove();
        }
        this._element = this._createElement();
        this._element.show();
    },
    _createElement: function() {
        var shape = new Element('div');
        shape.absolutize();
        shape.setStyle({ 'z-index': '999',
            'clear': 'none',
            'width': this._width + 'px',
            'height': this._height + 'px',
            'top': this._originalY + 'px',
            'left': this._originalX + 'px'
        });
        shape.addClassName('selection_tool');
        $(this._canvasEl).appendChild(shape);
        return shape;
    },
    draw: function(targetX, targetY) {

        if (!this._element) return false;
        if (this._element && this._element.visible()) {

            var surface = Math.abs(this._originalX - targetX) * Math.abs(this._originalY - targetY);

            var increase = false;
            if (surface - this._surface >= 0) {
                increase = true;
            }

            var position;
            if (targetX >= this._originalX && targetY >= this._originalY) {
                position = 'se'; // south east
            } else if (targetX >= this._originalX && targetY <= this._originalY) {
                position = 'ne';
            } else if (targetX <= this._originalX && targetY <= this._originalY) {
                position = 'nw';
            } else if (targetX <= this._originalX && targetY >= this._originalY) {
                position = 'sw';
            } else {
                return false;
            }
            if (position == 'se') {
                // just play with the height and width
                this._x1 = this._originalX;
                this._y1 = this._originalY;
            } else if (position == 'sw') {
                this._x1 = targetX;
                this._y1 = this._originalY;
            } else if (position == 'nw') {
                this._x1 = targetX;
                this._y1 = targetY;
            } else if (position == 'ne') {
                this._y1 = targetY;
                this._x1 = this._originalX;
            }

            this._width = Math.abs(targetX - this._originalX);
            this._height = Math.abs(targetY - this._originalY);
            this._surface = this._width * this._height;
            this._x2 = this._x1 + this._width;
            this._y2 = this._y1 + this._height;

            this._element.setStyle({ 'top': this._y1 + 'px', 'left': this._x1 + 'px' });
            this._element.setStyle({ 'height': this._height + 'px', 'width': this._width + 'px' });


            this._onDraw();
            this.onDraw();

        }

    },
    enableMultipleSelection: function(el) {
        this._selectMultiple = true;
    },
    disableMultipleSelection: function(el) {
        this._selectMultiple = false;
    },
    addSelectedElement: function(el) {
        this._selectedElements.set(el.identify(), el);
    },
    removeSelectedElement: function(elId) {
        this._selectedElements.unset(elId);
    },
    hasElement: function(element) {
        return this._selectedElements.any(function(el) {
            return element == el;
        });
    },
    getSelectedElements: function() {
        return this._selectedElements;
    },
    surroundObject: function(selectableObj) {
        if (this._x1 <= selectableObj.getX1()
           && this._y1 <= selectableObj.getY1()
           && this._x2 >= selectableObj.getX2()
           && this._y2 >= selectableObj.getY2()) {
            return true;
        }
        return false;
    },
    intersectsObject: function(selectableObj) {
        //log.purge();
        /*
        log.write('ST x1: '+this._x1);
        log.write('ST y1: '+this._y1);
        log.write('ST x2: '+this._x2);
        log.write('ST y2: '+this._y2);
        
        log.write('Ob x1: '+selectableObj.getX1());
        log.write('Ob y1: '+selectableObj.getY1());
        log.write('Ob x2: '+selectableObj.getX2());
        log.write('Ob y2: '+selectableObj.getY2());
        */


        // Create a virtual rectangle
        rectX1 = (this._x1 <= selectableObj.getX1()) ? this._x1 : selectableObj.getX1();
        rectY1 = (this._y1 <= selectableObj.getY1()) ? this._y1 : selectableObj.getY1();
        rectX2 = (this._x2 >= selectableObj.getX2()) ? this._x2 : selectableObj.getX2();
        rectY2 = (this._y2 >= selectableObj.getY2()) ? this._y2 : selectableObj.getY2();
        rectW = rectX2 - rectX1;
        rectH = rectY2 - rectY1;

        /*
        log.write('VR x1: '+rectX1);
        log.write('VR y1: '+rectY1);
        log.write('VR x2: '+rectX2);
        log.write('VR y2: '+rectY2  );
        log.write('VR wd: '+rectW +' '+(this._width+selectableObj.getWidth()));
        log.write('VR ht: '+rectH +' '+(this._height+selectableObj.getHeight()));
        */
        // Intsection if the height of this rectangle is lower than the sum of the height of the 2 element
        // or
        // if width is lower than width of 2 elements
        if (rectW < this._width + selectableObj.getWidth()
           &&
           rectH < this._height + selectableObj.getHeight()) {

            return true;
        }
        return false;

    },
    _onDraw: function() {
        // Get selectable obj in canvas and...
        var selectableObjects = this._canvas.getSelectableObjects();
        selectableObjects.each(function(pair) {

            var obj = pair.value;

            if (this.intersectsObject(obj)) {
                this._canvas.addSelectedObject(obj);
            } else if (!this.intersectsObject(obj)) {
                this._canvas.removeSelectedObject(obj);
            }

        } .bind(this));

    },
    onDraw: function() { }, // this one can be customized _onDraw is private and shouldn't be modified
    destroy: function() {
        if (this._element) this._element.hide();
    },
    _canDraw: function(targetX, targetY) {
        if (targetX < this._canvasX) {
            return false;
        }
        if (targetY < this._canvasY) {
            return false;
        };
        if (targetX > this._canvasX + this._canvasWidth) {
            return false;
        }
        if (targetY > this._canvasY + this._canvasHeight) {
            return false;
        }
        return true;
    },
    getX1: function() {
        return this._x1;
    },
    getX2: function() {
        return this._x2;
    },
    getY1: function() {
        return this._y1;
    },
    getY2: function() {
        return this._y2;
    }

};

function deleteSelected(event) {
    selectionPane.getSelectedElements().each(function(el) {
        $(el.value).remove();
    })
}


function updateFriendRequests(approve) {
    if (approve != 0 && approve != 1) {
        return false;
    }
    var paneElHash = requestsPane.getSelectedElementsPositionOrder();
    var selectedElements = paneElHash.values();
    var selectedElIds = paneElHash.keys();
    var requestIds = new Array();
    selectedElIds.each(function(elId) {
        requestIds.push(elId.substring(3));
    });
    if (requestIds.length == 0) {
        alert('Please select friend requests');
        return false;
    }

    new Ajax.Request('?AJAX=1&_act=sortorder',
                    {
                        asynchronous: false,
                        onComplete: function(response) {
                            var lastEl = findLastSelectableElement('friends_sortable');
                            selectedElements.each(function(el) {

                                if (approve == 1) {
                                    // add to friend lists

                                    el.removeClassName('selection_pane_selected');
                                    Element.insert(lastEl, { after: el });
                                } else {
                                    el.remove();
                                }
                            });
                            friendsPane.reset();
                            featuredFriendsPane.reset();
                            friendRequestsPane.reset();

                        },
                        method: 'post',
                        parameters: updateFriendRequestBaseParams + 'approve=' + approve + 'sortorder=' + requestIds.join(',')
                    });
    initMySelectionTool();
}

function updateFeaturedFriends() {
    var elementIds = featuredFriendsPane.getSelectableElementsIdsWithOrder();
    var friendIds = new Array();
    elementIds.each(function(elId, idx) {
        friendIds.push(elementIds[idx].substring(3));
    });
    var success = false;
    new Ajax.Request('?AJAX=1&_act=sortorder',
                     {
                         asynchronous: false,
                         method: 'post',
                         onComplete: function(response) {
                             success = true;
                             friendsPane.reset();
                             featuredFriendsPane.reset();
                         },
                         parameters: updateFeaturedFriendsBaseParams + friendIds.join(',')
                     }
                    );

    initMySelectionTool();
    return success;
}

function updateRegularFriends() {
    var elementIds = friendsPane.getSelectableElementsIdsWithOrder();
    var friendIds = new Array();
    elementIds.each(function(elId, idx) {
        friendIds.push(elementIds[idx].substring(3));
    });
    var success = false;
    new Ajax.Request('?AJAX=1&_act=sortorder',
                     {
                         asynchronous: false,
                         method: 'post',
                         onComplete: function(response) {
                             success = true;
                             friendsPane.reset();
                         },
                         parameters: updateFriendsBaseParams + friendIds.join(',')
                     }
                    );
    initMySelectionTool();
    return success;
}


function deleteFriends() {

    // Ajax
    var success = updateRegularFriends();
    if (!success) {
        alert('An error occured');
        return false;
    }
    var selectedElements = friendsPane.getSelectedElementsPositionOrder().values(); // Values of Prototype Hash
    selectedElements.each(function(el) {
        el.remove();
    } .bind(this));
    initMySelectionTool();
    return false;
}

function addToFeaturedFriends() {
    var selectedElements = friendsPane.getSelectedElementsPositionOrder().values(); // Values of Prototype Hash
    if (selectedElements.length == 0) {
        alert("Please select friends to add");
    }
    // Ajax request
    var success = updateFeaturedFriends();
    if (!success) {
        alert('An error occured');
        return false;
    }
    // Get last element to insert after in feature friends
    var lastEl = findLastSelectableElement('featured_sortable');
    selectedElements.each(function(el) {
        var friendId = el.identify().substring(3);
        var newElId = 'ff_' + friendId;
        el.writeAttribute('id', newElId);
        lastEl.insert({ after: el });
        el.removeClassName('selection_pane_selected');
        lastEl = el;
    } .bind(this));
    initMySelectionTool();
    return false;
}

function findLastSelectableElement(divId) {
    var els = $$('#' + divId + ' .selection_pane_selectable');
    return els[els.length - 1];
}
var deleteAlbumAction = '?AJAX=1&_act=pictures_delete';

function deleteAlbums() {

    var elementIds = albumsPane.getSelectedElementIdsWithOrder();
    var albumIds = new Array();
    elementIds.each(function(elId, idx) {
        albumIds.push(elementIds[idx].substring(6));
    });
    var success = false;
    new Ajax.Request(deleteAlbumAction,
                     {
                         asynchronous: true,
                         method: 'post',
                         onComplete: function(response) {
                             success = true;
                             elementIds.each(function(elId) {
                                 $(elId).remove();
                             });
                             albumsPane.reset();
                         },
                         parameters: albumIds.join(',')
                     }
                    );

    initMySelectionTool();
    return success;

}

function updatePhotoOrder() {

    var elementIds = photosPane.getSelectableElementsIdsWithOrder();
    var photoIds = new Array();
    elementIds.each(function(elId, idx) {
        photoIds.push(elementIds[idx].substring(1));
    });
    var success = false;
    new Ajax.Request('?AJAX=1&_act=sortorder',
                     {
                         asynchronous: false,
                         method: 'post',
                         onComplete: function(response) {
                             success = true;

                         },
                         parameters: photoIds.join(',')
                     }
                    );
    initMySelectionTool();
    return success;
}

function deletePhotos(albumId) {
    var elementIds = photosPane.getSelectedElementIdsWithOrder();
    var photoIds = new Array();
    elementIds.each(function(elId, idx) {
        photoIds.push(elementIds[idx].substring(2));
    });
    var success = false;
    new Ajax.Request('?AJAX=1&_act=deleteselected&_tab=PictureAlbum&_for=PictureAlbumSortOrder&_pky=' + albumId,
                     {
                         asynchronous: true,
                         method: 'post',
                         onComplete: function(response) {
                             success = true;
                             elementIds.each(function(elId) {
                                 $(elId).remove();
                             });
                             photosPane.reset();
                         },
                         parameters: 'selected=' + photoIds.join(',')
                     }
                    );
    initMySelectionTool();
    return success;

}
