/**
 * Looks for links with class 'dialog' within the supplied element e or the 
 * whole document if no element was supplied. Creates a Dialog instance for
 * each link.
 *
 * @constructor
 *
 * @param  {String}  e  DOMElement where the constructor will look for dialog links
 *
 * @returns  An array of Dialog objects
 * @type     Array
 */
var DialogHandler = function(){
  this.dialogs = $A();
};
DialogHandler.prototype = {
  /** 
   * Finds and loops through all dialog links, creates new Dialog instances
   * @param  {String OR Object}  e    DOMNode or id of a DOMNode in which to look for dialogs
   * @param  {Hash}              opt  Options for all created dialogs. See Dialog class for valid option keys
   */
  findAndCreate: function(e,opt) {
	e = $(e) || $(document);
	if (dialogs = $(e).select('.dialog')){
	  $A(dialogs).each(function(e){
		e = $(e);
		if(opt){
		  dialog_opt   = opt;
		  dialog_opt.e = e;
		}
		else{
		  dialog_opt = {e: e, submit_by_ajax: e.hasClassName('ajax')};
		}
		i = this.dialogs.push(new Dialog(dialog_opt));
		dialogue = this.dialogs[i-1];
		dialogue.handler = this;
	  }.bind(this));
	}
	return this.dialogs;
  },
  /**
   * Creates a new dialog and adds it to the dialogs array
   * @param  {Hash}  opt  Options for the new dialog. See Dialog class for valid option keys
   */
  add: function(opt){
	this.dialogs.push(new Dialog(opt));
	return this.dialogs.last();
  },
  /**
   * Sets a given dialog's z-index to position it on top of all other dialogs
   * @param  {Object}  dialogue  A Dialog object
   */
  moveToTop: function(dialogue){
    var z,newIndex;
    newIndex = this.dialogs.max(function(d){
								  z = d.box ? parseInt(d.box.getStyle('z-index'),10) : 0;
								  return z;
								}) + 1;
	dialogue.box.style.zIndex = newIndex;
  },
  /**
   * Dialogs for the map
   * */
  addMapOnLoadDialogs: function (e) {
	  var opt            = {};
	  opt.e              = $(e);
	  opt.submit_by_ajax = e.hasClassName('ajax');
	  if($(e.parentNode).hasClassName('email-map')){
		opt.before_open  = function makeLink(dialog){ setMaplink(dialog.opt.e); };
	  }
	  else if(e.hasClassName('routeSMS')){
		opt.before_open    = function makeLink(dialog){ setRouteXY(dialog.opt.e); };
	  }
	  else if(e.hasClassName('mapSMS')){
		opt.before_open    = function makeLink(dialog){ setSMSMap(dialog.opt.e); };
	  }
	  this.add(opt);
  },
  
  addLinkToMapDialog: function(linkEl) {
	var self=this;
	  function fixHref(href) {
	$$('.linkdestination').each(function (el) {
	  function fixedCopy() {
		el.value=href;
		el.selectionStart=0;
		el.selectionEnd=href.length;
		  }
	  fixedCopy();
	  el.observe("click",fixedCopy);
					  
	  var textRange;
	  function ieFixedCopy(){
		if (!el) {
		  return;
		}
		el.value=href;
		if (!textRange) {
		  textRange=el.createTextRange();
		}
		if (textRange.findText(href)) {
		  try {
			textRange.select();
		  } catch (e){}; // This happens if the dialog is closed when we get here.
		}
	  }
	  function focusAndClickTrick(e) {
	   window.setTimeout(function () {
		 ieFixedCopy();
		 if (el) {
		   el.focus();	      
		 }
	   },10);
	 }
	  if (Prototype.Browser.IE){
		ieFixedCopy();
		el.unselectable="on";
		el.onkeydown=focusAndClickTrick;
		el.onclick=focusAndClickTrick;
		el.onfocus=focusAndClickTrick;	    
		el.onfocusout=focusAndClickTrick;
		el.onmousedown=focusAndClickTrick;
		  }
	});
	  }

	  function fixDialog(e,html,isRoute){
		var opt            = {};
		opt.e              = $(e);
		
		opt.content="";
		opt.extra_class="clipboard-copier";
		opt.title=texts.heading;
	opt.x = $('map').getWidth() - 380;
	opt.y = 200;
		opt.before_open  = function (dialog){ 
	  if (isRoute) {
		updateMapstate();
		dialog.opt.e.href=unescape(buildMailRoutelink('routeForm'));
	  } else
		dialog.opt.e.href=getLinkToMap();
		};
		opt.after_open= function (dialog){
		  var content=new Element("div").update(html);    
		  dialog.setContent(content);
		  
		  fixHref(dialog.opt.e.href);
		  
		  $$('.clipboard-copier').each(function(el) {
			el.observe('click',function () {
			  var href=dialog.opt.e.href;
			  var holdText=$('clipboard-holder');
			  if (holdText && holdText.createTextRange) {
				holdText.innerText = href;
				var copied = holdText.createTextRange();
				copied.execCommand("Copy");
			  } else if (window.clipboardData){ 
				window.clipboardData.setData("Text",href);
			  }
		});
		  });
		};
	self.add(opt);	
	  };
	  
	  var linkToRoute=$('link-to-route');      
	  var link=linkToRoute || linkEl;

	  if (link) {
	var clipboard=Prototype.Browser.IE;
	var texts=linkToMapTexts; //Global variable on maps
	
		var button="";
		if (clipboard) {
		  button="<div><input class='clipboard-button' type='button' value='" +texts.button+ "' style='width:auto;'></input><span id='clipboard-holder' style='display:none;'></span></div>";
		}
		var html=''+ 
		  '<div>' +
		  '<div>'+ texts.text + '</div>' +
		  '<div><input type="text" class="linkdestination"></input><div>' +
		  button +
		  '</div>' +
		  '';
	fixDialog(link,html,linkToRoute);
	  }
	}

  
};

