
var ftgTag = Class.create();

ftgTag.prototype = {
  initialize: function(container) {

    var options = Object.extend({
      move: true,
      edit: true,
      resize: true,
      callout: true,
      callout_move: true,
      width: 230, // default tag width
      height: 22, // default tag height
      marker_width: 20, // default marker width
      marker_height: 20, // default marker height
      left: 200, // default left tag pos
      top: 0, // default top tag pos
      marker_left: 120, // default marker left (px)
      marker_top: 80,
      color: '#ffffad', // background color
      text_color: '#000000',
      callout_color: '#000000', // border and callout color
      content: "Double click to edit. Drag border to resize."
    }, arguments[1] || {});

    this.container = $(container);
    this.editmode = 0;
    this.options = options;

    //
    this.div = null; // tag container
    this.marker = null; // callout pointer for move
    this.is_visible = true;
    

    this.create();
  },
  
  destroy: function() {

	// calling callback function if exists
        if (this.options.deleteCallback)
		this.options.deleteCallback(this);

	Element.remove(this.div);
	Element.remove(this.line);
  },


  insertElement: function (elem)
  {
	if (this.container.hasChildNodes())
	  this.container.insertBefore(elem, this.container.firstChild);
	else
	 this.container.appendChild(elem);
  },

  updateLine: function() {

	  var x,y;

	if (navigator.appName =="Netscape" || navigator.appName =="Opera")
	{
	        // SVG
		x = parseInt(this.div.style.left) || 0;
	        y = parseInt(this.div.style.top) || 0;

                pleft = parseInt(Element.getStyle(this.container, "padding-left")) || 0;
                ptop = parseInt(Element.getStyle(this.container, "padding-top")) || 0;

		this.line.setAttribute('x1', (x-pleft)); 
		this.line.setAttribute('y1', (y-ptop));	

	        var d = Element.getDimensions(this.marker);

	        x = (2*parseInt(this.marker.style.left) + d.width) / 2;
       	        y = (2*parseInt(this.marker.style.top) + d.height) / 2;
//		x = parseInt(this.marker.style.left) || 0;
//                y = parseInt(this.marker.style.top) || 0;

                pleft = parseInt(Element.getStyle(this.container, "padding-left")) || 0;
                ptop = parseInt(Element.getStyle(this.container, "padding-top")) || 0;

		this.line.setAttribute('x2', (x-pleft)); 
		this.line.setAttribute('y2', (y-ptop));

        } else {

	  //VML
	  x = parseInt(this.div.style.left) || 0;
          y = parseInt(this.div.style.top) || 0;

          pleft = parseInt(Element.getStyle(this.container, "padding-left")) || 0;
          ptop = parseInt(Element.getStyle(this.container, "padding-top")) || 0;

          this.line.setAttribute("from", (x-pleft)+","+(y-ptop));

          var d = Element.getDimensions(this.marker);

	  x = (2*parseInt(this.marker.style.left) + d.width) / 2;
	  y = (2*parseInt(this.marker.style.top) + d.height) / 2;
	  //x = parseInt(this.marker.style.left) || 0;
          //y = parseInt(this.marker.style.top) || 0;

          pleft = parseInt(Element.getStyle(this.container, "padding-left")) || 0;
          ptop = parseInt(Element.getStyle(this.container, "padding-top")) || 0;

          this.line.setAttribute("to", (x-pleft)+","+(y-ptop));

        }

	// if edit box, update
        if (this.edit_div)
	{
		
		this.edit_div.style.left = this.div.style.left;
		var dim = Element.getDimensions(this.div);
		this.edit_div.style.top = parseFloat(this.div.style.top) + dim.height + 5 + "px";
	}
  },

  
  createCallout: function ()
  {
	this.marker = document.createElement("DIV");
	$(this.marker).addClassName("ftgMarker");

	$(this.marker).style.width = this.options.marker_width + "px";
	$(this.marker).style.height = this.options.marker_height + "px";

	$(this.marker).style.left = (this.options.marker_left  - (this.options.marker_width / 2))+ "px";
	$(this.marker).style.top = (this.options.marker_top - (this.options.marker_height / 2))+ "px";


	var div = document.createElement("DIV");
	$(div).style.background = "black";
	$(div).style.width = "100%";
	$(div).style.height = "100%";
	this.marker.appendChild(div);

	Element.setOpacity(div, .01);
	this.insertElement(this.marker);

	if (navigator.appName =="Netscape" || navigator.appName =="Opera")
	{
			var ftgSVGContainer = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); 
			var d = Element.getDimensions(this.container);

			ftgSVGContainer.setAttribute("width", d.width);
//			ftgSVGContainer.setAttribute("height", document.body.clientHeight);
			ftgSVGContainer.setAttribute("height", d.height);
			ftgSVGContainer.setAttribute("style", "position:absolute; z-index:1");

			var defs = document.createElementNS('http://www.w3.org/2000/svg', 'defs'); 

			var marker1 = document.createElementNS('http://www.w3.org/2000/svg', 'marker');
			var head1 = document.createElementNS('http://www.w3.org/2000/svg', 'polyline');

			marker1.setAttribute('id', 'Triangle'); 
			marker1.setAttribute('viewBox', '5 5 10 10'); 
			marker1.setAttribute('markerUnits', 'strokeWidth'); 
			marker1.setAttribute('markerWidth', '8'); 
			marker1.setAttribute('markerHeight', '8');
			marker1.setAttribute('orient', 'auto');

			head1.setAttribute('points', '0,0 10,5 0,10 1,5'); 
			head1.setAttribute('fill', this.options.callout_color);  

			marker1.appendChild(head1);

			defs.appendChild(marker1); 

			ftgSVGContainer.appendChild(defs);

		this.line = document.createElementNS('http://www.w3.org/2000/svg', 'line'); 
		this.line.setAttribute('marker-end', 'url(#Triangle)'); 
		this.line.setAttribute('stroke', this.options.callout_color);
		this.line.setAttribute('stroke-width','1');

		ftgSVGContainer.appendChild(this.line);

		//$(document.body).insertBefore(ftgSVGContainer, $(document.body).firstChild);
		this.insertElement(ftgSVGContainer);

		this.updateLine();

        } else {
          this.line = document.createElement("v:line");
          this.line.setAttribute("strokecolor", this.options.callout_color);
	  this.line.style.zIndex = "56";
	  this.line.style.position = "absolute";

 	  var stroke = document.createElement("v:stroke");
	  stroke.setAttribute("endarrow", "block");
	  this.line.appendChild(stroke);

          this.insertElement(this.line);
	  this.updateLine();
        }

	if (!this.options.callout)
	{
		this.line.style.display = 'none';
	}

	this.marker.style.display = 'none';
  },

  create: function() {

	  this.div = document.createElement("DIV");
  
	  $(this.div).addClassName("ftgTag");
	  $(this.div).innerHTML = this.options.content;
	  $(this.div).style.left = this.options.left + "px";
	  $(this.div).style.top = this.options.top + "px";

	  if (this.options.width)
		  $(this.div).style.width = this.options.width + "px";

	  if (this.options.height)
		  $(this.div).style.height = this.options.height + "px";

	  // background color	
	  if (this.options.color)
		$(this.div).style.background = this.options.color;

	  // text color
	  if (this.options.text_color)
		$(this.div).style.color = this.options.text_color;

	  // callouts and borders
	  if (this.options.callout_color)
		$(this.div).style.borderColor = this.options.callout_color;

	  this.insertElement(this.div);


	  this.createCallout();

	  this.eventDblClick = this.dblClick.bindAsEventListener(this);
	  Event.observe(this.div, "dblclick", this.eventDblClick);
}
   , dblClick: function()
   {
	this.startEdit();
   }

   , startEdit: function()
   {
        if (this.editing || !this.options.edit)
		return;

	this.editing = true;

	this._show_marker();

	// saving old border style

/*        this.marker_drag =   new Draggable(this.marker, {
	tag: this,
    snap: function(x,y,draggable) {
      function constrain(n, lower, upper) {
        if (n > upper) return upper;
        else if (n < lower) return lower;
        else return n;
      }
     
      element_dimensions = Element.getDimensions(draggable.element);
      parent_dimensions = Element.getDimensions(draggable.element.parentNode);
      return[
        constrain(x, 0, parent_dimensions.width - element_dimensions.width),
        constrain(y, 0, parent_dimensions.height - element_dimensions.height)];
    },
			onDrag: function(e) {

				e.options.tag.updateLine();

			}

	});
*/
	this.marker_drag = new Resizeable(this.marker, { tag: this, 
			resizeable: false 
    ,snap: function(x,y,draggable) {
      function constrain(n, lower, upper) {
        if (n > upper) return upper;
        else if (n < lower) return lower;
        else return n;
      }
     
      element_dimensions = Element.getDimensions(draggable.element);
      parent_dimensions = Element.getDimensions(draggable.element.parentNode);
      return[
        constrain(x, 0, parent_dimensions.width - element_dimensions.width),
        constrain(y, 0, parent_dimensions.height - element_dimensions.height)];
    }
		}
	);

	this.div.style.border = '2px dotted black';

	this.div_resize = new Resizeable(this.div, { tag: this 
    ,snap: function(x,y,draggable) {
      function constrain(n, lower, upper) {
        if (n > upper) return upper;
        else if (n < lower) return lower;
        else return n;
      }
     
      element_dimensions = Element.getDimensions(draggable.element);
      parent_dimensions = Element.getDimensions(draggable.element.parentNode);
      return[
        constrain(x, 0, parent_dimensions.width - element_dimensions.width),
        constrain(y, 0, parent_dimensions.height - element_dimensions.height)];
    }
});

	
	var div = document.createElement("DIV");
//	div.innerHTML = "<div>Edit</div>";
	var area = document.createElement("TEXTAREA");
	area.style.width = '170px';
	area.style.height = "50px";
	area.rows = 3;
	area.value = String(this.div.innerHTML).replace(/<br.*?>/gi, "\r\n");
	div.appendChild(area);
	$(div).addClassName("ftgEditBox");
	div.style.left = this.div.style.left;
	var dim = Element.getDimensions(this.div);
	div.style.top = parseFloat(this.div.style.top) + dim.height + 5 + "px";

	var ok = document.createElement("INPUT");
	ok.type = 'button';
	ok.value = 'OK';
      
        this.eventclickOK = this.clickOK.bindAsEventListener(this);
	Event.observe(ok, "click", this.eventclickOK);

	div.appendChild(ok);
	
	var del = document.createElement("INPUT");
	del.type = 'button';
	del.value = 'Delete';
      
        this.eventdeleteTag = this.deleteTag.bindAsEventListener(this);
	Event.observe(del, "click", this.eventdeleteTag);

	div.appendChild(del);

	this.insertElement(div);

	this.edit_box = area;
	this.edit_div = div;

   }

   ,stopEdit: function()
   {
 	 this.div.innerHTML = String(this.edit_box.value).replace(/\n/gi, "<br />");
 	 this.div.innerHTML = String(this.div.innerHTML).replace(/\r/gi, "");
	 Element.remove(this.edit_div);

	 this._hide_marker();

	 if (this.options.callout_color)
		 this.div.style.border  = "1px solid " + this.options.callout_color;
	 else
		 this.div.style.border  = "1px solid black";

	 this.div_resize.destroy();
	 this.marker_drag.destroy();
	 this.editing = false;

   }

   , clickOK: function()
   {
     this.stopEdit();

     if (this.options.clickOK)
	this.options.clickOK(this);

   }

   , deleteTag: function()
   {
	this.stopEdit();
	this.destroy();
   }

   , hide: function()
   {
	this.is_visible = false;

	if (this.div)
	  this.div.style.display = 'none';

	this._hide_callout();

 	if (this.editing)
		this._hide_marker();

   }

   , show: function()
   {
	this.is_visible = true;

	if (this.div)
	  this.div.style.display = 'block';

	this._show_callout();

 	if (this.editing)
		this._show_marker();
   }

   , getLeft: function() { return parseInt(Element.getStyle(this.div, "left")) || 0; }
   , getTop: function() { return parseInt(Element.getStyle(this.div, "top")) || 0; }
   , getWidth: function() { return parseInt(Element.getStyle(this.div, "width")) || 0; }
   , getHeight: function() { return parseInt(Element.getStyle(this.div, "height")) || 0; }

   , getMarkerLeft: function() { 
			var left = parseInt(Element.getStyle(this.marker, "left")) || 0; 
			return left;
		}
   , getMarkerTop: function() { 
			var top = parseInt(Element.getStyle(this.marker, "top")) || 0; 
			return top;
		}
   , getMarkerWidth: function() { return parseInt(Element.getStyle(this.marker, "width")) || 0; }
   , getMarkerHeight: function() { return parseInt(Element.getStyle(this.marker, "height")) || 0; }
    

   , getColor: function() { return this.options.color; }
   , getTextColor: function() { return this.options.text_color; }
   , getCalloutColor: function() { return this.options.callout_color; }

   , getContent: function() { return this.div.innerHTML; }
   , isHaveCallout: function() { return this.options.callout; }

    // private 
   , _show_callout: function()
   {
	if (this.line && this.options.callout)
	 this.line.style.display = 'block';
   }

   , _show_marker: function()
   {
	if (this.marker && this.options.callout)
	 this.marker.style.display = 'block';
   }

   , _hide_callout: function()
   {
	if (this.line && this.options.callout)
	 this.line.style.display = 'none';
   }

   , _hide_marker: function()
   {
	if (this.marker && this.options.callout)
	 this.marker.style.display = 'none';
   }
}  


