/**
 * jquery.holedDiv
 * 
 * Copyright (c) 2011 Patrick Guido Arminio
 * http://patrick.arminio.info
 *
 * Licensed under GPLv3
 * http://www.opensource.org/licenses/gpl-3.0.html
 *
 * Launch  : February 2011
 * Version : 0.2alpha
 * Released: February 4, 2011 - 15:00
 * Added support for IE 9
 * * IE 9 doesn't support compositing on canvas.
 * Added support for browsers that don't implement canvas
 * Added basic support for excanvas
 */
 (function($) {
    $.fn.holedDiv = function(options) {
        function isUndefined(x) { var u; return x === u; }
        function supports_canvas() {
            return !!document.createElement('canvas').getContext;
        }
        
        function _roundRect(ctx, x, y, width, height, radius, stroke) {        
            ctx.beginPath();
            ctx.moveTo(x + radius, y);
            ctx.lineTo(x + width - radius, y);
            ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
            ctx.lineTo(x + width, y + height - radius);
            ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
            ctx.lineTo(x + radius, y + height);
            ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
            ctx.lineTo(x, y + radius);
            ctx.quadraticCurveTo(x, y, x + radius, y);
            ctx.closePath();
    
            if (stroke) { ctx.stroke(); }
            ctx.fill();
        };
        
        function roundRectExCanvas(ctx, x, y, width, height, radius, stroke) {      
            /* excanvas doesn't support neither clearRect nor Compositing */ 
            
            var holeRadius = settings.holeRadius * 2;
             
            ctx.beginPath();
            ctx.moveTo(x + radius, y);
            ctx.lineTo(x + width - radius, y);
            ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
            ctx.lineTo(x + width, y + height - radius);
            ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
            
            ctx.lineTo(x + (width - holeRadius) / 2, y + height);
            ctx.lineTo(x + (width - holeRadius) / 2, y + height - holeRadius / 2);
            ctx.lineTo(x + (width + holeRadius) / 2, y + height - holeRadius / 2);
            ctx.lineTo(x + (width + holeRadius) / 2, y + height - holeRadius * 1.5);
            ctx.lineTo(x + (width - holeRadius) / 2, y + height - holeRadius * 1.5);
            ctx.lineTo(x + (width - holeRadius) / 2, y + height);
            
            ctx.lineTo(x + radius, y + height);
            
            ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
            ctx.lineTo(x, y + radius);
            ctx.quadraticCurveTo(x, y, x + radius, y);
            ctx.closePath();
    
            if (stroke) { ctx.stroke(); }
            ctx.fill();
        };
        
        function drawHole(ctx, x, y, radius) {
            ctx.globalCompositeOperation = 'destination-out'; //compositing 
            ctx.fillStyle = "rgba(0, 0, 0, 1)";
          
            ctx.beginPath();
            ctx.arc(x, y, radius, 0, 6.283185307179586, true); 
            ctx.closePath();
            ctx.fill();  
            ctx.globalCompositeOperation = 'source-over'; //reset compositing
        };
        
        function drawHoleNoComposite(ctx, x, y, radius) {
            if (isUndefined(window.G_vmlCanvasManager))
                ctx.clearRect(x-radius, y-radius, radius*2, radius*2);
            ctx.beginPath();
            ctx.arc(x, y, radius+0.414 * radius/2, 0, 6.283185307179586, true); 
            ctx.lineWidth = 0.414 * radius;
            ctx.strokeStyle = ctx.fillStyle;
            ctx.stroke();
        }
        
        function drawInnerHole(ctx, x, y, radius) {  
            var new_radius = radius / 1.5;
                    
            ctx.beginPath();
            ctx.arc(x, y, new_radius, 0, 6.283185307179586, true); 
            ctx.closePath();
            ctx.fill();  
        };
        
        var settings = {
            'padding': 10,
            'radius': 10,
            'stroke': false,
            'holeRadius': 25,
            'drawInner': true,
            'drawInnerColor': 'black'
        };

        function doOldStyle($this) {
            var h = $this.outerHeight();
            var w = $this.outerWidth();
            var $div = $('<div />');
            
            var $content = $div.clone().addClass('content');
            var $top = $div.clone().addClass('top');
            var $bottom = $div.clone().addClass('bottom');

            var $left = $div.clone().addClass('left');
            var $inner = $div.clone().addClass('inner');
            var $right = $div.clone().addClass('right');
            
            var $a = $inner.clone().addClass('a');
            var $b = $inner.clone().addClass('b');
            var $c = $inner.clone().addClass('c');
            
            $left.appendTo($top);
            $inner.appendTo($top);
            $right.appendTo($top);
            
            $bottom.append($left.clone())
                   .append($a)
                   .append($b)
                   .append($c)
                   .append($right.clone());
            
            $content.css('height', h - $left.height()*2);
            
            $this.addClass('hd')
                 .wrapInner($content)
                 .prepend($top)
                 .append($bottom)
                 .css('width', w + $left.height()*2)
                 .css('backgroundColor', 'transparent')
                 .css('height', h + $left.height()*2);
            
            
            $a.css('right', (w + $b.width())/2 + $left.width());
            $c.css('left', (w + $b.width())/2 + $left.width());
        }
 
        return this.each(function() {
            if (options) { 
                $.extend(settings, options);
            }
      
            var $this = $(this);
                      
            if (!supports_canvas() && isUndefined(window.G_vmlCanvasManager)) return doOldStyle($this); //support for old browsers

            $this.css({
                position: 'relative',
                padding: settings.padding + 'px'  
            });
          
            var w = $this.outerWidth();
            var h = $this.outerHeight();

            var $canvas = $('<canvas></canvas>').attr('width', w).attr('height', h);
            
            $canvas.css({
                position: 'absolute',
                top: 0,
                left: 0,
                zIndex: -1
            }).appendTo($this);
            
            if (!isUndefined(window.G_vmlCanvasManager)) { //using excanvas
                G_vmlCanvasManager.initElement($canvas.get(0));
                roundRect = roundRectExCanvas;
            } else {
                roundRect = _roundRect;
            }
            
            var ctx = $canvas.get(0).getContext('2d');
            var bg_color = $this.css('backgroundColor');
            $this.css('backgroundColor', 'transparent');

            ctx.fillStyle = bg_color == 'transparent' ? 'white' : bg_color;
            
            roundRect(ctx, 0, 0, w, h, settings.radius, settings.stroke, settings.holeRadius);
            
            var x = w / 2;
            var y = h - settings.holeRadius * 2; //a bit of padding
            
            if ($.browser.msie && $.browser.version == '9.0' || !isUndefined(window.G_vmlCanvasManager)) { //IE 9 is buggy and excanvas too
                drawHoleNoComposite(ctx, x, y, settings.holeRadius);
            } else {
                drawHole(ctx, x, y, settings.holeRadius);
            }
            
            if (settings.drawInner) {
                ctx.fillStyle = settings.drawInnerColor;
                drawInnerHole(ctx, x, y, settings.holeRadius);
            }
        });
    };
})(jQuery);