/**
 * Provides functionality for dialog boxes
 */
 
var Dialog = Class.create({
  /**
   * @constructor
   * @param  {Hash}  opt  Hash with options
   * Valid option keys:
   *   e:                 {Object OR String}  HTMLLinkElement of id of an HTMLLinkElement on which 
   *                                          click events will be observed to toggle the dialog box
   *   content:           {Object OR String}  The content that the dialog will show. Can be a DOMNode,
   *                                          a string or an URL which will be fetched by AJAX if content_is_url
   *                                          is set to true
   *   content_is_url:    {Boolean}           Will fetch content by AJAX if set to true and content is a string
   *   title:             {String}            Text to show in the title bar of the dialog box
   *   extra_class:       {String}            Extra class(es) added to the main div of the dialog
   *   x:                 {Integer}           X position of dialog box, relative to document
   *   y:                 {Integer}           Y position of dialog box, relative to document
   *   append_button      {String}            If set, a submit button which closes the dialog will be appended to
   *                                          the dialog. The text on the button is the text in append_button
   *   open_after_create  {Boolean}           Opens the dialog after creation
   *   submit_by_ajax     {Boolean}           Submits forms in the dialog with AJAX
   *   before_open        {Function}          Function to run before the dialog is opened. Passes along the Dialog 
   *                                          instance as a parameter
   *   after_open         {Function}          Same as before_open but runs after the dialog is opened
   *   before_submit      {Function}          Same as before_open but runs before a form is submitted by AJAX
   *   after_submit       {Function}          Same as before_open but runs after a form is submitted by AJAX
   *   before_close       {Function}          Same as before_open but runs before the dialog is closed
   *   after_close        {Function}          Same as before_open but runs after the dialog is closed
   */
  initialize: function(opt){
	this.opened        = false;
	this.opt           = Object.extend({
	  e:                 null,
	  content:           false,
	  content_is_url:    false,
	  title:             '',
	  extra_class:       null,
	  x:                 0,
	  y:                 0,
	  append_button:     false,
	  open_after_create: false,
	  submit_by_ajax:    false,
	  before_open:	     false,
	  after_open:        false,
	  before_submit:     false,
	  after_submit:      false,
	  before_close:      false,
	  after_close:       false
	},opt || {});
	this.toggle_dialog = this.toggle.bind(this);
	if(this.opt.e){ 
	  this.opt.e = $(this.opt.e);
		this.opt.e.onclick = function(){return false;}; // This is to stop the click event in early Safari versions
	  Event.observe(this.opt.e,'click',this.toggle_dialog,false); 
	}
	if(this.opt.open_after_create){ this.open(); }
  },
  toggle: function(v){
	if(v){ Event.stop(v); }
	if(this.opened){ this.close(v); }
			   else{ this.open(v);  }
  },
  open: function(v){
	if(this.opt.before_open){ this.opt.before_open(this); }
	if(v){ Event.stop(v); }
	this.setupBox();
	this.setTitle();
	this.spin();
	this.position();
	this.setContent();
	this.box.show();
	this.box.style.opacity = 1;
	this.position();
	this.moveToTop();
	this.dontSpin();
	this.opened = true;
	if(this.opt.after_open){ 
	  this.opt.after_open(this); 
	}
  },
  close: function(v){
	if(this.opt.before_close){ this.opt.before_close(this); }
	if(v){ Event.stop(v); }
	if(typeof Effect != 'undefined'){
	  fade = new Effect.Fade(this.box,{
		  duration:0.4,
		  afterFinish: function(){
			this.body.remove();
			this.body = null;
		  }.bind(this)
	  });
	}
	else{ 
	  this.box.hide();
	  this.body.remove();
	  this.body = null;
	}
	this.opened = false;
	if(this.opt.after_close){ this.opt.after_close(this); }
  },
  remove: function(){
	if(this.opt.e){ Event.stopObserving(this.opt.e,'click',this.toggle_ev,false); }
	this.box.remove();
	this.box = null;
  },
  setupBox: function(){
	box_class = this.opt.extra_class ? 'dialogue ' + this.opt.extra_class : 'dialogue';
	if(!this.box){
	  this.box = new Element("div", {'class':box_class});
      this.box.setAttribute('class', box_class);
	  $$("body")[0].insert({bottom: this.box});
	}
	Event.observe(this.box, "click", this.moveToTop.bind(this));
	if(this.body){
	  this.body.remove(); 
	  this.body = null;
	}
	this.body = new Element("div", {'class':'body'});
    this.body.setAttribute('class', 'body');
	this.box.insert({bottom: this.body});
  },
  setTitle: function(title){
	if(!title){
	  if(this.opt.title){
		title = this.opt.title; }
	  else if(this.opt.e && this.opt.e.title){
		title = this.opt.e.title; }
	  else if(this.opt.e && this.opt.e.firstChild && this.opt.e.firstChild.nodeValue){
		title = this.opt.e.firstChild.nodeValue; }
	  else{
		title = 'Eniro'; }
	}
	title = new Element("span").update(title);
	if(this.h3){ 
	  this.h3.remove();
	  this.h3 = null;
	}
	this.h3          = new Element("h3", {'class':'handle'}).update(title);
    this.h3.setAttribute('class','handle');
	$(this.box.firstChild).insert({before: this.h3});
	this.closeButton = new Element("a",{'class':'close',href:'#'}).update('Stäng');
    this.closeButton.setAttribute('class','close');
	this.h3.insert({bottom: this.closeButton});
	this.closeButton.onclick = function(){return false;}; // This is to stop the click event in early Safari versions
	Event.observe(this.closeButton,'click',this.close.bind(this),false);
	if(typeof Drag != "undefined"){
	  Drag.init(this.h3, this.box, 20, null, 20);
	  if(this.handler){ this.box.onDragStart = function(){ this.handler.moveToTop(this); }.bind(this); }
	}
  },
  setContent: function(c,append){
	c = c || this.opt.content;
	this.spin();
	if(!append){
	  this.body.remove();
	  this.body = null;
	  this.body = $(this.box.appendChild(new Element("div", {'class':'body'})));  
      this.body.setAttribute('class', 'body');
	}
	if(typeof c == 'string' && !this.opt.content_is_url){
	  this.body.appendChild(new Element("p").update(c)); }
	// TODO: This does not always work on IE, if the content is advanced. p is not a good tag, we should use a div instead
	// like this:            this.body.appendChild(new Element("div").update(c));
	// But since I don't have the full picture of where these dialogs are used, I am not changing anything right now.

	else if(typeof c == 'object'){
	  this.body.appendChild(c); }
	else if(this.opt.e || (typeof c == 'string' && this.opt.content_is_url)){
	  c       = this.opt.e ? this.opt.e.href : c;
	  url     = c.split("?");
	  goeniro = /http:\/\/go.eniro.se(\/.+)*?\/(http:.*)/.exec(url[0]);
	  if(goeniro){ 
		url[0] = goeniro[2];
	  }
	var currentTime = new Date();
	TimeStamp = currentTime.getUTCFullYear() + "" + currentTime.getUTCMonth() + "" + currentTime.getUTCDay() + "" +	currentTime.getUTCHours() + "" + currentTime.getUTCMinutes() + "" +	currentTime.getUTCSeconds() + "" + currentTime.getUTCMilliseconds();							
	  url[1] = url[1] + "&timestamp=" + "" + TimeStamp;
	  upd = new Ajax.Updater(
		this.body,
		url[0],
		{
		  requestHeaders: ['REFERER', document.location],
		  method: 'get',
		  parameters: url[1],
		  evalScripts: true,
		  onComplete: this.afterSetContent.bind(this)
		});
	  return;
	}
	else{ 
	  ins = new Insertion.Top(this.body,'You did not supply any content for this dialogbox'); 
	}
	this.afterSetContent();
  },
  afterSetContent: function(xhr){
	if(this.opt.after_content){ this.opt.after_content(this,xhr); }
	this.dontSpin();
	this.position();
	if(this.opt.submit_by_ajax && (form = this.body.getElementsByTagName('form')) && form.length > 0){
	  Event.observe(form[0],'submit',this.submitForm.bind(this),false);
	}
	this.cancelButton = this.body.select('form .cancel')[0];
		if(this.cancelButton) {
		this.cancelButton.onclick = function(){return false;};
		Event.observe(this.cancelButton,'click',this.close.bind(this),false);
		}
  },
  appendButton: function(txt){
	var tmpDiv = this.body.appendChild(new Element("p", {"class": 'submit'}));
	this.button = tmpDiv.appendChild(new Element("input", {type:'submit','class':'submit',value:txt}));
	Event.observe(this.button,'click',this.close.bind(this),false); 
  },
  position: function(x,y){
	this.box.style.left = "-9999px";
	this.box.style.top  = "-9999px";
	x = x || this.opt.x || false;
	if(!x){
	  size    = this.getViewportSize();
	  _scroll = this.getScrollOffset();
	  x = _scroll.x + size.x/2 - this.box.clientWidth/2;
	  if(x<0){ x = 20; }
	}
	y = y || this.opt.y || false;
	if(!y){
	  size    = this.getViewportSize();
	  _scroll = this.getScrollOffset();
	  y = _scroll.y + size.y/2 - this.box.clientHeight/2;
	  if(y<0){ y = 20; }
	  if((bnr = $('banner'))){
		bottom_of_banner = bnr.offsetTop + bnr.offsetHeight;
		if(y < bottom_of_banner){
		  y = bottom_of_banner + 10;
		}
	  }
	}
	this.box.style.left = (x = 0 ? x : x + 'px');
	this.box.style.top  = (y = 0 ? y : y + 'px');
  },
  moveToTop: function(){
	if(this.handler){ this.handler.moveToTop(this); }
  },
  spin: function(){
	this.box.addClassName("dialogue_loading");
  },
  dontSpin: function(){
	this.box.removeClassName("dialogue_loading");
  },
  submitForm: function(v){
	if (v) {
		Event.stop(v);
	}
	var self = this;
	f = Event.element(v);
	if (!f.tagName || f.tagName.toLowerCase() != 'form') {
		return false;
	}
	if (this.opt.before_submit) {
		this.opt.before_submit(this);
	}
	this.spin();
	url = f.getAttribute('action') || '';
	if(url.match && url.match(/\?/)){
		params = url.split('?')[1] + f.serialize();
		url = url.split('?')[0];
	}
	else{
		params = f.serialize();
	}
	upd = new Ajax.Updater(this.body, url, {
		requestHeaders: ['REFERER', document.location],
		method: f.method || 'post',
		parameters: params,
		evalScripts: true,
		onComplete: this.afterSetContent.bind(self),
		onException: this.afterSetContent.bind(self),
		onError: this.afterSetContent.bind(self)
	});
  },
  getViewportSize: function(){
	size = {};
	if(self.innerHeight){
		size.x = self.innerWidth;
		size.y = self.innerHeight;
	}
	else if (document.documentElement && document.documentElement.clientHeight){
		size.x = document.documentElement.clientWidth;
		size.y = document.documentElement.clientHeight;
	}
	else if (document.body){
		size.x = document.body.clientWidth;
		size.y = document.body.clientHeight;
	}
	return size;
  },
  getScrollOffset: function(){
	_scroll = {};
	if (self.pageYOffset){ // all except Explorer
		_scroll.x = self.pageXOffset;
		_scroll.y = self.pageYOffset;
	}
	else if (document.documentElement && document.documentElement.scrollTop){	// Explorer 6 Strict
		_scroll.x = document.documentElement.scrollLeft;
		_scroll.y = document.documentElement.scrollTop;
	}
	else if (document.body){ // all other Explorers
		_scroll.x = document.body.scrollLeft;
		_scroll.y = document.body.scrollTop;
	}
	return _scroll;
  },
  getCoordinates: function(v){
	pos = {
	  x: 0,
	  y: 0
	};
	if(!v){ v = window.event; }
	if(v.pageX || v.pageY){
	  pos.x = v.pageX;
	  pos.y = v.pageY;
	}
	else if(v.clientX || v.clientY){
	  pos.x = v.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
	  pos.y = v.clientY + document.body.scrollTop + document.documentElement.scrollTop;
	}
	return pos;
  }
});

