/*
 * jGraduate 0.3.x
 *
 * jQuery Plugin for a gradient picker
 *
 * Copyright (c) 2010 Jeff Schiller
 * http://blog.codedread.com/
 * Copyright (c) 2010 Alexis Deveria
 * http://a.deveria.com/
 *
 * Apache 2 License

jGraduate( options, okCallback, cancelCallback )

where options is an object literal:
  {
    window: { title: "Pick the start color and opacity for the gradient" },
    images: { clientPath: "images/" },
    paint: a Paint object
  }
 
- the Paint object is:
  Paint {
    type: String, // one of "none", "solidColor", "linearGradient", "radialGradient"
    alpha: Number representing opacity (0-100),
    solidColor: String representing #RRGGBB hex of color,
    linearGradient: object of interface SVGLinearGradientElement,
    radialGradient: object of interface SVGRadialGradientElement,
  }

$.jGraduate.Paint() -> constructs a 'none' color
$.jGraduate.Paint({copy: o}) -> creates a copy of the paint o
$.jGraduate.Paint({hex: "#rrggbb"}) -> creates a solid color paint with hex = "#rrggbb"
$.jGraduate.Paint({linearGradient: o, a: 50}) -> creates a linear gradient paint with opacity=0.5
$.jGraduate.Paint({radialGradient: o, a: 7}) -> creates a radial gradient paint with opacity=0.07
$.jGraduate.Paint({hex: "#rrggbb", linearGradient: o}) -> throws an exception?

- picker accepts the following object as input:
  {
    okCallback: function to call when Ok is pressed
    cancelCallback: function to call when Cancel is pressed
    paint: object describing the paint to display initially, if not set, then default to opaque white
  }

- okCallback receives a Paint object

 *
 */
 
