Function.implement({

  getName: function(){
		var name = this.toString().match(/^function\s(\w+)/);
    return name ? name[1] : "anonymous";
	}

});

// compatibility
document.search = function(selector){
	return $$(selector);
 };

var RadioGroup = new Class({

  Implements: [Events, Options],
	
	options: function(){
		//onChange: $empty(radio, index),
		//onCheck: $empty(radio, index),
		//onUncheck: $empty(radio, index)
	},
	
	initialize: function(radioGroup, options){
		this.group = $$(radioGroup);
		
		this.setOptions(options);
		
		this.current = false;
	  
		this.group.each(function(radio, index){
			var span = radio.getParent('span');
			span.addClass('ready');
		  var label = radio.getParent('label');
			label.addEvent('click', function(event){
			  event.preventDefault();
				this.check(index);
			}.bind(this));
		}, this);
		
		this.check();
		
		return this;
		
	},
	
	check: function(radioIndex){
		this.group.each(function(radio, index){
			if ($defined(radioIndex)) radio.checked = (radioIndex == index);
			var parent = radio.getParent('span');
		  if (radio.checked){
				parent.addClass('checked');
				this.fireEvent('check', [radio, index]);
				this.fireEvent('change', [radio, index]);
			} else {
				if (parent.hasClass('checked')){
					parent.removeClass('checked');
					this.fireEvent('uncheck', [radio, index]);
				}
			}
		}, this);
	},
	
	getElements: function(){
		return this.group;
	}

});

var Checkbox = new Class({

  Implements: [Events, Options],
	
	options: function(){
		//onChange: $empty(radio)
	},
	
	initialize: function(element, options){
		this.element = document.id(element);
		
		this.setOptions(options);
	  
		this.span = this.element.getParent('span');
    this.span.addClass('ready');
		this.label = this.element.getParent('label');
		this.label.addEvent('click', function(event){
			event.preventDefault();
			this.check();
		}.bind(this));
		
		this.check(true);
		
		return this;
		
	},
	
	check: function(onload){
		if (!onload) this.element.checked = !this.element.checked;
		this.checked = this.element.checked;
		this.element.checked ? this.span.addClass('checked') : this.span.removeClass('checked');
    if (!onload) this.fireEvent('change', this.element);
	},
	
	checked: false

});

var InputValidator = new Class({

  Implements: [Options, Events],
	
  options: {
		required: false,
		requiredMessage: Messages.get('REQUIRED'),
		tests: [],
		position: 'left',
		trigger: null,         // element: checkbox/radio which approve validation
		triggerStatus: true    // compares to check attribute of radio or checkbox
	},
	
  initialize: function(input, options){
		this.setOptions(options);
		this.input = document.id(input);
		this.tests = new Array(this.options.tests).flatten();
		this.errors = [];
		this.input.addEvent('blur', function(){
		  this.validate();
		}.bind(this));
		
		this.errorMark = null;
		
		if (this.options.trigger) this.options.trigger = document.id(this.options.trigger);
		
		// events
		this.addEvent('error', function(event){
		  this.placeErrors();
		});
		this.addEvent('success', function(){
		  this.removeErrors();
		});
		
		return this;
	},
	
	addTest: function(test, message){
		this.tests.include(test, message);
	},
	
	validate: function(){
		this.errors.empty();
		
		if (!this.options.trigger || (this.options.trigger && this.options.trigger.checked == this.options.triggerStatus)){
			var value = this.input.value.trim();
			if (value == ''){
				if (this.options.required) this.errors.include(this.options.requiredMessage);
			} else {
				this.tests.each(function(test){
					value.test(test.regexp) ? this.errors.erase(test.message) : this.errors.include(test.message);
				}, this);
			}
		}
		
		this.errors.length ? this.fireEvent('error') : this.fireEvent('success');
		
		return this;
	},
	
	getError: function(){
		return this.errors;
	},
	
	placeErrors: function(){
	  var parent = this.input.getParent('p')
		var target = parent.getElement('span.error-help');
		if (!target) target = new Element('span', {'class': 'error-help' }).inject(this.input, 'after');
		var message = this.errors.join('<br />');
		if (this.errorMark) this.errorMark.destroy();
		this.errorMark = new Element('span',{
		  'class': ['error-mark', this.options.position].join(' '),
			'html': message
		}).inject(target);
		parent.addClass('error');
	},
	
	removeErrors: function(){
	  var parent = this.input.getParent('p');
		parent.removeClass('error');
		if (this.errorMark) this.errorMark.destroy();
	},
	
	isEmpty: function(){
		return this.input.value == '';
	}
	
});

