Object.extend(String.prototype, {
	
	ajax: function() {
  	return this + '&ajax=true';
	},
	
	timestamp: function() {
		return this + '&timestamp=' + new Date().getTime();
	}
	
});

var SlideShow = Class.create();
SlideShow.prototype = {
	
	initialize: function(options) {
		this.options 				= Object.extend({
			autostart: 					false,
			play: 						"play",
			pause: 						"pause",
			previous: 					"previous",
			next: 						"next",
			current: 					"current",
			speed: 						4000,
			duration: 					.3,
			delay: 						.3
		}, options || {});	
		this.container				= $(this.options.container);
		this.list					= this.container.down('ul');
		this.items					= $A(this.list.getElementsByTagName('li'));
		this.play					= this.container.down('.' + this.options.play);
		this.pause					= this.container.down('.' + this.options.pause);
		this.previous				= this.container.down('.' + this.options.previous);
		this.next					= this.container.down('.' + this.options.next);
		this.current				= this.container.down('.' + this.options.current);
		this.length					= this.items.size();
		this.item					= this.items.first();
		this.slideshow				= null;
		this.autostart();
		this.registerCallbacks();
	},
	
	registerCallbacks: function(options) {
		this.container.down('.controls').select('a').invoke('observe', 'click', this.onClick.bind(this));
	},
	
	morph: function(start, end) {
		new Effect.Fade(start, {
			duration: this.options.duration
		});
		new Effect.Appear(end, {
			duration: this.options.duration,
			delay: this.options.duration
		});			
	},
	
	autostart: function() {
		if(this.options.autostart) this._play();		
	},
	
	_play: function() {
		this._pause();
		this.slideshow = setInterval(
			function() {
				this._next();
			}.bind(this), 
		this.options.speed);
	},
	
	_pause: function() { 
		this.slideshow = clearInterval(this.slideshow);
	},	
	
	_previous: function() {
		if(this.items.first() != this.item) {
			this.morph(this.item, this.item.previous());
			this.item = this.item.previous()
			this.current.update(parseInt(this.current.innerHTML) - 1);
		} else {
			this.morph(this.item, this.items.last());
			this.item = this.items.last();
			this.current.update(this.length);
		}		
	},
	
	_next: function() {
		if(this.items.last() != this.item) {
			this.morph(this.item, this.item.next());
			this.item = this.item.next();
			this.current.update(parseInt(this.current.innerHTML) + 1);
		} else {
			this.morph(this.item, this.items.first());
			this.item = this.items.first();
			this.current.update(1);
		}		
	},
	
	onClick: function(event) { 
		var element = Event.element(event);
		switch(element.up().className) {
			case this.options.play:
				this._play();
				break;
			case this.options.pause:
				this._pause();
				break;
			case this.options.previous:
				this._previous();
				break;
			case this.options.next:
				this._next();
				break;
		}
		Event.stop(event);
	}
		
}

var Menu = Class.create();
Menu.prototype = { 
	
	initialize: function(options) {
		this.options 				= Object.extend({}, options || {});	
		this.element				= $(this.options.element);
		this.menu					= this.element.next('.user_actions');
		this.url 					= this.options.url;
		this.visible				= false;
		this.registerCallbacks();
	},
	
	registerCallbacks: function() {
		this.element.observe('click', this.onClick.bind(this));
		document.observe('mouseup', this.deactivate.bind(this));
		//this.menu.observe('click', this.activate.bind(this));
	},
	
	activate: function() {
		this.menu.setStyle({display: 'block'});
		this.visible = true;
		this.load();
	},
	
	deactivate: function() {
		this.menu.setStyle({display: 'none'});
		this.visible = false;		
	},
	
	load: function() {			
		this.request = new Ajax.Request(this.url, { 
			//parameters:	this.parameters(),
			onSuccess:	this.onSuccess.bind(this), 
			onComplete:	this.onComplete.bind(this), 
			onFailure: 	this.onFailure.bind(this)
		});			
	},	
	
	onClick: function(event) {
		if(!this.visible) {
			this.activate();
		} else {
			this.deactivate();
		}
		Event.stop(event);
	},
	
	onComplete: function() {
		this.request = false;
	},
	
	onSuccess: function(response) {
		this.menu.update(response.responseText);
	},
	
	onFailure: function() {
		
	}

}

var PhotoHighlighter = Class.create();
PhotoHighlighter.prototype = {

	initialize: function(options) {
		this.options 				= Object.extend({
			trigger: 						".checkbox", 
			photo: 							".photo"
		}, options || {});	
		this.element				= $(this.options.element);
		this.trigger 				= this.element.down(this.options.trigger);
		this.photo					= this.element.down(this.options.photo).down();
		this.registerCallbacks();
		this.inspect();
	},
	
	registerCallbacks: function() {
		this.trigger.observe('change', this.highlight.bind(this));
	},
	
	highlight: function() {
		if(this.trigger.checked) {
			this.activate();
		} else {
			this.deactivate();
		}
	},
	
	inspect: function() {
		if(this.trigger.checked) {
			this.activate();
		}
 	},
	
	activate: function() { 
		this.photo.className = "highlight";
	},
	
	deactivate: function() {
		this.photo.className = "";
	}
	
}

var CheckAll = Class.create();
CheckAll.prototype = {
	
	initialize: function(options) {
		this.options 				    = Object.extend({}, options || {});						
		this.form					      = $(this.options.form);
		this.elements				    = this.form.select('.check_all');
		this.registerCallbacks();
	},
	
	registerCallbacks: function() { 
		this.elements.invoke('observe', 'click', this.onClick.bind(this));
	},
	
	onClick: function(event) { 
		var checkboxes = this.form.getInputs('checkbox');
		checkboxes.invoke('click');
		event.stop();
	}
};

