/*
* jFlip plugin for jQuery v0.4 (28/2/2009)
* 
* A plugin to make a page flipping gallery    
*
* Copyright (c) 2008-2009 Renato Formato (rformato@gmail.com)
* Dual licensed under the MIT (MIT-LICENSE.txt)
* and GPL (GPL-LICENSE.txt) licenses.
*    
*/
; (function ($) {

    var Used = false;
    var Flip = function (canvas, width, height, images, opts) {
        //private vars
        opts = $.extend({ background: "green", cornersTop: true, scale: "noresize" }, opts);
        var obj = this,
      el = canvas.prev(),
      index = 0,
      init = false,
      background = opts.background,
      cornersTop = opts.cornersTop,
      gradientColors = opts.gradientColors || ['#4F2727', '#FF8F8F', '#F00'],
      curlSize = opts.curlSize || 0.1,
      scale = opts.scale,
      patterns = [],
      canvas2 = canvas.clone(),
      ctx2 = $.browser.msie ? null : canvas2[0].getContext("2d"),
      canvas = $.browser.msie ? $(G_vmlCanvasManager.initElement(canvas[0])) : canvas,
      ctx = canvas[0].getContext("2d"),
      loaded = 0;
        var images = images.each(function (i) {
            if (patterns[i]) return;
            var img = this;
            img.onload = function () {
                var r = 1;
                if (scale != "noresize") {
                    var rx = width / this.width,
            ry = height / this.height;
                    if (scale == "fit")
                        r = (rx < 1 || ry < 1) ? Math.min(rx, ry) : 1;
                    if (scale == "fill") {
                        r = Math.min(rx, ry);
                    }
                };
                $(img).data("flip.scale", r);
                patterns[i] = ctx.createPattern(img, "no-repeat");
                loaded++;
                if (loaded == images.length && !init) {
                    init = true;
                    draw();
                }
            };
            if (img.complete)
                window.setTimeout(function () { img.onload() }, 10);
        }).get();
        var 
        width = width, height = height, mX = width, mY = height,
        basemX = mX * (1 - curlSize), basemY = mY * curlSize, sideLeft = false,
        off = $.browser.msie ? canvas.offset() : null,
        onCorner = false,
        curlDuration = 400, curling = false,
        animationTimer, startDate,
        flipDuration = 700, flipping = false, baseFlipX, baseFlipY,
        lastmX, lastmY,
        inCanvas = false,
        mousedown = false,
        dragging = false;

        $(window).scroll(function () {
            //off = canvas.offset(); //update offset on scroll
        });

        //IE can't handle correctly mouseenter and mouseleave on VML 
        var c = $.browser.msie ? (function () {
            var div = $("<div>").width(width).height(height).css({ position: "absolute", cursor: "default", zIndex: 1 }).appendTo("body");
            //second hack for IE7 that can't handle correctly mouseenter and mouseleave if the div has no background color 
            if (parseInt($.browser.version) == 7)
            
                div.css({ opacity: 0.000001, background: "#FFF" });
            var positionDiv = function () {
                off = canvas.offset();
                return div.css({ left: off.left + 'px', top: off.top + 'px' });
            }
            $(window).resize(positionDiv);
            return positionDiv();
        })() : canvas;
        c.mousemove(function (e) {
            if (Used) return;
            //track the mouse
            /*
            if(!off) off = canvas.offset(); //safari can't calculate correctly offset at DOM ready
            mX = e.clientX-off.left;
            mY = e.clientY-off.top;
            window.setTimeout(draw,0);
            return;
            */
            if (!off)
                off = canvas.offset(); //safari can't calculate correctly offset at DOM ready

            if (mousedown && onCorner) {
                if (!dragging) {
                    dragging = true;
                    window.clearInterval(animationTimer);
                }
                mX = !sideLeft ? e.pageX - off.left : width - (e.pageX - off.left);
                mY = cornersTop ? e.pageY - off.top : height - (e.pageY - off.top);
                window.setTimeout(draw, 0);
                return false;
            }

            lastmX = e.pageX || lastmX, lastmY = e.pageY || lastmY;
            if (!flipping) {
                sideLeft = (lastmX - off.left) < width / 2;
                //cornersTop = (lastmY-off.top)<height/2;
            }
            if (!flipping &&
          ((lastmX - off.left) > basemX || (lastmX - off.left) < (width - basemX)) &&
          ((cornersTop && (lastmY - off.top) < basemY) || (!cornersTop && (lastmY - off.top) > (height - basemY)))) {
                if (!onCorner) {
                    onCorner = true;
                    c.css("cursor", "pointer");
                }
            } else {
                if (onCorner) {
                    onCorner = false;
                    c.css("cursor", "default");
                }
            };
            return false;
        }).bind("mouseenter", function (e) {
            if (Used) return;
            inCanvas = true;
            if (flipping) return;
            window.clearInterval(animationTimer);
            startDate = new Date().getTime();
            animationTimer = window.setInterval(cornerCurlIn, 10);
            return false;
        }).bind("mouseleave", function (e) {
            inCanvas = false;
            dragging = false;
            mousedown = false;
            if (flipping) return;
            window.clearInterval(animationTimer);
            startDate = new Date().getTime();
           if (!Used)  animationTimer = window.setInterval(cornerCurlOut, 10); 
            return false;
        }).click(function () {
            if (Used) return;
            if (onCorner && !flipping) {
                Used = true;
                flipping = true;
                c.triggerHandler("mousemove");
                window.clearInterval(animationTimer);
                startDate = new Date().getTime();
                baseFlipX = mX;
                baseFlipY = mY;
                animationTimer = window.setInterval(flip, 10);
                index += sideLeft ? -1 : 1;
                if (index < 0) index = images.length - 1;
                if (index == images.length) index = 0;
                el.trigger("flip.jflip", [index, images.length]);
                c.css("cursor", "default");
            }
            return false;
        }).mousedown(function () {
            if (Used) return;
            dragging = false;
            mousedown = true;
            return false;
        }).mouseup(function () {
            if (Used) return;
            mousedown = false;
            return false;
        });

        var flip = function () {
            var date = new Date(), delta = date.getTime() - startDate;
            if (delta >= flipDuration) {
                window.clearInterval(animationTimer);
                if (sideLeft) {
                    images.unshift(images.pop());
                    patterns.unshift(patterns.pop());
                } else {
                    images.push(images.shift());
                    patterns.push(patterns.shift());
                }
                mX = width;
                mY = height;
                draw();
                flipping = false;
                //init corner move if still in Canvas
                if (inCanvas) {
                    startDate = new Date().getTime();
                    animationTimer = window.setInterval(cornerCurlIn, 10);
                    c.triggerHandler("mousemove");
                }
                return;
            }
            //da mX a -width  (mX+width) in duration millisecondi 
            mX = baseFlipX - 2 * (width) * delta / flipDuration;
            mY = baseFlipY + 2 * (height) * delta / flipDuration;
            draw();
        },
      cornerMove = function () {
          var date = new Date(), delta = date.getTime() - startDate;

          mX = basemX + Math.sin(Math.PI * 2 * delta / 1000);
          mY = basemY + Math.cos(Math.PI * 2 * delta / 1000);
          drawing = true;
          window.setTimeout(draw, 0);
      },
      cornerCurlIn = function () {
          var date = new Date(), delta = date.getTime() - startDate;
          if (delta >= curlDuration) {
              window.clearInterval(animationTimer);
              startDate = new Date().getTime();
              animationTimer = window.setInterval(cornerMove, 10);
          }
          mX = width - (width - basemX) * delta / curlDuration;
          mY = basemY * delta / curlDuration;
          draw();
      },
      cornerCurlOut = function () {
          var date = new Date(), delta = date.getTime() - startDate;
          if (delta >= curlDuration) {
              window.clearInterval(animationTimer);
          }
          mX = basemX + (width - basemX) * delta / curlDuration;
          mY = basemY - basemY * delta / curlDuration;
          draw();
      },
      curlShape = function (m, q) {
          //cannot draw outside the viewport because of IE blurring the pattern
          var intyW = m * width + q, intx0 = -q / m;
          if ($.browser.msie) {
              intyW = Math.round(intyW);
              intx0 = Math.round(intx0);
          };
          ctx.beginPath();
          ctx.moveTo(width, Math.min(intyW, height));
          ctx.lineTo(width, 0);
          ctx.lineTo(Math.max(intx0, 0), 0);
          if (intx0 < 0) {
              ctx.lineTo(0, Math.min(q, height));
              if (q < height) {
                  ctx.lineTo((height - q) / m, height);
              }
              ctx.lineTo(width, height);
          } else {
              if (intyW < height)
                  ctx.lineTo(width, intyW);
              else {
                  ctx.lineTo((height - q) / m, height);
                  ctx.lineTo(width, height);
              }
          }
      },
      draw = function () {
          if (!init) return;
          if ($.browser.msie)
              ctx.clearRect(0, 0, width, height);

          ctx.fillStyle = background;
          ctx.fillRect(0, 0, width, height);
          var img = images[0], r = $(img).data("flip.scale");
          if ($.browser.msie) {
              ctx.fillStyle = patterns[0];
              ctx.fillStyle.width2 = ctx.fillStyle.width * r;
              ctx.fillStyle.height2 = ctx.fillStyle.height * r;
              ctx.fillRect(0, 0, width, height);
          } else {
              ctx.drawImage(img, (width - img.width * r) / 2, (height - img.height * r) / 2, img.width * r, img.height * r);
          }

          if (mY && mX != width) {

              var m = 2,
              q = (mY - m * (mX + width)) / 2;
              m2 = mY / (width - mX),
              q2 = mX * m2;
              if (m == m2) return;

              var sx = 1, sy = 1, tx = 0, ty = 0;
              ctx.save();
//              if (sideLeft) {
//                  tx = width;
//                  sx = -1;
//              }
              if (!cornersTop) {
                  ty = height;
                  sy = -1;
              }
              ctx.translate(tx, ty);
              ctx.scale(sx, sy);
              //draw page flip
              //intx,inty is the intersection between the line of the curl and the line
              //from the canvas corner to the curl point 
              var intx = (q2 - q) / (m - m2);
              var inty = m * intx + q;
              //y=m*x+mY-m*mX line per (mX,mY) parallel to the curl line
              //y=-x/m+inty+intx/m line perpendicular to the curl line
              //intersection x between the 2 lines = int2x
              //y of perpendicular for the intersection x = int2y 
              //opera do not fill a shape if gradient is finished
              var int2x = (2 * inty + intx + 2 * m * mX - 2 * mY) / (2 * m + 1);
              var int2y = -int2x / m + inty + intx / m;
              var d = Math.sqrt(Math.pow(intx - int2x, 2) + Math.pow(inty - int2y, 2));
              var stopHighlight = Math.min(d * 0.5, 30);

              var c;
              if (!($.browser.mozilla && parseFloat($.browser.version) < 1.9)) {
                  c = ctx;
              } else {
                  c = ctx2;
                  c.clearRect(0, 0, width, height);
                  c.save();
                  c.translate(1, 0); //the curl shapes do not overlap perfeclty
              }
              var gradient = c.createLinearGradient(intx, inty, int2x, int2y);
              gradient.addColorStop(0, gradientColors[0]);
              gradient.addColorStop(stopHighlight / d, gradientColors[1]);
              gradient.addColorStop(1, gradientColors[2]);
              c.fillStyle = gradient;
              c.beginPath();
              c.moveTo(-q / m, 0);
              c.quadraticCurveTo((-q / m + mX) / 2 + 0.02 * mX, mY / 2, mX, mY);
              c.quadraticCurveTo((width + mX) / 2, (m * width + q + mY) / 2 - 0.02 * (height - mY), width, m * width + q);
              if (!($.browser.mozilla && parseFloat($.browser.version) < 1.9)) {
                  c.fill();
              } else {
                  //for ff 2.0 use a clip region on a second canvas and copy all its content (much faster)
                  c.save();
                  c.clip();
                  c.fillRect(0, 0, width, height);
                  c.restore();
                  ctx.drawImage(canvas2[0], 0, 0);
                  c.restore();
              }
              //can't understand why this doesn't work on ff 2, fill is slow
              /*
              ctx.save();
              ctx.clip();
              ctx.fillRect(0,0,width,height);
              ctx.restore();
              */
              gradient = null;

              //draw solid color background
              ctx.fillStyle = background;
              curlShape(m, q);
              ctx.fill();

              //draw back image
              curlShape(m, q);
              //safari and opera delete the path when doing restore
              if (!$.browser.safari && !$.browser.opera)
                  ctx.restore();

              var img = sideLeft ? images[images.length - 1] : images[1];
              r = $(img).data("flip.scale");
              if ($.browser.msie) {
                  //excanvas does not support clip
                  ctx.fillStyle = sideLeft ? patterns[patterns.length - 1] : patterns[1];
                  //hack to scale the pattern on IE (modified excanvas)
                  ctx.fillStyle.width2 = ctx.fillStyle.width * r;
                  ctx.fillStyle.height2 = ctx.fillStyle.height * r;
                  ctx.fill();
              } else {
                  ctx.save();
                  ctx.clip();
                  //safari and opera delete the path when doing restore
                  //at this point we have not reverted the trasform
                  if ($.browser.safari || $.browser.opera) {
                      //revert transform
                      ctx.scale(1 / sx, 1 / sy);
                      ctx.translate(-tx, -ty);
                  }

                  ctx.drawImage(img, (width - img.width * r) / 2, (height - img.height * r) / 2, img.width * r, img.height * r);

                  //ctx.drawImage(img,(width-img.width)/2,(height-img.height)/2);
                  ctx.restore();
                  if ($.browser.safari || $.browser.opera)
                      ctx.restore()
              }
          }
      }
    }

    $.fn.jFlip = function (width, height, opts) {
        return this.each(function () {
            $(this).wrap("<div class='flip_gallery'>");
            var images = $(this).find("img");
            //cannot hide because explorer does not give the image dimensions if hidden
            var canvas = $(document.createElement("canvas")).attr({ width: width, height: height }).css({ margin: 0, width: width + "px", height: height + "px" })
            $(this).css({ position: "absolute", left: "-9000px", top: "-9000px" }).after(canvas);
            new Flip($(this).next(), width || 300, height || 300, images, opts);
        });
    };



})(jQuery);