Event.observe(document, "dom:loaded", function() {
	if (Eniro.Page.tab_is("web") && $("super-search")) {
		dialogs = new DialogHandler();
		dialogs.findAndCreate($("super-search"));
		/*
		var dialog_links = $("super-search").select(".dialog");
		if (dialog_links) {
			dialog_links.each(function(link) {
				var dialog_opt = {e: link, submit_by_ajax: link.hasClassName('ajax')};
				var dialogue = dialogs.add(dialog_opt);
				dialogue.handler = dialogs;
				dialog_opt = null;
			});
		}
		*/
	}
	else if (Eniro.Page.tab() == 'web' && (ypres = $$('.companyResults')) && (ypres = ypres[0])) {
		dialogs = new DialogHandler();
		dialog_opt = {};
		dialog_opt.extra_class    = "phone-us-dialogue";
		dialog_opt.submit_by_ajax = true;
		dialog_opt.before_submit  = function() {
			if (client = $("client")) {
				Eniro.settings().data.c2c_phone_no = client.value;
				Eniro.settings().save();
			}
		};
		dialog_opt.after_content = function(dialogue) {
			if(client = $("client")) {
				client.value = Eniro.settings().data.c2c_phone_no || "";
			}
			if (info = dialogue.box.select(".infobox")) {
				info = info[0];
				info.hide();
				dialogue.box.select("a.toggle_info").each(function(e){
					Event.observe(e, "click", function(v) {
						v.stop();
						dialogue.box.select(".infobox")[0].toggle();
					}, false);
				}.bind(dialogue));
			}
		};
		dialogs.findAndCreate(ypres,dialog_opt);
	}
	else if (Eniro.Page.tab() == 'classified' && Eniro.Page.is_resultpage() && ($$("body")[0].hasClassName('product'))) {
		dialogs = new DialogHandler();
		$$('a.dialog').each(function(e) {
			var opt            = {};
			opt.e              = $(e);
			if ($(e).hasClassName("payson")) {
				opt.submit_by_ajax = false;
			}
			else {
				opt.submit_by_ajax = true;
			}
			if (e.hasClassName("tip-a-friend")) {
				opt.title = "Tipsa en v\xe4n";
			}
			dialogs.add(opt);
		});
	}
	else if(Eniro.Page.tab() == 'municipality' && Eniro.Page.is_resultpage()){
		dialogs = new DialogHandler();
		dialogs.findAndCreate();
	}
	else if(Eniro.Page.tab() == 'map'){
		dialogs = new DialogHandler();
		// Create a dialog only for elements with class=onload-dialog. Other will be taken care of by the Hit js class.
		$$('.onload-dialog').each(dialogs.addMapOnLoadDialogs.bind(dialogs));
		dialogs.addLinkToMapDialog($('link-to-map'));
		if ($('send-map')) {
			var toggler = new Eniro.Classes.Toggler('send-map-content',{no_link:true});
			$('send-map').observe('click',toggler.toggle.bind(toggler));
		}
	}
	else if(Eniro.Page.tab() == 'yp' && $$("body")[0].hasClassName('infopage')){
	dialogs = new DialogHandler();
	
	if((els = $$('a.dialog'))){
	  els.each(function(e){
		e = $(e);
		dialog_opt = {e:e, submit_by_ajax:e.hasClassName('ajax')};

		  dialog_opt.extra_class   = "phone-us-dialogue";
		  dialog_opt.before_submit = function(){
			if((client = $("client"))){
			  Eniro.settings().data.c2c_phone_no = client.value;
			  Eniro.settings().save();
			}
		  };

		  dialog_opt.after_content = function(dialogue){
		  var info = dialogue.box.select(".infobox");
			if((client = $("client"))){
			  client.value = Eniro.settings().data.c2c_phone_no || "";
			}
		  var info = dialogue.box.select(".infobox")[0];
			if(info){
			  info.hide();
			  dialogue.box.select("a.toggle_info").each(function(e){
				Event.observe(e, "click", 
				  function(v){
					v.stop();
					dialogue.box.select(".infobox")[0].toggle();
				  },
				false);
			  }.bind(dialogue));
			}			
		  };
		  
		
		
		dialogue = dialogs.add(dialog_opt);
		dialogue.handler = dialogs;
	  });
	}
	
  }
});