var NoteMessage = Class.create();
NoteMessage.prototype = { 

	initialize: function(options) {
		this.options 				    = Object.extend({}, options || {});						
		this.container		      = $(this.options.container);
		this.title					    = this.container.down('.title').down('a');
		this.message						= this.container.down('.message');
		this.read_link					= this.container.down('.read_link');
		this.unread_link				= this.container.down('.unread_link');
		this.request 						= false;
		this.registerCallbacks();
	},
	
	registerCallbacks: function() {
		this.title.observe('click', this.onClick.bind(this));
		this.read_link.observe('click', this.onMarkReadClicked.bind(this));
		this.unread_link.observe('click', this.onMarkUnreadClicked.bind(this));
	},
	
	onClick: function(event) { 
		this.markRead();
		this.message.toggle();
		event.stop();
	},
	
	onMarkReadClicked : function(event) {
		this.markRead(event);
		event.stop();
	},
	
	onMarkUnreadClicked : function(event) {
		this.markUnread(event);
		event.stop();
	},
	
	markRead: function(event) {
		if(this.container.className == "read") return;
		this.read_link.hide();
		this.unread_link.show();
		this.container.addClassName("read");
		this.container.removeClassName("unread");
		this.sendRequest(event.findElement('a').href);
	},
	
	markUnread: function(event) {
		if(this.container.className == "unread") return;
		this.unread_link.hide();
		this.read_link.show();
		this.container.addClassName("unread");
		this.container.removeClassName("read");
		this.sendRequest(event.findElement('a').href);
	},
	
	sendRequest: function(url) {
		if(this.request) return;
		if(!url) { 
			url = this.options.url;
		}
		this.request = new Ajax.Request(url, {
			method: 'get',
			parameters: 'js=true',
			onComplete: this.onComplete.bind(this)
		});	
	},
	
	onComplete: function(response) {
		this.request = false;
	}
	
};

var Toggle = Class.create();
Toggle.prototype = { 
	
	initialize: function(options) {
		this.options 				= Object.extend({
			visible: 						false,
			showText: 						"Show",
			hideText: 						"Hide"
		}, options || {});	
		this.element 				= $(this.options.element);
		this.container				= $(this.options.container);
		this.visible				= this.options.visible;
		this.registerCallbacks();
	},
	
	registerCallbacks: function() {
		this.element.observe('click', this.onClick.bind(this));
	},
	
	show: function() {
		this.element.innerHTML = this.element.innerHTML.replace(this.options.showText, this.options.hideText);
		this.element.className = this.element.className.replace("show", "hide");
		this.element.href = this.element.href.replace("show", "hide");
		this.container.show();
		this.visible = true;
	},
	
	hide: function() {
		this.element.innerHTML = this.element.innerHTML.replace(this.options.hideText, this.options.showText);
		this.element.className = this.element.className.replace("hide", "show");
		this.element.href = this.element.href.replace("hide", "show");
		this.container.hide();		
		this.visible = false;
	},
	
	onClick: function(event) {
		Event.stop(event);
		if(this.visible) { 
			this.hide();
		} else {
			this.show();
		}
	}	
}

var Deleter = Class.create();
Deleter.prototype = {

	initialize: function(options) {
		this.options 				= Object.extend({
			confirmText: 						'Really Delete?',
			deletingText:						'Deleting...',
			confirmParam:						'?confirm=true',
			interval:								5000,
			ajax:										false
		}, options || {});	
		this.element 				= $(this.options.element);
		this.text					= this.element.innerHTML;
		this.clicked				= false;
		this.registerCallbacks();
	},
	
	registerCallbacks: function() {
		this.element.observe('click', this.onClick.bind(this));
	},
		
	ask: function() {
		this.element.update(this.options.confirmText);
		this.wait();	
	},
	
	wait: function() {
		var self = this;
		function revert() { self.revert(); }
		setTimeout(revert, this.options.interval);
	},
	
	trash: function() {
		this.element.update(this.options.deletingText);
		if(this.options.ajax) {
			new Ajax.Request(this.element.href);
		} else {
			document.location = this.element.href + this.options.confirmParam;	
		}
	},
	
	revert: function() {
		this.clicked = false;
		this.element.update(this.text);
	},
	
	onClick: function(event) {
		(this.clicked) ? this.trash() : this.ask();
		this.clicked = true;
		Event.stop(event);
	}

}

var Tabs = Class.create();
Tabs.prototype = {
	
	initialize: function(options) {
		this.options 				= Object.extend({
			suffix:			'_container'			
		}, options || {});
		
		this.list 					= $(this.options.list);
		this.container			= $(this.options.list + this.options.suffix);
		this.nodes 					= $A(this.list.childNodes);
		this.registerCallbacks();
	},
	
	registerCallbacks: function() {	
		this.nodes.each(function(element) {
			if(element.nodeName == 'LI') {
				element.down('a').observe('click', this.onClick.bind(this, element));
			}
		}.bind(this));
	},
	
	setActive: function(element) {
		this.nodes.each(function(el) {
			if(el.nodeName == 'LI') {
				el.className = '';
			}
		}.bind(this));
		element.className = 'active';
	},
	
	load: function(trigger) {
		if(trigger.getAttribute('rel') != '') {
			var div = $(trigger.getAttribute('rel'));
			this.container.update(div.innerHTML);
		}
	},
	
	onClick: function(element, event) {
		var trigger = element.down('a');
		this.load(trigger);
		this.setActive(element);
		Event.stop(event);
	}
	
}