var Font = {
	
	replace: function(settings){
		
		this.selectors = new Hash(settings);
		
		this.selectors.each(function(options, selector){
		  Cufon.replace(selector, options);
		});

  }
 };

/* MODIFIED SLIDER FROM MOOTOOLS MORE */
/* Fixed positions, prevent click event on range bar */
var Slider = new Class({

	Implements: [Events, Options],

	Binds: ['clickedElement', 'draggedKnob', 'scrolledElement'],

	options: {/*
		onTick: $empty(intPosition),
		onChange: $empty(intStep),
		onComplete: $empty(strStep),*/
		onTick: function(position){
			if (this.options.snap) position = this.toPosition(this.step);
			this.knob.setStyle(this.property, position);
		},
		snap: false,
		offset: 0,
		range: false,
		wheel: false,
		steps: 100,
		mode: 'horizontal'
	},

	initialize: function(element, knob, options){
		this.setOptions(options);
		this.element = document.id(element);
		this.knob = document.id(knob);
		this.previousChange = this.previousEnd = this.step = -1;
		var offset, limit = {}, modifiers = {'x': false, 'y': false};
		switch (this.options.mode){
			case 'vertical':
				this.axis = 'y';
				this.property = 'top';
				offset = 'offsetHeight';
				break;
			case 'horizontal':
				this.axis = 'x';
				this.property = 'left';
				offset = 'offsetWidth';
		}
		this.half = this.knob[offset] / 2;
		this.full = this.element[offset] - this.knob[offset] + (this.options.offset * 2);
		this.min = $chk(this.options.range[0]) ? this.options.range[0] : 0;
		this.max = $chk(this.options.range[1]) ? this.options.range[1] : this.options.steps;
		this.range = this.max - this.min;
		this.steps = this.options.steps || this.full;
		this.stepSize = Math.abs(this.range) / this.steps;
		this.stepWidth = this.stepSize * this.full / Math.abs(this.range) ;

		this.knob.setStyle(this.property, - this.options.offset);
		modifiers[this.axis] = this.property;
		limit[this.axis] = [- this.options.offset, this.full - this.options.offset];

		this.bound = {
			clickedElement: this.clickedElement.bind(this),
			scrolledElement: this.scrolledElement.bindWithEvent(this),
			draggedKnob: this.draggedKnob.bind(this)
		};

		var dragOptions = {
			snap: 0,
			limit: limit,
			modifiers: modifiers,
			onDrag: this.bound.draggedKnob,
			onStart: this.bound.draggedKnob,
			onBeforeStart: (function(){
				this.isDragging = true;
			}).bind(this),
			onComplete: function(){
				this.isDragging = false;
				this.draggedKnob();
				this.end();
			}.bind(this)
		};
		if (this.options.snap){
			dragOptions.grid = Math.ceil(this.stepWidth);
			dragOptions.limit[this.axis][1] = this.full;
		}

		this.drag = new Drag(this.knob, dragOptions);
		this.attach();
	},

	attach: function(){
		//this.element.addEvent('mousedown', this.bound.clickedElement);
		if (this.options.wheel) this.element.addEvent('mousewheel', this.bound.scrolledElement);
		this.drag.attach();
		return this;
	},

	detach: function(){
		//this.element.removeEvent('mousedown', this.bound.clickedElement);
		this.element.removeEvent('mousewheel', this.bound.scrolledElement);
		this.drag.detach();
		return this;
	},

	set: function(step){
		if (!((this.range > 0) ^ (step < this.min))) step = this.min;
		if (!((this.range > 0) ^ (step > this.max))) step = this.max;

		this.step = Math.round(step);
		this.checkStep();
		this.fireEvent('tick', this.toPosition(this.step));
		this.end();
		return this;
	},

	clickedElement: function(event){
		if (this.isDragging || event.target == this.knob) return;

		var dir = this.range < 0 ? -1 : 1;
		var position = event.page[this.axis] - this.element.getPosition()[this.axis] - this.half;
		position = position.limit(-this.options.offset, this.full -this.options.offset);

		this.step = Math.round(this.min + dir * this.toStep(position));
		this.checkStep();
		this.fireEvent('tick', position);
		this.end();
	},

	scrolledElement: function(event){
		var mode = (this.options.mode == 'horizontal') ? (event.wheel < 0) : (event.wheel > 0);
		this.set(mode ? this.step - this.stepSize : this.step + this.stepSize);
		event.stop();
	},

	draggedKnob: function(){
		var dir = this.range < 0 ? -1 : 1;
		var position = this.drag.value.now[this.axis];


		position = position.limit(-this.options.offset, this.full -this.options.offset);
		this.step = Math.round(this.min + dir * this.toStep(position));
		this.checkStep();
	},

	checkStep: function(){
		if (this.previousChange != this.step){
			this.previousChange = this.step;
			this.fireEvent('change', this.step);
		}
	},

	end: function(){
		if (this.previousEnd !== this.step){
			this.previousEnd = this.step;
			this.fireEvent('complete', this.step + '');
		}
	},

	toStep: function(position){
		var step = (position + this.options.offset) * this.stepSize / this.full * this.steps;
		return this.options.steps ? Math.round(step -= step % this.stepSize) : step;
	},

	toPosition: function(step){
		return (this.full * Math.abs(this.min - step)) / (this.steps * this.stepSize) - this.options.offset;
	}

});