(function() {
 
var ns = { svg: 'http://www.w3.org/2000/svg', xlink: 'http://www.w3.org/1999/xlink' };
if(!window.console) {
  window.console = new function() {
    this.log = function(str) {};
    this.dir = function(str) {};
  };
}

$.jGraduate = { 
  Paint:
    function(opt) {
      var options = opt || {};
      this.alpha = options.alpha || 100;
      // copy paint object
        if (options.copy) {
          this.type = options.copy.type;
          this.alpha = options.copy.alpha;
        this.solidColor = null;
        this.linearGradient = null;
        this.radialGradient = null;

          switch(this.type) {
            case "none":
              break;
            case "solidColor":
              this.solidColor = options.copy.solidColor;
              break;
            case "linearGradient":
              this.linearGradient = options.copy.linearGradient.cloneNode(true);
              break;
            case "radialGradient":
              this.radialGradient = options.copy.radialGradient.cloneNode(true);
              break;
          }
        }
        // create linear gradient paint
        else if (options.linearGradient) {
          this.type = "linearGradient";
          this.solidColor = null;
          this.radialGradient = null;
          this.linearGradient = options.linearGradient.cloneNode(true);
        }
        // create linear gradient paint
        else if (options.radialGradient) {
          this.type = "radialGradient";
          this.solidColor = null;
          this.linearGradient = null;
          this.radialGradient = options.radialGradient.cloneNode(true);
        }
        // create solid color paint
        else if (options.solidColor) {
          this.type = "solidColor";
          this.solidColor = options.solidColor;
        }
        // create empty paint
        else {
          this.type = "none";
          this.solidColor = null;
          this.linearGradient = null;
          this.radialGradient = null;
        }
    }
};

jQuery.fn.jGraduateDefaults = {
  paint: new $.jGraduate.Paint(),
  window: {
    pickerTitle: "Drag markers to pick a paint",
  },
  images: {
    clientPath: "images/",
  },
};

jQuery.fn.jGraduate =
  function(options) {
     var $arguments = arguments;
    return this.each( function() {
      var $this = $(this), $settings = $.extend(true, {}, jQuery.fn.jGraduateDefaults, options),
        id = $this.attr('id'),
        idref = '#'+$this.attr('id')+' ';
      
            if (!idref)
            {
              alert('Container element must have an id attribute to maintain unique id strings for sub-elements.');
              return;
            }
            
            var okClicked = function() {
              // TODO: Fix this ugly hack
              if($this.paint.type == "radialGradient") {
                $this.paint.linearGradient = null;
              } else if($this.paint.type == "linearGradient") {
                $this.paint.radialGradient = null;              
              } else if($this.paint.type == "solidColor") {
                $this.paint.linearGradient = null;
                $this.paint.radialGradient = null;
              }
              $.isFunction($this.okCallback) && $this.okCallback($this.paint);
              $this.hide();
            },
            cancelClicked = function() {
              $.isFunction($this.cancelCallback) && $this.cancelCallback();
              $this.hide();
            };

            $.extend(true, $this, // public properties, methods, and callbacks
              {
                // make a copy of the incoming paint
                paint: new $.jGraduate.Paint({copy: $settings.paint}),
                okCallback: $.isFunction($arguments[1]) && $arguments[1] || null,
                cancelCallback: $.isFunction($arguments[2]) && $arguments[2] || null,
              });

      var pos = $this.position(),
        color = null;

      if ($this.paint.type == "none") {
        $this.paint = $.jGraduate.Paint({solidColor: 'ffffff'});
      }
      
            $this.addClass('jGraduate_Picker');
            $this.html('<ul class="jGraduate_tabs">' +
                    '<li class="jGraduate_tab_color jGraduate_tab_current" data-type="col">Solid Color</li>' +
                    '<li class="jGraduate_tab_lingrad" data-type="lg">Linear Gradient</li>' +
                    '<li class="jGraduate_tab_radgrad" data-type="rg">Radial Gradient</li>' +
                  '</ul>' +
                  '<div class="jGraduate_colPick"></div>' +
                  '<div class="jGraduate_lgPick"></div>' +
                  '<div class="jGraduate_rgPick"></div>');
      var colPicker = $(idref + '> .jGraduate_colPick');
      var lgPicker = $(idref + '> .jGraduate_lgPick');
      var rgPicker = $(idref + '> .jGraduate_rgPick');
      
            lgPicker.html(
              '<div id="' + id + '_jGraduate_Swatch" class="jGraduate_Swatch">' +
                '<h2 class="jGraduate_Title">' + $settings.window.pickerTitle + '</h2>' +
                '<div id="' + id + '_lg_jGraduate_GradContainer" class="jGraduate_GradContainer"></div>' +
                '<div id="' + id + '_lg_jGraduate_Opacity" class="jGraduate_Opacity" title="Click to set overall opacity of the gradient paint">' +
                  '<img id="' + id + '_lg_jGraduate_AlphaArrows" class="jGraduate_AlphaArrows" src="' + $settings.images.clientPath + 'rangearrows2.gif"></img>' +
                '</div>' +
              '</div>' + 
              '<div class="jGraduate_Form">' +
                '<div class="jGraduate_StopSection">' +
                  '<label class="jGraduate_Form_Heading">Begin Stop</label>' +
                  '<div class="jGraduate_Form_Section">' +
                    '<label>x:</label>' +
                    '<input type="text" id="' + id + '_jGraduate_x1" size="3" title="Enter starting x value between 0.0 and 1.0"/>' +
                    '<label> y:</label>' +
                    '<input type="text" id="' + id + '_jGraduate_y1" size="3" title="Enter starting y value between 0.0 and 1.0"/>' +
                    '<div id="' + id + '_jGraduate_colorBoxBegin" class="colorBox"></div>' +
                    '<label id="' + id + '_jGraduate_beginOpacity"> 100%</label>' +
                   '</div>' +
                 '</div>' +
                 '<div class="jGraduate_StopSection">' +
                  '<label class="jGraduate_Form_Heading">End Stop</label>' +
                  '<div class="jGraduate_Form_Section">' +
                    '<label>x:</label>' +
                    '<input type="text" id="' + id + '_jGraduate_x2" size="3" title="Enter ending x value between 0.0 and 1.0"/>' +
                    '<label> y:</label>' +
                    '<input type="text" id="' + id + '_jGraduate_y2" size="3" title="Enter ending y value between 0.0 and 1.0"/>' +
                    '<div id="' + id + '_jGraduate_colorBoxEnd" class="colorBox"></div>' +
                    '<label id="' + id + '_jGraduate_endOpacity">100%</label>' +
                  '</div>' +
                '</div>' +
                '<div class="lg_jGraduate_OpacityField">' +
                  '<label class="lg_jGraduate_OpacityLabel">A: </label>' +
                  '<input type="text" id="' + id + '_lg_jGraduate_OpacityInput" class="jGraduate_OpacityInput" size="3" value="100"/>%' +
                '</div>' +
               '</div>' +
              '<div class="jGraduate_OkCancel">' +
                '<input type="button" id="' + id + '_lg_jGraduate_Ok" class="jGraduate_Ok" value="OK"/>' +
                '<input type="button" id="' + id + '_lg_jGraduate_Cancel" class="jGraduate_Cancel" value="Cancel"/>' +
              '</div>' +
              '<div class="jGraduate_LightBox"></div>' +
              '<div id="' + id + '_jGraduate_stopPicker" class="jGraduate_stopPicker"></div>');
              
            rgPicker.html(
              '<div class="jGraduate_Swatch">' +
                '<h2 class="jGraduate_Title">' + $settings.window.pickerTitle + '</h2>' +
                '<div id="' + id + '_rg_jGraduate_GradContainer" class="jGraduate_GradContainer"></div>' +
                '<div id="' + id + '_rg_jGraduate_Opacity" class="jGraduate_Opacity" title="Click to set overall opacity of the gradient paint">' +
                  '<img id="' + id + '_rg_jGraduate_AlphaArrows" class="jGraduate_AlphaArrows" src="' + $settings.images.clientPath + 'rangearrows2.gif"></img>' +
                '</div>' +
              '</div>' + 
        '<div id="jGraduate_radColors" class="jGraduate_StopSection">' +
          '<label class="jGraduate_Form_Heading">Colors</label>' +
          '<div class="jGraduate_Form_Section jGraduate_Colorblocks">' +
            '<div class="jGraduate_colorblock"><span>Center:</span>' +
            '<div id="' + id + '_jGraduate_colorBoxCenter" class="colorBox"></div>' +
            '<label id="' + id + '_rg_jGraduate_centerOpacity"> 100%</label></div>' +

            '<div class="jGraduate_colorblock"><span>Outer:</span>' +
              '<div id="' + id + '_jGraduate_colorBoxOuter" class="colorBox"></div>' +
              '<label id="' + id + '_jGraduate_outerOpacity"> 100%</label></div>' +
          '</div>' +
        '</div>' +
        '<div class="jGraduate_StopSection">' +
        '</div>' +
              '<div class="jGraduate_Form">' +
                 '<div class="jGraduate_StopSection">' +
                  '<label class="jGraduate_Form_Heading">Center Point</label>' +
                  '<div class="jGraduate_Form_Section">' +
                    '<label>x:</label>' +
                    '<input type="text" id="' + id + '_jGraduate_cx" size="3" title="Enter x value between 0.0 and 1.0"/>' +
                    '<label> y:</label>' +
                    '<input type="text" id="' + id + '_jGraduate_cy" size="3" title="Enter y value between 0.0 and 1.0"/>' +
                  '</div>' +
                '</div>' +
                 '<div class="jGraduate_StopSection">' +
                  '<label class="jGraduate_Form_Heading">Focal Point</label>' +
                  '<div class="jGraduate_Form_Section">' +
                    '<label>Match center: <input type="checkbox" checked="checked" id="' + id + '_jGraduate_match_ctr"/></label><br/>' +
                    '<label>x:</label>' +
                    '<input type="text" id="' + id + '_jGraduate_fx" size="3" title="Enter x value between 0.0 and 1.0"/>' +
                    '<label> y:</label>' +
                    '<input type="text" id="' + id + '_jGraduate_fy" size="3" title="Enter y value between 0.0 and 1.0"/>' +
                  '</div>' +
                '</div>' +
                 '<div class="jGraduate_RadiusField">' +
                  '<label class="jGraduate_Form_Heading">Radius</label>' +
                  '<div class="jGraduate_Form_Section">' +
              '<div id="' + id + '_jGraduate_RadiusContainer" class="jGraduate_RadiusContainer"></div>' +
              '<input type="text" id="' + id + '_jGraduate_RadiusInput" size="3" value="100"/>%' +
              '<div id="' + id + '_jGraduate_Radius" class="jGraduate_Radius" title="Click to set radius">' +
                '<img id="' + id + '_jGraduate_RadiusArrows" class="jGraduate_RadiusArrows" src="' + $settings.images.clientPath + 'rangearrows2.gif"></img>' +
              '</div>' +
                  '</div>' +
                '</div>' +
               '</div>' +
        '<div class="rg_jGraduate_OpacityField">' +
          '<label class="rg_jGraduate_OpacityLabel">A: </label>' +
          '<input type="text" id="' + id + '_rg_jGraduate_OpacityInput" class="jGraduate_OpacityInput" size="3" value="100"/>%' +
        '</div>' +
              '<div class="jGraduate_OkCancel">' +
                '<input type="button" id="' + id + '_rg_jGraduate_Ok" class="jGraduate_Ok" value="OK"/>' +
                '<input type="button" id="' + id + '_rg_jGraduate_Cancel" class="jGraduate_Cancel" value="Cancel"/>' +
              '</div>' +
              '<div class="jGraduate_LightBox"></div>' +
              '<div id="' + id + '_rg_jGraduate_stopPicker" class="jGraduate_stopPicker"></div>');
      
      // --------------
            // Set up all the SVG elements (the gradient, stops and rectangle)
            var MAX = 256, MARGINX = 0, MARGINY = 0, STOP_RADIUS = 15/2,
              SIZEX = MAX - 2*MARGINX, SIZEY = MAX - 2*MARGINY;
              
            $.each(['lg', 'rg'], function(i) {
              var grad_id = id + '_' + this;
        var container = document.getElementById(grad_id+'_jGraduate_GradContainer');
        var svg = container.appendChild(document.createElementNS(ns.svg, 'svg'));
        svg.id = grad_id + '_jgraduate_svg';            
        svg.setAttribute('width', MAX);
        svg.setAttribute('height', MAX);
        svg.setAttribute("xmlns", ns.svg);
            });
      
      
      // Linear gradient
      (function() {
        var svg = document.getElementById(id + '_lg_jgraduate_svg');
        
        // if we are sent a gradient, import it 
        if ($this.paint.type == "linearGradient") {
          $this.paint.linearGradient.id = id+'_jgraduate_grad';
          $this.paint.linearGradient = svg.appendChild($this.paint.linearGradient.cloneNode(true));
        } else { // we create a gradient
          var grad = svg.appendChild(document.createElementNS(ns.svg, 'linearGradient'));
          grad.id = id+'_jgraduate_grad';
          grad.setAttribute('x1','0.0');
          grad.setAttribute('y1','0.0');
          grad.setAttribute('x2','1.0');
          grad.setAttribute('y2','1.0');
          
          var begin = grad.appendChild(document.createElementNS(ns.svg, 'stop'));
          begin.setAttribute('offset', '0.0');
          begin.setAttribute('stop-color', '#ff0000');
  
          var end = grad.appendChild(document.createElementNS(ns.svg, 'stop'));
          end.setAttribute('offset', '1.0');
          end.setAttribute('stop-color', '#ffff00');
        
          $this.paint.linearGradient = grad;
        }
  
        var gradalpha = $this.paint.alpha;
        $('#' + id + '_lg_jGraduate_OpacityInput').val(gradalpha);
        var posx = parseInt(255*(gradalpha/100)) - 4.5;
        $('#' + id + '_lg_jGraduate_AlphaArrows').css({'margin-left':posx});
        
        var x1 = parseFloat($this.paint.linearGradient.getAttribute('x1')||0.0),
          y1 = parseFloat($this.paint.linearGradient.getAttribute('y1')||0.0),
          x2 = parseFloat($this.paint.linearGradient.getAttribute('x2')||1.0),
          y2 = parseFloat($this.paint.linearGradient.getAttribute('y2')||0.0);
        
        var rect = document.createElementNS(ns.svg, 'rect');
        rect.id = id + '_lg_jgraduate_rect';
        rect.setAttribute('x', MARGINX);
        rect.setAttribute('y', MARGINY);
        rect.setAttribute('width', SIZEY);
        rect.setAttribute('height', SIZEY);
        rect.setAttribute('fill', 'url(#'+id+'_jgraduate_grad)');
        rect.setAttribute('fill-opacity', '1.0');
        rect = svg.appendChild(rect);
        $('#' + id + '_lg_jgraduate_rect').attr('fill-opacity', gradalpha/100);
        
        // stop visuals created here
        var beginStop = document.createElementNS(ns.svg, 'image');
        beginStop.id = id + "_stop1";
        beginStop.setAttribute('class', 'stop');
        beginStop.setAttributeNS(ns.xlink, 'href', $settings.images.clientPath + 'mappoint.gif');
        beginStop.setAttributeNS(ns.xlink, "title", "Begin Stop");
        beginStop.appendChild(document.createElementNS(ns.svg, 'title')).appendChild(
          document.createTextNode("Begin Stop"));
        beginStop.setAttribute('width', 18);
        beginStop.setAttribute('height', 18);
        beginStop.setAttribute('x', MARGINX + SIZEX*x1 - STOP_RADIUS);
        beginStop.setAttribute('y', MARGINY + SIZEY*y1 - STOP_RADIUS);
        beginStop.setAttribute('cursor', 'move');
        // must append only after setting all attributes due to Webkit Bug 27952
        // https://bugs.webkit.org/show_bug.cgi?id=27592
        beginStop = svg.appendChild(beginStop);
        
        var endStop = document.createElementNS(ns.svg, 'image');
        endStop.id = id + "_stop2";
        endStop.setAttribute('class', 'stop');
        endStop.setAttributeNS(ns.xlink, 'href', $settings.images.clientPath + 'mappoint.gif');
        endStop.setAttributeNS(ns.xlink, "title", "End Stop");
        endStop.appendChild(document.createElementNS(ns.svg, 'title')).appendChild(
          document.createTextNode("End Stop"));
        endStop.setAttribute('width', 18);
        endStop.setAttribute('height', 18);
        endStop.setAttribute('x', MARGINX + SIZEX*x2 - STOP_RADIUS);
        endStop.setAttribute('y', MARGINY + SIZEY*y2 - STOP_RADIUS);
        endStop.setAttribute('cursor', 'move');
        endStop = svg.appendChild(endStop);
        
        // bind GUI elements
        $('#'+id+'_lg_jGraduate_Ok').bind('click', function() {
          $this.paint.type = "linearGradient";
          $this.paint.solidColor = null;
          okClicked();
        });
        $('#'+id+'_lg_jGraduate_Cancel').bind('click', function(paint) {
          cancelClicked();
        });
        
        var x1 = $this.paint.linearGradient.getAttribute('x1');
        if(!x1) x1 = "0.0";
        var x1Input = $('#'+id+'_jGraduate_x1');
        x1Input.val(x1);
        x1Input.change( function() {
          if (isNaN(parseFloat(this.value)) || this.value < 0.0 || this.value > 1.0) { 
            this.value = 0.0; 
          }
          $this.paint.linearGradient.setAttribute('x1', this.value);
          beginStop.setAttribute('x', MARGINX + SIZEX*this.value - STOP_RADIUS);
        });
  
        var y1 = $this.paint.linearGradient.getAttribute('y1');
        if(!y1) y1 = "0.0";
        var y1Input = $('#'+id+'_jGraduate_y1');
        y1Input.val(y1);
        y1Input.change( function() {
          if (isNaN(parseFloat(this.value)) || this.value < 0.0 || this.value > 1.0) { 
            this.value = 0.0; 
          }
          $this.paint.linearGradient.setAttribute('y1', this.value);
          beginStop.setAttribute('y', MARGINY + SIZEY*this.value - STOP_RADIUS);
        });
        
        var x2 = $this.paint.linearGradient.getAttribute('x2');
        if(!x2) x2 = "1.0";
        var x2Input = $('#'+id+'_jGraduate_x2');
        x2Input.val(x2);
        x2Input.change( function() {
          if (isNaN(parseFloat(this.value)) || this.value < 0.0 || this.value > 1.0) { 
            this.value = 1.0;
          }
          $this.paint.linearGradient.setAttribute('x2', this.value);
          endStop.setAttribute('x', MARGINX + SIZEX*this.value - STOP_RADIUS);
        });
        
        var y2 = $this.paint.linearGradient.getAttribute('y2');
        if(!y2) y2 = "0.0";
        y2Input = $('#'+id+'_jGraduate_y2');
        y2Input.val(y2);
        y2Input.change( function() {
          if (isNaN(parseFloat(this.value)) || this.value < 0.0 || this.value > 1.0) { 
            this.value = 0.0;
          }
          $this.paint.linearGradient.setAttribute('y2', this.value);
          endStop.setAttribute('y', MARGINY + SIZEY*this.value - STOP_RADIUS);
        });            
        
        var stops = $this.paint.linearGradient.getElementsByTagNameNS(ns.svg, 'stop');
        var numstops = stops.length;
        // if there are not at least two stops, then 
        if (numstops < 2) {
          while (numstops < 2) {
            $this.paint.linearGradient.appendChild( document.createElementNS(ns.svg, 'stop') );
            ++numstops;
          }
          stops = $this.paint.linearGradient.getElementsByTagNameNS(ns.svg, 'stop');
        }
        
        var setLgOpacitySlider = function(e, div) {
          var offset = div.offset();
          var x = (e.pageX - offset.left - parseInt(div.css('border-left-width')));
          if (x > 255) x = 255;
          if (x < 0) x = 0;
          var posx = x - 4.5;
          x /= 255;
          $('#' + id + '_lg_jGraduate_AlphaArrows').css({'margin-left':posx});
          $('#' + id + '_lg_jgraduate_rect').attr('fill-opacity', x);
          x = parseInt(x*100);
          $('#' + id + '_lg_jGraduate_OpacityInput').val(x);
          $this.paint.alpha = x;
        };
        
        // handle dragging on the opacity slider
        var bSlidingOpacity = false;
        $('#' + id + '_lg_jGraduate_Opacity').mousedown(function(evt) {
          setLgOpacitySlider(evt, $(this));
          bSlidingOpacity = true;
          evt.preventDefault();
        }).mousemove(function(evt) {
          if (bSlidingOpacity) {
            setLgOpacitySlider(evt, $(this));
            evt.preventDefault();
          }
        }).mouseup(function(evt) {
          setLgOpacitySlider(evt, $(this));
          bSlidingOpacity = false;
          evt.preventDefault();
        });
        
        // handle dragging the stop around the swatch
        var draggingStop = null;
        var startx = -1, starty = -1;
        // for whatever reason, Opera does not allow $('image.stop') here,
        // and Firefox 1.5 does not allow $('.stop')
        $('.stop, #color_picker_lg_jGraduate_GradContainer image').mousedown(function(evt) {
          draggingStop = this;
          startx = evt.clientX;
          starty = evt.clientY;
          evt.preventDefault();
        });
        $('#'+id+'_lg_jgraduate_svg').mousemove(function(evt) {
          if (null != draggingStop) {
            var dx = evt.clientX - startx;
            var dy = evt.clientY - starty;
            startx += dx;
            starty += dy;
            var x = parseFloat(draggingStop.getAttribute('x')) + dx;
            var y = parseFloat(draggingStop.getAttribute('y')) + dy;
  
            // clamp stop to the swatch
            if (x < MARGINX - STOP_RADIUS) x = MARGINX - STOP_RADIUS;
            if (y < MARGINY - STOP_RADIUS) y = MARGINY - STOP_RADIUS;
            if (x > MARGINX + SIZEX - STOP_RADIUS) x = MARGINX + SIZEX - STOP_RADIUS;
            if (y > MARGINY + SIZEY - STOP_RADIUS) y = MARGINY + SIZEY - STOP_RADIUS;
                      
            draggingStop.setAttribute('x', x);
            draggingStop.setAttribute('y', y);
  
            // calculate stop offset                
            var fracx = (x - MARGINX + STOP_RADIUS)/SIZEX;
            var fracy = (y - MARGINY + STOP_RADIUS)/SIZEY;
            
            if (draggingStop.id == (id+'_stop1')) {
              x1Input.val(fracx);
              y1Input.val(fracy);
              $this.paint.linearGradient.setAttribute('x1', fracx);
              $this.paint.linearGradient.setAttribute('y1', fracy);
            }
            else {
              x2Input.val(fracx);
              y2Input.val(fracy);
              $this.paint.linearGradient.setAttribute('x2', fracx);
              $this.paint.linearGradient.setAttribute('y2', fracy);
            }
            
            evt.preventDefault();
          }
        });
        $('#'+id+'_lg_jgraduate_svg').mouseup(function(evt) {
          draggingStop = null;
        });
        
        var beginColor = stops[0].getAttribute('stop-color');
        if(!beginColor) beginColor = '#000';
        beginColorBox = $('#'+id+'_jGraduate_colorBoxBegin');
        beginColorBox.css({'background-color':beginColor});
  
        var beginOpacity = stops[0].getAttribute('stop-opacity');
        if(!beginOpacity) beginOpacity = '1.0';
        $('#'+id+'lg_jGraduate_beginOpacity').html( (beginOpacity*100)+'%' );
  
        var endColor = stops[stops.length-1].getAttribute('stop-color');
        if(!endColor) endColor = '#000';
        endColorBox = $('#'+id+'_jGraduate_colorBoxEnd');
        endColorBox.css({'background-color':endColor});
  
        var endOpacity = stops[stops.length-1].getAttribute('stop-opacity');
        if(!endOpacity) endOpacity = '1.0';
        $('#'+id+'jGraduate_endOpacity').html( (endOpacity*100)+'%' );
        
        $('#'+id+'_jGraduate_colorBoxBegin').click(function() {
          $('div.jGraduate_LightBox').show();      
          var colorbox = $(this);
          var thisAlpha = (parseFloat(beginOpacity)*255).toString(16);
          while (thisAlpha.length < 2) { thisAlpha = "0" + thisAlpha; }
          color = beginColor.substr(1) + thisAlpha;
          $('#'+id+'_jGraduate_stopPicker').css({'left': 100, 'bottom': 15}).jPicker({
              window: { title: "Pick the start color and opacity for the gradient" },
              images: { clientPath: $settings.images.clientPath },
              color: { active: color, alphaSupport: true }
            }, function(color){
              beginColor = color.get_Hex() ? ('#'+color.get_Hex()) : "none";
              beginOpacity = color.get_A() ? color.get_A()/100 : 1;
              colorbox.css('background', beginColor);
              $('#'+id+'_jGraduate_beginOpacity').html(parseInt(beginOpacity*100)+'%');
              stops[0].setAttribute('stop-color', beginColor);
              stops[0].setAttribute('stop-opacity', beginOpacity);
              $('div.jGraduate_LightBox').hide();
              $('#'+id+'_jGraduate_stopPicker').hide();
            }, null, function() {
              $('div.jGraduate_LightBox').hide();
              $('#'+id+'_jGraduate_stopPicker').hide();
            });
        });
        $('#'+id+'_jGraduate_colorBoxEnd').click(function() {
          $('div.jGraduate_LightBox').show();
          var colorbox = $(this);
          var thisAlpha = (parseFloat(endOpacity)*255).toString(16);
          while (thisAlpha.length < 2) { thisAlpha = "0" + thisAlpha; }
          color = endColor.substr(1) + thisAlpha;
          $('#'+id+'_jGraduate_stopPicker').css({'left': 100, 'top': 15}).jPicker({
              window: { title: "Pick the end color and opacity for the gradient" },
              images: { clientPath: $settings.images.clientPath },
              color: { active: color, alphaSupport: true }
            }, function(color){
              endColor = color.get_Hex() ? ('#'+color.get_Hex()) : "none";
              endOpacity = color.get_A() ? color.get_A()/100 : 1;
              colorbox.css('background', endColor);
              $('#'+id+'_jGraduate_endOpacity').html(parseInt(endOpacity*100)+'%');
              stops[1].setAttribute('stop-color', endColor);
              stops[1].setAttribute('stop-opacity', endOpacity);
              $('div.jGraduate_LightBox').hide();
              $('#'+id+'_jGraduate_stopPicker').hide();
            }, null, function() {
              $('div.jGraduate_LightBox').hide();
              $('#'+id+'_jGraduate_stopPicker').hide();
            });
        });            
        
        // --------------
        var thisAlpha = ($this.paint.alpha*255/100).toString(16);
        while (thisAlpha.length < 2) { thisAlpha = "0" + thisAlpha; }
        color = $this.paint.solidColor == "none" ? "" : $this.paint.solidColor + thisAlpha;
        colPicker.jPicker(
          {
            window: { title: $settings.window.pickerTitle },
            images: { clientPath: $settings.images.clientPath },
            color: { active: color, alphaSupport: true }
          },
          function(color) {
            $this.paint.type = "solidColor";
            $this.paint.alpha = color.get_A() ? color.get_A() : 100;
            $this.paint.solidColor = color.get_Hex() ? color.get_Hex() : "none";
            $this.paint.linearGradient = null;
            okClicked(); 
          },
          null,
          function(){ cancelClicked(); }
          );
      }());  
      
      
      // Radial gradient
      (function() {
        var svg = document.getElementById(id + '_rg_jgraduate_svg');
        
        // if we are sent a gradient, import it 
        if ($this.paint.type == "radialGradient") {
          $this.paint.radialGradient.id = id+'_rg_jgraduate_grad';
          $this.paint.radialGradient = svg.appendChild($this.paint.radialGradient.cloneNode(true));
        } else { // we create a gradient
          var grad = svg.appendChild(document.createElementNS(ns.svg, 'radialGradient'));
          grad.id = id+'_rg_jgraduate_grad';
          grad.setAttribute('cx','0.5');
          grad.setAttribute('cy','0.5');
          grad.setAttribute('r','0.5');
          
          var begin = grad.appendChild(document.createElementNS(ns.svg, 'stop'));
          begin.setAttribute('offset', '0.0');
          begin.setAttribute('stop-color', '#ff0000');
  
          var end = grad.appendChild(document.createElementNS(ns.svg, 'stop'));
          end.setAttribute('offset', '1.0');
          end.setAttribute('stop-color', '#ffff00');
        
          $this.paint.radialGradient = grad;
        }
  
        var gradalpha = $this.paint.alpha;
        $('#' + id + '_rg_jGraduate_OpacityInput').val(gradalpha);
        var posx = parseInt(255*(gradalpha/100)) - 4.5;
        $('#' + id + '_rg_jGraduate_AlphaArrows').css({'margin-left':posx});
        
        var grad = $this.paint.radialGradient;
        
        var cx = parseFloat(grad.getAttribute('cx')||0.5),
          cy = parseFloat(grad.getAttribute('cy')||0.5),
          fx = parseFloat(grad.getAttribute('fx')||0.5),
          fy = parseFloat(grad.getAttribute('fy')||0.5);
        
        // No match, so show focus point
        var showFocus = grad.getAttribute('fx') != null && !(cx == fx && cy == fy);
        
        var rect = document.createElementNS(ns.svg, 'rect');
        rect.id = id + '_rg_jgraduate_rect';
        rect.setAttribute('x', MARGINX);
        rect.setAttribute('y', MARGINY);
        rect.setAttribute('width', SIZEY);
        rect.setAttribute('height', SIZEY);
        rect.setAttribute('fill', 'url(#'+id+'_rg_jgraduate_grad)');
        rect.setAttribute('fill-opacity', '1.0');

        rect = svg.appendChild(rect);
        
        $('#' + id + '_rg_jgraduate_rect').attr('fill-opacity', gradalpha/100);

        // stop visuals created here
        var centerPoint = document.createElementNS(ns.svg, 'image');
        centerPoint.id = id + "_center_pt";
        centerPoint.setAttribute('class', 'stop');
        centerPoint.setAttributeNS(ns.xlink, 'href', $settings.images.clientPath + 'mappoint_c.png');
        centerPoint.setAttributeNS(ns.xlink, "title", "Center Point");
        centerPoint.appendChild(document.createElementNS(ns.svg, 'title')).appendChild(
          document.createTextNode("Center Point"));
        centerPoint.setAttribute('width', 18);
        centerPoint.setAttribute('height', 18);
        centerPoint.setAttribute('x', MARGINX + SIZEX*cx - STOP_RADIUS);
        centerPoint.setAttribute('y', MARGINY + SIZEY*cy - STOP_RADIUS);
        centerPoint.setAttribute('cursor', 'move');

        
        var focusPoint = document.createElementNS(ns.svg, 'image');
        focusPoint.id = id + "_focus_pt";
        focusPoint.setAttribute('class', 'stop');
        focusPoint.setAttributeNS(ns.xlink, 'href', $settings.images.clientPath + 'mappoint_f.png');
        focusPoint.setAttributeNS(ns.xlink, "title", "Focus Point");
        focusPoint.appendChild(document.createElementNS(ns.svg, 'title')).appendChild(
          document.createTextNode("Focus Point"));
        focusPoint.setAttribute('width', 18);
        focusPoint.setAttribute('height', 18);
        focusPoint.setAttribute('x', MARGINX + SIZEX*fx - STOP_RADIUS);
        focusPoint.setAttribute('y', MARGINY + SIZEY*fy - STOP_RADIUS);
        focusPoint.setAttribute('cursor', 'move');
        
        // must append only after setting all attributes due to Webkit Bug 27952
        // https://bugs.webkit.org/show_bug.cgi?id=27592
        
        // centerPoint is added last so it is moved first
        focusPoint = svg.appendChild(focusPoint);
        centerPoint = svg.appendChild(centerPoint);
        
        // bind GUI elements
        $('#'+id+'_rg_jGraduate_Ok').bind('click', function() {
          $this.paint.type = "radialGradient";
          $this.paint.solidColor = null;
          okClicked();
        });
        $('#'+id+'_rg_jGraduate_Cancel').bind('click', function(paint) {
          cancelClicked();
        });
        
        var cx = $this.paint.radialGradient.getAttribute('cx');
        if(!cx) cx = "0.0";
        var cxInput = $('#'+id+'_jGraduate_cx');
        cxInput.val(cx);
        cxInput.change( function() {
          if (isNaN(parseFloat(this.value)) || this.value < 0.0 || this.value > 1.0) { 
            this.value = 0.0; 
          }
          $this.paint.radialGradient.setAttribute('cx', this.value);
          centerPoint.setAttribute('x', MARGINX + SIZEX*this.value - STOP_RADIUS);
        });
  
        var cy = $this.paint.radialGradient.getAttribute('cy');
        if(!cy) cy = "0.0";
        var cyInput = $('#'+id+'_jGraduate_cy');
        cyInput.val(cy);
        cyInput.change( function() {
          if (isNaN(parseFloat(this.value)) || this.value < 0.0 || this.value > 1.0) { 
            this.value = 0.0; 
          }
          $this.paint.radialGradient.setAttribute('cy', this.value);
          centerPoint.setAttribute('y', MARGINY + SIZEY*this.value - STOP_RADIUS);
        });
        
        var fx = $this.paint.radialGradient.getAttribute('fx');
        if(!fx) fx = "1.0";
        var fxInput = $('#'+id+'_jGraduate_fx');
        fxInput.val(fx);
        fxInput.change( function() {
          if (isNaN(parseFloat(this.value)) || this.value < 0.0 || this.value > 1.0) { 
            this.value = 1.0;
          }
          $this.paint.radialGradient.setAttribute('fx', this.value);
          focusPoint.setAttribute('x', MARGINX + SIZEX*this.value - STOP_RADIUS);
        });
        
        var fy = $this.paint.radialGradient.getAttribute('fy');
        if(!fy) fy = "0.0";
        var fyInput = $('#'+id+'_jGraduate_fy');
        fyInput.val(fy);
        fyInput.change( function() {
          if (isNaN(parseFloat(this.value)) || this.value < 0.0 || this.value > 1.0) { 
            this.value = 0.0;
          }
          $this.paint.radialGradient.setAttribute('fy', this.value);
          focusPoint.setAttribute('y', MARGINY + SIZEY*this.value - STOP_RADIUS);
        });      
        
        if(!showFocus) {
          focusPoint.setAttribute('display', 'none');  
          fxInput.val("");
          fyInput.val("");
        }

        $("#" + id + "_jGraduate_match_ctr")[0].checked = !showFocus;
        
        var lastfx, lastfy;
        
        $("#" + id + "_jGraduate_match_ctr").change(function() {
          showFocus = !this.checked;
          focusPoint.setAttribute('display', showFocus?'inline':'none');
          fxInput.val("");
          fyInput.val("");
          var grad = $this.paint.radialGradient;
          if(!showFocus) {
            lastfx = grad.getAttribute('fx');
            lastfy = grad.getAttribute('fy');
            grad.removeAttribute('fx');
            grad.removeAttribute('fy');
          } else {
            var fx = lastfx || .5;
            var fy = lastfy || .5;
            grad.setAttribute('fx', fx);
            grad.setAttribute('fy', fy);
            fxInput.val(fx);
            fyInput.val(fy);
          }
        });
        
        var stops = $this.paint.radialGradient.getElementsByTagNameNS(ns.svg, 'stop');
        var numstops = stops.length;
        // if there are not at least two stops, then 
        if (numstops < 2) {
          while (numstops < 2) {
            $this.paint.radialGradient.appendChild( document.createElementNS(ns.svg, 'stop') );
            ++numstops;
          }
          stops = $this.paint.radialGradient.getElementsByTagNameNS(ns.svg, 'stop');
        }
        var radius = $this.paint.radialGradient.getAttribute('r')-0;
        var radiusx = parseInt((245/2)*(radius)) - 4.5;
        $('#' + id + '_jGraduate_RadiusArrows').css({'margin-left':radiusx});
        $('#' + id + '_jGraduate_RadiusInput').val(parseInt(radius*100)).change(function(e) {
          var x = this.value / 100;
          if(x < 0.01) {
            x = 0.01;
          }
          
          $this.paint.radialGradient.setAttribute('r', x);
          // Allow higher value, but pretend it's the max for the slider
          if(x > 2) x = 2;
          var posx = parseInt((245/2) * x) - 4.5;
          $('#' + id + '_jGraduate_RadiusArrows').css({'margin-left':posx});
          
        });
        
        var setRgOpacitySlider = function(e, div) {
          var offset = div.offset();
          var x = (e.pageX - offset.left - parseInt(div.css('border-left-width')));
          if (x > 255) x = 255;
          if (x < 0) x = 0;
          var posx = x - 4.5;
          x /= 255;
          $('#' + id + '_rg_jGraduate_AlphaArrows').css({'margin-left':posx});
          $('#' + id + '_rg_jgraduate_rect').attr('fill-opacity', x);
          x = parseInt(x*100);
          $('#' + id + '_rg_jGraduate_OpacityInput').val(x);
          $this.paint.alpha = x;
        };
        
        // handle dragging on the opacity slider
        var bSlidingOpacity = false;
        $('#' + id + '_rg_jGraduate_Opacity').mousedown(function(evt) {
          setRgOpacitySlider(evt, $(this));
          bSlidingOpacity = true;
          evt.preventDefault();
        }).mousemove(function(evt) {
          if (bSlidingOpacity) {
            setRgOpacitySlider(evt, $(this));
            evt.preventDefault();
          }
        }).mouseup(function(evt) {
          setRgOpacitySlider(evt, $(this));
          bSlidingOpacity = false;
          evt.preventDefault();
        });
        
        var setRadiusSlider = function(e, div) {
          var offset = div.offset();
          var x = (e.pageX - offset.left - parseInt(div.css('border-left-width')));
          if (x > 245) x = 245;
          if (x <= 1) x = 1;
          var posx = x - 5;
          x /= (245/2);
          $('#' + id + '_jGraduate_RadiusArrows').css({'margin-left':posx});
          $this.paint.radialGradient.setAttribute('r', x);
          x = parseInt(x*100);
          
          $('#' + id + '_jGraduate_RadiusInput').val(x);
        };
        
        // handle dragging on the radius slider
        var bSlidingRadius = false;
        $('#' + id + '_jGraduate_Radius').mousedown(function(evt) {
          setRadiusSlider(evt, $(this));
          bSlidingRadius = true;
          evt.preventDefault();
        }).mousemove(function(evt) {
          if (bSlidingRadius) {
            setRadiusSlider(evt, $(this));
            evt.preventDefault();
          }
        }).mouseup(function(evt) {
          setRadiusSlider(evt, $(this));
          bSlidingRadius = false;
          evt.preventDefault();
        });
        
        
        // handle dragging the stop around the swatch
        var draggingStop = null;
        var startx = -1, starty = -1;
        // for whatever reason, Opera does not allow $('image.stop') here,
        // and Firefox 1.5 does not allow $('.stop')
        $('.stop, #color_picker_rg_jGraduate_GradContainer image').mousedown(function(evt) {
          draggingStop = this;
          startx = evt.clientX;
          starty = evt.clientY;
          evt.preventDefault();
        });
        $('#'+id+'_rg_jgraduate_svg').mousemove(function(evt) {
          if (null != draggingStop) {
            var dx = evt.clientX - startx;
            var dy = evt.clientY - starty;
            startx += dx;
            starty += dy;
            var x = parseFloat(draggingStop.getAttribute('x')) + dx;
            var y = parseFloat(draggingStop.getAttribute('y')) + dy;
  
            // clamp stop to the swatch
            if (x < MARGINX - STOP_RADIUS) x = MARGINX - STOP_RADIUS;
            if (y < MARGINY - STOP_RADIUS) y = MARGINY - STOP_RADIUS;
            if (x > MARGINX + SIZEX - STOP_RADIUS) x = MARGINX + SIZEX - STOP_RADIUS;
            if (y > MARGINY + SIZEY - STOP_RADIUS) y = MARGINY + SIZEY - STOP_RADIUS;
                      
            draggingStop.setAttribute('x', x);
            draggingStop.setAttribute('y', y);
  
            // calculate stop offset                
            var fracx = (x - MARGINX + STOP_RADIUS)/SIZEX;
            var fracy = (y - MARGINY + STOP_RADIUS)/SIZEY;
            
            
            if (draggingStop.id == (id+'_center_pt')) {
              cxInput.val(fracx);
              cyInput.val(fracy);
              $this.paint.radialGradient.setAttribute('cx', fracx);
              $this.paint.radialGradient.setAttribute('cy', fracy);
              
              if(!showFocus) {
                $this.paint.radialGradient.setAttribute('fx', fracx);
                $this.paint.radialGradient.setAttribute('fy', fracy);
              }
            }
            else {
              fxInput.val(fracx);
              fyInput.val(fracy);
              $this.paint.radialGradient.setAttribute('fx', fracx);
              $this.paint.radialGradient.setAttribute('fy', fracy);
            }
            
            evt.preventDefault();
          }
        });
        $('#'+id+'_rg_jgraduate_svg').mouseup(function(evt) {
          draggingStop = null;
        });
        
        var centerColor = stops[0].getAttribute('stop-color');
        if(!centerColor) centerColor = '#000';
        centerColorBox = $('#'+id+'_jGraduate_colorBoxCenter');
        centerColorBox.css({'background-color':centerColor});
  
        var centerOpacity = stops[0].getAttribute('stop-opacity');
        if(!centerOpacity) centerOpacity = '1.0';
        $('#'+id+'jGraduate_centerOpacity').html( (centerOpacity*100)+'%' );
  
        var outerColor = stops[stops.length-1].getAttribute('stop-color');
        if(!outerColor) outerColor = '#000';
        outerColorBox = $('#'+id+'_jGraduate_colorBoxOuter');
        outerColorBox.css({'background-color':outerColor});
  
        var outerOpacity = stops[stops.length-1].getAttribute('stop-opacity');
        if(!outerOpacity) outerOpacity = '1.0';
        $('#'+id+'rg_jGraduate_outerOpacity').html( (outerOpacity*100)+'%' );
        
        $('#'+id+'_jGraduate_colorBoxCenter').click(function() {
          $('div.jGraduate_LightBox').show();      
          var colorbox = $(this);
          var thisAlpha = (parseFloat(centerOpacity)*255).toString(16);
          while (thisAlpha.length < 2) { thisAlpha = "0" + thisAlpha; }
          color = centerColor.substr(1) + thisAlpha;
          $('#'+id+'_rg_jGraduate_stopPicker').css({'left': 100, 'bottom': 15}).jPicker({
              window: { title: "Pick the center color and opacity for the gradient" },
              images: { clientPath: $settings.images.clientPath },
              color: { active: color, alphaSupport: true }
            }, function(color){
              centerColor = color.get_Hex() ? ('#'+color.get_Hex()) : "none";
              centerOpacity = color.get_A() ? color.get_A()/100 : 1;
              colorbox.css('background', centerColor);
              $('#'+id+'_rg_jGraduate_centerOpacity').html(parseInt(centerOpacity*100)+'%');
              stops[0].setAttribute('stop-color', centerColor);
              stops[0].setAttribute('stop-opacity', centerOpacity);
              $('div.jGraduate_LightBox').hide();
              $('#'+id+'_rg_jGraduate_stopPicker').hide();
            }, null, function() {
              $('div.jGraduate_LightBox').hide();
              $('#'+id+'_rg_jGraduate_stopPicker').hide();
            });
        });
        $('#'+id+'_jGraduate_colorBoxOuter').click(function() {
          $('div.jGraduate_LightBox').show();
          var colorbox = $(this);
          var thisAlpha = (parseFloat(outerOpacity)*255).toString(16);
          while (thisAlpha.length < 2) { thisAlpha = "0" + thisAlpha; }
          color = outerColor.substr(1) + thisAlpha;
          $('#'+id+'_rg_jGraduate_stopPicker').css({'left': 100, 'top': 15}).jPicker({
              window: { title: "Pick the outer color and opacity for the gradient" },
              images: { clientPath: $settings.images.clientPath },
              color: { active: color, alphaSupport: true }
            }, function(color){
              outerColor = color.get_Hex() ? ('#'+color.get_Hex()) : "none";
              outerOpacity = color.get_A() ? color.get_A()/100 : 1;
              colorbox.css('background', outerColor);
              $('#'+id+'_jGraduate_outerOpacity').html(parseInt(outerOpacity*100)+'%');
              stops[1].setAttribute('stop-color', outerColor);
              stops[1].setAttribute('stop-opacity', outerOpacity);
              $('div.jGraduate_LightBox').hide();
              $('#'+id+'_rg_jGraduate_stopPicker').hide();
            }, null, function() {
              $('div.jGraduate_LightBox').hide();
              $('#'+id+'_rg_jGraduate_stopPicker').hide();
            });
        });            
        
        // --------------
        var thisAlpha = ($this.paint.alpha*255/100).toString(16);
        while (thisAlpha.length < 2) { thisAlpha = "0" + thisAlpha; }
        color = $this.paint.solidColor == "none" ? "" : $this.paint.solidColor + thisAlpha;
        colPicker.jPicker(
          {
            window: { title: $settings.window.pickerTitle },
            images: { clientPath: $settings.images.clientPath },
            color: { active: color, alphaSupport: true }
          },
          function(color) {
            $this.paint.type = "solidColor";
            $this.paint.alpha = color.get_A() ? color.get_A() : 100;
            $this.paint.solidColor = color.get_Hex() ? color.get_Hex() : "none";
            $this.paint.radialGradient = null;
            okClicked(); 
          },
          null,
          function(){ cancelClicked(); }
          );
      }());  
      
      var tabs = $(idref + ' .jGraduate_tabs li');
      tabs.click(function() {
        tabs.removeClass('jGraduate_tab_current');
        $(this).addClass('jGraduate_tab_current');
        $(idref + " > div").hide();
        $(idref + ' .jGraduate_' +  $(this).attr('data-type') + 'Pick').show();
      });
      
      $(idref + " > div").hide();
      tabs.removeClass('jGraduate_tab_current');
      var tab;
      switch ( $this.paint.type ) {
        case 'linearGradient':
          tab = $(idref + ' .jGraduate_tab_lingrad');
          break;
        case 'radialGradient':
          tab = $(idref + ' .jGraduate_tab_radgrad');
          break;
        default:
          tab = $(idref + ' .jGraduate_tab_color');
          break;
      }
      tab.addClass('jGraduate_tab_current').click();  

      $this.show();
    });
  };
})();