var Email = Class.create();
Email.prototype = {
	
	initialize: function(options) {
		this.options 				= Object.extend({}, options || {});		
		this.form						= $(this.options.form);
		this.status					= $(this.options.status) ;
		this.url 						= this.options.url || this.form.action;
		this.method					= this.options.method || 'POST';
		this.request				= false;
		this.registerCallbacks();
		this.preload();
	},
	
	registerCallbacks: function() {
		Event.observe(this.form, 'submit', this.onSubmit.bind(this));
	},
	
	preload: function() {
		if(this.options.arrows) (new Image()).src 	= this.options.arrows;
		if(this.options.success) (new Image()).src = this.options.success;
		if(this.options.error) (new Image()).src 	= this.options.error;
	},
		
	parameters: function() {
		return Form.serialize(this.form).ajax().timestamp();
	},
	
	send: function() {	
		this.broadcast('Sending', 'processing');
		this.request = new Ajax.Request(this.url, { 
			method:		this.method,
			parameters:	this.parameters(),
			onSuccess:	this.onSuccess.bind(this), 
			onComplete:	this.onComplete.bind(this), 
			onFailure: 	this.onFailure.bind(this)
		});			
	},
	
	broadcast: function(text, klass) {
		//if(this.options.status) 
		this.status.className = klass;
		this.status.update(text);
	},
		
	onSubmit: function(event) {
		if(this.request) {
			Event.stop(event);
			return;
		}
		this.send();	
		Event.stop(event);
	},
	
	onComplete: function(response) {
		this.request = false;
	},
	
	onSuccess: function(response) {
		this.form.reset();
		this.broadcast(response.responseText, 'success');
	},
	
	onFailure: function(response) {
		var text = (response.status != 404) ? response.responseText : '404 - Page Not Found.';
		this.broadcast(text, 'error');
	}
	
}

var Clipboard = Class.create();
Clipboard.prototype = {
	
	initialize: function(options) {
		this.options 				= Object.extend({}, options || {});	
		this.element				= $(this.option3s.element);
		this.anchor					= $(this.options.anchor);
		this.registerCallbacks();
	},
		
	registerCallbacks: function() {
		Event.observe(this.anchor, 'click', this.onClick.bind(this));
	},
	
	copy: function() {
		if(this.element.createTextRange) {
			var range = this.element.createTextRange();
			if(range) range.execCommand('Copy');		
		} else {
			var embed = '<embed src="' + this.options.swf + '" FlashVars="clipboard='+encodeURIComponent(this.element.value)+'" width="0" height="0" type="application/x-shockwave-flash"></embed>';
			var div = document.createElement('div');
			document.body.appendChild(div);
			div.update(embed);				
		}
	},
		
	onClick: function(event) {
		this.copy();
		Event.stop(event);
	}
	
}

var VideoImporter = Class.create();
VideoImporter.prototype = {

	initialize: function(options) {
		this.options 					= Object.extend({}, options || {});
		this.form						= $(this.options.form);
		this.step2						= $(this.options.step2);
		this.step3						= $(this.options.step3);
		this.url						= this.form.action;
		this.status						= this.form.down('.status');
		this.request 					= false;
		this.registerCallbacks();
	},
	
	registerCallbacks: function() { 
		this.form.observe('submit', this.onSubmit.bind(this));
	},	
	
	broadcast: function(text, klass) {
		this.status.className = klass;
		this.status.update(text);
	},	
	
	parameters: function() {
		return Form.serialize(this.form).ajax().timestamp();
	},	
	
	send: function() {	
		this.broadcast('Processing', 'processing');
		this.request = new Ajax.Request(this.url, { 
			parameters:	this.parameters(),
			onSuccess:	this.onSuccess.bind(this), 
			onComplete:	this.onComplete.bind(this), 
			onFailure: 	this.onFailure.bind(this)
		});			
	},
		
	onSubmit: function(event) {
		if(this.request) {
			Event.stop(event);
			return;
		}
		this.send();	
		Event.stop(event);		
	},
	
	onComplete: function(response) {
		this.request = false;
		this.broadcast('', '');
	},
	
	onSuccess: function(response) {
		//this.broadcast(response.responseText, 'success');
		this.step3.update(response.responseText);
	},
	
	onFailure: function(response) {
		var text = (response.status != 404) ? response.responseText : '404 - Page Not Found.';
		this.broadcast(text, 'error');
	}	
	
}

var Submitter = Class.create();
Submitter.prototype = {
	
	initialize: function(options) {
		this.options 				= Object.extend({}, options || {});		
		this.form					= $(this.options.form);
		this.status					= $(this.options.status) ;
		this.url 					= this.options.url || this.form.action;
		this.method					= this.options.method || 'POST';
		this.request				= false;
		this.registerCallbacks();
		this.preload();
	},
	
	registerCallbacks: function() {
		Event.observe(this.form, 'submit', this.onSubmit.bind(this));
	},
	
	preload: function() {
		if(this.options.arrows) (new Image()).src 	= this.options.arrows;
		if(this.options.success) (new Image()).src = this.options.success;
		if(this.options.error) (new Image()).src 	= this.options.error;
	},
		
	parameters: function() {
		return Form.serialize(this.form).ajax().timestamp();
	},
	
	send: function() {	
		this.broadcast('Sending', 'processing');
		this.request = new Ajax.Request(this.url, { 
			method:			this.method,
			parameters:	this.parameters(),
			onSuccess:	this.onSuccess.bind(this), 
			onComplete:	this.onComplete.bind(this), 
			onFailure: 	this.onFailure.bind(this)
		});			
	},
	
	broadcast: function(text, klass) {
		this.status.className = klass;
		this.status.update(text);
	},
		
	onSubmit: function(event) {
		if(this.request) {
			Event.stop(event);
			return;
		}
		this.send();	
		Event.stop(event);
	},
	
	onComplete: function(response) {
		this.request = false;
	},
	
	onSuccess: function(response) {
		this.form.reset();
		this.broadcast(response.responseText, 'success');
	},
	
	onFailure: function(response) {
		var text = (response.status != 404) ? response.responseText : '404 - Page Not Found.';
		this.broadcast(text, 'error');
	}
	
}