var DoubleSlider = new Class({

  Implements: [Options, Events],
	
	options: {
		knob: {
			min: '.knob-min',
			max: '.knob-max',
			suffix: ''
		},
		limit: {
			min: '.input-min',
			max: '.input-max'
		},
		output: {
			min: '.output-min',
			max: '.output-max'
		},
		range: '.range',
		loadedClass: 'loaded',
		steps: 100,
		diff: 100,
		offset: 0
	},
	
	initialize: function(container, options){
		
		this.setOptions(options);
		this.container = document.id(container);
		this.range = this.container.getElement(this.options.range);
		
		this.knob = this.toElements(this.options.knob);
		this.knob.minText = this.knob.min.getElement('span');
		this.knob.maxText = this.knob.max.getElement('span');
		
		this.limit = this.toElements(this.options.limit);
		this.limit.minValue = this.limit.min.get('value').toInt();
		this.limit.maxValue = this.limit.max.get('value').toInt();
		
		
  	this.output = this.toElements(this.options.output);
		
		this.start = {
			min: (this.output.min.value ? this.output.min.value.toInt() : this.limit.minValue),
			max: (this.output.max.value ? this.output.max.value.toInt() : this.limit.maxValue)
		};
		
	
		// min and max values are equal
		if (this.limit.minValue == this.limit.maxValue){
			this.setKnobValue(this.limit.minValue, 'min');
			this.setKnobValue(this.limit.maxValue, 'max');
			this.container.addClass('disabled');
			return null;
		}
		
		this.slider = {
			min: new Slider(this.range, this.knob.min, {
				range: [this.limit.minValue, this.limit.maxValue],
				offset: this.options.offset,
				steps: this.options.steps,
			  onChange: function(step){
					var limitValue = this.getKnobValue('max')-this.options.diff;
					if (step > limitValue){
						this.slider.min.set(limitValue);
						step = limitValue;
					}
					this.setKnobValue('min', step);
					this.output.min.value = step;
				}.bind(this)
			}),
			max: new Slider(this.range, this.knob.max, {
				range: [this.limit.minValue, this.limit.maxValue],
				offset: this.options.offset,
				steps: this.options.steps,
			  onChange: function(step){
					var limitValue = this.getKnobValue('min')+this.options.diff;
					if (step < limitValue){
						this.slider.max.set(limitValue);
						step = limitValue;
					}
					this.setKnobValue('max', step);
					this.output.max.value = step;
				}.bind(this)
			})
		}

    // default knob values
		this.setKnobValue('min', this.start.min);
		this.setKnobValue('max', this.start.max);
		this.slider.min.set(this.start.min);
		this.slider.max.set(this.start.max);
		
	},
	
	toElements: function(hash){
		var hash = new Hash(hash);
		hash.each(function(item, key){
		  hash.set(key, this.container.getElement(item));
		}, this);
		return hash;
	},
	
	setKnobValue: function(knobType, value){
		switch (knobType){
			case 'min':
			  this.knob.minText.set('text', value + this.options.knob.suffix);
			break;
			case 'max':
			  this.knob.maxText.set('text', value + this.options.knob.suffix);
			break;
		}
	},
	
	getKnobValue: function(knobType){
		switch (knobType){
			case 'min': 
			  return this.knob.minText.get('text').toInt();
			break;
			case 'max':
			  return this.knob.maxText.get('text').toInt();
			break;
		}
	}
	

});

