if(VVF === undefined) {
	var VVF = {};
}

(function(namespace) {
	namespace = namespace || window;
	var $ = jQuery;
	
	// Default class names
	var cls = {show: 'on', hide: 'off'};
	
	// Table of contents Toc constructor
	var Toc = function(datas) {
		if(!(this instanceof Toc)) {
			return new Toc(datas);
		}
		
		Toc.superclass.constructor.call(this);
		
		this.addEvents(['open', 'close'], datas.onload);
		this.initialize(datas);
	};
	namespace.extend(Toc, namespace.Observable, {
		initialize: function(datas) {
			var that = this;
			
			this.cls =           datas.cls || cls;
			this.context =       datas.context || document;
			this.bookmark =      location.hash;
			this.lastTarget =    null;
			this.lastTrigger =   null;
			
			$(datas.context + ' a').each(function() {
				if($(this.parentNode).hasClass(that.cls.show)) {
					that.lastTrigger = $(this.parentNode);
					that.lastTarget = $($(this).attr('href'));
				}
				$(this).attr('href', $(this).attr('href') + 'JS');
			}).click(function(e) {that.clickTrigger(e, this);});
			if(this.bookmark) {
				this.open($(this.bookmark.replace('JS', '')), $(datas.context + ' a[href=' + this.bookmark + ']').parent());
			}
		},
		
		clickTrigger: function(e, obj) {
			var target = $($(obj).attr('href').replace('JS', '')),
				trigger = $(obj.parentNode);
			if(trigger.hasClass(this.cls.show)) {return;}
			target.hasClass(this.cls.hide) ? this.open(target, trigger) : this.close(target, trigger);
		},
		
		open: function(target, trigger) {
			if(this.lastTarget && this.lastTrigger) {
				this.close(this.lastTarget, this.lastTrigger);
			}
			target.removeClass(this.cls.hide).addClass(this.cls.show);
			trigger.removeClass(this.cls.hide).addClass(this.cls.show);
			this.lastTarget = target;
			this.lastTrigger = trigger;
			this.fireEvent('open', target, trigger);
		},
		
		close: function(target, trigger) {
			target.removeClass(this.cls.show).addClass(this.cls.hide);
			trigger.removeClass(this.cls.show).addClass(this.cls.hide);
			this.lastTarget = this.lastTrigger = null;
			this.fireEvent('close', target, trigger);
		}
	});
	
	// Simple (via ID) constructor
	var Simple = function(datas) {
		if(!(this instanceof Simple)) {
			return new Simple(datas);
		}
		
		Simple.superclass.constructor.call(this);
		
		this.addEvents(['open', 'close'], datas.onload);
		this.initialize(datas);
	};
	namespace.extend(Simple, namespace.Observable, {
		initialize: function(datas) {
			var that = this;
			
			this.cls =           datas.cls || cls;
			if(datas.context) {
				this.element =   (typeof datas.context == 'string') ? $(datas.context + ' a') : $('a', datas.context);
			} else {
				this.element =   $(datas.element || 'a');
			}
			this.solo =          datas.solo || false;
			this.lastTarget =    null;
			this.lastTrigger =   null;
			
			this.element.each(function() {
				if(!that.solo && $(this.parentNode).hasClass(that.cls.show)) {
					that.lastTrigger = $(this.parentNode);
					that.lastTarget = $($(this).attr('href'));
				}
			}).click(function(e) {that.clickTrigger(e, this);});
		},
		
		clickTrigger: function(e, obj) {
			var target = $(obj.href.replace(/[^#]*/, '')),
				trigger = this.solo ? obj : $(obj.parentNode);
			target.hasClass(this.cls.hide) ? this.open(target, trigger) : this.close(target, trigger);
			e.preventDefault();
		},
		
		open: function(target, trigger) {
			if(this.lastTarget && this.lastTrigger) {
				this.close(this.lastTarget, this.lastTrigger);
			}
			target.removeClass(this.cls.hide).addClass(this.cls.show);
			if(!this.solo) {
				trigger.removeClass(this.cls.hide).addClass(this.cls.show);
			}
			this.lastTarget = target;
			this.lastTrigger = trigger;
			this.fireEvent('open', target, trigger);
		},
		
		close: function(target, trigger) {
			target.removeClass(this.cls.show).addClass(this.cls.hide);
			if(!this.solo) {
				trigger.removeClass(this.cls.show).addClass(this.cls.hide);
			}
			this.lastTarget = this.lastTrigger = null;
			this.fireEvent('close', target, trigger);
		}
	});
	
	// Title constructor
	var Title = function(datas) {
		if(!(this instanceof Title)) {
			return new Title(datas);
		}
		
		Title.superclass.constructor.call(this);
		
		this.addEvents(['open', 'close'], datas.onload);
		this.initialize(datas);
	};
	namespace.extend(Title, namespace.Observable, {
		initialize: function(datas) {
			var that = this;
			
			this.cls =           datas.cls || cls;
			this.unique =        datas.unique || false;
			this.lastTarget =    null;
			
			$(datas.trigger).each(function() {
				if(this.nodeName != 'A') {
					this.innerHTML = '<a href="#">' + this.innerHTML + '</a>';
				}
				$(this.firstChild).click(function(e) {that.clickTrigger(e, this);});
				if(that.unique && $(this.parentNode).hasClass(that.cls.show)) {
					that.lastTarget = $(this.parentNode);
				}
			});
		},
		
		clickTrigger: function(e, obj) {
			var target = $(obj.parentNode.parentNode);
			target.hasClass(this.cls.hide) ? this.open(target) : this.close(target);
			e.preventDefault();
		},
		
		open: function(target) {
			if(this.unique && this.lastTarget) {
				this.close(this.lastTarget);
			}
			target.removeClass(this.cls.hide).addClass(this.cls.show);
			this.lastTarget = target;
			this.fireEvent('open', target);
		},
		
		close: function(target) {
			target.removeClass(this.cls.show).addClass(this.cls.hide);
			this.fireEvent('close', target, this.lastTarget);
			this.lastTarget = null;
		}
	});
	
	if(!namespace.ShowHide) {
		namespace.ShowHide = {
			Toc: Toc,
			Simple: Simple,
			Title: Title
		};
	}
})(VVF);