var Uploader = Class.create();
Uploader.prototype = {

	initialize: function(options) {
		this.options 				= Object.extend({
		  interval:             1000
		}, options || {});	
		this.form				    = $(this.options.form);
		this.container              = $(this.options.container);
		this.request                = false;
		this.interval               = this.options.interval;
		this.percent				= 0;
		this.registerCallbacks();
	},

	registerCallbacks: function(options) {
		this.form.observe('submit', this.onSubmit.bind(this))
	},
		
	start: function() {
	  if(this.request) return;
	
	  this.request = new Ajax.Request(this.options.url, { 
			parameters: 'percent=' + this.percent++,
			onComplete:	this.onComplete.bind(this),
			onSuccess:	this.onSuccess.bind(this), 
			onFailure: 	this.onFailure.bind(this)
		});	
	},
	
	restart: function() {
		this.progressInterval = clearTimeout(this.progressInterval);
		this.progressInterval = setInterval(
  		  function() { 
  		    this.start(); 
  		  }.bind(this), 
		  this.interval
		);  	  
	},

	onSubmit: function(event) {
		this.start();
		Event.stop(event);
	},
	
	onComplete: function(response) {
  		this.request = false;
	},
	
	onSuccess: function(response) {
		this.container.update(response.responseText);	 
		this.restart(); 	  
	},
	
	onFailure: function() {
	  this.container.update('There was a problem with your request');
	}

}

var YahooUploader = Class.create(); 
YahooUploader.prototype = {  

  initialize: function(options) {
    this.options                  = Object.extend({}, options || {});
    this.uploader                 = new YAHOO.widget.Uploader( "uploaderPlaceHolder" );
    this.container                = $(this.options.container);
    this.form                     = $(this.options.form);
    this.progress                 = $(this.options.progress);
    this.progressBar              = this.container.down('.middle');
    this.uploadedBytes            = this.container.down('.uploadedBytes');
    this.totalBytes               = this.container.down('.totalBytes');
    this.uploadRate               = this.container.down('.uploadRate');
    this.timeRemaining            = this.container.down('.timeRemaining');
    this.percent                  = this.container.down('.percent');
	this.stats					  = this.container.down('.stats');
	this.processing				  = this.container.down('.processing');

	this.uploads				  = this.form.down('.uploads');
    this.browseButton             = this.form.down('.browse');
    this.uploadButton             = this.form.down('.upload') //$(this.options.uploadButton);
    
    this.fileList                 = null;
    this.lastTime                 = null;
    this.fileBytesTotal           = null;
	this.observers				  = [];
    this.registerCallbacks();
  },  

  registerCallbacks: function() {
    this.browseButton.observe('click', this.browse.bind(this));
    this.uploadButton.observe('click', this.upload.bind(this));
    this.uploader.addListener('fileSelect', this.onFileSelect.bind(this));
    this.uploader.addListener('uploadStart:', this.onUploadStart.bind(this));
    this.uploader.addListener('uploadProgress', this.onUploadProgress.bind(this));
    this.uploader.addListener('uploadCancel', this.onUploadCancel.bind(this));
    this.uploader.addListener('uploadComplete', this.onUploadComplete.bind(this));
    this.uploader.addListener('uploadCompleteData', this.onUploadResponse.bind(this));
    this.uploader.addListener('uploadError', this.onUploadError.bind(this));
  },

  browse: function(event) {
    this.uploader.clearFileList();
    this.uploader.browse(false, [{description:"Images", extensions:"*"}]);
    YAHOO.log("Started browsing for images");
    Event.stop(event);
  },

  upload: function(event) {
    if(this.fileList == null) return;
    for(i in this.fileList) {
      this.fileBytesTotal = this.fileList[i].size;
      this.lastTime = (new Date()).getTime(); 
      this.lastBytesUploaded = 0;
      this.uploader.upload(i, this.options.url, "GET", this.options.post_params, this.options.field_name);
    }
    this.form.hide();
    this.progress.show();
    Event.stop(event);
  },

  setFileName: function(file) {
	this.uploads.value = file;
  },

  onFileSelect: function(event) {
    this.fileList = event.fileList;
	this.setFileName(this.fileList["file0"].name);
  },

  onUploadProgress: function(event) {
    this.updatePercent(event.bytesLoaded, event.bytesTotal, false);
    if(event.bytesLoaded == event.bytesTotal) {
	  this.stats.hide();
	  this.processing.setStyle({display: "block"});
    }
  },

  onUploadStart: function(event) {
    this.uploader.debug("upload start");
  },

  updatePercent: function(bytesLoaded, bytesTotal, force) {
    updateEveryMs = 320;
    currentTime = (new Date()).getTime(); 
    percent = this.precision((bytesLoaded / bytesTotal) * 100, 0);

    if(((currentTime - this.lastTime) >= updateEveryMs) || percent == 100 || force) {
      this.progressBar.setStyle({width: percent + "%" });

      one_kb = 1024;
      one_mb = one_kb * 1024;

      this.uploadedBytes.update(this.precision(bytesLoaded / one_mb, 2) + "M");
      this.totalBytes.update(this.precision(bytesTotal / one_mb, 2) + "M");
      this.percent.update(percent + "%");


      if(this.lastTime != null) { 
        bytesPerMillisecond = (bytesLoaded - this.lastBytesUploaded) / (currentTime - this.lastTime)
        bytesLeft = bytesTotal - bytesLoaded;

        this.uploadRate.update(this.precision(bytesPerMillisecond * (1000/one_kb), 2) + "KB")

        timeRemainingSeconds = parseInt(bytesLeft / (bytesPerMillisecond*1000))
        this.timeRemaining.update(this.precision(bytesLeft / (bytesPerMillisecond*1000), 2) + "sec");
        remainingSeconds = timeRemainingSeconds % 60
        this.timeRemaining.update( parseInt(timeRemainingSeconds/60) + ":" + (remainingSeconds >= 10 ? remainingSeconds : "0"+remainingSeconds) ); 
      } else {
        this.uploadRate.update("Calculating");
        this.timeRemaining.update("Calculating");
      }

      this.lastTime = currentTime;
      this.lastBytesUploaded = bytesLoaded;
    }

  },

  onUploadComplete: function(event) {
    this.updatePercent(this.fileBytesTotal, this.fileBytesTotal, true);
	  this.onComplete();
  },

  onUploadError: function(event) {
  },

  onUploadCancel: function(event) {
  },

  onUploadResponse: function(event) {
  },

  onComplete: function(func) { 
  	return func;
  },
  
  precision: function(number, decimal_points) {
    return parseInt(number * Math.pow(10, decimal_points)) / Math.pow(10, decimal_points);
  }

}

