/**
 * LX' ImageCrop v2.1
 *
 *  Authors: Alexander Schulze
 *  License: GPL v2 or compatible
 *           http://www.fsf.org/licenses/gpl.html
 *  Contact: webmaster@lxhome.de
 *           http://www.lxhome.de
 *
 * This script provides a basic functionality to draw crop areas on an
 * image and to paste necessary size information into a form, which
 * can then be posted to a server side script to crop an image to the
 * specified size.
 *
 *********************************************************************
 *
 * History of Changes:
 * v2.1   Bugfix:  key presses are correctly handled even if no action
 *                 was possible
 *        Bugfix:  selections with a height greater than width are now
 *                 treated correctly when being resized
 * v2.0   Note:    this version is not backwards compatible to v1.x
 *        Misc:    accumulated all functions in an object to allow for
 *                 multiple instances on the same page and freely
 *                 nameable input fields and elements
 *        Feature: maximizing a selection can now also be done by
 *                 calling method maximizeSelection()
 *        Feature: predefining of selections by calling method
 *                 setSelection()
 *        Feature: scrolling while using cursor or page keys should be
 *                 disabled now
 *        Misc:    some minor performance improvements
 * v1.5   Bugfix:  drawing selection from bottom upwards now returns
 *                 correct values
 * v1.4   Feature: horizontal scrolling is now allowed
 *        Bugfix:  positioning with keys returned wrong values
 *        Bugfix:  shading of maximized selection after already
 *                 drawing a selection
 *        Misc:    better XHTML support
 * v1.3   Feature: action frame
 * v1.2   Bugfix:  selection can be expanded to the top when document
 *                 is scrolled
 *        Feature: selection can be finished when cursor is out of
 *                 the image
 *        Feature: clear selection with DEL and ESC, too
 * v1.1   Bugfix:  moving without selection
 *        Bugfix:  selection problem in Firefox
 *        Feature: moving and resizing the selection faster using the
 *                 SHIFT key
 *        Feature: switched resizing the selection to PGUP/PGDN
 *                 because of "find as you type" funtionality in some
 *                 browsers
 *        Feature: biggest possible selection using HOME key
 *        Feature: clear selection using END key
 * v1.0   first release
 *
 *********************************************************************
 *
 * Works with (at least):
 * - modern browsers (Gecko derivates, Opera, Konqueror, IE7)
 * - IE6 (without action frame)
 *
 *********************************************************************
 *
 * Usage:
 * See http://www.lxhome.de/eng/scripts/imagecrop
 *
 *********************************************************************
 *
 * Restrictions:
 * - drawing selections behaves funny if any parent element in
 *   the DOM tree has a special positioning (absolute, relative)
 *
 *********************************************************************
 *
 * Disclaimer:
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License as
 * published  by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA 02110-1301, USA.
 */