/**************************************************
 * dom-drag.js
 * 09.25.2001
 * www.youngpup.net
 **************************************************
 * Helps make dialogs drag-able, pretty lightweight
 **************************************************/

var Drag = {

	obj : null,

	init : function(o, oRoot, minX, maxX, minY, maxY, bSwapHorzRef, bSwapVertRef, fXMapper, fYMapper)
	{
		o.onmousedown	= Drag.start;

		o.hmode			= bSwapHorzRef ? false : true ;
		o.vmode			= bSwapVertRef ? false : true ;

		o.root = oRoot && oRoot !== null ? oRoot : o ;

		if (o.hmode  && isNaN(parseInt(o.root.style.left,10  ))){ o.root.style.left   = "0px"; }
		if (o.vmode  && isNaN(parseInt(o.root.style.top,10   ))){ o.root.style.top    = "0px"; }
		if (!o.hmode && isNaN(parseInt(o.root.style.right,10 ))){ o.root.style.right  = "0px"; }
		if (!o.vmode && isNaN(parseInt(o.root.style.bottom,10))){ o.root.style.bottom = "0px"; }

		o.minX	= typeof minX != 'undefined' ? minX : null;
		o.minY	= typeof minY != 'undefined' ? minY : null;
		o.maxX	= typeof maxX != 'undefined' ? maxX : null;
		o.maxY	= typeof maxY != 'undefined' ? maxY : null;

		o.xMapper = fXMapper ? fXMapper : null;
		o.yMapper = fYMapper ? fYMapper : null;

		o.root.onDragStart = function(){};
		o.root.onDragEnd	 = function(){};
		o.root.onDrag		   = function(){};
	},

	start : function(e)
	{
	  Drag.obj = this;
		var o = Drag.obj;
		e = Drag.fixE(e);
		var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom, 10);
		var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10 );
		o.root.onDragStart(x, y);

		o.lastMouseX	= e.clientX;
		o.lastMouseY	= e.clientY;

		if (o.hmode) {
			if (o.minX !== null){	o.minMouseX	= e.clientX - x + o.minX; }
			if (o.maxX !== null){	o.maxMouseX	= o.minMouseX + o.maxX - o.minX;}
		} else {
			if (o.minX !== null){ o.maxMouseX = -o.minX + e.clientX + x;}
			if (o.maxX !== null){ o.minMouseX = -o.maxX + e.clientX + x;}
		}

		if (o.vmode) {
			if (o.minY !== null){	o.minMouseY	= e.clientY - y + o.minY;}
			if (o.maxY !== null){	o.maxMouseY	= o.minMouseY + o.maxY - o.minY;}
		} else {
			if (o.minY !== null){ o.maxMouseY = -o.minY + e.clientY + y;}
			if (o.maxY !== null){ o.minMouseY = -o.maxY + e.clientY + y;}
		}

		document.onmousemove	= Drag.drag;
		document.onmouseup		= Drag.end;

		return false;
	},

	drag : function(e)
	{
		e = Drag.fixE(e);
		var o = Drag.obj;

		var ey	= e.clientY;
		var ex	= e.clientX;
		var y = parseInt(o.vmode ? o.root.style.top  : o.root.style.bottom, 10);
		var x = parseInt(o.hmode ? o.root.style.left : o.root.style.right, 10 );
		var nx, ny;

		if (o.minX !== null){ ex = o.hmode ? Math.max(ex, o.minMouseX) : Math.min(ex, o.maxMouseX);}
		if (o.maxX !== null){ ex = o.hmode ? Math.min(ex, o.maxMouseX) : Math.max(ex, o.minMouseX);}
		if (o.minY !== null){ ey = o.vmode ? Math.max(ey, o.minMouseY) : Math.min(ey, o.maxMouseY);}
		if (o.maxY !== null){ ey = o.vmode ? Math.min(ey, o.maxMouseY) : Math.max(ey, o.minMouseY);}

		nx = x + ((ex - o.lastMouseX) * (o.hmode ? 1 : -1));
		ny = y + ((ey - o.lastMouseY) * (o.vmode ? 1 : -1));

		if (o.xMapper){		nx = o.xMapper(y);     }
		else if (o.yMapper){	ny = o.yMapper(x); }

		Drag.obj.root.style[o.hmode ? "left" : "right"] = nx + "px";
		Drag.obj.root.style[o.vmode ? "top" : "bottom"] = ny + "px";
		Drag.obj.lastMouseX	= ex;
		Drag.obj.lastMouseY	= ey;

		Drag.obj.root.onDrag(nx, ny);
		return false;
	},

	end : function()
	{
		document.onmousemove = null;
		document.onmouseup   = null;
		Drag.obj.root.onDragEnd(	parseInt(Drag.obj.root.style[Drag.obj.hmode ? "left" : "right"], 10), 
									parseInt(Drag.obj.root.style[Drag.obj.vmode ? "top" : "bottom"], 10));
		Drag.obj = null;
	},

	fixE : function(e)
	{
		if (typeof e == 'undefined'){ e = window.event; }
		if (typeof e.layerX == 'undefined'){ e.layerX = e.offsetX;}
		if (typeof e.layerY == 'undefined'){ e.layerY = e.offsetY;}
		return e;
	}
};