var Flagger = Class.create();
Flagger.prototype = {

	initialize: function(options) {
		this.options 					= Object.extend({}, options || {});
		this.container					= $(this.options.container);
		this.element					= this.container.down('a');
		this.registerCallbacks();
	},
	
	registerCallbacks: function() { 
		this.element.observe('click', this.onClick.bind(this));
	},
	
	parameters: function() {
		return "js=true";
	},
	
	broadcast: function(text, klass) {
		this.container.update(text);
		this.container.addClassName(klass);
	},
	
	_load: function(element) {
		this.broadcast('Saving', 'loading');
		this.request = new Ajax.Request(element.href, {
			parameters:	this.parameters(),																
			onComplete: this.onComplete.bind(this),
			onSuccess: this.onSuccess.bind(this),
			onFailure: this.onFailure.bind(this)
		});
	},
	
	onClick: function(event) {
		var element = Event.element(event);
		this._load(element);
		Event.stop(event);
	},
	
	onComplete: function() {
		this.request = false;
		this.container.removeClassName('loading');
		new Flagger({container: this.options.container});
	},
	
	onSuccess: function(response) {
		this.container.update(response.responseText);
	},
	
	onFailure: function(response) {
		this.broadcast('Error', 'error');
	}
	
}

var YahooUploader = Class.create(); 
YahooUploader.prototype = {  

  initialize: function(options) {
    this.options                  = Object.extend({}, options || {});
    this.uploader                 = new YAHOO.widget.Uploader( "uploaderPlaceHolder" );
    this.container                = $(this.options.container);
    this.form                     = $(this.options.form);
    this.progress                 = $(this.options.progress);
    this.progressBar              = this.container.down('.middle');
    this.uploadedBytes            = this.container.down('.uploadedBytes');
    this.totalBytes               = this.container.down('.totalBytes');
    this.uploadRate               = this.container.down('.uploadRate');
    this.timeRemaining            = this.container.down('.timeRemaining');
    this.percent                  = this.container.down('.percent');
	this.stats					  = this.container.down('.stats');
	this.processing				  = this.container.down('.processing');

	this.uploads				  = this.form.down('.uploads');
    this.browseButton             = this.form.down('.browse');
    this.uploadButton             = this.form.down('.upload') //$(this.options.uploadButton);
    
    this.fileList                 = null;
    this.lastTime                 = null;
    this.fileBytesTotal           = null;
	this.observers				  = [];
    this.registerCallbacks();
if (typeof this.options.fileDescription =='undefined')
		this.fileDescription      = Images;
	else
		this.fileDescription      = this.options.fileDescription;
	if (typeof this.options.fileFormats == 'undefined')
		this.fileFormats		  = '*';
	else
		this.fileFormats		  = this.options.fileFormats;

  },  

  registerCallbacks: function() {
    this.browseButton.observe('click', this.browse.bind(this));
    this.uploadButton.observe('click', this.upload.bind(this));
    this.uploader.addListener('fileSelect', this.onFileSelect.bind(this));
    this.uploader.addListener('uploadStart:', this.onUploadStart.bind(this));
    this.uploader.addListener('uploadProgress', this.onUploadProgress.bind(this));
    this.uploader.addListener('uploadCancel', this.onUploadCancel.bind(this));
    this.uploader.addListener('uploadComplete', this.onUploadComplete.bind(this));
    this.uploader.addListener('uploadCompleteData', this.onUploadResponse.bind(this));
    this.uploader.addListener('uploadError', this.onUploadError.bind(this));
  },

  browse: function(event) {
    this.uploader.clearFileList();
    this.uploader.browse(false, [{description:this.fileDescription, extensions:this.fileFormats}]);
    YAHOO.log("Started browsing for images");
    Event.stop(event);
  },
  upload: function(event) {
    if(this.fileList == null) return;
    for(i in this.fileList) {
      this.fileBytesTotal = this.fileList[i].size;
      this.lastTime = (new Date()).getTime(); 
      this.lastBytesUploaded = 0;
      this.uploader.upload(i, this.options.url, "GET", this.options.post_params, this.options.field_name);
    }
    this.form.hide();
    this.progress.show();
    Event.stop(event);
  },

  setFileName: function(file) {
	this.uploads.value = file;
  },

  onFileSelect: function(event) {
    this.fileList = event.fileList;
	this.setFileName(this.fileList["file0"].name);
  },

  onUploadProgress: function(event) {
    this.updatePercent(event.bytesLoaded, event.bytesTotal, false);
    if(event.bytesLoaded == event.bytesTotal) {
	  this.stats.hide();
	  this.processing.setStyle({display: "block"});
    }
  },

  onUploadStart: function(event) {
  },

  updatePercent: function(bytesLoaded, bytesTotal, force) {
    updateEveryMs = 300;
    currentTime = (new Date()).getTime(); 
    percent = this.precision((bytesLoaded / bytesTotal) * 100, 0);

    if(((currentTime - this.lastTime) >= updateEveryMs) || percent == 100 || force) {
      this.progressBar.setStyle({width: percent + "%" });

      one_kb = 1024;
      one_mb = one_kb * 1024;

      this.uploadedBytes.update(this.precision(bytesLoaded / one_mb, 2) + "M");
      this.totalBytes.update(this.precision(bytesTotal / one_mb, 2) + "M");
      this.percent.update(percent + "%");


      if(this.lastTime != null) { 
        bytesPerMillisecond = (bytesLoaded - this.lastBytesUploaded) / (currentTime - this.lastTime)
        bytesLeft = bytesTotal - bytesLoaded;

        this.uploadRate.update(this.precision(bytesPerMillisecond * (1000/one_kb), 2) + "KB")

        timeRemainingSeconds = parseInt(bytesLeft / (bytesPerMillisecond*1000))
        this.timeRemaining.update(this.precision(bytesLeft / (bytesPerMillisecond*1000), 2) + "sec");
        remainingSeconds = timeRemainingSeconds % 60
        this.timeRemaining.update( parseInt(timeRemainingSeconds/60) + ":" + (remainingSeconds >= 10 ? remainingSeconds : "0"+remainingSeconds) ); 
      } else {
        this.uploadRate.update("Calculating");
        this.timeRemaining.update("Calculating");
      }

      this.lastTime = currentTime;
      this.lastBytesUploaded = bytesLoaded;
    }

  },

  onUploadComplete: function(event) {
    this.updatePercent(this.fileBytesTotal, this.fileBytesTotal, true);
	this.onComplete();
  },

  onUploadError: function(event) {
  },

  onUploadCancel: function(event) {
  },

  onUploadResponse: function(event) {
  },

  onComplete: function(func) { 
  	return func;
  },
  
  precision: function(number, decimal_points) {
    return parseInt(number * Math.pow(10, decimal_points)) / Math.pow(10, decimal_points);
  }

};

