text: /*
  ---
  MooTools: the javascript framework
  
  web build:
   - http://mootools.net/more/16f279284293383895ae88bb0e54545f
  
  packager build:
   - packager build More/Class.Binds More/Hash More/Element.Shortcuts More/Fx.Elements More/Fx.Accordion More/Drag More/Drag.Move More/Hash.Cookie More/Swiff
  
  ...
  */
  
  /*
  ---
  
  script: More.js
  
  name: More
  
  description: MooTools More
  
  license: MIT-style license
  
  authors:
    - Guillermo Rauch
    - Thomas Aylott
    - Scott Kyle
    - Arian Stolwijk
    - Tim Wienk
    - Christoph Pojer
    - Aaron Newton
    - Jacob Thornton
  
  requires:
    - Core/MooTools
  
  provides: [MooTools.More]
  
  ...
  */
  
  MooTools.More = {
  	version: '1.5.1',
  	build: '2dd695ba957196ae4b0275a690765d6636a61ccd'
  };
  
  
  /*
  ---
  
  script: Class.Binds.js
  
  name: Class.Binds
  
  description: Automagically binds specified methods in a class to the instance of the class.
  
  license: MIT-style license
  
  authors:
    - Aaron Newton
  
  requires:
    - Core/Class
    - MooTools.More
  
  provides: [Class.Binds]
  
  ...
  */
  
  Class.Mutators.Binds = function(binds){
  	if (!this.prototype.initialize) this.implement('initialize', function(){});
  	return Array.from(binds).concat(this.prototype.Binds || []);
  };
  
  Class.Mutators.initialize = function(initialize){
  	return function(){
  		Array.from(this.Binds).each(function(name){
  			var original = this[name];
  			if (original) this[name] = original.bind(this);
  		}, this);
  		return initialize.apply(this, arguments);
  	};
  };
  
  
  /*
  ---
  
  name: Hash
  
  description: Contains Hash Prototypes. Provides a means for overcoming the JavaScript practical impossibility of extending native Objects.
  
  license: MIT-style license.
  
  requires:
    - Core/Object
    - MooTools.More
  
  provides: [Hash]
  
  ...
  */
  
  (function(){
  
  if (this.Hash) return;
  
  var Hash = this.Hash = new Type('Hash', function(object){
  	if (typeOf(object) == 'hash') object = Object.clone(object.getClean());
  	for (var key in object) this[key] = object[key];
  	return this;
  });
  
  this.$H = function(object){
  	return new Hash(object);
  };
  
  Hash.implement({
  
  	forEach: function(fn, bind){
  		Object.forEach(this, fn, bind);
  	},
  
  	getClean: function(){
  		var clean = {};
  		for (var key in this){
  			if (this.hasOwnProperty(key)) clean[key] = this[key];
  		}
  		return clean;
  	},
  
  	getLength: function(){
  		var length = 0;
  		for (var key in this){
  			if (this.hasOwnProperty(key)) length++;
  		}
  		return length;
  	}
  
  });
  
  Hash.alias('each', 'forEach');
  
  Hash.implement({
  
  	has: Object.prototype.hasOwnProperty,
  
  	keyOf: function(value){
  		return Object.keyOf(this, value);
  	},
  
  	hasValue: function(value){
  		return Object.contains(this, value);
  	},
  
  	extend: function(properties){
  		Hash.each(properties || {}, function(value, key){
  			Hash.set(this, key, value);
  		}, this);
  		return this;
  	},
  
  	combine: function(properties){
  		Hash.each(properties || {}, function(value, key){
  			Hash.include(this, key, value);
  		}, this);
  		return this;
  	},
  
  	erase: function(key){
  		if (this.hasOwnProperty(key)) delete this[key];
  		return this;
  	},
  
  	get: function(key){
  		return (this.hasOwnProperty(key)) ? this[key] : null;
  	},
  
  	set: function(key, value){
  		if (!this[key] || this.hasOwnProperty(key)) this[key] = value;
  		return this;
  	},
  
  	empty: function(){
  		Hash.each(this, function(value, key){
  			delete this[key];
  		}, this);
  		return this;
  	},
  
  	include: function(key, value){
  		if (this[key] == undefined) this[key] = value;
  		return this;
  	},
  
  	map: function(fn, bind){
  		return new Hash(Object.map(this, fn, bind));
  	},
  
  	filter: function(fn, bind){
  		return new Hash(Object.filter(this, fn, bind));
  	},
  
  	every: function(fn, bind){
  		return Object.every(this, fn, bind);
  	},
  
  	some: function(fn, bind){
  		return Object.some(this, fn, bind);
  	},
  
  	getKeys: function(){
  		return Object.keys(this);
  	},
  
  	getValues: function(){
  		return Object.values(this);
  	},
  
  	toQueryString: function(base){
  		return Object.toQueryString(this, base);
  	}
  
  });
  
  Hash.alias({indexOf: 'keyOf', contains: 'hasValue'});
  
  
  })();
  
  
  
  /*
  ---
  
  script: Element.Shortcuts.js
  
  name: Element.Shortcuts
  
  description: Extends the Element native object to include some shortcut methods.
  
  license: MIT-style license
  
  authors:
    - Aaron Newton
  
  requires:
    - Core/Element.Style
    - MooTools.More
  
  provides: [Element.Shortcuts]
  
  ...
  */
  
  Element.implement({
  
  	isDisplayed: function(){
  		return this.getStyle('display') != 'none';
  	},
  
  	isVisible: function(){
  		var w = this.offsetWidth,
  			h = this.offsetHeight;
  		return (w == 0 && h == 0) ? false : (w > 0 && h > 0) ? true : this.style.display != 'none';
  	},
  
  	toggle: function(){
  		return this[this.isDisplayed() ? 'hide' : 'show']();
  	},
  
  	hide: function(){
  		var d;
  		try {
  			//IE fails here if the element is not in the dom
  			d = this.getStyle('display');
  		} catch(e){}
  		if (d == 'none') return this;
  		return this.store('element:_originalDisplay', d || '').setStyle('display', 'none');
  	},
  
  	show: function(display){
  		if (!display && this.isDisplayed()) return this;
  		display = display || this.retrieve('element:_originalDisplay') || 'block';
  		return this.setStyle('display', (display == 'none') ? 'block' : display);
  	},
  
  	swapClass: function(remove, add){
  		return this.removeClass(remove).addClass(add);
  	}
  
  });
  
  Document.implement({
  
  	clearSelection: function(){
  		if (window.getSelection){
  			var selection = window.getSelection();
  			if (selection && selection.removeAllRanges) selection.removeAllRanges();
  		} else if (document.selection && document.selection.empty){
  			try {
  				//IE fails here if selected element is not in dom
  				document.selection.empty();
  			} catch(e){}
  		}
  	}
  
  });
  
  
  /*
  ---
  
  script: Fx.Elements.js
  
  name: Fx.Elements
  
  description: Effect to change any number of CSS properties of any number of Elements.
  
  license: MIT-style license
  
  authors:
    - Valerio Proietti
  
  requires:
    - Core/Fx.CSS
    - MooTools.More
  
  provides: [Fx.Elements]
  
  ...
  */
  
  Fx.Elements = new Class({
  
  	Extends: Fx.CSS,
  
  	initialize: function(elements, options){
  		this.elements = this.subject = $$(elements);
  		this.parent(options);
  	},
  
  	compute: function(from, to, delta){
  		var now = {};
  
  		for (var i in from){
  			var iFrom = from[i], iTo = to[i], iNow = now[i] = {};
  			for (var p in iFrom) iNow[p] = this.parent(iFrom[p], iTo[p], delta);
  		}
  
  		return now;
  	},
  
  	set: function(now){
  		for (var i in now){
  			if (!this.elements[i]) continue;
  
  			var iNow = now[i];
  			for (var p in iNow) this.render(this.elements[i], p, iNow[p], this.options.unit);
  		}
  
  		return this;
  	},
  
  	start: function(obj){
  		if (!this.check(obj)) return this;
  		var from = {}, to = {};
  
  		for (var i in obj){
  			if (!this.elements[i]) continue;
  
  			var iProps = obj[i], iFrom = from[i] = {}, iTo = to[i] = {};
  
  			for (var p in iProps){
  				var parsed = this.prepare(this.elements[i], p, iProps[p]);
  				iFrom[p] = parsed.from;
  				iTo[p] = parsed.to;
  			}
  		}
  
  		return this.parent(from, to);
  	}
  
  });
  
  
  /*
  ---
  
  script: Fx.Accordion.js
  
  name: Fx.Accordion
  
  description: An Fx.Elements extension which allows you to easily create accordion type controls.
  
  license: MIT-style license
  
  authors:
    - Valerio Proietti
  
  requires:
    - Core/Element.Event
    - Fx.Elements
  
  provides: [Fx.Accordion]
  
  ...
  */
  
  Fx.Accordion = new Class({
  
  	Extends: Fx.Elements,
  
  	options: {/*
  		onActive: function(toggler, section){},
  		onBackground: function(toggler, section){},*/
  		fixedHeight: false,
  		fixedWidth: false,
  		display: 0,
  		show: false,
  		height: true,
  		width: false,
  		opacity: true,
  		alwaysHide: false,
  		trigger: 'click',
  		initialDisplayFx: true,
  		resetHeight: true
  	},
  
  	initialize: function(){
  		var defined = function(obj){
  			return obj != null;
  		};
  
  		var params = Array.link(arguments, {
  			'container': Type.isElement, //deprecated
  			'options': Type.isObject,
  			'togglers': defined,
  			'elements': defined
  		});
  		this.parent(params.elements, params.options);
  
  		var options = this.options,
  			togglers = this.togglers = $$(params.togglers);
  
  		this.previous = -1;
  		this.internalChain = new Chain();
  
  		if (options.alwaysHide) this.options.link = 'chain';
  
  		if (options.show || this.options.show === 0){
  			options.display = false;
  			this.previous = options.show;
  		}
  
  		if (options.start){
  			options.display = false;
  			options.show = false;
  		}
  
  		var effects = this.effects = {};
  
  		if (options.opacity) effects.opacity = 'fullOpacity';
  		if (options.width) effects.width = options.fixedWidth ? 'fullWidth' : 'offsetWidth';
  		if (options.height) effects.height = options.fixedHeight ? 'fullHeight' : 'scrollHeight';
  
  		for (var i = 0, l = togglers.length; i < l; i++) this.addSection(togglers[i], this.elements[i]);
  
  		this.elements.each(function(el, i){
  			if (options.show === i){
  				this.fireEvent('active', [togglers[i], el]);
  			} else {
  				for (var fx in effects) el.setStyle(fx, 0);
  			}
  		}, this);
  
  		if (options.display || options.display === 0 || options.initialDisplayFx === false){
  			this.display(options.display, options.initialDisplayFx);
  		}
  
  		if (options.fixedHeight !== false) options.resetHeight = false;
  		this.addEvent('complete', this.internalChain.callChain.bind(this.internalChain));
  	},
  
  	addSection: function(toggler, element){
  		toggler = document.id(toggler);
  		element = document.id(element);
  		this.togglers.include(toggler);
  		this.elements.include(element);
  
  		var togglers = this.togglers,
  			options = this.options,
  			test = togglers.contains(toggler),
  			idx = togglers.indexOf(toggler),
  			displayer = this.display.pass(idx, this);
  
  		toggler.store('accordion:display', displayer)
  			.addEvent(options.trigger, displayer);
  
  		if (options.height) element.setStyles({'padding-top': 0, 'border-top': 'none', 'padding-bottom': 0, 'border-bottom': 'none'});
  		if (options.width) element.setStyles({'padding-left': 0, 'border-left': 'none', 'padding-right': 0, 'border-right': 'none'});
  
  		element.fullOpacity = 1;
  		if (options.fixedWidth) element.fullWidth = options.fixedWidth;
  		if (options.fixedHeight) element.fullHeight = options.fixedHeight;
  		element.setStyle('overflow', 'hidden');
  
  		if (!test) for (var fx in this.effects){
  			element.setStyle(fx, 0);
  		}
  		return this;
  	},
  
  	removeSection: function(toggler, displayIndex){
  		var togglers = this.togglers,
  			idx = togglers.indexOf(toggler),
  			element = this.elements[idx];
  
  		var remover = function(){
  			togglers.erase(toggler);
  			this.elements.erase(element);
  			this.detach(toggler);
  		}.bind(this);
  
  		if (this.now == idx || displayIndex != null){
  			this.display(displayIndex != null ? displayIndex : (idx - 1 >= 0 ? idx - 1 : 0)).chain(remover);
  		} else {
  			remover();
  		}
  		return this;
  	},
  
  	detach: function(toggler){
  		var remove = function(toggler){
  			toggler.removeEvent(this.options.trigger, toggler.retrieve('accordion:display'));
  		}.bind(this);
  
  		if (!toggler) this.togglers.each(remove);
  		else remove(toggler);
  		return this;
  	},
  
  	display: function(index, useFx){
  		if (!this.check(index, useFx)) return this;
  
  		var obj = {},
  			elements = this.elements,
  			options = this.options,
  			effects = this.effects;
  
  		if (useFx == null) useFx = true;
  		if (typeOf(index) == 'element') index = elements.indexOf(index);
  		if (index == this.current && !options.alwaysHide) return this;
  
  		if (options.resetHeight){
  			var prev = elements[this.current];
  			if (prev && !this.selfHidden){
  				for (var fx in effects) prev.setStyle(fx, prev[effects[fx]]);
  			}
  		}
  
  		if ((this.timer && options.link == 'chain') || (index === this.current && !options.alwaysHide)) return this;
  
  		if (this.current != null) this.previous = this.current;
  		this.current = index;
  		this.selfHidden = false;
  
  		elements.each(function(el, i){
  			obj[i] = {};
  			var hide;
  			if (i != index){
  				hide = true;
  			} else if (options.alwaysHide && ((el.offsetHeight > 0 && options.height) || el.offsetWidth > 0 && options.width)){
  				hide = true;
  				this.selfHidden = true;
  			}
  			this.fireEvent(hide ? 'background' : 'active', [this.togglers[i], el]);
  			for (var fx in effects) obj[i][fx] = hide ? 0 : el[effects[fx]];
  			if (!useFx && !hide && options.resetHeight) obj[i].height = 'auto';
  		}, this);
  
  		this.internalChain.clearChain();
  		this.internalChain.chain(function(){
  			if (options.resetHeight && !this.selfHidden){
  				var el = elements[index];
  				if (el) el.setStyle('height', 'auto');
  			}
  		}.bind(this));
  
  		return useFx ? this.start(obj) : this.set(obj).internalChain.callChain();
  	}
  
  });
  
  
  
  
  /*
  ---
  
  script: Drag.js
  
  name: Drag
  
  description: The base Drag Class. Can be used to drag and resize Elements using mouse events.
  
  license: MIT-style license
  
  authors:
    - Valerio Proietti
    - Tom Occhinno
    - Jan Kassens
  
  requires:
    - Core/Events
    - Core/Options
    - Core/Element.Event
    - Core/Element.Style
    - Core/Element.Dimensions
    - MooTools.More
  
  provides: [Drag]
  ...
  
  */
  
  var Drag = new Class({
  
  	Implements: [Events, Options],
  
  	options: {/*
  		onBeforeStart: function(thisElement){},
  		onStart: function(thisElement, event){},
  		onSnap: function(thisElement){},
  		onDrag: function(thisElement, event){},
  		onCancel: function(thisElement){},
  		onComplete: function(thisElement, event){},*/
  		snap: 6,
  		unit: 'px',
  		grid: false,
  		style: true,
  		limit: false,
  		handle: false,
  		invert: false,
  		preventDefault: false,
  		stopPropagation: false,
  		compensateScroll: false,
  		modifiers: {x: 'left', y: 'top'}
  	},
  
  	initialize: function(){
  		var params = Array.link(arguments, {
  			'options': Type.isObject,
  			'element': function(obj){
  				return obj != null;
  			}
  		});
  
  		this.element = document.id(params.element);
  		this.document = this.element.getDocument();
  		this.setOptions(params.options || {});
  		var htype = typeOf(this.options.handle);
  		this.handles = ((htype == 'array' || htype == 'collection') ? $$(this.options.handle) : document.id(this.options.handle)) || this.element;
  		this.mouse = {'now': {}, 'pos': {}};
  		this.value = {'start': {}, 'now': {}};
  		this.offsetParent = (function(el){
  			var offsetParent = el.getOffsetParent();
  			var isBody = !offsetParent || (/^(?:body|html)$/i).test(offsetParent.tagName);
  			return isBody ? window : document.id(offsetParent);
  		})(this.element);
  		this.selection = 'selectstart' in document ? 'selectstart' : 'mousedown';
  
  		this.compensateScroll = {start: {}, diff: {}, last: {}};
  
  		if ('ondragstart' in document && !('FileReader' in window) && !Drag.ondragstartFixed){
  			document.ondragstart = Function.from(false);
  			Drag.ondragstartFixed = true;
  		}
  
  		this.bound = {
  			start: this.start.bind(this),
  			check: this.check.bind(this),
  			drag: this.drag.bind(this),
  			stop: this.stop.bind(this),
  			cancel: this.cancel.bind(this),
  			eventStop: Function.from(false),
  			scrollListener: this.scrollListener.bind(this)
  		};
  		this.attach();
  	},
  
  	attach: function(){
  		this.handles.addEvent('mousedown', this.bound.start);
  		if (this.options.compensateScroll) this.offsetParent.addEvent('scroll', this.bound.scrollListener);
  		return this;
  	},
  
  	detach: function(){
  		this.handles.removeEvent('mousedown', this.bound.start);
  		if (this.options.compensateScroll) this.offsetParent.removeEvent('scroll', this.bound.scrollListener);
  		return this;
  	},
  
  	scrollListener: function(){
  
  		if (!this.mouse.start) return;
  		var newScrollValue = this.offsetParent.getScroll();
  
  		if (this.element.getStyle('position') == 'absolute'){
  			var scrollDiff = this.sumValues(newScrollValue, this.compensateScroll.last, -1);
  			this.mouse.now = this.sumValues(this.mouse.now, scrollDiff, 1);
  		} else {
  			this.compensateScroll.diff = this.sumValues(newScrollValue, this.compensateScroll.start, -1);
  		}
  		if (this.offsetParent != window) this.compensateScroll.diff = this.sumValues(this.compensateScroll.start, newScrollValue, -1);
  		this.compensateScroll.last = newScrollValue;
  		this.render(this.options);
  	},
  
  	sumValues: function(alpha, beta, op){
  		var sum = {}, options = this.options;
  		for (z in options.modifiers){
  			if (!options.modifiers[z]) continue;
  			sum[z] = alpha[z] + beta[z] * op;
  		}
  		return sum;
  	},
  
  	start: function(event){
  		var options = this.options;
  
  		if (event.rightClick) return;
  
  		if (options.preventDefault) event.preventDefault();
  		if (options.stopPropagation) event.stopPropagation();
  		this.compensateScroll.start = this.compensateScroll.last = this.offsetParent.getScroll();
  		this.compensateScroll.diff = {x: 0, y: 0};
  		this.mouse.start = event.page;
  		this.fireEvent('beforeStart', this.element);
  
  		var limit = options.limit;
  		this.limit = {x: [], y: []};
  
  		var z, coordinates, offsetParent = this.offsetParent == window ? null : this.offsetParent;
  		for (z in options.modifiers){
  			if (!options.modifiers[z]) continue;
  
  			var style = this.element.getStyle(options.modifiers[z]);
  
  			// Some browsers (IE and Opera) don't always return pixels.
  			if (style && !style.match(/px$/)){
  				if (!coordinates) coordinates = this.element.getCoordinates(offsetParent);
  				style = coordinates[options.modifiers[z]];
  			}
  
  			if (options.style) this.value.now[z] = (style || 0).toInt();
  			else this.value.now[z] = this.element[options.modifiers[z]];
  
  			if (options.invert) this.value.now[z] *= -1;
  
  			this.mouse.pos[z] = event.page[z] - this.value.now[z];
  
  			if (limit && limit[z]){
  				var i = 2;
  				while (i--){
  					var limitZI = limit[z][i];
  					if (limitZI || limitZI === 0) this.limit[z][i] = (typeof limitZI == 'function') ? limitZI() : limitZI;
  				}
  			}
  		}
  
  		if (typeOf(this.options.grid) == 'number') this.options.grid = {
  			x: this.options.grid,
  			y: this.options.grid
  		};
  
  		var events = {
  			mousemove: this.bound.check,
  			mouseup: this.bound.cancel
  		};
  		events[this.selection] = this.bound.eventStop;
  		this.document.addEvents(events);
  	},
  
  	check: function(event){
  		if (this.options.preventDefault) event.preventDefault();
  		var distance = Math.round(Math.sqrt(Math.pow(event.page.x - this.mouse.start.x, 2) + Math.pow(event.page.y - this.mouse.start.y, 2)));
  		if (distance > this.options.snap){
  			this.cancel();
  			this.document.addEvents({
  				mousemove: this.bound.drag,
  				mouseup: this.bound.stop
  			});
  			this.fireEvent('start', [this.element, event]).fireEvent('snap', this.element);
  		}
  	},
  
  	drag: function(event){
  		var options = this.options;
  		if (options.preventDefault) event.preventDefault();
  		this.mouse.now = this.sumValues(event.page, this.compensateScroll.diff, -1);
  
  		this.render(options);
  		this.fireEvent('drag', [this.element, event]);
  	},  
  
  	render: function(options){
  		for (var z in options.modifiers){
  			if (!options.modifiers[z]) continue;
  			this.value.now[z] = this.mouse.now[z] - this.mouse.pos[z];
  
  			if (options.invert) this.value.now[z] *= -1;
  			if (options.limit && this.limit[z]){
  				if ((this.limit[z][1] || this.limit[z][1] === 0) && (this.value.now[z] > this.limit[z][1])){
  					this.value.now[z] = this.limit[z][1];
  				} else if ((this.limit[z][0] || this.limit[z][0] === 0) && (this.value.now[z] < this.limit[z][0])){
  					this.value.now[z] = this.limit[z][0];
  				}
  			}
  			if (options.grid[z]) this.value.now[z] -= ((this.value.now[z] - (this.limit[z][0]||0)) % options.grid[z]);
  			if (options.style) this.element.setStyle(options.modifiers[z], this.value.now[z] + options.unit);
  			else this.element[options.modifiers[z]] = this.value.now[z];
  		}
  	},
  
  	cancel: function(event){
  		this.document.removeEvents({
  			mousemove: this.bound.check,
  			mouseup: this.bound.cancel
  		});
  		if (event){
  			this.document.removeEvent(this.selection, this.bound.eventStop);
  			this.fireEvent('cancel', this.element);
  		}
  	},
  
  	stop: function(event){
  		var events = {
  			mousemove: this.bound.drag,
  			mouseup: this.bound.stop
  		};
  		events[this.selection] = this.bound.eventStop;
  		this.document.removeEvents(events);
  		this.mouse.start = null;
  		if (event) this.fireEvent('complete', [this.element, event]);
  	}
  
  });
  
  Element.implement({
  
  	makeResizable: function(options){
  		var drag = new Drag(this, Object.merge({
  			modifiers: {
  				x: 'width',
  				y: 'height'
  			}
  		}, options));
  
  		this.store('resizer', drag);
  		return drag.addEvent('drag', function(){
  			this.fireEvent('resize', drag);
  		}.bind(this));
  	}
  
  });
  
  
  /*
  ---
  
  script: Drag.Move.js
  
  name: Drag.Move
  
  description: A Drag extension that provides support for the constraining of draggables to containers and droppables.
  
  license: MIT-style license
  
  authors:
    - Valerio Proietti
    - Tom Occhinno
    - Jan Kassens
    - Aaron Newton
    - Scott Kyle
  
  requires:
    - Core/Element.Dimensions
    - Drag
  
  provides: [Drag.Move]
  
  ...
  */
  
  Drag.Move = new Class({
  
  	Extends: Drag,
  
  	options: {/*
  		onEnter: function(thisElement, overed){},
  		onLeave: function(thisElement, overed){},
  		onDrop: function(thisElement, overed, event){},*/
  		droppables: [],
  		container: false,
  		precalculate: false,
  		includeMargins: true,
  		checkDroppables: true
  	},
  
  	initialize: function(element, options){
  		this.parent(element, options);
  		element = this.element;
  
  		this.droppables = $$(this.options.droppables);
  		this.setContainer(this.options.container);
  
  		if (this.options.style){
  			if (this.options.modifiers.x == 'left' && this.options.modifiers.y == 'top'){
  				var parent = element.getOffsetParent(),
  					styles = element.getStyles('left', 'top');
  				if (parent && (styles.left == 'auto' || styles.top == 'auto')){
  					element.setPosition(element.getPosition(parent));
  				}
  			}
  
  			if (element.getStyle('position') == 'static') element.setStyle('position', 'absolute');
  		}
  
  		this.addEvent('start', this.checkDroppables, true);
  		this.overed = null;
  	},
  	
  	setContainer: function(container) {
  		this.container = document.id(container);
  		if (this.container && typeOf(this.container) != 'element'){
  			this.container = document.id(this.container.getDocument().body);
  		}
  	},
  
  	start: function(event){
  		if (this.container) this.options.limit = this.calculateLimit();
  
  		if (this.options.precalculate){
  			this.positions = this.droppables.map(function(el){
  				return el.getCoordinates();
  			});
  		}
  
  		this.parent(event);
  	},
  
  	calculateLimit: function(){
  		var element = this.element,
  			container = this.container,
  
  			offsetParent = document.id(element.getOffsetParent()) || document.body,
  			containerCoordinates = container.getCoordinates(offsetParent),
  			elementMargin = {},
  			elementBorder = {},
  			containerMargin = {},
  			containerBorder = {},
  			offsetParentPadding = {},
  			offsetScroll = offsetParent.getScroll();
  
  		['top', 'right', 'bottom', 'left'].each(function(pad){
  			elementMargin[pad] = element.getStyle('margin-' + pad).toInt();
  			elementBorder[pad] = element.getStyle('border-' + pad).toInt();
  			containerMargin[pad] = container.getStyle('margin-' + pad).toInt();
  			containerBorder[pad] = container.getStyle('border-' + pad).toInt();
  			offsetParentPadding[pad] = offsetParent.getStyle('padding-' + pad).toInt();
  		}, this);
  
  		var width = element.offsetWidth + elementMargin.left + elementMargin.right,
  			height = element.offsetHeight + elementMargin.top + elementMargin.bottom,
  			left = 0 + offsetScroll.x,
  			top = 0 + offsetScroll.y,
  			right = containerCoordinates.right - containerBorder.right - width + offsetScroll.x,
  			bottom = containerCoordinates.bottom - containerBorder.bottom - height + offsetScroll.y;
  
  		if (this.options.includeMargins){
  			left += elementMargin.left;
  			top += elementMargin.top;
  		} else {
  			right += elementMargin.right;
  			bottom += elementMargin.bottom;
  		}
  
  		if (element.getStyle('position') == 'relative'){
  			var coords = element.getCoordinates(offsetParent);
  			coords.left -= element.getStyle('left').toInt();
  			coords.top -= element.getStyle('top').toInt();
  
  			left -= coords.left;
  			top -= coords.top;
  			if (container.getStyle('position') != 'relative'){
  				left += containerBorder.left;
  				top += containerBorder.top;
  			}
  			right += elementMargin.left - coords.left;
  			bottom += elementMargin.top - coords.top;
  
  			if (container != offsetParent){
  				left += containerMargin.left + offsetParentPadding.left;
  				if (!offsetParentPadding.left && left < 0) left = 0;
  				top += offsetParent == document.body ? 0 : containerMargin.top + offsetParentPadding.top;
  				if (!offsetParentPadding.top && top < 0) top = 0;
  			}
  		} else {
  			left -= elementMargin.left;
  			top -= elementMargin.top;
  			if (container != offsetParent){
  				left += containerCoordinates.left + containerBorder.left;
  				top += containerCoordinates.top + containerBorder.top;
  			}
  		}
  
  		return {
  			x: [left, right],
  			y: [top, bottom]
  		};
  	},
  
  	getDroppableCoordinates: function(element){
  		var position = element.getCoordinates();
  		if (element.getStyle('position') == 'fixed'){
  			var scroll = window.getScroll();
  			position.left += scroll.x;
  			position.right += scroll.x;
  			position.top += scroll.y;
  			position.bottom += scroll.y;
  		}
  		return position;
  	},
  
  	checkDroppables: function(){
  		var overed = this.droppables.filter(function(el, i){
  			el = this.positions ? this.positions[i] : this.getDroppableCoordinates(el);
  			var now = this.mouse.now;
  			return (now.x > el.left && now.x < el.right && now.y < el.bottom && now.y > el.top);
  		}, this).getLast();
  
  		if (this.overed != overed){
  			if (this.overed) this.fireEvent('leave', [this.element, this.overed]);
  			if (overed) this.fireEvent('enter', [this.element, overed]);
  			this.overed = overed;
  		}
  	},
  
  	drag: function(event){
  		this.parent(event);
  		if (this.options.checkDroppables && this.droppables.length) this.checkDroppables();
  	},
  
  	stop: function(event){
  		this.checkDroppables();
  		this.fireEvent('drop', [this.element, this.overed, event]);
  		this.overed = null;
  		return this.parent(event);
  	}
  
  });
  
  Element.implement({
  
  	makeDraggable: function(options){
  		var drag = new Drag.Move(this, options);
  		this.store('dragger', drag);
  		return drag;
  	}
  
  });
  
  
  /*
  ---
  
  script: Hash.Cookie.js
  
  name: Hash.Cookie
  
  description: Class for creating, reading, and deleting Cookies in JSON format.
  
  license: MIT-style license
  
  authors:
    - Valerio Proietti
    - Aaron Newton
  
  requires:
    - Core/Cookie
    - Core/JSON
    - MooTools.More
    - Hash
  
  provides: [Hash.Cookie]
  
  ...
  */
  
  Hash.Cookie = new Class({
  
  	Extends: Cookie,
  
  	options: {
  		autoSave: true
  	},
  
  	initialize: function(name, options){
  		this.parent(name, options);
  		this.load();
  	},
  
  	save: function(){
  		var value = JSON.encode(this.hash);
  		if (!value || value.length > 4096) return false; //cookie would be truncated!
  		if (value == '{}') this.dispose();
  		else this.write(value);
  		return true;
  	},
  
  	load: function(){
  		this.hash = new Hash(JSON.decode(this.read(), true));
  		return this;
  	}
  
  });
  
  Hash.each(Hash.prototype, function(method, name){
  	if (typeof method == 'function') Hash.Cookie.implement(name, function(){
  		var value = method.apply(this.hash, arguments);
  		if (this.options.autoSave) this.save();
  		return value;
  	});
  });
  
  
  /*
  ---
  
  name: Swiff
  
  description: Wrapper for embedding SWF movies. Supports External Interface Communication.
  
  license: MIT-style license.
  
  credits:
    - Flash detection & Internet Explorer + Flash Player 9 fix inspired by SWFObject.
  
  requires: [Core/Options, Core/Object, Core/Element]
  
  provides: Swiff
  
  ...
  */
  
  (function(){
  
  var Swiff = this.Swiff = new Class({
  
  	Implements: Options,
  
  	options: {
  		id: null,
  		height: 1,
  		width: 1,
  		container: null,
  		properties: {},
  		params: {
  			quality: 'high',
  			allowScriptAccess: 'always',
  			wMode: 'window',
  			swLiveConnect: true
  		},
  		callBacks: {},
  		vars: {}
  	},
  
  	toElement: function(){
  		return this.object;
  	},
  
  	initialize: function(path, options){
  		this.instance = 'Swiff_' + String.uniqueID();
  
  		this.setOptions(options);
  		options = this.options;
  		var id = this.id = options.id || this.instance;
  		var container = document.id(options.container);
  
  		Swiff.CallBacks[this.instance] = {};
  
  		var params = options.params, vars = options.vars, callBacks = options.callBacks;
  		var properties = Object.append({height: options.height, width: options.width}, options.properties);
  
  		var self = this;
  
  		for (var callBack in callBacks){
  			Swiff.CallBacks[this.instance][callBack] = (function(option){
  				return function(){
  					return option.apply(self.object, arguments);
  				};
  			})(callBacks[callBack]);
  			vars[callBack] = 'Swiff.CallBacks.' + this.instance + '.' + callBack;
  		}
  
  		params.flashVars = Object.toQueryString(vars);
  		if ('ActiveXObject' in window){
  			properties.classid = 'clsid:D27CDB6E-AE6D-11cf-96B8-444553540000';
  			params.movie = path;
  		} else {
  			properties.type = 'application/x-shockwave-flash';
  		}
  		properties.data = path;
  
  		var build = '<object id="' + id + '"';
  		for (var property in properties) build += ' ' + property + '="' + properties[property] + '"';
  		build += '>';
  		for (var param in params){
  			if (params[param]) build += '<param name="' + param + '" value="' + params[param] + '" />';
  		}
  		build += '</object>';
  		this.object = ((container) ? container.empty() : new Element('div')).set('html', build).firstChild;
  	},
  
  	replaces: function(element){
  		element = document.id(element, true);
  		element.parentNode.replaceChild(this.toElement(), element);
  		return this;
  	},
  
  	inject: function(element){
  		document.id(element, true).appendChild(this.toElement());
  		return this;
  	},
  
  	remote: function(){
  		return Swiff.remote.apply(Swiff, [this.toElement()].append(arguments));
  	}
  
  });
  
  Swiff.CallBacks = {};
  
  Swiff.remote = function(obj, fn){
  	var rs = obj.CallFunction('<invoke name="' + fn + '" returntype="javascript">' + __flash__argumentsToXML(arguments, 2) + '</invoke>');
  	return eval(rs);
  };
  
  })();
  
  