function lx_crop ( el_wand, el_crop, el_actarea,
                   i_top, i_bottom, i_left, i_right,
                   c_left, c_top, c_width, c_height )
{
    // Scrollposition auf der Seite bestimmen
    this.getScrollPosition = function()
    {
        if ( !me.active ) return;

        me.scrollX = ( typeof window.pageXOffset == 'undefined' )
                   ? document.body.scrollLeft
                   : window.pageXOffset;

        me.scrollY = ( typeof window.pageYOffset == 'undefined' )
                   ? document.body.scrollTop
                   : window.pageYOffset;
    }

    // diese Crop-Instanz aktivieren oder deaktivieren
    this.setActive = function ( active )
    {
        me.active = active;

        // Tastendrücke an diese Funktion leiten
        if ( active )
            document.onkeydown = me.keyAction;
    }

    // nichts, wirklich nichts
    this.doNothing = function()
    {
        return false;
    }

    // Auswahl markieren Anfang
    this.selectStart = function ( e )
    {
        if ( !me.active ) return;

        e = ( !e ) ? window.event : e;

        me.getScrollPosition();

        if (      e.clientX < me.Crop.offsetLeft - me.scrollX )
            me.startX = me.Crop.offsetLeft;
        else if ( e.clientX > me.Crop.offsetLeft - me.scrollX + me.Crop.width )
            me.startX = me.Crop.offsetLeft + me.Crop.width;
        else
            me.startX = e.clientX + me.scrollX;

        if (      e.clientY < me.Crop.offsetTop - me.scrollY )
            me.startY = me.Crop.offsetTop;
        else if ( e.clientY > me.Crop.offsetTop - me.scrollY + me.Crop.height )
            me.startY = me.Crop.offsetTop + me.Crop.height;
        else
            me.startY = e.clientY + me.scrollY;

        me.reset();

        me.WandAction     = true;
        me.Wand.className = 'crosshair';

        me.Wand.onmouseup     = me.selectEnd;
        me.Wand.onmousemove   = me.selectSize;

        me.Act.onmousemove = me.selectSize;
        document.onmouseup = me.selectEnd;
    }

    // Auswahl markieren Größe ändern
    this.selectSize = function ( e )
    {
        if ( !me.active ) return;

        if ( typeof me.WandAction == 'undefined' || !me.WandAction )
            return;

        e = ( !e ) ? window.event : e;

        if (      e.clientX < me.Crop.offsetLeft - me.scrollX )
            me.s_width = Math.max ( 0,                 me.startX - me.Crop.offsetLeft );
        else if ( e.clientX > me.Crop.offsetLeft - me.scrollX + me.Crop.width )
            me.s_width = Math.max ( 0, me.Crop.width - me.startX + me.Crop.offsetLeft );
        else
            me.s_width = Math.abs ( me.startX - e.clientX - me.scrollX );

        if (      e.clientY < me.Crop.offsetTop - me.scrollY )
            me.s_height = Math.max ( 0,                  me.startY - me.Crop.offsetTop );
        else if ( e.clientY > me.Crop.offsetTop - me.scrollY + me.Crop.height )
            me.s_height = Math.max ( 0, me.Crop.height - me.startY + me.Crop.offsetTop );
        else
            me.s_height = Math.abs ( me.startY - e.clientY - me.scrollY );

        if ( me.s_height == 0 && me.s_width == 0 )
        {
            me.reset();

            return;
        }

        if ( me.fixAspectRatio != 0 )
        {
            if (      me.s_width / me.s_height > me.fixAspectRatio )
                me.s_width  = Math.round ( me.fixAspectRatio * me.s_height );
            else if ( me.s_width / me.s_height < me.fixAspectRatio )
                me.s_height = Math.round ( me.s_width / me.fixAspectRatio  );
        }

        me.Wand.style.width  = me.s_width  + 'px';
        me.Wand.style.height = me.s_height + 'px';

        me.C_L.height = me.C_R.height = me.s_height + 'px';

        // waagerecht
        if ( me.startX > e.clientX + me.scrollX )
            me.Wand.style.left = me.C_L.width = me.startX - me.s_width - me.Crop.offsetLeft + 'px';
        else
            me.Wand.style.left = me.C_L.width = me.startX - me.Crop.offsetLeft + 'px';

        // senkrecht
        if ( me.startY > e.clientY + me.scrollY )
            me.Wand.style.top = me.C_T.height = me.C_L.top = me.C_R.top =
                me.startY - me.s_height - me.Crop.offsetTop + 'px';
        else
            me.Wand.style.top = me.C_T.height = me.C_L.top = me.C_R.top =
                me.startY - me.Crop.offsetTop + 'px';

        me.C_R.width  = me.Crop.width  - parseInt ( me.C_L.width  ) - me.s_width  + 'px';
        me.C_B.height = me.Crop.height - parseInt ( me.C_T.height ) - me.s_height + 'px';
    }

    // Auswahl markieren Ende
    this.selectEnd = function ( e )
    {
        if ( !me.active ||
             typeof me.WandAction == 'undefined' ||
             !me.WandAction )
            return;

        e = ( !e ) ? window.event : e;

        if ( me.s_width  == 0 || typeof me.s_width  == 'undefined' ||
             me.s_height == 0 || typeof me.s_height == 'undefined' ||
             me.startX == e.clientX + me.scrollX ||
             me.startY == e.clientY + me.scrollY )
        {
            me.reset();
        }
        else
        {
            me.icl.value = parseInt ( me.Wand.style.left   );
            me.ict.value = parseInt ( me.Wand.style.top    );
            me.icw.value = parseInt ( me.Wand.style.width  );
            me.ich.value = parseInt ( me.Wand.style.height );

            if ( me.WandAction )
            {
                me.Wand.className = 'move';

                me.Wand.onmousedown   = me.moveStart;
                me.Wand.onmouseup     = me.moveEnd;
                me.Wand.onmousemove   = me.moveMove;
            }
        }

        document.onmouseup = me.doNothing;
        me.aspectRatio = me.s_height / me.s_width;
        me.WandAction  = false;
    }

    // Auswahl verschieben Anfang
    this.moveStart = function ( e )
    {
        if ( !me.active ) return;

        e = ( !e ) ? window.event : e;

        me.WandAction = true;

        me.Act.onmousedown = me.moveStart;
        me.Act.onmouseup   = me.moveEnd;
        me.Act.onmousemove = me.moveMove;

        me.startX = e.clientX - me.Crop.offsetLeft;
        me.startY = e.clientY - me.Crop.offsetTop;
    }

    // Auswahl verschieben Bewegung
    this.moveMove = function ( e )
    {
        if ( !me.active ) return;

        if ( !me.WandAction )
            return;

        e = ( !e ) ? window.event : e;

        var t = parseInt ( me.Wand.style.top  ) - me.startY + e.clientY - me.Crop.offsetTop;
        var l = parseInt ( me.Wand.style.left ) - me.startX + e.clientX - me.Crop.offsetLeft;

        // prüfen, ob Selektion außerhalb des Bildes geht
        if ( t < 0 )
            t = 0;
        else if ( t + me.s_height > me.Crop.height )
            t = me.Crop.height - me.s_height;

        if ( l < 0 )
            l = 0;
        else if ( l + me.s_width  > me.Crop.width )
            l = me.Crop.width  - me.s_width;

        me.Wand.style.top  = t + 'px';
        me.Wand.style.left = l + 'px';

        me.startX = e.clientX - me.Crop.offsetLeft;
        me.startY = e.clientY - me.Crop.offsetTop;

        me.C_T.height = t + 'px';
        me.C_L.top    = t + 'px';
        me.C_L.width  = l + 'px';
        me.C_R.top    = t + 'px';
        me.C_R.width  = me.Crop.width  - parseInt ( me.C_L.width  ) - me.s_width  + 'px';
        me.C_B.height = me.Crop.height - parseInt ( me.C_T.height ) - me.s_height + 'px';
    }

    // Auswahl verschieben Ende
    this.moveEnd = function ( e )
    {
        if ( !me.active ) return;

        e = ( !e ) ? window.event : e;

        me.ict.value = parseInt ( me.Wand.style.top  );
        me.icl.value = parseInt ( me.Wand.style.left );

        me.WandAction = false;

        me.Act.onmousedown = me.selectStart;
        me.Act.onmouseup   = me.selectEnd;
        me.Act.onmousemove = me.selectSize;
    }

    // größtmögliche Auswahl treffen
    this.maximizeSelection = function()
    {
        me.s_width  = me.Crop.width;
        me.s_height = me.Crop.height;

        if ( me.fixAspectRatio != 0 )
        {
            if (      me.s_width / me.s_height > me.fixAspectRatio )
                me.s_width  = Math.round ( me.fixAspectRatio * me.s_height );
            else if ( me.s_width / me.s_height < me.fixAspectRatio )
                me.s_height = Math.round ( me.s_width / me.fixAspectRatio  );
        }

        me.icl.value = me.ict.value = 0;
        me.ich.value = me.s_height;
        me.icw.value = me.s_width;

        me.aspectRatio = me.s_height / me.s_width;

        me.Wand.style.top    = '0px';
        me.Wand.style.left   = '0px';
        me.Wand.style.height = me.s_height + 'px';
        me.Wand.style.width  = me.s_width  + 'px';

        me.C_T.height = me.C_L.width   = me.C_L.top = me.C_R.top ='0px';
        me.C_L.height = me.C_R.height  = me.s_height + 'px';
        me.C_R.width  = me.Crop.width  - me.s_width  + 'px';
        me.C_B.height = me.Crop.height - me.s_height + 'px';

        me.Wand.className = 'move';

        me.Wand.onmousedown = me.moveStart;
        me.Wand.onmouseup   = me.moveEnd;
        me.Wand.onmousemove = me.moveMove;
    }

    // Behandlung von Tastatur-Eingaben
    this.keyAction = function ( e )
    {
        if ( !me.active ) return;

        e = ( !e ) ? window.event : e;

        if ( document.all )
            var k = e.keyCode;
        else
            var k = e.which;

        // größtmögliche Auswahl treffen (POS1)
        if ( k == 36 )
        {
            me.maximizeSelection();
            return false;
        }

        // Auswahl aufheben (END, ESC, ENTF)
        if ( k == 35 || k == 27 || k == 46 )
        {
            me.reset();
            return false;
        }

        if ( me.s_height == 0 || me.s_width == 0 )
            return;

        if ( e.shiftKey )
            value = 10;
        else
            value = 1;

        // links
        if ( k == 37 &&
             parseInt ( me.Wand.style.left ) - value >= 0 )
        {
            me.Wand.style.left = ( parseInt ( me.Wand.style.left ) - value ) + 'px';
            me.icl.value = parseInt ( me.icl.value ) - value;

            me.C_L.width = parseInt ( me.C_L.width ) - value + 'px';
            me.C_R.width = parseInt ( me.C_R.width ) + value + 'px';

            return false;
        }

        // hoch
        if ( k == 38 &&
             parseInt ( me.Wand.style.top ) - value >= 0 )
        {
            me.Wand.style.top = ( parseInt ( me.Wand.style.top ) - value ) + 'px';
            me.ict.value = parseInt ( me.ict.value ) - value;

            me.C_T.height = parseInt ( me.C_T.height ) - value + 'px';
            me.C_B.height = parseInt ( me.C_B.height ) + value + 'px';
            me.C_R.top    = parseInt ( me.C_R.top    ) - value + 'px';
            me.C_L.top    = parseInt ( me.C_L.top    ) - value + 'px';

            return false;
        }

        // rechts
        if ( k == 39 &&
             parseInt ( me.Wand.style.left ) + me.s_width + value <= me.Crop.width )
        {
            me.Wand.style.left = ( parseInt ( me.Wand.style.left ) + value ) + 'px';
            me.icl.value = parseInt ( me.icl.value ) + value;

            me.C_L.width = parseInt ( me.C_L.width ) + value + 'px';
            me.C_R.width = parseInt ( me.C_R.width ) - value + 'px';

            return false;
        }

        // runter
        if ( k == 40 &&
             parseInt ( me.Wand.style.top ) + me.s_height + value <= me.Crop.height )
        {
            me.Wand.style.top = ( parseInt ( me.Wand.style.top ) + value ) + 'px';
            me.ict.value = parseInt ( me.ict.value ) + value;

            me.C_T.height = parseInt ( me.C_T.height ) + value + 'px';
            me.C_B.height = parseInt ( me.C_B.height ) - value + 'px';
            me.C_R.top    = parseInt ( me.C_R.top    ) + value + 'px';
            me.C_L.top    = parseInt ( me.C_L.top    ) + value + 'px';

            return false;
        }

        // proportional vergrößern (PGUP)
        if ( k == 33 &&
             parseInt ( me.Wand.style.top  ) + me.s_height + value <= me.Crop.height &&
             parseInt ( me.Wand.style.left ) + me.s_width  + value <= me.Crop.width )
        {
            if ( me.aspectRatio < 1 )
            {
                me.s_width += value;
                me.s_height = Math.round ( me.aspectRatio * me.s_width );
            }
            else
            {
                me.s_height += value;
                me.s_width   = Math.round ( me.s_height / me.aspectRatio );
            }

            me.Wand.style.width  = me.s_width  + 'px';
            me.Wand.style.height = me.s_height + 'px';

            me.icw.value = me.s_width;
            me.ich.value = me.s_height;

            me.C_R.width  = me.Crop.width  - parseInt ( me.C_L.width  ) - me.s_width  + 'px';
            me.C_B.height = me.Crop.height - parseInt ( me.C_T.height ) - me.s_height + 'px';
            me.C_L.height = me.s_height + 'px';
            me.C_R.height = me.s_height + 'px';

            return false;
        }

        // proportional verkleinern (PGDN)
        if ( k == 34 && me.s_height > value && me.s_width > value )
        {
            if ( me.aspectRatio < 1 )
            {
                me.s_width -= value;
                me.s_height = Math.round ( me.aspectRatio * me.s_width );
            }
            else
            {
                me.s_height -= value;
                me.s_width   = Math.round ( me.s_height / me.aspectRatio );
            }

            me.Wand.style.width  = me.s_width  + 'px';
            me.Wand.style.height = me.s_height + 'px';

            me.icw.value = me.s_width;
            me.ich.value = me.s_height;

            me.C_R.width  = me.Crop.width  - parseInt ( me.C_L.width  ) - me.s_width  + 'px';
            me.C_B.height = me.Crop.height - parseInt ( me.C_T.height ) - me.s_height + 'px';
            me.C_L.height = me.s_height + 'px';
            me.C_R.height = me.s_height + 'px';

            return false;
        }

        // auch wenn keine Aktion passiert ist
        return false;
    }

    // definierte Auswahl treffen
    this.setSelection = function ( left, top, width, height )
    {
        // erstmal in Zahlen umwandeln, sicher ist sicher
        me.s_width  = parseInt ( width  );
        me.s_height = parseInt ( height );
        left        = parseInt ( left   );
        top         = parseInt ( top    );

        // festes Seitenverhältnis beachten
        if ( me.fixAspectRatio != 0 )
        {
            if (      me.s_width / me.s_height > me.fixAspectRatio )
                me.s_width  = Math.round ( me.fixAspectRatio * me.s_height );
            else if ( me.s_width / me.s_height < me.fixAspectRatio )
                me.s_height = Math.round ( me.s_width / me.fixAspectRatio  );
        }

        me.Wand.style.left   = me.C_L.width  = left + 'px';
        me.Wand.style.top    = me.C_T.height = me.C_R.top = me.C_L.top = top  + 'px';
        me.Wand.style.width  = me.s_width + 'px';
        me.Wand.style.height = me.C_R.height = me.C_L.height = me.s_height + 'px';

        me.C_R.width  = me.Crop.width  - me.s_width  - left + 'px';
        me.C_B.height = me.Crop.height - me.s_height - top  + 'px';

        me.icl.value = left;
        me.ict.value = top;
        me.icw.value = me.s_width;
        me.ich.value = me.s_height;

        me.Wand.className = 'move';

        me.Wand.onmousedown = me.moveStart;
        me.Wand.onmouseup   = me.moveEnd;
        me.Wand.onmousemove = me.moveMove;

        document.onmouseup = me.doNothing;

        me.aspectRatio = me.s_height / me.s_width;
        me.WandAction  = false;
    }

    // alle Werte zurücksetzen
    this.reset = function()
    {
        me.Wand.style.width = me.Wand.style.height = me.Wand.style.top = me.Wand.style.left =
        me.C_T.height       = me.C_L.width         = me.C_R.width      = me.C_B.height      = '0px';
        me.icl.value        = me.ict.value         = me.icw.value      = me.ich.value       = 0;
    }

    var me = this;

    // handelt es sich hierbei um die gerade aktive Instanz?
    // muss mittels setActive()-Methode definiert werden, bevor
    // Maus- oder Tastatureingaben angenommen werden
    me.active   = false;

    // Startpunkt der Selektion und deren Höhe und Breite
    me.startX   = 0;
    me.startY   = 0;
    me.scrollX  = 0;
    me.scrollY  = 0;
    me.s_height = 0;
    me.s_width  = 0;

    // Seitenverhältnis der Selektion sowie deren vorgeschriebener Wert
    me.aspectRatio    = 1;
    me.fixAspectRatio = 0;

    me.WandAction = false;

    me.Wand = window.document.getElementById(el_wand);
    me.Crop = window.document.getElementById(el_crop);
    me.Act  = window.document.getElementById(el_actarea);

    me.C_T  = window.document.getElementById(i_top).style;
    me.C_B  = window.document.getElementById(i_bottom).style;
    me.C_L  = window.document.getElementById(i_left).style;
    me.C_R  = window.document.getElementById(i_right).style;

    me.icl  = document.getElementById(c_left);
    me.ict  = document.getElementById(c_top);
    me.icw  = document.getElementById(c_width);
    me.ich  = document.getElementById(c_height);

    // wird häufiger gebraucht, daher kürzer als Property
    me.Crop.width  = parseInt ( me.Crop.style.width  );
    me.Crop.height = parseInt ( me.Crop.style.height );

    // Events registrieren
    me.Act.onmousedown = me.selectStart;
    me.Act.onmouseup   = me.selectEnd;

    // alle Werte initialisieren
    me.reset();
}