Object.extend(Selector, {
  findElements: function(elements, expression) {
    if (typeof expression == 'number') index = expression, expression = false;
    return Selector.matchElements(elements, expression || '*');
  }
});

Element.addMethods({
  
  all_down: function(element, expression) {
    return Selector.findElements($(element).descendants(), expression);
  },
	
  outside: function(element, scope) {
    element = $(element).firstChild != null ? $(element).firstChild : $(element);
    scope = $(scope);
    return !Element.childOf(element, scope);
  }	

});

var Combo = Class.create();
Combo.prototype = {
  initialize: function(el) {
    this.el = el;
    this.text_el = this.el.down("input");
    this.click_el = this.el.down('img');
    this.menu = this.el.down('div.menu');
    this.locked = this.el.hasClassName('no_select');
    this.observers();
    this.menu_observers();
  },
  observers: function() {
    this.click_el.observe("click", this.click.bind(this));
    this.text_el.observe("focus", this.click.bind(this));
    if(this.locked) {
      this.text_el.observe("focus", this.text_el.blur);
    } else {
      this.text_el.observe("focus", function() {
        if($F(this.text_el) == this.text_el.promted_text) {
          this.text_el.value = '';
          if(!this.keys_bound) {
            this.keys = new key_navigate(this.menu, {observe_field: this.text_el, selector: 'a', active_class: 'on', callback: this.navigate_to_url.bind(this)});
            if(this.el.next().hasClassName('submit')) {
              this.el.next().observe('click', this.keys.do_callback.bind(this.keys))
            }
            this.keys_bound = true;
          }
        }
      }.bind(this));
    }
  },
  menu_observers: function() {
    this.menu.all_down("a").each(function(el) {
      el.observe("click", this.menu_click.bind(this, el.down('span').innerHTML));
    }.bind(this));
  },
  menu_click: function(value) {
    //page.notify(value);
    this.text_el.value = value;
    this.menu.toggle();
    this.onmousedown = null;
  },
  click: function() {
    if(!this.locked) {
      this.menu.show();
    } else {
      this.menu.toggle();
    }
    this.auto_hide(this.el, this.menu, this.call_out.bind(this)); 
  },
  call_out: function() {
    if($F(this.text_el) == '')
    this.text_el.value = this.text_el.promted_text;
  },
  navigate_to_url: function(el) {
    if(!el) return;
    if (el.hasClassName('attach_query')) {
      var query_str = $F(this.text_el);
      HREF = el.getAttribute("href")+"?query="+query_str;
    } else {
      HREF = el.getAttribute("href");
      this.text_el.value = el.down('span').innerHTML;
    }
    document.location.href = HREF;
  },
  auto_hide: function(element, hide_element, callback) {
    var hide_element = hide_element == "undefined" ? element : hide_element;
    var cb = callback == "undefined" ? null : callback;
    document.onmousedown = this.do_on_click_outside.bind(element, {to_hide: hide_element, callback: cb});  
  },	
	
  do_on_click_outside: function(options, event) {
    var target = (document.all) ? window.event.srcElement : event.target;
    if($(target).outside(this)) {
      to_hide = options.to_hide || this;
      document.onmouseup = null;
      document.onmousedown = null;
      to_hide.hide();
      if(options.callback != null) options.callback();
    }
  } 
	
};