var Tabs = new Class({

  Implements: [Events, Options],
	
	options: {
		tabs: '.tab-anchors li',
		panels: '.tab-panel',
		disableClass: 'disabled',
		start: 0,
		observeDuration: 200
		//onShow: $empty(tab, panel, anchor),
		//onClose: $empty(tab, panel, anchor)
	},
	
	initialize: function(container, options){
		
		this.setOptions(options);
		
		this.container = document.id(container);
		this.tabs = new Hash(); // contains pairs of tabs and panels with anchor as id
		this.anchors = new Array(); // contains all available tab ids
		this.handlers = this.container.getElements(this.options.tabs);
		
		this.current = null;

    this.handlers.each(function(handler){
		   this.addTab(handler);
			 var link = handler.getElement('a');
			 if (handler.hasClass('disabled') && link){
				 link.addEvent('click', function(event){
				   event.preventDefault()
				 });
			 }
		}, this);
		
		// init default tab
		this.defaultTab = this.anchors[this.options.start];
		var hash = new URI().get('fragment');
    if (this.tabs.has(hash)) this.defaultTab = hash;
		if (this.defaultTab){
			this.show(this.defaultTab);
		}
		
		this.repeater = this.observe.periodical(this.options.observeDuration, this);
		
	},
	
	addTab: function(handler){
		var anchor = handler.getElement('a').get('href');
    if (anchor.test(/^#(.)+$/)){
			var panel =  this.container.getElement(anchor);
			if (panel){
				anchor = anchor.replace('#', '');
				this.tabs.set(anchor, {
				  tab: handler,
					panel: panel.removeProperty('id').hide(),  // remove id to prevent browser from jumping in page
					anchor: anchor
				});
				this.anchors.include(anchor);
			}
		}
	},
	
	show: function(id){
		if (id == '') id = this.defaultTab;
		if (this.tabs.has(id)){
			var tabPanel = this.tabs.get(id);
			if (this.current){
				this.current.tab.removeClass('active');
				this.current.panel.hide();
				this.fireEvent('close', [this.current.tab, this.current.panel, this.current.anchor]);
			}
			var tabPanel = this.tabs.get(id);
			tabPanel.tab.addClass('active');
			tabPanel.tab.getElement('a').blur();
			tabPanel.panel.show();
			this.current = tabPanel;
			this.fireEvent('show', [tabPanel.tab, tabPanel.panel, id]);
		}
	},
	
	observe: function(){
		var hash = window.location.hash.replace('#', '');
		if (hash != this.current.anchor){
      this.show(hash);
		}
	}
		
});

var Roller = new Class({

  Implements: [Options, Events],
	
	Binds: ['move'],
	
	options: {
		wrapper: '',  // selector: parent element
	  content: '', // selector: element which will be moved
		tools: '', // selector: tools
		item: {
			element: '',  // item selector
			template: '', // item content template
			defaults: null,  // default (object) data values for JSON object -- items should corespond to items in content template
			width: 0      // default width of an item, if not set, Roller tries to get it
		},
		button: {
			next: '', // selector: button 
			prev: '' // selector: button
		},
		output: {
			element: '', // selector
			template: '{from}&ndash;{to}/{total}'
		},
		api: {
			read: '',
			data: null
		},
		preload: 0,
		total: 0,
		fx: {
			property: 'margin-left',
			duration: 500,
			link: 'cancel',
			transition: Fx.Transitions.Circle
		},
		delta: 3
	},
	
	status: {
		moving: false,
		loading: false
	},
	
	initialize: function(container, options){
		
		this.container = document.id(container);
		
		this.setOptions(options);
	
		this.wrapper = this.container.getElement(this.options.wrapper);
		this.content = this.container.getElement(this.options.content);
		this.contentID = this.content.get('id');
		this.contentID  = this.contentID ? this.contentID.replace('id', '') : null;
		this.tools = this.container.getElement(this.options.tools);
		if (this.options.output) this.output = this.container.getElement(this.options.output.element);
		
		this.items = this.content.getElements(this.options.item.element);
		this.count = this.items.length;
		this.options.preload = this.options.preload ? this.options.preload : this.count;
		this.width = (this.options.item.width ? this.options.item.width : (this.items.length ? this.items[0].getComputedSize().totalWidth : 0));
		this.current = 0;
		this.delta = this.options.delta;
		
		this.fx = new Fx.Tween(this.content, this.options.fx);
    this.fx.set(0);

		this.items.each(function(item){
		  this.addItemEvents(item);
		}, this);
		
		// hide tools and prevent loading when count is equal to total
		if (this.options.total && this.count >= this.options.total){
			this.tools.hide();
		} else {
			this.tools.fade('in');
		}
		
		// buttons
		this.button = {
			prev: this.container.getElement(this.options.button.prev),
			next: this.container.getElement(this.options.button.next)
		};
		this.button.prev.addEvent('click', function(){ if (!this.button.prev.hasClass('disabled')) this.move('prev'); }.bind(this));
		this.button.next.addEvent('click', function(){ if (!this.button.next.hasClass('disabled')) this.move('next'); }.bind(this));
		
		// request
		this.request = new Request.JSON({
		  url: this.options.api.read,
			onSuccess: function(responseJSON){
				this.addItems(responseJSON);
				this.status.preloading = false;
			}.bind(this),
			onFailure: function(){
				this.status.preloading = false;
			}.bind(this)
		});
    
		this.setWidth();
				
		// preload
		this.preload();
	
		this.container.addClass('roller-loaded');
		
		this.fireEvent('load');
		
	},
	
	is: function(status){
		return this.status[status];
	},
	
	isNot: function(status){
		return !this.is(status);
	},
	
	addItemEvents: function(item){
		item.addEvent('click', function(event){
		  if (document.id(event.target).get('tag') != 'a') window.location = item.getElement('a').get('href');
		});
		item.setStyle('cursor', 'pointer');
	},
	
	updateProgress: function(){
		var data = {
			from: this.current + 1,
			to: this.current + this.delta,
			total: this.options.total
		}
		if (this.output) this.output.set('html', this.options.output.template.substitute(data));
	},
	
	setWidth: function(){
		this.content.setStyle('width', this.count * this.width);
	},
	
	move: function(dir){
		switch (dir){
			case 'prev':
			  this.current = (this.current - this.delta).limit(0, this.count-this.delta);
			  this.fx.start(-this.current*this.width);
				this.check();
			break;
			case 'next':
			  if (this.isNot('preloading')){
					this.current = (this.current + this.delta).limit(0, this.count-this.delta);
					this.fx.start(-this.current*this.width).chain(function(){
   				 this.count < this.options.total ? this.preload() : this.check();
					}.bind(this));
				}
			break;
		}
		this.updateProgress();
	},
	
	check: function(){
		this.setWidth();
		this.current == 0 ? this.button.prev.addClass('disabled').fade(0.3) : this.button.prev.removeClass('disabled').fade('in');
		this.current + this.delta > this.count - 1 ? this.button.next.addClass('disabled').fade(0.3) : this.button.next.removeClass('disabled').fade('in');
		this.fireEvent('check');
	},
	
	preload: function(){
		var options = {
			limit: this.options.preload,
			offset: (this.current + this.delta),
			id: this.contentID
		}
		this.status.preloading = true;
		this.request.get($extend(options, this.options.api.data));
	},
	
	addItems: function(json){
		var itemData = new Array(json).flatten();
		itemData.each(function(data){
		  var item = new Element(this.options.item.element, {
			  'html': this.options.item.template.substitute(data),
				'styles': {
					'cursor': 'pointer'
				},
				'events': {
					'click': function(event){
						if (event.target.get('tag') != 'a') window.location = item.getElement('a').get('href');
					}
				}
			});
			this.items.push(item);
			item.inject(this.content);
			this.fireEvent('addItem', item);
			++this.count;
		}, this);
		this.check();
	}

});

var SelectFilter = new Class({
														 
	Binds: ['render'],
	
	initialize: function(source, target){
		this.source = document.id(source);
		this.target = document.id(target);
		
		this.selectedTarget = this.target.getElement('option[selected]');
		
		this.sourceItems = new Array();
		this.source.getElements('options').each(function(option){
		  this.sourceItems.include(options.get('text'));
		}, this);
		
		this.emptyOption = this.target.getElement('option.empty').dispose();
		
		this.groups = this.target.getElements('optgroup');
		this.groupedItems = new Hash();
		this.groups.each(function(group){
		  var options = group.getElements('option').dispose();
			group.dispose();
			this.groupedItems.set(group.get('label'), options);
		}, this);
		
		this.source.addEvent('change', this.render);
		
		this.render();
		
	},
	
	render: function(){
		var selectedSource = document.id(this.source[this.source.selectedIndex]).get('text').trim();
		var options = this.groupedItems.get(selectedSource);
		this.target.getElements('option').dispose();
		this.emptyOption.inject(this.target);
		if (options){
			this.target.disabled = false;				
			options.each(function(option){
				option.inject(this.target);
			}, this);
			if (this.selectedTarget){
				var selectedIndex = options.indexOf(this.selectedTarget)+1;
				this.target.selectedIndex = (selectedIndex > 0 ? selectedIndex : 0);
				this.selectedTarget = null;
			} else {
  			this.target.selectedIndex = 0;
			}
		} else {
			this.target.disabled = true;
		}
	}
});

var Input = new Class({

  Implements: Options,
	
	options: {
		defaultValue: null,
		types: ['numbers', 'all'],
		type: 'all',
		keys: {
			common: [
			  8,  // backspace
				9,  // tab
				13, // enter
				16, // shift
				17, // ctrl
				18, // alt
				19, // pause/break
				20, // caps lock
				27, // escape
				33, // page up
				34, // page down
				35, // end
				36, // home
				37, // left arrow
				38, // up arrow
				39, // right arrow
				40, // down arrow
				45, // insert
				46, // delete
      ],
		  numbers: [48, 49, 50, 51, 52, 53, 54, 55, 56, 57,       // numkeys
								96, 97, 98, 99, 100, 101, 102, 103, 104, 105  // numpad
			],
			price: [110, 188, 190] // decimal point, comma and period
		}
	},
	
  initialize: function(input, options){
		this.setOptions(options);
		
		this.element = document.id(input);
		
		this.codes = [];
		this.setCodes();
		
		this.pressed = {
			ctrl: false,
			shift: false,
			alt: false
		}
		
		this.element.addEvent('keydown', function(event){
			switch (event.code){
				case 17: // ctrl
				  this.pressed.ctrl = true;
				break;
				case 16: // shift
				  this.pressed.shift = true;
				break;
				case 18: // alt
				  this.pressed.alt = true;
			  break;
				default:
				  switch (event.code){
						case 86: // v
						  if (this.pressed.ctrl){
								this.pressed.ctrl = false;
							} else {
								event.stop();
							}
						break;
						default:
						  if (!this.codes.contains(event.code)) event.stop();
					}
			}
		}.bind(this));
		
		if (this.options.defaultValue){
			this.element.addEvent('blur', function(){
			  if (this.element.value == '') this.element.value = this.options.defaultValue;
			}.bind(this));
		}
		
		return this;
	},
	
	setType: function(type){
		if (this.options.types.contains(type)){
			this.options.type = type;
			this.setCodes();
		}
	},
	
	setCodes: function(){
		switch (this.options.type){
			case 'numbers': 
			  this.codes = new Array([this.options.keys.common, this.options.keys.numbers]).flatten();
			break;
			case 'price': 
			  this.codes = new Array([this.options.keys.common, this.options.keys.numbers, this.options.keys.price]).flatten();
			break;
		}
	},
	
	getValue: function(){
		return this.element.value;
	},
	
	setValue: function(value){
		this.element.value = value;
		return this;
	}
});

// observe click events and calls registered methods
var Observer = {
	
	init: function(){
		this.body = document.id(document.body);
		
		this.body.addEvent('click', this.check.bind(this));
		
		this.stack = new Array();
		
	},
	
	register: function(func, bind){
		this.stack.include(func.bind(bind));
		return func;
	},
	
	unregister: function(func){
		this.stack.erase(func);
		return func;
	},
	
	check: function(){
		this.stack.each(function(func){
		  if (func != undefined) func.run();
			this.unregister(func);
		}, this);
	}
 };