// Copyright (c) 2005 Thomas Fakes (http://craz8.com)
// 
// This code is substantially based on code from script.aculo.us which has the 
// following copyright and permission notice
//
// Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// 
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

var Resizeable = Class.create();
Resizeable.prototype = {
  initialize: function(element) {
    var options = Object.extend({
      top: 6,
      bottom: 6,
      left: 6,
      right: 6,
      minHeight: 18,
      minWidth: 50,
      zindex: 1000,
      resize: null,
      resizeable: true,
      moveable: true
    }, arguments[1] || {});

    this.element      = $(element);
    this.handle 	  = this.element;

    Element.makePositioned(this.element); // fix IE    

    this.options      = options;

    this.active       = false;
    this.resizing     = false; 
    this.move 	      = false;  
    this.currentDirection = '';
    this.oldcursor = Element.getStyle(this.element, 'cursor') || '';

    this.eventMouseDown = this.startResize.bindAsEventListener(this);
    this.eventMouseUp   = this.endResize.bindAsEventListener(this);
    this.eventMouseMove = this.update.bindAsEventListener(this);
    this.eventCursorCheck = this.cursor.bindAsEventListener(this);
    this.eventKeypress  = this.keyPress.bindAsEventListener(this);
    
    this.registerEvents();
  },
  destroy: function() {
    Event.stopObserving(this.handle, "mousedown", this.eventMouseDown);
    Event.stopObserving(this.handle, "mousemove", this.eventCursorCheck);
    this.element.style.cursor = this.oldcursor;
    this.unregisterEvents();
  },
  registerEvents: function() {
    Event.observe(document, "mouseup", this.eventMouseUp);
    Event.observe(document, "mousemove", this.eventMouseMove);
    Event.observe(document, "keypress", this.eventKeypress);
    Event.observe(this.handle, "mousedown", this.eventMouseDown);
    Event.observe(this.element, "mousemove", this.eventCursorCheck);
  },
  unregisterEvents: function() {
    //if(!this.active) return;
    //Event.stopObserving(document, "mouseup", this.eventMouseUp);
    //Event.stopObserving(document, "mousemove", this.eventMouseMove);
    //Event.stopObserving(document, "mousemove", this.eventCursorCheck);
    //Event.stopObserving(document, "keypress", this.eventKeypress);
  },
  startResize: function(event) {
    if(Event.isLeftClick(event)) {
      
      // abort on form elements, fixes a Firefox issue
      var src = Event.element(event);
      if(src.tagName && (
        src.tagName=='INPUT' ||
        src.tagName=='SELECT' ||
        src.tagName=='BUTTON' ||
        src.tagName=='TEXTAREA')) return;

	  var dir = this.directions(event);
    	      var offsets = Position.cumulativeOffset(this.element);
	      this.startTop = offsets[1];
	      this.startLeft = offsets[0];
	      this.startWidth = parseInt(Element.getStyle(this.element, 'width'));
	      this.startHeight = parseInt(Element.getStyle(this.element, 'height'));
	      this.startX = event.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
	      this.startY = event.clientY + document.body.scrollTop + document.documentElement.scrollTop;
              this.startPointer = [Event.pointerX(event), Event.pointerY(event)];

	  if (dir.length > 0) {      

	      this.active = true;
	      
	      this.currentDirection = dir;
	      Event.stop(event);
	  }
	  else {
	    this.move = true;
	      Event.stop(event);
          }
    }
  },
  finishResize: function(event, success) {
    // this.unregisterEvents();

    this.active = false;
    this.resizing = false;
    this.move = false;

    if(this.options.zindex)
      this.element.style.zIndex = this.originalZ;
      
    if (this.options.resize) {
    	this.options.resize(this.element);
    }
  },
  keyPress: function(event) {
    if(this.active) {
      if(event.keyCode==Event.KEY_ESC) {
        this.finishResize(event, false);
        Event.stop(event);
      }
    }
  },
  endResize: function(event) {
    if(this.active && this.resizing) {
      this.finishResize(event, true);
      Event.stop(event);
    }
    this.active = false;
    this.resizing = false;
    this.move = false;
  },
  draw: function(event) {
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    var offsets = Position.cumulativeOffset(this.element);
    var style = this.element.style;

    if (this.currentDirection.indexOf('n') != -1) {
    	var pointerMoved = this.startPointer[1] - pointer[1];
    	var newHeight = this.startHeight + pointerMoved;
    	var styletop = parseInt(Element.getStyle(this.element, 'top')) || 0;

    	if (newHeight > this.options.minHeight) {

	        var newtop = pointer[1] - (offsets[1] - styletop) - (this.startPointer[1] - this.startTop);
		var diff = 0;

		if(this.options.snap && typeof this.options.snap == 'function') {
		        p = this.options.snap(0,newtop,this);
			diff = newtop - p[1];
			newtop = p[1];
			newHeight += diff;
		}

    		style.height = newHeight + "px";
    		style.top = newtop + "px";
    	}
    }
    if (this.currentDirection.indexOf('w') != -1) {
    	var pointerMoved = this.startPointer[0] - pointer[0];
    	var styleleft = parseInt(Element.getStyle(this.element, 'left')) || 0;
    	var newWidth = this.startWidth + pointerMoved;

    	if (newWidth > this.options.minWidth) {
	    
                var newleft = pointer[0] - (offsets[0] - styleleft) - (this.startPointer[0] - this.startLeft);
		var diff = 0;

		if(this.options.snap && typeof this.options.snap == 'function') {
		        p = this.options.snap(newleft,0,this);
			diff = newleft - p[0];
			newleft = p[0];
			newWidth += diff;
		}

    		style.left = newleft  + "px";
    		style.width = newWidth + "px";
    	}
    }
    if (this.currentDirection.indexOf('s') != -1) {
    	var newHeight = this.startHeight + pointer[1] - this.startY;

    	if (newHeight > this.options.minHeight) {

    		style.height = newHeight + "px";
    	}
    }
    if (this.currentDirection.indexOf('e') != -1) {
    	var newWidth = this.startWidth + pointer[0] - this.startX;
    	if (newWidth > this.options.minWidth) {
    		style.width = newWidth + "px";
    	}
    }
    if(style.visibility=="hidden") style.visibility = ""; // fix gecko rendering
  },
  between: function(val, low, high) {
  	return (val >= low && val < high);
  },
  directions: function(event) {
    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    var offsets = Position.cumulativeOffset(this.element);
    
	var cursor = '';
	if (this.between(pointer[1] - offsets[1], 0, this.options.top)) cursor += 'n';
	if (this.between((offsets[1] + this.element.offsetHeight) - pointer[1], 0, this.options.bottom)) cursor += 's';
	if (this.between(pointer[0] - offsets[0], 0, this.options.left)) cursor += 'w';
	if (this.between((offsets[0] + this.element.offsetWidth) - pointer[0], 0, this.options.right)) cursor += 'e';

	return cursor;
  },
  cursor: function(event) {
	var is_resize = 0;
  	var cursor = this.directions(event);
	if (cursor.length > 0) {
		cursor += '-resize';
		is_resize = 1;
	} else {
		cursor = 'move';
	}

	if (is_resize && !this.options.resizeable)
		cursor = '';
	
	if (!is_resize && !this.options.moveable)
		cursor = '';

	this.element.style.cursor = cursor;		
  },
  update: function(event) {
   if(this.active && this.options.resizeable) {
      if(!this.resizing) {
        var style = this.element.style;
        this.resizing = true;
        
        if(Element.getStyle(this.element,'position')=='') 
          style.position = "relative";
        
        if(this.options.zindex) {
          this.originalZ = parseInt(Element.getStyle(this.element,'z-index') || 0);
          style.zIndex = this.options.zindex;
        }

      }
      this.draw(event);
      if (this.options.tag) this.options.tag.updateLine();

      // fix AppleWebKit rendering
      if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); 
      Event.stop(event);
      return false;
   }

   if (this.move && this.options.moveable) { 

    var pointer = [Event.pointerX(event), Event.pointerY(event)];
    var offsets = Position.cumulativeOffset(this.element);
    var styleleft = parseInt(Element.getStyle(this.element, 'left')) || 0;
    var styletop = parseInt(Element.getStyle(this.element, 'top')) || 0;

    var newleft = pointer[0] - (offsets[0] - styleleft) - (this.startPointer[0] - this.startLeft);
    var newtop = pointer[1] - (offsets[1] - styletop) - (this.startPointer[1] - this.startTop);

    if(this.options.snap && typeof this.options.snap == 'function') {
        p = this.options.snap(newleft,newtop,this);
	newleft = p[0];
	newtop = p[1];
    }

    this.element.style.left = newleft + 'px';
    this.element.style.top = newtop + 'px';

    if (this.options.tag) this.options.tag.updateLine();
          

      // fix AppleWebKit rendering
      if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0); 
      Event.stop(event);
      return false;
   }

  }
}