var tooltip;
var Tooltip = Class.create(); 
Tooltip.prototype = {
	
	initialize: function(options) {
		
		this.options 				= Object.extend({
			klass: 'tip',
			tooltip: '<b class="top"><b class="b1"></b><b class="b2"></b><b class="b3"></b><b class="b4"></b></b><div class="content">#{content}</div><b class="bottom"><b class="b4"></b><b class="b3"></b><b class="b2"></b><b class="b1"></b></b>'
		}, options || {});
		
		this.elements					= $$('a.' + this.options.klass);
		this.left							= 0;
		this.top							= 0;
		this.xCord						= 0;
		this.yCord						= 0;
		this.tip							= null;
		this.element					= null;
		this.url							= null;
		this.timeout					= null;
		this.request					= false;
		this.interval					= 500;
		this.obj							= $('tooltip');
		this.tooltip					= this.options.tooltip; 
    this.remoteCache      = {};
		this.registerCallbacks();
	},
	
	registerCallbacks: function() {
		this.setup();
		this.elements.invoke('observe', 'mouseover', this.onMouseOver.bind(this));
		this.elements.invoke('observe', 'mouseout', this.onMouseOut.bind(this));
		Event.observe(window, 'mouseup', this.onMouseOut.bind(this));
		this.elements.each(function(element) {
			this.setTips(element);
		}.bind(this));
	},
	
	setTips: function(element) {
		element.writeAttribute({tip: element.readAttribute('rel')});
	},
	
	cache: function(text) {
		var element = (this.element.rel == undefined) ? this.element.up('a') : this.element;
		var object = element.readAttribute('tip');
		$$('a[tip=' + object + ']').invoke('setAttribute', 'caption', text)
	},	
		
	setup: function() {
		if(this.obj == null) {
			this.tip = document.createElement('div');
			Element.extend(this.tip);
			document.body.appendChild(this.tip);
			this.tip.id = 'tooltip';
		} else { 
			this.tip = this.obj;
		}
		this.tip.hide();
		this.tip.update('');
		this.tip.addClassName('round').addClassName('tiptool');
	},
	
	getCoordinates: function(event) {							

		this.updateXY(event);

		var tp = parseInt(Number(this.yCord)+15);
		var lt = parseInt(Number(this.xCord)+10);

    var offsetWidth = this.tip.offsetWidth;
    if(offsetWidth == 0) { offsetWidth = 300; }

    var offsetHeight = this.tip.offsetHeight;
    if(offsetHeight == 0) { offsetHeight = 140; }

		if ( parseInt(document.documentElement.clientWidth+document.documentElement.scrollLeft) < parseInt(offsetWidth+lt) ) {
			this.tip.setStyle({ left: parseInt(lt-(offsetWidth+10)) + 'px' });
		} else {
			this.tip.setStyle({ left: lt + 'px' });
		}
		
		if (parseInt(document.documentElement.clientHeight+document.documentElement.scrollTop) < parseInt(offsetHeight+tp)) {
			this.tip.setStyle({ top: parseInt(tp-(offsetHeight+10))+'px' });
		} else {
			this.tip.setStyle({ top: tp + 'px' });
		}
	},
	
	updateXY : function(e) {
		if ( document.captureEvents ) {
			this.xCord = e.pageX;
			this.yCord = e.pageY;
		} else if ( window.event.clientX ) {
			this.xCord = window.event.clientX+document.documentElement.scrollLeft;
			this.yCord = window.event.clientY+document.documentElement.scrollTop;
		}
	},	
		
	_show: function() {
		this.tip.show();		
		this.load();		
	},	
	
	_hide: function() {
		this.tip.hide();
		this.tip.update('');
	},
	
	draw: function(text, cache) {	
		this.tip.update(text);
		if(cache) this.cache(text);	
	},	
		
	load: function() {
		if(this.element == null || this.request) return;
		var attribute = this.element.readAttribute('caption');
		if(attribute == null) {
      var cachedResponse = this.fetchFromCache(this.url);

      if(cachedResponse) {
        this.updateTooltip(cachedResponse);

      } else {
        this.request = new Ajax.Request(this.url, { 
          method:	'get',
          onSuccess:	this.onSuccess.bind(this), 
          onComplete:	this.onComplete.bind(this), 
          onFailure: 	this.onFailure.bind(this)
        });			
      }
		} else {
			this.draw(attribute);
		}
	},	

  fetchFromCache: function(key) { return this.remoteCache[key]; },
	
	onMouseOver: function(event) {
		this.element = Event.element(event);
		this.url = (this.element.rel == undefined) ? this.element.up('a').readAttribute('rel') : this.element.readAttribute('rel');
		this.timeout = window.setTimeout(
  		  function() { 
  		    this._show(); 
  		  }.bind(this), 
		  this.interval
		);  	
		this.getCoordinates(event);		
	}, 
	
	onMouseOut: function(event) {
		this.timeout = clearTimeout(this.timeout); 
		this._hide();		
	},
	
	onComplete: function() {
		this.request = false;
	},
	
	onSuccess: function(response) {
    this.remoteCache[this.url] = response.responseText;
		this.updateTooltip(response.responseText);
	},

  updateTooltip: function(tooltipText) {
		this.response = tooltipText;
		var text = this.tooltip.replace('#{content}', this.response);
		this.element.writeAttribute({caption: text});
		this.draw(text, true);
  },

	onFailure: function(response) {
		this.draw('Unable to retrieve the data.');
	}
	
};

Event.observe(window, 'load', function() { 
	tooltip = new Tooltip(); 
});




