﻿// =============================================================
//                 ===== photo 3D =====
// script written by Gerard Ferrandez - October 21th, 2007
// http://www.dhteumeuleu.com
// =============================================================

var imageArea = null;

HandleError = function(e, text)
{
    try
    {
        var message = (text ? text : '') + ' ' + e.message;
        console.log(message);
        console.trace();
    }
    catch (e)
    {
        console.log("Error while attempting to log an error [sigh].");
    }
};

window.onload = function()
{
    try
    {
        imageArea = new ImageFloatArea();
        imageArea.Initialize();

        window.addEvent('domready', imageArea.Initialize);
    }
    catch (e)
    {
        HandleError(e, 'window.onload error');
    }
};

function pageLoad()
{
    imageArea.Initialize();
};

ImageFloatArea = function()
{
    var _columns = 4;
    var _rows = 4;
    var _elementBorderSize = 6;
    var _elementNotFocusedCssClass = 'tvout';
    var _elementFocusedCssClass = 'tvover';
    var _imageListElementId = 'bankImages';
    var _imageListImageTagType = 'img';

    var Library = {};
    Library.ease = function()
    {
        this.target = 0;
        this.position = 0;
        this.move = function(target, speed)
        {
            this.position += (target - this.position) * speed;
        }
    };

    this.Initialize = function()
    {
        tv.init();
    };

    var tv =
    {
        isZoomed: false,
        zoomTarget: null,
        images: [],
        screen: {},
        angle:
        {
            x: new Library.ease(),
            y: new Library.ease()
        },
        camera:
        {
            x: new Library.ease(),
            y: new Library.ease(),
            zoom: new Library.ease(),
            focalLength: 750 // camera Focal Length
        },

        mouse:  // tracks the mouse position for us
        {
            x: 0,
            y: 0
        },

        /* ==== init script ==== */
        init: function()
        {
            var screenElement = document.getElementById('screen');
            screenElement.innerHTML = "";

            tv.screen.obj = screenElement;
            tv.screen.obj.onselectstart = function() { return false; }
            tv.screen.obj.ondrag = function() { return false; }
            
            if (tv.images.length > 0)
            {
                tv.images = [];  // clear out any old values
            }

            document.onmousemove = function(e)
            {
                if (window.event) e = window.event;
                tv.mouse.x = e.clientX;
                tv.mouse.y = e.clientY;
                return false;
            };

            var images = document.getElementById(_imageListElementId).getElementsByTagName(_imageListImageTagType);

            /* ==== create images grid ==== */
            var ni = 0;
            var nx = (_columns / 2) - .5;
            var ny = (_rows / 2) - .5;
            for (var y = -ny; y <= ny; y++)
            {
                for (var x = -nx; x <= nx; x++)
                {
                    // create a new image element in the document for this image
                    // and assign it various properties for tracking its state
                    var element = document.createElement('img');

                    element.point3D =
                                    {
                                        x: x,
                                        y: y,
                                        z: new Library.ease()
                                    };
                    element.point2D = {};
                    element.ratioImage = 1;

                    var index = ni++;
                    var i = (index < images.length) ? (images[index % images.length]) : null;

                    if (i != null)
                    {
                        element.className = _elementNotFocusedCssClass;
                        element.src = i.src;

                        // wire up events triggered by this grid element to our TV's event handlers
                        element.onmouseover = function() { tv.OnElementMouseOver(this); }
                        element.onclick = function() { tv.OnElementMouseClick(this); }
                        element.render = function() { tv.RenderElement(this); }

                        // place the image in the screen and add it to our collection for tracking
                        tv.screen.obj.appendChild(element);
                        tv.images.push(element);
                    }
                }
            }

            /* ==== start script ==== */
            tv.resize();
            tv.mouse.y = tv.screen.y + tv.screen.h;
            tv.mouse.x = tv.screen.x + tv.screen.w;
            tv.run();
        },

        /* ==== resize window ==== */
        resize: function()
        {
            var o = tv.screen.obj;
            tv.screen.w = o.offsetWidth / 2;
            tv.screen.h = o.offsetHeight / 2;
            tv.camera.zoom.target = tv.screen.w / (_columns + .1);
            for (tv.screen.x = 0, tv.screen.y = 0; o != null; o = o.offsetParent)
            {
                tv.screen.x += o.offsetLeft;
                tv.screen.y += o.offsetTop;
            }
        },

        /* ==== main loop ==== */
        run: function()
        {
            /* ==== motion ease ==== */
            tv.angle.x.move(-(tv.mouse.y - tv.screen.h - tv.screen.y) * .0025, .1);
            tv.angle.y.move((tv.mouse.x - tv.screen.w - tv.screen.x) * .0025, .1);
            tv.camera.x.move(tv.camera.x.target, tv.isZoomed ? .25 : .025);
            tv.camera.y.move(tv.camera.y.target, tv.isZoomed ? .25 : .025);
            tv.camera.zoom.move(tv.camera.zoom.target, .05);
            /* ==== angles sin and cos ==== */
            tv.angle.cx = Math.cos(tv.angle.x.position);
            tv.angle.sx = Math.sin(tv.angle.x.position);
            tv.angle.cy = Math.cos(tv.angle.y.position);
            tv.angle.sy = Math.sin(tv.angle.y.position);
            /* ==== loop through all images ==== */
            for (var i = 0, o; o = tv.images[i]; i++)
            {
                o.render();
            }
            /* ==== loop ==== */
            setTimeout(tv.run, 32);
        },

        OnElementMouseOver: function(element)
        {
            if (!tv.isZoomed)  // apply effects only when *not* zoomed
            {
                if (tv.currentSelectedElement)
                {
                    // a different element is currently selected, deselect it
                    tv.currentSelectedElement.point3D.z.target = 0;
                    tv.currentSelectedElement.className = _elementNotFocusedCssClass;
                }

                // select the item which now has the mouse over
                element.className = _elementFocusedCssClass;
                element.point3D.z.target = -.5;
                tv.currentSelectedElement = element;
            }
        },

        OnElementMouseClick: function(element)
        {
            if (!tv.isZoomed)
            {
                // not currently zoomed, so zoom in on the clicked element
                tv.camera.x.target = element.point3D.x;
                tv.camera.y.target = element.point3D.y;
                tv.camera.zoom.target = tv.screen.w * 0.8;
                tv.isZoomed = true;
                tv.zoomTarget = element;
            }
            else if (element == tv.zoomTarget)
            {
                // we are zoomed, if they clicked the target of the zoom,
                // then zoom back out
                tv.camera.x.target = 0;
                tv.camera.y.target = 0;
                tv.camera.zoom.target = tv.screen.w / (_columns + .1);
                tv.isZoomed = false;
                tv.zoomTarget = null;
            }
        },

        RenderElement: function(element)
        {
            this.TransformElement(element);
            this.DrawElement(element);
        },

        TransformElement: function(element)
        {
            /* ==== ease mouseover ==== */
            element.point3D.z.move(element.point3D.z.target, .5);
            /* ==== assign 3D coords ==== */
            var x = (element.point3D.x - tv.camera.x.position) * tv.camera.zoom.position;
            var y = (element.point3D.y - tv.camera.y.position) * tv.camera.zoom.position;
            var z = element.point3D.z.position * tv.camera.zoom.position;
            /* ==== perform rotations ==== */
            var xy = tv.angle.cx * y - tv.angle.sx * z;
            var xz = tv.angle.sx * y + tv.angle.cx * z;
            var yz = tv.angle.cy * xz - tv.angle.sy * x;
            var yx = tv.angle.sy * xz + tv.angle.cy * x;
            /* ==== 2D transformation ==== */
            element.point2D.scale = tv.camera.focalLength / (tv.camera.focalLength + yz);
            element.point2D.x = yx * element.point2D.scale;
            element.point2D.y = xy * element.point2D.scale;
            element.point2D.w = Math.round(
				                               Math.max(
				                                 0,
				                                 element.point2D.scale * tv.camera.zoom.position * .8
				                               )
				                             );
            /* ==== image size ratio ==== */
            if (element.ratioImage > 1)
            {
                element.point2D.h = Math.round(element.point2D.w / element.ratioImage);
            }
            else
            {
                element.point2D.h = element.point2D.w;
                element.point2D.w = Math.round(element.point2D.h * element.ratioImage);
            }
        },

        DrawElement: function(element)
        {
            if (element.complete)
            {
                /* ==== paranoid image load ==== */
                if (!element.loaded)
                {
                    if (!element.img)
                    {
                        /* ==== create internal image ==== */
                        element.img = new Image();
                        element.img.src = element.src;
                    }
                    if (element.img.complete)
                    {
                        /* ==== get width / height ratio ==== */
                        element.style.visibility = 'visible';
                        element.ratioImage = element.img.width / element.img.height;
                        element.loaded = true;
                        element.img = false;
                    }
                }

                /* ==== HTML rendering ==== */
                element.style.left = Math.round(
		                                        element.point2D.x * element.point2D.scale +
		                                        tv.screen.w - element.point2D.w * .5
		                                      ) + 'px';
                element.style.top = Math.round(
		                                element.point2D.y * element.point2D.scale +
		                                tv.screen.h - element.point2D.h * .5
		                              ) + 'px';
                element.style.width = element.point2D.w + 'px';
                element.style.height = element.point2D.h + 'px';
                element.style.borderWidth = Math.round(
		                                       Math.max(
		                                         element.point2D.w,
		                                         element.point2D.h
		                                       ) * _elementBorderSize * .01
		                                     ) + 'px';
                element.style.zIndex = Math.floor(element.point2D.scale * 100);
            }
        }
    }
}