var FileUploader = Class.create(); 
FileUploader.prototype = {

  initialize: function(options) {
    this.options                  = Object.extend({}, options || {});
    this.container                = $(this.options.container);
    this.form                     = $(this.options.form);
    this.browseButton             = this.form.down('.browse');
    this.browseButtonPlaceholder  = this.form.down('.browseButtonPlaceholder');
    this.uploadButton             = this.form.down('.upload') //$(this.options.uploadButton);

    this.uploader                 = new SWFUpload({
      debug : this.options.debug,
      button_image_url: this.options.button_image_url,
      button_width: this.options.button_width,
      button_height: this.options.button_height,
      button_text: this.options.button_text,
      button_text_style: this.options.button_text_style,
      button_top_padding: this.options.button_top_padding,
      button_left_padding: this.options.button_left_padding,
      button_placeholder_id: this.browseButtonPlaceholder.identify(),
      file_post_name: this.options.field_name,
      file_queued_handler: this.onFileSelect.bind(this),
      file_queue_error_handler : this.onFileQueueError.bind(this),
      file_size_limit : this.options.file_size_limit,
      flash_url   : "/swf/swfupload.swf",
      post_params: this.options.post_params,
      upload_cancel_handler: this.onUploadCancel.bind(this),
      upload_complete_handler: this.onUploadComplete.bind(this),
      upload_error_handler: this.onUploadError.bind(this),
      upload_success_handler: this.onUploadSuccess.bind(this),
      upload_progress_handler: this.onUploadProgress.bind(this),
      upload_start_handler: this.onUploadStart.bind(this),
      upload_url  : this.options.url
    });

    this.progress                 = $(this.options.progress);
    this.progressBar              = this.container.down('.middle');
    this.uploadedBytes            = this.container.down('.uploadedBytes');
    this.totalBytes               = this.container.down('.totalBytes');
    this.uploadRate               = this.container.down('.uploadRate');
    this.timeRemaining            = this.container.down('.timeRemaining');
    this.percent                  = this.container.down('.percent');
    this.stats                    = this.container.down('.stats');
    this.processing               = this.container.down('.processing');
    this.errorMessage             = this.container.down('.errorMessage');

    this.uploads                  = this.form.down('.uploads');
    
    this.fileList                 = null;
    this.lastTime                 = null;
    this.fileBytesTotal           = null;
    this.observers          = [];
    this.registerCallbacks();
  },  

  registerCallbacks: function() {
    this.browseButton.observe('click', this.browse.bind(this));
    this.uploadButton.observe('click', this.upload.bind(this));

  },

  onFileQueueError: function (file, error_code, message) {
    //TODO: Reformat message
    this.uploader.debug(message);
    this.errorMessage.update(message);
  },


  browse: function(event) {
    //this.uploader.clearFileList();
    this.uploader.selectFile();
    Event.stop(event);
  },

  upload: function(event) {
    if(this.fileList == null) return;

    this.fileBytesTotal = this.fileList[0].size;
    this.lastTime = (new Date()).getTime(); 
    this.lastBytesUploaded = 0;
    this.form.setStyle({ visibility: 'hidden', width: '1px', height: '1px' });
    this.progress.show();

    this.uploader.startUpload();

    Event.stop(event);
  },

  setFileName: function(file) {
    this.uploads.value = file;
  },

  onFileSelect: function(event) {
    this.fileList = [ {
        name: event.name,
        size: event.size
      } ];
    this.setFileName(event.name);
  },

  onUploadProgress: function(file, bytesLoaded, bytesTotal) {
    this.uploader.debug("byesLoaded " + bytesLoaded);
    this.updatePercent(bytesLoaded, bytesTotal, false);
    if(bytesLoaded == bytesTotal) {
      this.stats.hide();
      this.processing.setStyle({display: "block"});
    }
  },

  onUploadStart: function(event) {
    1 + 1;
  },

  updatePercent: function(bytesLoaded, bytesTotal, force) {
    updateEveryMs = 1000;
    currentTime = (new Date()).getTime(); 
    percent = this.precision((bytesLoaded / bytesTotal) * 100, 0);

    if(((currentTime - this.lastTime) >= updateEveryMs) || percent == 100 || force) {
      this.progressBar.setStyle({width: percent + "%" });

      one_kb = 1024;
      one_mb = one_kb * 1024;

      this.uploadedBytes.update(this.precision(bytesLoaded / one_mb, 2) + "M");
      this.totalBytes.update(this.precision(bytesTotal / one_mb, 2) + "M");
      this.percent.update(percent + "%");


      if(this.lastTime != null) { 
        bytesPerMillisecond = (bytesLoaded - this.lastBytesUploaded) / (currentTime - this.lastTime)
        bytesLeft = bytesTotal - bytesLoaded;

        this.uploadRate.update(this.precision(bytesPerMillisecond * (1000/one_kb), 2) + "KB")

        timeRemainingSeconds = parseInt(bytesLeft / (bytesPerMillisecond*1000))
        this.timeRemaining.update(this.precision(bytesLeft / (bytesPerMillisecond*1000), 2) + "sec");
        remainingSeconds = timeRemainingSeconds % 60
        this.timeRemaining.update( parseInt(timeRemainingSeconds/60) + ":" + (remainingSeconds >= 10 ? remainingSeconds : "0"+remainingSeconds) ); 
      } else {
        this.uploadRate.update("Calculating");
        this.timeRemaining.update("Calculating");
      }

      this.lastTime = currentTime;
      this.lastBytesUploaded = bytesLoaded;
    }

  },

  onUploadComplete: function(file) {
    this.uploader.debug("complete");
  },

  onUploadSuccess: function(file, server_data) {
    this.uploader.debug("success");
    this.updatePercent(this.fileBytesTotal, this.fileBytesTotal, true);
    this.onComplete(file);
  },

  onUploadError: function(file, error_code, message) {
    if(message == 201) {
      this.onUploadSuccess(file, {})
    } else {
      this.errorMessage.update("message " + message);
      this.uploader.debug(message);
    }
  },

  onUploadCancel: function(event) {
  },

  onUploadResponse: function(event) {
    1 + 2;
  },

  onComplete: function(func) { 
    return func;
  },
  
  precision: function(number, decimal_points) {
    return parseInt(number * Math.pow(10, decimal_points)) / Math.pow(10, decimal_points);
  }

};
