
//This library: http://dev.clientcide.com/depender/build?download=true&version=Clientcide+3.0.8&excludeLibs=Core&require=More-Behaviors%2FDelegator.AddRemoveClass&require=More-Behaviors%2FDelegator.Ajax&require=More-Behaviors%2FDelegator.CheckAllOrNone&require=More-Behaviors%2FDelegator.FxReveal&require=More-Behaviors%2FDelegator.SubmitLink&require=More-Behaviors%2FBehavior.Resizable&require=More-Behaviors%2FBehavior.Sortable&require=More-Behaviors%2FBehavior.FormRequest&require=More-Behaviors%2FBehavior.FormValidator&require=More-Behaviors%2FBehavior.OverText&require=More-Behaviors%2FBehavior.Accordion&require=More-Behaviors%2FBehavior.HtmlTable&require=Clientcide%2FAutocompleter.Clientcide&require=Clientcide%2FAutocompleter.JSONP&require=Clientcide%2FAutocompleter.Local&require=Clientcide%2FAutocompleter.Observer&require=Clientcide%2FAutocompleter.Remote&require=Clientcide%2FAutocompleter&require=Clientcide%2FBehavior.Autocompleter&require=Clientcide%2FBehavior.Collapsible&require=Clientcide%2FBehavior.StickyWin&require=Clientcide%2FBehavior.Tabs&require=Clientcide%2FBehavior.Tips&require=Clientcide%2FClientcide&require=Clientcide%2Fdbug&require=Clientcide%2FForm.Validator.Tips&require=Clientcide%2FFx.Marquee&require=Clientcide%2FCollapsible&require=Clientcide%2FHoverGroup&require=Clientcide%2FMooScroller&require=Clientcide%2FMultipleOpenAccordion&require=Clientcide%2FTabSwapper&require=Clientcide%2FStickyWin.Ajax&require=Clientcide%2FStickyWin.Alert&require=Clientcide%2FStickyWin.Confirm&require=Clientcide%2FStickyWin.Drag&require=Clientcide%2FStickyWin.Fx&require=Clientcide%2FStickyWin.Modal&require=Clientcide%2FStickyWin.PointyTip&require=Clientcide%2FStickyWin.Prompt&require=Clientcide%2FStickyWin.UI.Pointy&require=Clientcide%2FStickyWin.UI&require=Clientcide%2FStickyWin&require=Clientcide%2FStyleWriter&require=Clientcide%2FTips.Pointy&excludeLibs=More
//Contents: Behavior:Source/Event.Mock.js, Behavior:Source/Element.Data.js, Behavior:Source/BehaviorAPI.js, Behavior:Source/Behavior.js, Behavior:Source/Delegator.js, More-Behaviors:Source/Delegators/Delegator.CheckAllOrNone.js, Clientcide:Source/Core/Clientcide.js, Clientcide:Source/Layout/MooScroller.js, Clientcide:Source/Core/dbug.js, Clientcide:Source/UI/StyleWriter.js, Clientcide:Source/UI/StickyWin.js, Clientcide:Source/UI/StickyWin.UI.js, Clientcide:Source/UI/StickyWin.UI.Pointy.js, Clientcide:Source/UI/StickyWin.PointyTip.js, Clientcide:Source/UI/Tips.Pointy.js, Clientcide:Source/Behaviors/Behavior.Tips.js, Clientcide:Source/Fx/Fx.Marquee.js, Clientcide:Source/UI/StickyWin.Modal.js, Clientcide:Source/UI/StickyWin.Fx.js, Clientcide:Source/UI/StickyWin.Drag.js, Clientcide:Source/Behaviors/Behavior.StickyWin.js, More-Behaviors:Source/Delegators/Delegator.AddRemoveClass.js, Clientcide:Source/UI/StickyWin.Alert.js, Clientcide:Source/UI/StickyWin.Confirm.js, More-Behaviors:Source/Delegators/Delegator.Ajax.js, Clientcide:Source/Layout/HoverGroup.js, More-Behaviors:Source/Fx/Behavior.FxAccordion.js, Clientcide:Source/Layout/MultipleOpenAccordion.js, Clientcide:Source/Forms/Form.Validator.Tips.js, Clientcide:Source/3rdParty/Autocompleter.Observer.js, Clientcide:Source/3rdParty/Autocompleter.js, Clientcide:Source/3rdParty/Autocompleter.Local.js, More-Behaviors:Source/Delegators/Delegator.FxReveal.js, Clientcide:Source/3rdParty/Autocompleter.Clientcide.js, More-Behaviors:Source/Forms/Behavior.FormValidator.js, More-Behaviors:Source/Drag/Behavior.Resizable.js, More-Behaviors:Source/Forms/Behavior.FormRequest.js, Clientcide:Source/Layout/TabSwapper.js, Clientcide:Source/Behaviors/Behavior.Tabs.js, Clientcide:Source/Layout/Collapsible.js, Clientcide:Source/Behaviors/Behavior.Collapsible.js, Clientcide:Source/3rdParty/Autocompleter.Remote.js, More-Behaviors:Source/Delegators/Delegator.SubmitLink.js, Clientcide:Source/UI/StickyWin.Ajax.js, More-Behaviors:Source/Interface/Behavior.HtmlTable.js, More-Behaviors:Source/Drag/Behavior.Sortable.js, Clientcide:Source/UI/StickyWin.Prompt.js, Clientcide:Source/3rdParty/Autocompleter.JSONP.js, More-Behaviors:Source/Forms/Behavior.OverText.js, Clientcide:Source/Behaviors/Behavior.Autocompleter.js

// Begin: Source/Event.Mock.js
/*
---
name: Event.Mock

description: Supplies a Mock Event object for use on fireEvent

license: MIT-style

authors:
- Arieh Glazer

requires: Core/Event

provides: [Event.Mock]

...
*/

(function($,window,undef){

/**
 * creates a Mock event to be used with fire event
 * @param Element target an element to set as the target of the event - not required
 *  @param string type the type of the event to be fired. Will not be used by IE - not required.
 *
 */
Event.Mock = function(target,type){
	type = type || 'click';

	var e = {
		type: type,
		target: target
	};

	if (document.createEvent){
		e = document.createEvent('HTMLEvents');
		e.initEvent(
			type //event type
			, false //bubbles - set to false because the event should like normal fireEvent
			, true //cancelable
		);
	}

	e = new Event(e);

	e.target = target;

	return e;
};

})(document.id,window);

// Begin: Source/Element.Data.js
/*
---
name: Element.Data
description: Stores data in HTML5 data properties
provides: [Element.Data]
requires: [Core/Element, Core/JSON]
script: Element.Data.js

...
*/
(function(){

	JSON.isSecure = function(string){
		//this verifies that the string is parsable JSON and not malicious (borrowed from JSON.js in MooTools, which in turn borrowed it from Crockford)
		//this version is a little more permissive, as it allows single quoted attributes because forcing the use of double quotes
		//is a pain when this stuff is used as HTML properties
		return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(string.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, '').replace(/'[^'\\\n\r]*'/g, ''));
	};

	Element.implement({
		/*
			sets an HTML5 data property.
			arguments:
				name - (string) the data name to store; will be automatically prefixed with 'data-'.
				value - (string, number) the value to store.
		*/
		setData: function(name, value){
			return this.set('data-' + name.hyphenate(), value);
		},

		getData: function(name, defaultValue){
			var value = this.get('data-' + name.hyphenate());
			if (value != undefined){
				return value;
			} else if (defaultValue != undefined){
				this.setData(name, defaultValue);
				return defaultValue;
			}
		},

		/* 
			arguments:
				name - (string) the data name to store; will be automatically prefixed with 'data-'
				value - (string, array, or object) if an object or array the object will be JSON encoded; otherwise stored as provided.
		*/
		setJSONData: function(name, value){
			return this.setData(name, JSON.encode(value));
		},

		/*
			retrieves a property from HTML5 data property you specify
		
			arguments:
				name - (retrieve) the data name to store; will be automatically prefixed with 'data-'
				strict - (boolean) if true, will set the JSON.decode's secure flag to true; otherwise the value is still tested but allows single quoted attributes.
				defaultValue - (string, array, or object) the value to set if no value is found (see storeData above)
		*/
		getJSONData: function(name, strict, defaultValue){
			var value = this.get('data-' + name);
			if (value != undefined){
				if (value && JSON.isSecure(value)) {
					return JSON.decode(value, strict);
				} else {
					return value;
				}
			} else if (defaultValue != undefined){
				this.setJSONData(name, defaultValue);
				return defaultValue;
			}
		}

	});

})();

// Begin: Source/BehaviorAPI.js
/*
---
name: BehaviorAPI
description: HTML getters for Behavior's API model.
requires: [Core/Class, /Element.Data]
provides: [BehaviorAPI]
...
*/


(function(){
	//see Docs/BehaviorAPI.md for documentation of public methods.

	var reggy = /[^a-z0-9\-]/gi;

	window.BehaviorAPI = new Class({
		element: null,
		prefix: '',
		defaults: {},

		initialize: function(element, prefix){
			this.element = element;
			this.prefix = prefix.toLowerCase().replace('.', '-', 'g').replace(reggy, '');
		},

		/******************
		 * PUBLIC METHODS
		 ******************/

		get: function(/* name[, name, name, etc] */){
			if (arguments.length > 1) return this._getObj(Array.from(arguments));
			return this._getValue(arguments[0]);
		},

		getAs: function(/*returnType, name, defaultValue OR {name: returnType, name: returnType, etc}*/){
			if (typeOf(arguments[0]) == 'object') return this._getValuesAs.apply(this, arguments);
			return this._getValueAs.apply(this, arguments);
		},

		require: function(/* name[, name, name, etc] */){
			for (var i = 0; i < arguments.length; i++){
				if (this._getValue(arguments[i]) == undefined) throw new Error('Could not retrieve ' + this.prefix + '-' + arguments[i] + ' option from element.');
			}
			return this;
		},

		requireAs: function(returnType, name /* OR {name: returnType, name: returnType, etc}*/){
			var val;
			if (typeOf(arguments[0]) == 'object'){
				for (var objName in arguments[0]){
					val = this._getValueAs(arguments[0][objName], objName);
					if (val === undefined || val === null) throw new Error("Could not retrieve " + this.prefix + '-' + objName + " option from element.");
				}
			} else {
				val = this._getValueAs(returnType, name);
				if (val === undefined || val === null) throw new Error("Could not retrieve " + this.prefix + '-' + name + " option from element.");
			}
			return this;
		},

		setDefault: function(name, value /* OR {name: value, name: value, etc }*/){
			if (typeOf(arguments[0]) == 'object'){
				for (var objName in arguments[0]){
					this.setDefault(objName, arguments[0][objName]);
				}
				return;
			}
			name = name.camelCase();
			this.defaults[name] = value;
			if (this._getValue(name) == null){
				var options = this._getOptions();
				options[name] = value;
			}
			return this;
		},

		refreshAPI: function(){
			delete this.options;
			this.setDefault(this.defaults);
			return;
		},

		/******************
		 * PRIVATE METHODS
		 ******************/

		//given an array of names, returns an object of key/value pairs for each name
		_getObj: function(names){
			var obj = {};
			names.each(function(name){
				var value = this._getValue(name);
				if (value !== undefined) obj[name] = value;
			}, this);
			return obj;
		},
		//gets the data-behaviorname-options object and parses it as JSON
		_getOptions: function(){
			if (!this.options){
				var options = this.element.getData(this.prefix + '-options', '{}');
				if (options && options.substring(0,1) != '{') options = '{' + options + '}';
				var isSecure = JSON.isSecure(options);
				if (!isSecure) throw new Error('warning, options value for element is not parsable, check your JSON format for quotes, etc.');
				this.options = isSecure ? JSON.decode(options) : {};
				for (option in this.options) {
					this.options[option.camelCase()] = this.options[option];
				}
			}
			return this.options;
		},
		//given a name (string) returns the value for it
		_getValue: function(name){
			name = name.camelCase();
			var options = this._getOptions();
			if (!options.hasOwnProperty(name)){
				var inline = this.element.getData(this.prefix + '-' + name.hyphenate());
				if (inline) options[name] = inline;
			}
			return options[name];
		},
		//given a Type and a name (string) returns the value for it coerced to that type if possible
		//else returns the defaultValue or null
		_getValueAs: function(returnType, name, defaultValue){
			var value = this._getValue(name);
			if (value == null || value == undefined) return defaultValue;
			var coerced = this._coerceFromString(returnType, value);
			if (coerced == null) throw new Error("Could not retrieve value '" + name + "' as the specified type. Its value is: " + value);
			return coerced;
		},
		//given an object of name/Type pairs, returns those as an object of name/value (as specified Type) pairs
		_getValuesAs: function(obj){
			var returnObj = {};
			for (var name in obj){
				returnObj[name] = this._getValueAs(obj[name], name);
			}
			return returnObj;
		},
		//attempts to run a value through the JSON parser. If the result is not of that type returns null.
		_coerceFromString: function(toType, value){
			if (typeOf(value) == 'string' && toType != String){
				if (JSON.isSecure(value)) value = JSON.decode(value);
			}
			if (instanceOf(value, toType)) return value;
			return null;
		}
	});

})();

// Begin: Source/Behavior.js
/*
---
name: Behavior
description: Auto-instantiates widgets/classes based on parsed, declarative HTML.
requires: [Core/Class.Extras, Core/Element.Event, Core/Selectors, More/Table, /Element.Data, /BehaviorAPI]
provides: [Behavior]
...
*/

(function(){

	var getLog = function(method){
		return function(){
			if (window.console && console[method]){
				if(console[method].apply) console[method].apply(console, arguments);
				else console[method](Array.from(arguments).join(' '));
			}
		};
	};

	var PassMethods = new Class({
		//pass a method pointer through to a filter
		//by default the methods for add/remove events are passed to the filter
		//pointed to this instance of behavior. you could use this to pass along
		//other methods to your filters. For example, a method to close a popup
		//for filters presented inside popups.
		passMethod: function(method, fn){
			if (this.API.prototype[method]) throw new Error('Cannot overwrite API method ' + method + ' as it already exists');
			this.API.implement(method, fn);
			return this;
		},

		passMethods: function(methods){
			for (method in methods) this.passMethod(method, methods[method]);
			return this;
		}

	});

	var spaceOrCommaRegex = /\s*,\s*|\s+/g;

	BehaviorAPI.implement({
		deprecate: function(deprecated, asJSON){
			var set,
			    values = {};
			Object.each(deprecated, function(prop, key){
				var value = this.element[ asJSON ? 'getJSONData' : 'getData'](prop);
				if (value !== undefined){
					set = true;
					values[key] = value;
				}
			}, this);
			this.setDefault(values);
			return this;
		}
	});

	this.Behavior = new Class({

		Implements: [Options, Events, PassMethods],

		options: {
			//by default, errors thrown by filters are caught; the onError event is fired.
			//set this to *true* to NOT catch these errors to allow them to be handled by the browser.
			// breakOnErrors: false,
			// container: document.body,

			//default error behavior when a filter cannot be applied
			onError: getLog('error'),
			onWarn: getLog('warn'),
			enableDeprecation: true,
			selector: '[data-behavior]'
		},

		initialize: function(options){
			this.setOptions(options);
			this.API = new Class({ Extends: BehaviorAPI });
			this.passMethods({
				addEvent: this.addEvent.bind(this),
				removeEvent: this.removeEvent.bind(this),
				addEvents: this.addEvents.bind(this),
				removeEvents: this.removeEvents.bind(this),
				fireEvent: this.fireEvent.bind(this),
				applyFilters: this.apply.bind(this),
				applyFilter: this.applyFilter.bind(this),
				getContentElement: this.getContentElement.bind(this),
				cleanup: this.cleanup.bind(this),
				getContainerSize: function(){
					return this.getContentElement().measure(function(){
						return this.getSize();
					});
				}.bind(this),
				error: function(){ this.fireEvent('error', arguments); }.bind(this),
				fail: function(){
					var msg = Array.join(arguments, ' ');
					throw new Error(msg);
				},
				warn: function(){
					this.fireEvent('warn', arguments);
				}.bind(this)
			});
		},

		getContentElement: function(){
			return this.options.container || document.body;
		},

		//Applies all the behavior filters for an element.
		//container - (element) an element to apply the filters registered with this Behavior instance to.
		//force - (boolean; optional) passed through to applyFilter (see it for docs)
		apply: function(container, force){
		  this._getElements(container).each(function(element){
				var plugins = [];
				element.getBehaviors().each(function(name){
					var filter = this.getFilter(name);
					if (!filter){
						this.fireEvent('error', ['There is no filter registered with this name: ', name, element]);
					} else {
						var config = filter.config;
						if (config.delay !== undefined){
							this.applyFilter.delay(filter.config.delay, this, [element, filter, force]);
						} else if(config.delayUntil){
							this._delayFilterUntil(element, filter, force);
						} else if(config.initializer){
							this._customInit(element, filter, force);
						} else {
							plugins.append(this.applyFilter(element, filter, force, true));
						}
					}
				}, this);
				plugins.each(function(plugin){ plugin(); });
			}, this);
			return this;
		},

		_getElements: function(container){
			if (typeOf(this.options.selector) == 'function') return this.options.selector(container);
			else return document.id(container).getElements(this.options.selector);
		},

		//delays a filter until the event specified in filter.config.delayUntil is fired on the element
		_delayFilterUntil: function(element, filter, force){
			var events = filter.config.delayUntil.split(','),
			    attached = {},
			    inited = false;
			var clear = function(){
				events.each(function(event){
					element.removeEvent(event, attached[event]);
				});
				clear = function(){};
			};
			events.each(function(event){
				var init = function(e){
					clear();
					if (inited) return;
					inited = true;
					var setup = filter.setup;
					filter.setup = function(element, api, _pluginResult){
						api.event = e;
						return setup.apply(filter, [element, api, _pluginResult]);
					};
					this.applyFilter(element, filter, force);
					filter.setup = setup;
				}.bind(this);
				element.addEvent(event, init);
				attached[event] = init;
			}, this);
		},

		//runs custom initiliazer defined in filter.config.initializer
		_customInit: function(element, filter, force){
			var api = new this.API(element, filter.name);
			api.runSetup = this.applyFilter.pass([element, filter, force], this);
			filter.config.initializer(element, api);
		},

		//Applies a specific behavior to a specific element.
		//element - the element to which to apply the behavior
		//filter - (object) a specific behavior filter, typically one registered with this instance or registered globally.
		//force - (boolean; optional) apply the behavior to each element it matches, even if it was previously applied. Defaults to *false*.
		//_returnPlugins - (boolean; optional; internal) if true, plugins are not rendered but instead returned as an array of functions
		//_pluginTargetResult - (obj; optional internal) if this filter is a plugin for another, this is whatever that target filter returned
		//                      (an instance of a class for example)
		applyFilter: function(element, filter, force, _returnPlugins, _pluginTargetResult){
			var pluginsToReturn = [];
			if (this.options.breakOnErrors){
				pluginsToReturn = this._applyFilter.apply(this, arguments);
			} else {
				try {
					pluginsToReturn = this._applyFilter.apply(this, arguments);
				} catch (e){
					this.fireEvent('error', ['Could not apply the behavior ' + filter.name, e]);
				}
			}
			return _returnPlugins ? pluginsToReturn : this;
		},

		//see argument list above for applyFilter
		_applyFilter: function(element, filter, force, _returnPlugins, _pluginTargetResult){
			var pluginsToReturn = [];
			element = document.id(element);
			//get the filters already applied to this element
			var applied = getApplied(element);
			//if this filter is not yet applied to the element, or we are forcing the filter
			if (!applied[filter.name] || force){
				//if it was previously applied, garbage collect it
				if (applied[filter.name]) applied[filter.name].cleanup(element);
				var api = new this.API(element, filter.name);

				//deprecated
				api.markForCleanup = filter.markForCleanup.bind(filter);
				api.onCleanup = function(fn){
					filter.markForCleanup(element, fn);
				};

				if (filter.config.deprecated && this.options.enableDeprecation) api.deprecate(filter.config.deprecated);
				if (filter.config.deprecateAsJSON && this.options.enableDeprecation) api.deprecate(filter.config.deprecatedAsJSON, true);

				//deal with requirements and defaults
				if (filter.config.requireAs){
					api.requireAs(filter.config.requireAs);
				} else if (filter.config.require){
					api.require.apply(api, Array.from(filter.config.require));
				}

				if (filter.config.defaults) api.setDefault(filter.config.defaults);

				//apply the filter
				var result = filter.setup(element, api, _pluginTargetResult);
				if (filter.config.returns && !instanceOf(result, filter.config.returns)){
					throw new Error("Filter " + filter.name + " did not return a valid instance.");
				}
				element.store('Behavior Filter result:' + filter.name, result);
				//and mark it as having been previously applied
				applied[filter.name] = filter;
				//apply all the plugins for this filter
				var plugins = this.getPlugins(filter.name);
				if (plugins){
					for (var name in plugins){
						if (_returnPlugins){
							pluginsToReturn.push(this.applyFilter.pass([element, plugins[name], force, null, result], this));
						} else {
							this.applyFilter(element, plugins[name], force, null, result);
						}
					}
				}
			}
			return pluginsToReturn;
		},

		//given a name, returns a registered behavior
		getFilter: function(name){
			return this._registered[name] || Behavior.getFilter(name);
		},

		getPlugins: function(name){
			return this._plugins[name] || Behavior._plugins[name];
		},

		//Garbage collects all applied filters for an element and its children.
		//element - (*element*) container to cleanup
		//ignoreChildren - (*boolean*; optional) if *true* only the element will be cleaned, otherwise the element and all the
		//	  children with filters applied will be cleaned. Defaults to *false*.
		cleanup: function(element, ignoreChildren){
			element = document.id(element);
			var applied = getApplied(element);
			for (var filter in applied){
				applied[filter].cleanup(element);
				element.eliminate('Behavior Filter result:' + filter);
				delete applied[filter];
			}
			if (!ignoreChildren) this._getElements(element).each(this.cleanup, this);
			return this;
		}

	});

	//Export these for use elsewhere (notabily: Delegator).
	Behavior.getLog = getLog;
	Behavior.PassMethods = PassMethods;


	//Returns the applied behaviors for an element.
	var getApplied = function(el){
		return el.retrieve('_appliedBehaviors', {});
	};

	//Registers a behavior filter.
	//name - the name of the filter
	//fn - a function that applies the filter to the given element
	//overwrite - (boolean) if true, will overwrite existing filter if one exists; defaults to false.
	var addFilter = function(name, fn, overwrite){
		if (!this._registered[name] || overwrite) this._registered[name] = new Behavior.Filter(name, fn);
		else throw new Error('Could not add the Behavior filter "' + name  +'" as a previous trigger by that same name exists.');
	};

	var addFilters = function(obj, overwrite){
		for (var name in obj){
			addFilter.apply(this, [name, obj[name], overwrite]);
		}
	};

	//Registers a behavior plugin
	//filterName - (*string*) the filter (or plugin) this is a plugin for
	//name - (*string*) the name of this plugin
	//setup - a function that applies the filter to the given element
	var addPlugin = function(filterName, name, setup, overwrite){
		if (!this._plugins[filterName]) this._plugins[filterName] = {};
		if (!this._plugins[filterName][name] || overwrite) this._plugins[filterName][name] = new Behavior.Filter(name, setup);
		else throw new Error('Could not add the Behavior filter plugin "' + name  +'" as a previous trigger by that same name exists.');
	};

	var addPlugins = function(obj, overwrite){
		for (var name in obj){
			addPlugin.apply(this, [obj[name].fitlerName, obj[name].name, obj[name].setup], overwrite);
		}
	};

	var setFilterDefaults = function(name, defaults){
		var filter = this.getFilter(name);
		if (!filter.config.defaults) filter.config.defaults = {};
		Object.append(filter.config.defaults, defaults);
	};

	//Add methods to the Behavior namespace for global registration.
	Object.append(Behavior, {
		_registered: {},
		_plugins: {},
		addGlobalFilter: addFilter,
		addGlobalFilters: addFilters,
		addGlobalPlugin: addPlugin,
		addGlobalPlugins: addPlugins,
		setFilterDefaults: setFilterDefaults,
		getFilter: function(name){
			return this._registered[name];
		}
	});
	//Add methods to the Behavior class for instance registration.
	Behavior.implement({
		_registered: {},
		_plugins: {},
		addFilter: addFilter,
		addFilters: addFilters,
		addPlugin: addPlugin,
		addPlugins: addPlugins,
		setFilterDefaults: setFilterDefaults
	});

	//This class is an actual filter that, given an element, alters it with specific behaviors.
	Behavior.Filter = new Class({

		config: {
			/**
				returns: Foo,
				require: ['req1', 'req2'],
				//or
				requireAs: {
					req1: Boolean,
					req2: Number,
					req3: String
				},
				defaults: {
					opt1: false,
					opt2: 2
				},
				//simple example:
				setup: function(element, API){
					var kids = element.getElements(API.get('selector'));
					//some validation still has to occur here
					if (!kids.length) API.fail('there were no child elements found that match ', API.get('selector'));
					if (kids.length < 2) API.warn("there weren't more than 2 kids that match", API.get('selector'));
					var fooInstance = new Foo(kids, API.get('opt1', 'opt2'));
					API.onCleanup(function(){
						fooInstance.destroy();
					});
					return fooInstance;
				},
				delayUntil: 'mouseover',
				//OR
				delay: 100,
				//OR
				initializer: function(element, API){
					element.addEvent('mouseover', API.runSetup); //same as specifying event
					//or
					API.runSetup.delay(100); //same as specifying delay
					//or something completely esoteric
					var timer = (function(){
						if (element.hasClass('foo')){
							clearInterval(timer);
							API.runSetup();
						}
					}).periodical(100);
					//or
					API.addEvent('someBehaviorEvent', API.runSetup);
				});
				*/
		},

		//Pass in an object with the following properties:
		//name - the name of this filter
		//setup - a function that applies the filter to the given element
		initialize: function(name, setup){
			this.name = name;
			if (typeOf(setup) == "function"){
				this.setup = setup;
			} else {
				Object.append(this.config, setup);
				this.setup = this.config.setup;
			}
			this._cleanupFunctions = new Table();
		},

		//Stores a garbage collection pointer for a specific element.
		//Example: if your filter enhances all the inputs in the container
		//you might have a function that removes that enhancement for garbage collection.
		//You would mark each input matched with its own cleanup function.
		//NOTE: this MUST be the element passed to the filter - the element with this filters
		//      name in its data-behavior property. I.E.:
		//<form data-behavior="FormValidator">
		//  <input type="text" name="email"/>
		//</form>
		//If this filter is FormValidator, you can mark the form for cleanup, but not, for example
		//the input. Only elements that match this filter can be marked.
		markForCleanup: function(element, fn){
			var functions = this._cleanupFunctions.get(element);
			if (!functions) functions = [];
			functions.include(fn);
			this._cleanupFunctions.set(element, functions);
			return this;
		},

		//Garbage collect a specific element.
		//NOTE: this should be an element that has a data-behavior property that matches this filter.
		cleanup: function(element){
			var marks = this._cleanupFunctions.get(element);
			if (marks){
				marks.each(function(fn){ fn(); });
				this._cleanupFunctions.erase(element);
			}
			return this;
		}

	});

	Behavior.elementDataProperty = 'behavior';

	Element.implement({

		addBehavior: function(name){
			return this.setData(Behavior.elementDataProperty, this.getBehaviors().include(name).join(' '));
		},

		removeBehavior: function(name){
			return this.setData(Behavior.elementDataProperty, this.getBehaviors().erase(name).join(' '));
		},

		getBehaviors: function(){
			var filters = this.getData(Behavior.elementDataProperty);
			if (!filters) return [];
			return filters.trim().split(spaceOrCommaRegex);
		},

		hasBehavior: function(name){
			return this.getBehaviors().contains(name);
		},

		getBehaviorResult: function(name){
			return this.retrieve('Behavior Filter result:' + name);
		}

	});


})();


// Begin: Source/Delegator.js
/*
---
name: Delegator
description: Allows for the registration of delegated events on a container.
requires: [Core/Element.Delegation, Core/Options, Core/Events, /Event.Mock, /Behavior]
provides: [Delegator]
...
*/
(function(){

	var spaceOrCommaRegex = /\s*,\s*|\s+/g;

	window.Delegator = new Class({

		Implements: [Options, Events, Behavior.PassMethods],

		options: {
			// breakOnErrors: false,
			getBehavior: function(){},
			onError: Behavior.getLog('error'),
			onWarn: Behavior.getLog('warn')
		},

		initialize: function(options){
			this.setOptions(options);
			this._bound = {
				eventHandler: this._eventHandler.bind(this)
			};
			Delegator._instances.push(this);
			Object.each(Delegator._triggers, function(trigger){
				this._eventTypes.combine(trigger.types);
			}, this);
			this.API = new Class({ Extends: BehaviorAPI });
			this.passMethods({
				addEvent: this.addEvent.bind(this),
				removeEvent: this.removeEvent.bind(this),
				addEvents: this.addEvents.bind(this),
				removeEvents: this.removeEvents.bind(this),
				fireEvent: this.fireEvent.bind(this),
				attach: this.attach.bind(this),
				trigger: this.trigger.bind(this),
				error: function(){ this.fireEvent('error', arguments); }.bind(this),
				fail: function(){
					var msg = Array.join(arguments, ' ');
					throw new Error(msg);
				},
				warn: function(){
					this.fireEvent('warn', arguments);
				}.bind(this),
				getBehavior: function(){
					return this.options.getBehavior();
				}.bind(this)
			});

			this.bindToBehavior(this.options.getBehavior());
		},

		bindToBehavior: function(behavior){
			if (!behavior) return;
			this.unbindFromBehavior();
			this._behavior = behavior;
			if (!this._behaviorEvents){
				var self = this;
				this._behaviorEvents = {
					destroyDom: function(elements){
						Array.from(elements).each(function(element){
							self._behavior.cleanup(element);
							self._behavior.fireEvent('destroyDom', element);
						});
					},
					ammendDom: function(container){
						self._behavior.apply(container);
						self._behavior.fireEvent('ammendDom', container);
					}
				};
			}
			this.addEvents(this._behaviorEvents);
		},

		getBehavior: function(){
			return this._behavior;
		},

		unbindFromBehavior: function(){
			if (this._behaviorEvents && this._behavior){
				this._behavior.removeEvents(this._behaviorEvents);
				delete this._behavior;
			}
		},

		attach: function(target, _method){
			_method = _method || 'addEvent';
			target = document.id(target);
			if ((_method == 'addEvent' && this._attachedTo.contains(target)) ||
			    (_method == 'removeEvent') && !this._attachedTo.contains(target)) return this;
			this._eventTypes.each(function(event){
				target[_method](event + ':relay([data-trigger])', this._bound.eventHandler);
			}, this);
			this._attachedTo.push(target);
			return this;
		},

		detach: function(target){
			if (target){
				this.attach(target, 'removeEvent');
				return this;
			} else {
				this._attachedTo.each(this.detach, this);
			}
		},

		trigger: function(name, element, event){
			if (!event || typeOf(event) == "string") event = new Event.Mock(element, event);
			var trigger = this._getTrigger(name);
			if (trigger && trigger.types.contains(event.type)) {
				if (this.options.breakOnErrors){
					this._trigger(trigger, element, event);
				} else {
					try {
						this._trigger(trigger, element, event);
					} catch(e) {
						this.fireEvent('error', ['Could not apply the trigger', name, e]);
					}
				}
			} else {
				this.fireEvent('error', 'Could not find a trigger with the name ' + name + ' for event: ' + event.type);
			}
			return this;
		},

		/******************
		 * PRIVATE METHODS
		 ******************/

		_getTrigger: function(name){
			return this._triggers[name] || Delegator._triggers[name];
		},

		_trigger: function(trigger, element, event){
			var api = new this.API(element, trigger.name);
			if (trigger.requireAs){
				api.requireAs(trigger.requireAs);
			} else if (trigger.require){
				api.require.apply(api, Array.from(trigger.require));
			} if (trigger.defaults){
				api.setDefault(trigger.defaults);
			}
			trigger.handler.apply(this, [event, element, api]);
			this.fireEvent('trigger', [trigger, element, event]);
		},

		_eventHandler: function(event, target){
			var triggers = target.getTriggers();
			if (triggers.contains('Stop')) event.stop();
			if (triggers.contains('PreventDefault')) event.preventDefault();
			triggers.each(function(trigger){
				if (trigger != "Stop" && trigger != "PreventDefault") this.trigger(trigger, target, event);
			}, this);
		},

		_onRegister: function(eventTypes){
			eventTypes.each(function(eventType){
				if (!this._eventTypes.contains(eventType)){
					this._attachedTo.each(function(element){
						element.addEvent(eventType + ':relay([data-trigger])', this._bound.eventHandler);
					}, this);
				}
				this._eventTypes.include(eventType);
			}, this);
		},

		_attachedTo: [],
		_eventTypes: [],
		_triggers: {}

	});

	Delegator._triggers = {};
	Delegator._instances = [];
	Delegator._onRegister = function(eventType){
		this._instances.each(function(instance){
			instance._onRegister(eventType);
		});
	};

	Delegator.register = function(eventTypes, name, handler, overwrite /** or eventType, obj, overwrite */){
		eventTypes = Array.from(eventTypes);
		if (typeOf(name) == "object"){
			var obj = name;
			for (name in obj){
				this.register.apply(this, [eventTypes, name, obj[name], handler]);
			}
			return this;
		}
		if (!this._triggers[name] || overwrite){
			if (typeOf(handler) == "function"){
				handler = {
					handler: handler
				};
			}
			handler.types = eventTypes;
			handler.name = name;
			this._triggers[name] = handler;
			this._onRegister(eventTypes);
		} else {
			throw new Error('Could add the trigger "' + name  +'" as a previous trigger by that same name exists.');
		}
		return this;
	};

	Delegator.implement('register', Delegator.register);

	Element.implement({

		addTrigger: function(name){
			return this.setData('trigger', this.getTriggers().include(name).join(' '));
		},

		removeTrigger: function(name){
			return this.setData('trigger', this.getTriggers().erase(name).join(' '));
		},

		getTriggers: function(){
			var triggers = this.getData('trigger');
			if (!triggers) return [];
			return triggers.trim().split(spaceOrCommaRegex);
		},

		hasTrigger: function(name){
			return this.getTriggers().contains(name);
		}

	});

})();

// Begin: Source/Delegators/Delegator.CheckAllOrNone.js
/*
---
description: Checks all or none of a group of checkboxes.
provides: [Delegator.CheckAllOrNone]
requires: [Behavior/Delegator]
script: Delegator.CheckAllOrNone.js
name: Delegator.CheckAllOrNone

...
*/

Delegator.register('click', {

	'checkAll': {
		require: ['targets'],
		handler: function(event, link, api){
			var targets = link.getElements(api.get('targets'));
			if (targets.length) targets.set('checked', true);
			else api.warn('There were no inputs found to check.');
		}
	},

	'checkNone': {
		require: ['targets'],
		handler: function(event, link, api){
			var targets = link.getElements(api.get('targets'));
			if (targets.length) targets.set('checked', false);
			else api.warn('There were no inputs found to uncheck.');
		}
	}

});

// Begin: Source/Core/Clientcide.js
/*
---

name: Clientcide

description: The Clientcide namespace.

license: MIT-style license.

provides: Clientcide

...
*/
var Clientcide = {
	version: '3.0.8',
	assetLocation: "http://github.com/anutron/clientcide/raw/master/Assets",
	setAssetLocation: function(baseHref) {
		Clientcide.assetLocation = baseHref;
		if (Clientcide.preloaded) Clientcide.preLoadCss();
	},
	preLoadCss: function(){
		if (window.StickyWin && StickyWin.ui) StickyWin.ui();
		if (window.StickyWin && StickyWin.pointy) StickyWin.pointy();
		Clientcide.preloaded = true;
		return true;
	},
	preloaded: false
};
(function(){
	if (!window.addEvent) return;
	var preload = function(){
		if (window.dbug) dbug.log('preloading clientcide css');
		if (!Clientcide.preloaded) Clientcide.preLoadCss();
	};
	window.addEvent('domready', preload);
	window.addEvent('load', preload);
})();
setCNETAssetBaseHref = Clientcide.setAssetLocation;


// Begin: Source/Layout/MooScroller.js
/*
---
name: MooScroller

description: Recreates the standard scrollbar behavior for elements with overflow but using DOM elements so that the scroll bar elements are completely styleable by css.

license: MIT-Style License

requires: [Core/Class.Extras, Core/Element.Dimensions, Core/Element.Event, Core/Element.Style, Core/Slick.Finder, Clientcide]

provides: MooScroller

...
*/
var MooScroller = new Class({
	Implements: [Options, Events],
	options: {
		maxThumbSize: 10,
		mode: 'vertical',
		width: 0, //required only for mode: horizontal
		scrollSteps: 10,
		wheel: true,
		scrollLinks: {
			forward: 'scrollForward',
			back: 'scrollBack'
		},
		hideWhenNoOverflow: true
//		onScroll: function(){},
//		onPage: function(){}
	},

	initialize: function(content, knob, options){
		this.setOptions(options);
		this.horz = (this.options.mode == "horizontal");

		this.content = document.id(content).setStyle('overflow', 'hidden');
		this.knob = document.id(knob);
		this.track = this.knob.getParent();
		this.setPositions();
		
		if (this.horz && this.options.width) {
			this.wrapper = new Element('div');
			this.content.getChildren().each(function(child){
				this.wrapper.adopt(child);
			}, this);
			this.wrapper.inject(this.content).setStyle('width', this.options.width);
		}
		

		this.bound = {
			'start': this.start.bind(this),
			'end': this.end.bind(this),
			'drag': this.drag.bind(this),
			'wheel': this.wheel.bind(this),
			'page': this.page.bind(this)
		};

		this.position = {};
		this.mouse = {};
		this.update();
		this.attach();
		
		this.clearScroll = function (){
			clearInterval(this.scrolling);
		}.bind(this);
		['forward','back'].each(function(direction) {
			var lnk = document.id(this.options.scrollLinks[direction]);
			if (lnk) {
				lnk.addEvents({
					mousedown: function() {
						this.scrolling = this[direction].periodical(50, this);
					}.bind(this),
					mouseup: this.clearScroll.bind(this),
					click: this.clearScroll.bind(this)
				});
			}
		}, this);
		this.knob.addEvent('click', this.clearScroll.bind(this));
		window.addEvent('domready', function(){
			try {
				document.id(document.body).addEvent('mouseup', this.clearScroll);
			}catch(e){}
		}.bind(this));
	},
	setPositions: function(){
		[this.track, this.knob].each(function(el){
			if (el.getStyle('position') == 'static') el.setStyle('position','relative');
		});

	},
	toElement: function(){
		return this.content;
	},
	update: function(){
		var plain = this.horz?'Width':'Height';
		this.contentSize = this.content['offset'+plain];
		this.contentScrollSize = this.content['scroll'+plain];
		this.trackSize = this.track['offset'+plain];

		this.contentRatio = this.contentSize / this.contentScrollSize;

		this.knobSize = (this.trackSize * this.contentRatio).limit(this.options.maxThumbSize, this.trackSize);

		if (this.options.hideWhenNoOverflow) {
			this.hidden = this.knobSize == this.trackSize;
			this.track.setStyle('opacity', this.hidden?0:1);
		}
		
		this.scrollRatio = this.contentScrollSize / this.trackSize;
		this.knob.setStyle(plain.toLowerCase(), this.knobSize);

		this.updateThumbFromContentScroll();
		this.updateContentFromThumbPosition();
	},

	updateContentFromThumbPosition: function(){
		this.content[this.horz?'scrollLeft':'scrollTop'] = this.position.now * this.scrollRatio;
	},

	updateThumbFromContentScroll: function(){
		this.position.now = (this.content[this.horz?'scrollLeft':'scrollTop'] / this.scrollRatio).limit(0, (this.trackSize - this.knobSize));
		this.knob.setStyle(this.horz?'left':'top', this.position.now);
	},

	attach: function(){
		this.knob.addEvent('mousedown', this.bound.start);
		if (this.options.scrollSteps) {
			this.content.addEvent('mousewheel', this.bound.wheel);
			this.track.addEvent('mousewheel', this.bound.wheel);
		}
		this.track.addEvent('mouseup', this.bound.page);
	},

	detach: function(){
		this.knob.removeEvent('mousedown', this.bound.start);
		if (this.options.scrollSteps) {
			this.content.removeEvent('mousewheel', this.bound.wheel);
			this.track.removeEvent('mousewheel', this.bound.wheel);
		}
		this.track.removeEvent('mouseup', this.bound.page);
		document.id(document.body).removeEvent('mouseup', this.clearScroll);
	},

	wheel: function(event){
		if (this.hidden) return;
		this.scroll(-(event.wheel * this.options.scrollSteps));
		this.updateThumbFromContentScroll();
		event.stop();
	},

	scroll: function(steps){
		steps = steps||this.options.scrollSteps;
		this.content[this.horz?'scrollLeft':'scrollTop'] += steps;
		this.updateThumbFromContentScroll();
		this.fireEvent('onScroll', steps);
	},
	forward: function(steps){
		this.scroll(steps);
	},
	back: function(steps){
		steps = steps||this.options.scrollSteps;
		this.scroll(-steps);
	},

	page: function(event){
		var axis = this.horz?'x':'y';
		var forward = (event.page[axis] > this.knob.getPosition()[axis]);
		this.scroll((forward?1:-1)*this.content['offset'+(this.horz?'Width':'Height')]);
		this.updateThumbFromContentScroll();
		this.fireEvent('onPage', forward);
		event.stop();
	},

	
	start: function(event){
		var axis = this.horz?'x':'y';
		this.mouse.start = event.page[axis];
		this.position.start = this.knob.getStyle(this.horz?'left':'top').toInt();
		document.addEvent('mousemove', this.bound.drag);
		document.addEvent('mouseup', this.bound.end);
		this.knob.addEvent('mouseup', this.bound.end);
		event.stop();
	},

	end: function(event){
		document.removeEvent('mousemove', this.bound.drag);
		document.removeEvent('mouseup', this.bound.end);
		this.knob.removeEvent('mouseup', this.bound.end);
		event.stop();
	},

	drag: function(event){
		var axis = this.horz?'x':'y';
		this.mouse.now = event.page[axis];
		this.position.now = (this.position.start + (this.mouse.now - this.mouse.start)).limit(0, (this.trackSize - this.knobSize));
		this.updateContentFromThumbPosition();
		this.updateThumbFromContentScroll();
		event.stop();
	}

});


// Begin: Source/Core/dbug.js
/*
---

name: dbug

description: A wrapper for Firebug console.* statements.

license: MIT-style license.

authors:
- Aaron Newton

provides: dbug

...
*/
var dbug = {
	logged: [],
	timers: {},
	firebug: false, 
	enabled: false, 
	log: function() {
		dbug.logged.push(arguments);
	},
	nolog: function(msg) {
		dbug.logged.push(arguments);
	},
	time: function(name){
		dbug.timers[name] = new Date().getTime();
	},
	timeEnd: function(name){
		if (dbug.timers[name]) {
			var end = new Date().getTime() - dbug.timers[name];
			dbug.timers[name] = false;
			dbug.log('%s: %s', name, end);
		} else dbug.log('no such timer: %s', name);
	},
	enable: function(silent) { 
		var con = window.firebug ? firebug.d.console.cmd : window.console;

		if((!!window.console && !!window.console.warn) || window.firebug) {
			try {
				dbug.enabled = true;
				dbug.log = function(){
						try {
							(con.debug || con.log).apply(con, arguments);
						} catch(e) {
							console.log(Array.slice(arguments));
						}
				};
				dbug.time = function(){
					con.time.apply(con, arguments);
				};
				dbug.timeEnd = function(){
					con.timeEnd.apply(con, arguments);
				};
				if(!silent) dbug.log('enabling dbug');
				for(var i=0;i<dbug.logged.length;i++){ dbug.log.apply(con, dbug.logged[i]); }
				dbug.logged=[];
			} catch(e) {
				dbug.enable.delay(400);
			}
		}
	},
	disable: function(){ 
		if(dbug.firebug) dbug.enabled = false;
		dbug.log = dbug.nolog;
		dbug.time = function(){};
		dbug.timeEnd = function(){};
	},
	cookie: function(set){
		var value = document.cookie.match('(?:^|;)\\s*jsdebug=([^;]*)');
		var debugCookie = value ? unescape(value[1]) : false;
		if((set == null && debugCookie != 'true') || (set != null && set)) {
			dbug.enable();
			dbug.log('setting debugging cookie');
			var date = new Date();
			date.setTime(date.getTime()+(24*60*60*1000));
			document.cookie = 'jsdebug=true;expires='+date.toGMTString()+';path=/;';
		} else dbug.disableCookie();
	},
	disableCookie: function(){
		dbug.log('disabling debugging cookie');
		document.cookie = 'jsdebug=false;path=/;';
	},
	conditional: function(fn, fnIfError) {
		if (dbug.enabled) {
			return fn();
		} else {
			try {
				return fn();
			} catch(e) {
				if (fnIfError) fnIfError(e);
			}
		}
	}
};

(function(){
	var fb = !!window.console || !!window.firebug;
	var con = window.firebug ? window.firebug.d.console.cmd : window.console;
	var debugMethods = ['debug','info','warn','error','assert','dir','dirxml'];
	var otherMethods = ['trace','group','groupEnd','profile','profileEnd','count'];
	function set(methodList, defaultFunction) {
		
		var getLogger = function(method) {
			return function(){
				con[method].apply(con, arguments);
			};
		};
		
		for(var i = 0; i < methodList.length; i++){
			var method = methodList[i];
			if (fb && con[method]) {
				dbug[method] = getLogger(method);
			} else {
				dbug[method] = defaultFunction;
			}
		}
	};
	set(debugMethods, dbug.log);
	set(otherMethods, function(){});
})();
if ((!!window.console && !!window.console.warn) || window.firebug){
	dbug.firebug = true;
	var value = document.cookie.match('(?:^|;)\\s*jsdebug=([^;]*)');
	var debugCookie = value ? unescape(value[1]) : false;
	if(window.location.href.indexOf("jsdebug=true")>0 || debugCookie=='true') dbug.enable();
	if(debugCookie=='true')dbug.log('debugging cookie enabled');
	if(window.location.href.indexOf("jsdebugCookie=true")>0){
		dbug.cookie();
		if(!dbug.enabled)dbug.enable();
	}
	if(window.location.href.indexOf("jsdebugCookie=false")>0)dbug.disableCookie();
}


// Begin: Source/UI/StyleWriter.js
/*
---
name: StyleWriter

description: Provides a simple method for injecting a css style element into the DOM if it's not already present.

license: MIT-Style License

requires: [Core/Class, Core/DomReady, Core/Element, dbug]

provides: StyleWriter

...
*/

var StyleWriter = new Class({
	createStyle: function(css, id) {
		window.addEvent('domready', function(){
			try {
				if (document.id(id) && id) return;
				var style = new Element('style', {id: id||''}).inject($$('head')[0]);
				if (Browser.ie) style.styleSheet.cssText = css;
				else style.set('text', css);
			}catch(e){dbug.log('error: %s',e);}
		}.bind(this));
	}
});

// Begin: Source/UI/StickyWin.js
/*
---

name: StickyWin

description: Creates a div within the page with the specified contents at the location relative to the element you specify; basically an in-page popup maker.

license: MIT-Style License

requires: [
  Core/DomReady,
  Core/Slick.Finder,
  More/Element.Position,
  More/Class.Binds,
  More/Element.Shortcuts,
  More/Element.Pin,
  More/IframeShim,
  More/Object.Extras,
  Clientcide,
  StyleWriter
]

provides: [StickyWin, StickyWin.Stacker]
...
*/


var StickyWin = new Class({
	Binds: ['destroy', 'hide', 'togglepin', 'esc'],
	Implements: [Options, Events, StyleWriter],
	options: {
//		onDisplay: function(){},
//		onClose: function(){},
//		onDestroy: function(){},
		closeClassName: 'closeSticky',
		pinClassName: 'pinSticky',
		content: '',
		zIndex: 10000,
		className: '',
//		id: ... set above in initialize function
/*  	these are the defaults for Element.position anyway
		************************************************
		edge: false, //see Element.position
		position: 'center', //center, corner == upperLeft, upperRight, bottomLeft, bottomRight
		offset: {x:0,y:0},
		relativeTo: document.body, */
		width: false,
		height: false,
		timeout: -1,
		allowMultipleByClass: true,
		allowMultiple: true,
		showNow: true,
		useIframeShim: true,
		iframeShimSelector: '',
		destroyOnClose: false,
		closeOnClickOut: false,
		closeOnEsc: false,
		getWindowManager: function(){ return StickyWin.WM; }
	},

	css: '.SWclearfix:after {content: "."; display: block; height: 0; clear: both; visibility: hidden;}'+
		 '.SWclearfix {display: inline-table;} * html .SWclearfix {height: 1%;} .SWclearfix {display: block;}',

	initialize: function(options){
		this.options.inject = this.options.inject || {
			target: document.body,
			where: 'bottom'
		};
		this.setOptions(options);
		this.windowManager = this.options.getWindowManager();
		this.id = this.options.id || 'StickyWin_'+new Date().getTime();
		this.makeWindow();
		if (this.windowManager) this.windowManager.add(this);

		if (this.options.content) this.setContent(this.options.content);
		if (this.options.timeout > 0) {
			this.addEvent('onDisplay', function(){
				this.hide.delay(this.options.timeout, this);
			}.bind(this));
		}
		//add css for clearfix
		this.createStyle(this.css, 'StickyWinClearFix');
		if (this.options.closeOnClickOut || this.options.closeOnEsc) this.attach();
		if (this.options.destroyOnClose) this.addEvent('close', this.destroy);
		if (this.options.showNow) this.show();
	},
	toElement: function(){
		return this.element;
	},
	attach: function(dettach){
		var method = dettach ? 'removeEvents' : 'addEvents';
		var events = {};
		if (this.options.closeOnClickOut) events.click = this.esc;
		if (this.options.closeOnEsc) events.keyup = this.esc;
		document[method](events);
	},
	esc: function(e) {
		if (e.key == "esc") this.hide();
		if (e.type == "click" && this.element != e.target && !this.element.contains(e.target)) this.hide();
	},
	makeWindow: function(){
		this.destroyOthers();
		if (!document.id(this.id)) {
			this.win = new Element('div', {
				id: this.id
			}).addClass(this.options.className).addClass('StickyWinInstance').addClass('SWclearfix').setStyles({
			 	display: 'none',
				position: 'absolute',
				zIndex: this.options.zIndex
			}).inject(this.options.inject.target, this.options.inject.where).store('StickyWin', this);
		} else this.win = document.id(this.id);
		this.element = this.win;
		if (this.options.width && typeOf(this.options.width.toInt())=="number") this.win.setStyle('width', this.options.width.toInt());
		if (this.options.height && typeOf(this.options.height.toInt())=="number") this.win.setStyle('height', this.options.height.toInt());
		return this;
	},
	show: function(suppressEvent){
		this.showWin();
		if (!suppressEvent) this.fireEvent('display');
		if (this.options.useIframeShim) this.showIframeShim();
		this.visible = true;
		return this;
	},
	showWin: function(){
		if (this.windowManager) this.windowManager.focus(this);
		if (!this.positioned) this.position();
		this.win.show();
	},
	hide: function(suppressEvent){
		if (typeOf(suppressEvent) == "event" || !suppressEvent) this.fireEvent('close');
		this.hideWin();
		if (this.options.useIframeShim) this.hideIframeShim();
		this.visible = false;
		return this;
	},
	hideWin: function(){
		this.win.setStyle('display','none');
	},
	destroyOthers: function() {
		if (!this.options.allowMultipleByClass || !this.options.allowMultiple) {
			$$('div.StickyWinInstance').each(function(sw) {
				if (!this.options.allowMultiple || (!this.options.allowMultipleByClass && sw.hasClass(this.options.className)))
					sw.retrieve('StickyWin').destroy();
			}, this);
		}
	},
	setContent: function(html) {
		if (this.win.getChildren().length>0) this.win.empty();
		if (typeOf(html) == "string") this.win.set('html', html);
		else if (document.id(html)) this.win.adopt(html);
		this.win.getElements('.'+this.options.closeClassName).each(function(el){
			el.addEvent('click', this.hide);
		}, this);
		this.win.getElements('.'+this.options.pinClassName).each(function(el){
			el.addEvent('click', this.togglepin);
		}, this);
		return this;
	},
	position: function(options){
		this.positioned = true;
		this.setOptions(options);
		this.win.position(
			Object.cleanValues({
				allowNegative: [this.options.allowNegative, this.options.relativeTo != document.body].pick(),
				relativeTo: this.options.relativeTo,
				position: this.options.position,
				offset: this.options.offset,
				edge: this.options.edge
			})
		);
		if (this.shim) this.shim.position();
		return this;
	},
	pin: function(pin) {
		if (!this.win.pin) {
			dbug.log('you must include element.pin.js!');
			return this;
		}
		this.pinned = pin != null && pin;
		this.win.pin(pin);
		return this;
	},
	unpin: function(){
		return this.pin(false);
	},
	togglepin: function(){
		return this.pin(!this.pinned);
	},
	makeIframeShim: function(){
		if (!this.shim){
			var el = (this.options.iframeShimSelector)?this.win.getElement(this.options.iframeShimSelector):this.win;
			this.shim = new IframeShim(el, {
				display: false,
				name: 'StickyWinShim'
			});
		}
	},
	showIframeShim: function(){
		if (this.options.useIframeShim) {
			this.makeIframeShim();
			this.shim.show();
		}
	},
	hideIframeShim: function(){
		if (this.shim) this.shim.hide();
	},
	destroy: function(){
		this.destroyed = true;
		this.attach(true);
		if (this.windowManager) this.windowManager.remove(this);
		if (this.win) this.win.destroy();
		if (this.options.useIframeShim && this.shim) this.shim.destroy();
		if (document.id('modalOverlay')) document.id('modalOverlay').destroy();
		this.fireEvent('destroy');
	}
});

StickyWin.Stacker = new Class({
	Implements: [Options, Events],
	Binds: ['click'],
	instances: [],
	options: {
		zIndexBase: 9000
	},
	initialize: function(options) {
		this.setOptions(options);
	},
	add: function(sw) {
		this.instances.include(sw);
		$(sw).addEvent('mousedown', this.click);
	},
	click: function(e) {
		this.instances.each(function(sw){
			var el = $(sw);
			if (el == e.target || el.contains($(e.target))) this.focus(sw);
		}, this);
	},
	focus: function(instance){
		if (this.focused == instance) return;
		this.focused = instance;
		if (instance) this.instances.erase(instance).push(instance);
		this.instances.each(function(current, i){
			$(current).setStyle('z-index', this.options.zIndexBase + i);
		}, this);
		this.focused = instance;
	},
	remove: function(sw) {
		this.instances.erase(sw);
		$(sw).removeEvent('click', this.click);
	}
});
StickyWin.WM = new StickyWin.Stacker();

// Begin: Source/UI/StickyWin.UI.js
 /*
---
name: StickyWin.UI

description: Creates an html holder for in-page popups using a default style.

license: MIT-Style License

requires: [Core/Element.Style, More/String.Extras, StyleWriter, StickyWin]

provides: StickyWin.UI
...
*/
StickyWin.UI = new Class({
	Implements: [Options, StyleWriter],
	options: {
		width: 300,
		css: "div.DefaultStickyWin {font-family:verdana; font-size:11px; line-height: 13px;position: relative;}"+
			"div.DefaultStickyWin div.top{-moz-user-select: none;-khtml-user-select: none;}"+
			"div.DefaultStickyWin div.top_ul{background:url({%baseHref%}full.png) top left no-repeat; height:30px; width:15px; float:left}"+
			"div.DefaultStickyWin div.top_ur{position:relative; left:0px !important; left:-4px; background:url({%baseHref%}full.png) top right !important; height:30px; margin:0px 0px 0px 15px !important; margin-right:-4px; padding:0px}"+
			"div.DefaultStickyWin h1.caption{clear: none !important; margin:0px !important; overflow: hidden; padding:0 !important; font-weight:bold; color:#555; font-size:14px !important; position:relative; top:8px !important; left:5px !important; float: left; height: 22px !important;}"+
			"div.DefaultStickyWin div.middle, div.DefaultStickyWin div.closeBody {background:url({%baseHref%}body.png) top left repeat-y; margin:0px 20px 0px 0px !important;	margin-bottom: -3px; position: relative;	top: 0px !important; top: -3px;}"+
			"div.DefaultStickyWin div.body{background:url({%baseHref%}body.png) top right repeat-y; padding:8px 23px 8px 0px !important; margin-left:5px !important; position:relative; right:-20px !important; z-index: 1;}"+
			"div.DefaultStickyWin div.bottom{clear:both;}"+
			"div.DefaultStickyWin div.bottom_ll{background:url({%baseHref%}full.png) bottom left no-repeat; width:15px; height:15px; float:left}"+
			"div.DefaultStickyWin div.bottom_lr{background:url({%baseHref%}full.png) bottom right; position:relative; left:0px !important; left:-4px; margin:0px 0px 0px 15px !important; margin-right:-4px; height:15px}"+
			"div.DefaultStickyWin div.closeButtons{text-align: center; background:url({%baseHref%}body.png) top right repeat-y; padding: 4px 30px 8px 0px; margin-left:5px; position:relative; right:-20px}"+
			"div.DefaultStickyWin a.button:hover{background:url({%baseHref%}big_button_over.gif) repeat-x}"+
			"div.DefaultStickyWin a.button {background:url({%baseHref%}big_button.gif) repeat-x; margin: 2px 8px 2px 8px; padding: 2px 12px; cursor:pointer; border: 1px solid #999 !important; text-decoration:none; color: #000 !important;}"+
			"div.DefaultStickyWin div.closeButton{width:13px; height:13px; background:url({%baseHref%}closebtn.gif) no-repeat; position: absolute; right: 0px; margin:10px 15px 0px 0px !important; cursor:pointer;top:0px}"+
			"div.DefaultStickyWin div.dragHandle {	width: 11px;	height: 25px;	position: relative;	top: 5px;	left: -3px;	cursor: move;	background: url({%baseHref%}drag_corner.gif); float: left;}",
		cornerHandle: false,
		cssClass: '',
		buttons: [],
		cssId: 'defaultStickyWinStyle',
		cssClassName: 'DefaultStickyWin',
		closeButton: true
/*	These options are deprecated:
		closeTxt: false,
		onClose: function(){},
		confirmTxt: false,
		onConfirm: function(){}	*/
	},
	initialize: function() {
		var args = this.getArgs(arguments);
		this.setOptions(args.options);
		this.legacy();
		var css = this.options.css.substitute({baseHref: this.options.baseHref || Clientcide.assetLocation + '/stickyWinHTML/'}, /\\?\{%([^}]+)%\}/g);
		if (Browser.ie) css = css.replace(/png/g, 'gif');
		this.createStyle(css, this.options.cssId);
		this.build();
		if (args.caption || args.body) this.setContent(args.caption, args.body);
	},
	toElement: function(){
		return this.element;
	},
	getArgs: function(){
		return StickyWin.UI.getArgs.apply(this, arguments);
	},
	legacy: function(){
		var opt = this.options; //saving bytes
		//legacy support
		if (opt.confirmTxt) opt.buttons.push({text: opt.confirmTxt, onClick: opt.onConfirm || function(){}});
		if (opt.closeTxt) opt.buttons.push({text: opt.closeTxt, onClick: opt.onClose || function(){}});
	},
	build: function(){
		var opt = this.options;

		var container = new Element('div', {
			'class': opt.cssClassName
		});
		if (opt.width) container.setStyle('width', opt.width);
		this.element = container;
		this.element.store('StickyWinUI', this);
		if (opt.cssClass) container.addClass(opt.cssClass);
		

		var bodyDiv = new Element('div').addClass('body');
		this.body = bodyDiv;
		
		var top_ur = new Element('div').addClass('top_ur');
		this.top_ur = top_ur;
		this.top = new Element('div').addClass('top').adopt(
				new Element('div').addClass('top_ul')
			).adopt(top_ur);
		container.adopt(this.top);
		
		if (opt.cornerHandle) new Element('div').addClass('dragHandle').inject(top_ur, 'top');
		
		//body
		container.adopt(new Element('div').addClass('middle').adopt(bodyDiv));
		//close buttons
		if (opt.buttons.length > 0){
			var closeButtons = new Element('div').addClass('closeButtons');
			opt.buttons.each(function(button){
				if (button.properties && button.properties.className){
					button.properties['class'] = button.properties.className;
					delete button.properties.className;
				}
				var properties = Object.merge({'class': 'closeSticky'}, button.properties);
				new Element('a').addEvent('click', button.onClick || function(){})
					.appendText(button.text).inject(closeButtons).set(properties).addClass('button');
			});
			container.adopt(new Element('div').addClass('closeBody').adopt(closeButtons));
		}
		//footer
		container.adopt(
			new Element('div').addClass('bottom').adopt(
					new Element('div').addClass('bottom_ll')
				).adopt(
					new Element('div').addClass('bottom_lr')
			)
		);
		if (this.options.closeButton) container.adopt(new Element('div').addClass('closeButton').addClass('closeSticky'));
		return this;
	},
	setCaption: function(caption) {
		this.caption = caption;
		if (!this.h1) {
			this.makeCaption(caption);
		} else {
			if (document.id(caption)) this.h1.adopt(caption);
			else this.h1.set('html', caption);
		}
		return this;
	},
	makeCaption: function(caption) {
		if (!caption) return this.destroyCaption();
		var opt = this.options;
		this.h1 = new Element('h1').addClass('caption');
		if (opt.width) this.h1.setStyle('width', (opt.width-(opt.cornerHandle?55:40)-(opt.closeButton?10:0)));
		this.setCaption(caption);
		this.top_ur.adopt(this.h1);
		if (!this.options.cornerHandle) this.h1.addClass('dragHandle');
		return this;
	},
	destroyCaption: function(){
		if (this.h1) {
			this.h1.destroy();
			this.h1 = null;
		}
		return this;
	},
	setContent: function(){
		var args = this.getArgs.apply(this, arguments);
		var caption = args.caption;
		var body = args.body;
		this.setCaption(caption);
		if (document.id(body)) this.body.empty().adopt(body);
		else this.body.set('html', body);
		return this;
	}
});
StickyWin.UI.getArgs = function(){
	var input = typeOf(arguments[0]) == "arguments"?arguments[0]:arguments;
	if (Browser.opera && 1 === input.length) input = input[0];

	var cap = input[0], bod = input[1];
	var args = Array.link(input, {options: Type.isObject});
	if (input.length == 3 || (!args.options && input.length == 2)) {
		args.caption = cap;
		args.body = bod;
	} else if ((typeOf(bod) == 'object' || !bod) && cap && typeOf(cap) != 'object'){
		args.body = cap;
	}
	return args;
};

StickyWin.ui = function(caption, body, options){
	return document.id(new StickyWin.UI(caption, body, options));
};


// Begin: Source/UI/StickyWin.UI.Pointy.js
/*
---
name: StickyWin.UI.Pointy

description: Creates an html holder for in-page popups using a default style - this one including a pointer in the specified direction.

license: MIT-Style License

requires: [More/Element.Shortcuts, More/Element.Position, StickyWin.UI]

provides: [StickyWin.UI.Pointy, StickyWin.UI.pointy]
...
*/
StickyWin.UI.Pointy = new Class({
	Extends: StickyWin.UI,
	options: {
		theme: 'dark',
		themes: {
			dark: {
				bgColor: '#333',
				fgColor: '#ddd',
				imgset: 'dark'
			},
			light: {
				bgColor: '#ccc',
				fgColor: '#333',
				imgset: 'light'
			}
		},
		css: "div.DefaultPointyTip {vertical-align: auto; position: relative;}"+
		"div.DefaultPointyTip * {text-align:left !important}"+
		"div.DefaultPointyTip .pointyWrapper div.body{background: {%bgColor%}; color: {%fgColor%}; left: 0px; right: 0px !important;padding:  0px 10px !important;margin-left: 0px !important;font-family: verdana;font-size: 11px;line-height: 13px;position: relative;}"+
		"div.DefaultPointyTip .pointyWrapper div.top {position: relative;height: 25px; overflow: visible;}"+
		"div.DefaultPointyTip .pointyWrapper div.top_ul{background: url({%baseHref%}{%imgset%}_back.png) top left no-repeat;width: 8px;height: 25px; position: absolute; left: 0px;}"+
		"div.DefaultPointyTip .pointyWrapper div.top_ur{background: url({%baseHref%}{%imgset%}_back.png) top right !important;margin: 0 0 0 8px !important;height: 25px;position: relative;left: 0px !important;padding: 0;}"+
		"div.DefaultPointyTip .pointyWrapper h1.caption{color: {%fgColor%};left: 0px !important;top: 4px !important;clear: none !important;overflow: hidden;font-weight: 700;font-size: 12px !important;position: relative;float: left;height: 22px !important;margin: 0 !important;padding: 0 !important;}"+
		"div.DefaultPointyTip .pointyWrapper div.middle, div.DefaultPointyTip .pointyWrapper div.closeBody{background:  {%bgColor%};margin: 0 0px 0 0 !important;position: relative;top: 0 !important;}"+
		"div.DefaultPointyTip .pointyWrapper div.middle {min-height: 16px; background:  {%bgColor%};margin: 0 0px 0 0 !important;position: relative;top: 0 !important;}"+
		"div.DefaultPointyTip .pointyWrapper div.bottom {clear: both; width: 100% !important; background: none; height: 6px} "+
		"div.DefaultPointyTip .pointyWrapper div.bottom_ll{font-size:1; background: url({%baseHref%}{%imgset%}_back.png) bottom left no-repeat;width: 6px;height: 6px;position: absolute; left: 0px;}"+
		"div.DefaultPointyTip .pointyWrapper div.bottom_lr{font-size:1; background: url({%baseHref%}{%imgset%}_back.png) bottom right;height: 6px;margin: 0 0 0 6px !important;position: relative;left: 0 !important;}"+
		"div.DefaultPointyTip .pointyWrapper div.noCaption{ height: 6px; overflow: hidden}"+
		"div.DefaultPointyTip .pointyWrapper div.closeButton{width:13px; height:13px; background:url({%baseHref%}{%imgset%}_x.png) no-repeat; position: absolute; right: 0px; margin:0px !important; cursor:pointer; z-index: 1; top: 4px;}"+
		"div.DefaultPointyTip .pointyWrapper div.pointyDivot {background: url({%divot%}) no-repeat;}",
		divot: '{%baseHref%}{%imgset%}_divot.png',
		divotSize: 22,
		direction: 12,
		cssId: 'defaultPointyTipStyle',
		cssClassName: 'DefaultPointyTip'
	},
	initialize: function() {
		var args = this.getArgs(arguments);
		this.setOptions(args.options);
		Object.append(this.options, this.options.themes[this.options.theme]);
		this.options.baseHref = this.options.baseHref || Clientcide.assetLocation + '/PointyTip/';
		this.options.divot = this.options.divot.substitute(this.options, /\\?\{%([^}]+)%\}/g);
		if (Browser.ie) this.options.divot = this.options.divot.replace(/png/g, 'gif');
		this.options.css = this.options.css.substitute(this.options, /\\?\{%([^}]+)%\}/g);
		if (args.options && args.options.theme) {
			while (!this.id) {
				var id = Number.random(0, 999999999);
				if (!StickyWin.UI.Pointy[id]) {
					StickyWin.UI.Pointy[id] = this;
					this.id = id;
				}
			}
			this.options.css = this.options.css.replace(/div\.DefaultPointyTip/g, "div#pointy_"+this.id);
			this.options.cssId = "pointyTipStyle_" + this.id;
		}
		if (typeOf(this.options.direction) == 'string') {
			var map = {
				left: 9,
				right: 3,
				up: 12,
				down: 6
			};
			this.options.direction = map[this.options.direction];
		}
		
		this.parent(args.caption, args.body, this.options);
		if (this.id) document.id(this).set('id', "pointy_"+this.id);
	},
	build: function(){
		this.parent();
		var opt = this.options;
		this.pointyWrapper = new Element('div', {
			'class': 'pointyWrapper'
		}).inject(document.id(this));
		document.id(this).getChildren().each(function(el){
			if (el != this.pointyWrapper) this.pointyWrapper.grab(el);
		}, this);

		var w = opt.divotSize;
		var h = w;
		var left = (opt.width - opt.divotSize)/2;
		var orient = function(){
			switch(opt.direction) {
				case 12: case 1: case 11:
					return {
						height: h/2
					};
				case 5: case 6: case 7:
					return {
						height: h/2,
						backgroundPosition: '0 -'+h/2+'px'
					};
				case 8: case 9: case 10:
					return {
						width: w/2
					};
				case 2: case 3: case 4:
					return {
						width: w/2,
						backgroundPosition: '100%'
					};
			};
		};
		this.pointer = new Element('div', {
			styles: Object.append({
				width: w,
				height: h,
				overflow: 'hidden'
			}, orient()),
			'class': 'pointyDivot pointy_'+opt.direction
		}).inject(this.pointyWrapper);
	},
	expose: function(){
		if (document.id(this).getStyle('display') != 'none' &&
		  document.body != document.id(this) && 
		  document.id(document.body).contains(document.id(this))) return function(){};
		document.id(this).setStyles({
			visibility: 'hidden',
			position: 'absolute'
		});
		var dispose;
		if (document.body != document.id(this) && !document.body.contains(document.id(this))) {
			document.id(this).inject(document.body);
			dispose = true;
		}
		return (function(){
			if (dispose) document.id(this).dispose();
			document.id(this).setStyles({
				visibility: 'visible',
				position: 'relative'
			});
		}).bind(this);
	},
	positionPointer: function(options){
		if (!this.pointer) return;
		var opt = options || this.options;
		var pos;
		var d = opt.direction;
		switch (d){
			case 12: case 1: case 11:
				pos = {
					edge: {x: 'center', y: 'bottom'},
					position: {
						x: d==12?'center':d==1?'right':'left', 
						y: 'top'
					},
					offset: {
						x: (d==12?0:d==1?-1:1)*opt.divotSize,
						y: 1
					}
				};
				break;
			case 2: case 3: case 4:
				pos = {
					edge: {x: 'left', y: 'center'},
					position: {
						x: 'right', 
						y: d==3?'center':d==2?'top':'bottom'
					},
					offset: {
						x: -1,
						y: (d==3?0:d==4?-1:1)*opt.divotSize
					}
				};
				break;
			case 5: case 6: case 7:
				pos = {
					edge: {x: 'center', y: 'top'},
					position: {
						x: d==6?'center':d==5?'right':'left', 
						y: 'bottom'
					},
					offset: {
						x: (d==6?0:d==5?-1:1)*opt.divotSize,
						y: -1
					}
				};
				break;
			case 8: case 9: case 10:
				pos = {
					edge: {x: 'right', y: 'center'},
					position: {
						x: 'left', 
						y: d==9?'center':d==10?'top':'bottom'
					},
					offset: {
						x: 1,
						y: (d==9?0:d==8?-1:1)*opt.divotSize
					}
				};
				break;
		};
		var putItBack = this.expose();
		this.pointer.position(Object.append({
			relativeTo: this.pointyWrapper,
			allowNegative: true
		}, pos, options));
		putItBack();
	},
	setContent: function(a1, a2){
		this.parent(a1, a2);
		this.top[this.h1?'removeClass':'addClass']('noCaption');
		if (Browser.ie) document.id(this).getElements('.bottom_ll, .bottom_lr').setStyle('font-size', 1); //IE6 bullshit
		if (this.options.closeButton) this.body.setStyle('margin-right', 6);
		this.positionPointer();
		return this;
	},
	makeCaption: function(caption){
		this.parent(caption);
		if (this.options.width && this.h1) this.h1.setStyle('width', (this.options.width-(this.options.closeButton?25:15)));
	}
});

StickyWin.UI.pointy = function(caption, body, options){
	return document.id(new StickyWin.UI.Pointy(caption, body, options));
};
StickyWin.ui.pointy = StickyWin.UI.pointy;

// Begin: Source/UI/StickyWin.PointyTip.js
/*
---
name: StickyWin.PointyTip

description: Positions a pointy tip relative to the element you specify.

license: MIT-Style License

requires: StickyWin.UI.Pointy

provides: StickyWin.PointyTip

...
*/
StickyWin.PointyTip = new Class({
	Extends: StickyWin,
	options: {
		point: "left",
		pointyOptions: {}
	},
	initialize: function(){
		var args = this.getArgs(arguments);
		this.setOptions(args.options);
		var popts = this.options.pointyOptions;
		var d = popts.direction;
		if (!d) {
			var map = {
				left: 9,
				right: 3,
				up: 12,
				down: 6
			};
			d = map[this.options.point];
			if (!d) d = this.options.point;
			popts.direction = d;
		}
		if (!popts.width) popts.width = this.options.width;
		this.pointy = new StickyWin.UI.Pointy(args.caption, args.body, popts);
		this.options.content = null;
		this.setOptions(args.options, this.getPositionSettings());
		this.parent(this.options);
		this.win.empty().adopt(document.id(this.pointy));
		this.attachHandlers(this.win);
		if (this.options.showNow) this.position();
	},
	getArgs: function(){
		return StickyWin.UI.getArgs.apply(this, arguments);
	},
	getPositionSettings: function(){
		var s = this.pointy.options.divotSize;
		var d = this.options.point;
		var offset = this.options.offset || {};
		switch(d) {
			case "left": case 8: case 9: case 10:
				return {
					edge: {
						x: 'left', 
						y: d==10?'top':d==8?'bottom':'center'
					},
					position: {x: 'right', y: 'center'},
					offset: {
						x: s + (offset.x || 0),
						y: offset.y || 0
					}
				};
			case "right": case 2:  case 3: case 4:
				return {
					edge: {
						x: 'right', 
						y: (d==2?'top':d==4?'bottom':'center') + (offset.y || 0)
					},
					position: {x: 'left', y: 'center'},
					offset: {
						x: -s + (offset.x || 0),
						y: offset.y || 0
					}
				};
			case "up": case 11: case 12: case 1:
				return {
					edge: { 
						x: d==11?'left':d==1?'right':'center', 
						y: 'top' 
					},
					position: {	x: 'center', y: 'bottom' },
					offset: {
						y: s + (offset.y || 0),
						x: (d==11?-s:d==1?s:0) + (offset.x || 0)
					}
				};
			case "down": case 5: case 6: case 7:
				return {
					edge: {
						x: (d==7?'left':d==5?'right':'center') + (offset.x || 0), 
						y: 'bottom'
					},
					position: {x: 'center', y: 'top'},
					offset: {
						y: -s + (offset.y || 0),
						x: (d==7?-s:d==5?s:0) + (offset.x || 0)
					}
				};
		};
	},
	setContent: function() {
		var args = this.getArgs(arguments);
		this.pointy.setContent(args.caption, args.body);
		[this.pointy.h1, this.pointy.body].each(this.attachHandlers, this);
		if (this.visible) this.position();
		return this;
	},
	showWin: function(){
		this.parent();
		this.pointy.positionPointer();
	},
	position: function(options){
		this.parent(options);
		this.pointy.positionPointer();
	},
	attachHandlers: function(content) {
		if (!content) return;
		content.getElements('.'+this.options.closeClassName).addEvent('click', function(){ this.hide(); }.bind(this));
		content.getElements('.'+this.options.pinClassName).addEvent('click', function(){ this.togglepin(); }.bind(this));
	}
});

// Begin: Source/UI/Tips.Pointy.js
/*
---
name: Tips.Pointy

description: Defines Tips.Pointy, An extension to Tips that adds a pointy style to the tip.

license: MIT-Style License

requires: [More/Tips, StickyWin.PointyTip]

provides: Tips.Pointy

...
*/

Tips.Pointy = new Class({
	Extends: Tips,
	options: {
		onShow: function(tip, stickyWin){
			stickyWin.show();
		},
		onHide: function(tip, stickyWin){
			stickyWin.hide();
		},
		pointyTipOptions: {
			point: 11,
			width: 150,
			pointyOptions: {
				closeButton: false
			}
		}
	},
	initialize: function(){
		var params = Array.link(arguments, {options: Type.isObject, elements: function(arg){ return arg != null; }});
		this.setOptions(params.options);
		this.tip = new StickyWin.PointyTip(Object.append(this.options.pointyTipOptions, {
			showNow: false
		}));
		if (this.options.className) document.id(this.tip).addClass(this.options.className);
		if (params.elements) this.attach(params.elements);
	},
	elementEnter: function(event, element){

		var title = element.retrieve('tip:title');
		var text = element.retrieve('tip:text');
		this.tip.setContent(title, text);

		this.timer = clearTimeout(this.timer);
		this.timer = this.show.delay(this.options.showDelay, this);

		this.position(element);
	},

	elementLeave: function(event){
		clearTimeout(this.timer);
		this.timer = this.hide.delay(this.options.hideDelay, this);
	},

	elementMove: function(event){
		return; //always fixed
	},

	position: function(element){
		this.tip.setOptions({
			relativeTo: element
		});
		this.tip.position();
	},

	show: function(){
		this.fireEvent('show', [document.id(this.tip), this.tip]);
	},

	hide: function(){
		this.fireEvent('hide', [document.id(this.tip), this.tip]);
	},

	destroy: function(){
		this.detach();
		this.tip.destroy();
	}

});

// Begin: Source/Behaviors/Behavior.Tips.js
/*
---
name: Behavior.Tips
description: Attaches Tips.Pointy objects to elements with PointyTip in their data-behavior property and turns elements with HelpTip or InfoTip in their data-filters property into elements which show a Tips.Pointy object which contains their content, on rollover.
provides: [Behavior.Tips]
requires: [Behavior/Behavior, /Tips.Pointy]
script: Behavior.Tips.js
...
*/

(function() {

var createLink = function(element) {
	var isHelp = element.hasBehavior('HelpTip');
	var link = new Element('a', {
		'class': element.get('class'),
		'data-behavior': (isHelp ? 'HelpTip' : 'InfoTip'),
		'html': isHelp ? '?' : 'i'
	}).inject(element, 'after').store('tip:text', element.get('html'));
	//see where that text is supposed to have its pointer and group them by point
	var point = element.getData('help-direction', 1);
	return {point: point, link: link};
};

var createTip = function(link, point) {
	var tip = new Tips.Pointy(link, {
		pointyTipOptions: {
			destroyOnClose: false,
			width: 250,
			point: point.toInt()
		}
	});
	return tip;
};

Behavior.addGlobalFilters({

	PointyTip: function(element){
		var point = element.getData('tip-direction', 12);
		var tip = createTip(element, point);
		//destroy the tips on cleanup
		this.markForCleanup(element, function(){
			tip.destroy();
		});
		return tip;
	},

	//display help tips for users
	HelpTip: function(element) {
		var help = element.hide();
		var link = createLink(help);
		//for each point, create a new instance of Tips.Pointy (clientcide plugin)
		var tip = createTip(link.link, link.point); 
		//destroy the tips on cleanup
		this.markForCleanup(element, function(){
			tip.destroy();
		});
		return tip;
	},

	InfoTip: function(element) {
		var info = element.hide();
		var link = createLink(info);
		var tip = createTip(link.link, link.point);
		this.markForCleanup(element, function(){
			tip.destroy();
		});
		return tip;
	}

});

})();


// Begin: Source/Fx/Fx.Marquee.js
/*
---

name: Fx.Marquee

description: Defines Fx.Marquee, a marquee class for animated notifications.

License: MIT-Style License

requires: [Core/Fx.Morph, More/Element.Shortcuts]

provides: Fx.Marquee

...
*/
Fx.Marquee = new Class({
	Extends: Fx.Morph,
	options: {
		mode: 'horizontal', //or vertical
		message: '', //the message to display
		revert: true, //revert back to the previous message after a specified time
		delay: 5000, //how long to wait before reverting
		cssClass: 'msg', //the css class to apply to that message
		showEffect: { opacity: 1 },
		hideEffect: {opacity: 0},
		revertEffect: { opacity: [0,1] },
		currentMessage: null
/*	onRevert: function(){},
		onMessage: function(){} */
	},
	initialize: function(container, options){
		container = document.id(container); 
		var msg = this.options.currentMessage || (container.getChildren().length == 1)?container.getFirst():''; 
		var wrapper = new Element('div', {	
				styles: { position: 'relative' },
				'class':'fxMarqueeWrapper'
			}).inject(container); 
		this.parent(wrapper, options);
		this.current = this.wrapMessage(msg);
	},
	wrapMessage: function(msg){
		var wrapper;
		if (document.id(msg) && document.id(msg).hasClass('fxMarquee')) { //already set up
			wrapper = document.id(msg);
		} else {
			//create the wrapper
			wrapper = new Element('span', {
				'class':'fxMarquee',
				styles: {
					position: 'relative'
				}
			});
			if (document.id(msg)) wrapper.grab(document.id(msg)); //if the message is a dom element, inject it inside the wrapper
			else if (typeOf(msg) == "string") wrapper.set('html', msg); //else set it's value as the inner html
		}
		return wrapper.inject(this.element); //insert it into the container
	},
	announce: function(options) {
		this.setOptions(options).showMessage();
		return this;
	},
	showMessage: function(reverting){
		//delay the fuction if we're reverting
		(function(){
			//store a copy of the current chained functions
			var chain = this.$chain ? Array.clone(this.$chain) : [];
			//clear teh chain
			this.clearChain();
			this.element = document.id(this.element);
			this.current = document.id(this.current);
			this.message = document.id(this.message);
			//execute the hide effect
			this.start(this.options.hideEffect).chain(function(){
				//if we're reverting, hide the message and show the original
				if (reverting) {
					this.message.hide();
					if (this.current) this.current.show();
				} else {
					//else we're showing; remove the current message
					if (this.message) this.message.dispose();
					//create a new one with the message supplied
					this.message = this.wrapMessage(this.options.message);
					//hide the current message
					if (this.current) this.current.hide();
				}
				//if we're reverting, execute the revert effect, else the show effect
				this.start((reverting)?this.options.revertEffect:this.options.showEffect).chain(function(){
					//merge the chains we set aside back into this.$chain
					if (this.$chain) this.$chain.combine(chain);
					else this.$chain = chain;
					this.fireEvent((reverting)?'onRevert':'onMessage');
					//then, if we're reverting, show the original message
					if (!reverting && this.options.revert) this.showMessage(true);
					//if we're done, call the chain stack
					else this.callChain.delay(this.options.delay, this);
				}.bind(this));
			}.bind(this));
		}).delay((reverting)?this.options.delay:10, this);
		return this;
	}
});

// Begin: Source/UI/StickyWin.Modal.js
/*
---

name: StickyWin.Modal

description: This script extends StickyWin and StickyWin.Fx classes to add Mask functionality.

license: MIT-Style License

requires: [More/Mask, StickyWin]

provides: StickyWin.Modal
...
*/
StickyWin.Modal = new Class({

	Extends: StickyWin,

	options: {
		modalize: true,
		maskOptions: {
			style: {
				'background-color':'#333',
				opacity:0.8
			}
		},
		hideOnClick: true,
		getWindowManager: function(){ return StickyWin.ModalWM; }
	},

	initialize: function(options) {
		this.options.maskTarget = this.options.maskTarget || document.body;
		this.setOptions(options);
		this.mask = new Mask(this.options.maskTarget, this.options.maskOptions).addEvent('click', function() {
			if (this.options.hideOnClick) this.hide();
		}.bind(this));
		this.parent(options);
	},

	show: function(showModal){
		if ([showModal, this.options.modalize].pick()) this.mask.show();
		this.parent();
	},

	hide: function(hideModal){
		if ([hideModal, true].pick()) this.mask.hide();
		this.parent();
	},

	destroy: function(){
		this.mask.destroy();
		this.parent.apply(this, arguments);
	}

});

StickyWin.ModalWM = new StickyWin.Stacker({
	zIndexBase: 11000
});
if (StickyWin.Fx) StickyWin.Fx.Modal = StickyWin.Modal;

// Begin: Source/UI/StickyWin.Fx.js
/*
---

name: StickyWin.Fx

description: Extends StickyWin to create popups that fade in and out.

license: MIT-style license.

requires: [More/Class.Refactor, Core/Fx.Tween, StickyWin]

provides: StickyWin.Fx

...
*/
if (!Browser.Engine.trident){
	StickyWin = Class.refactor(StickyWin, {
		options: {
			//fadeTransition: 'sine:in:out',
			fade: true,
			fadeDuration: 150
		},
		hideWin: function(){
			if (this.options.fade) this.fade(0);
			else this.previous();
		},
		showWin: function(){
			if (this.options.fade) this.fade(1);
			else this.previous();
		},
		hide: function(){
			this.previous(this.options.fade);
		},
		show: function(){
			this.previous(this.options.fade);
		},
		fade: function(to){
			if (!this.fadeFx) {
				this.win.setStyles({
					opacity: 0,
					display: 'block'
				});
				var opts = {
					property: 'opacity',
					duration: this.options.fadeDuration
				};
				if (this.options.fadeTransition) opts.transition = this.options.fadeTransition;
				this.fadeFx = new Fx.Tween(this.win, opts);
			}
			if (to > 0) {
				this.win.setStyle('display','block');
				this.position();
			}
			this.fadeFx.clearChain();
			this.fadeFx.start(to).chain(function (){
				if (to == 0) {
					this.win.setStyle('display', 'none');
					this.fireEvent('onClose');
				} else {
					this.fireEvent('onDisplay');
				}
			}.bind(this));
			return this;
		}
	});
}
StickyWin.Fx = StickyWin;


// Begin: Source/UI/StickyWin.Drag.js
/*
---

name: StickyWin.Drag

description: Implements drag and resize functionaity into StickyWin.Fx. See StickyWin.Fx for the options.

license: MIT-Style License

requires: [More/Class.Refactor, More/Drag.Move, StickyWin]

provides: StickyWin.Drag

...
*/
StickyWin = Class.refactor(StickyWin, {
	options: {
		draggable: false,
		dragOptions: {
			onComplete: function(){}
		},
		dragHandleSelector: '.dragHandle',
		resizable: false,
		resizeOptions: {
			onComplete: function(){}
		},
		resizeHandleSelector: ''
	},
	setContent: function(){
		this.previous.apply(this, arguments);
		if (this.options.draggable) this.makeDraggable();
		if (this.options.resizable) this.makeResizable();
		return this;
	},
	makeDraggable: function(){
		var toggled = this.toggleVisible(true);
		if (this.options.useIframeShim) {
			this.makeIframeShim();
			var onComplete = (this.options.dragOptions.onComplete);
			this.options.dragOptions.onComplete = function(){
				onComplete();
				this.shim.position();
			}.bind(this);
		}
		if (this.options.dragHandleSelector) {
			var handle = this.win.getElement(this.options.dragHandleSelector);
			if (handle) {
				handle.setStyle('cursor','move');
				this.options.dragOptions.handle = handle;
			}
		}
		this.win.makeDraggable(this.options.dragOptions);
		if (toggled) this.toggleVisible(false);
	}, 
	makeResizable: function(){
		var toggled = this.toggleVisible(true);
		if (this.options.useIframeShim) {
			this.makeIframeShim();
			var onComplete = (this.options.resizeOptions.onComplete);
			this.options.resizeOptions.onComplete = function(){
				onComplete();
				this.shim.position();
			}.bind(this);
		}
		if (this.options.resizeHandleSelector) {
			var handle = this.win.getElement(this.options.resizeHandleSelector);
			if (handle) this.options.resizeOptions.handle = this.win.getElement(this.options.resizeHandleSelector);
		}
		this.win.makeResizable(this.options.resizeOptions);
		if (toggled) this.toggleVisible(false);
	},
	toggleVisible: function(show){
		if (!this.visible && show == null || show) {
			this.win.setStyles({
				display: 'block',
				opacity: 0
			});
			return true;
		} else if (show != null && !show){
			this.win.setStyles({
				display: 'none',
				opacity: 1
			});
			return false;
		}
		return false;
	}
});
StickyWin.Fx = StickyWin;

// Begin: Source/Behaviors/Behavior.StickyWin.js
/*
---
name: Behavior.StickyWin
description: Behaviors for StickyWin instances.
provides: [Behavior.StickyWin]
requires: [Behavior/Behavior, /StickyWin, /StickyWin.Modal, /StickyWin.Fx, /StickyWin.Drag, More/Array.Extras]
script: Behavior.Tabs.js

...
*/

Behavior.addGlobalFilters({

	'StickyWin.Modal': {
		defaults: {
			destroyOnClose: true,
			closeOnClickOut: true,
			closeOnEsc: true,
			draggable: false,
			resizable: false
		},
		returns: StickyWin.Modal,
		setup: function(element, api) {
			var flex = element.getElement('.flex'),
			    height = api.getAs(Number, 'height') || (window.getSize().y * .9);

			if (flex){
				element.measure(function(){
					var tmp = new Element('span', { styles: { display: 'none' }}).replaces(flex),
					    remainder = element.getSize().y;
					var padding = ['padding-top', 'padding-bottom', 'margin-top', 'margin-bottom', 'border-top-width', 'border-bottom-width'].map(function(style){
						return flex.getStyle(style).toInt();
					}).sum();
					flex.setStyle('max-height', height - remainder - padding);
					flex.replaces(tmp);
				});
			}

			var options = Object.merge({
					content: element
				},
				Object.cleanValues(
					api.getAs({
						closeClassName: String,
						pinClassName: String,
						className: String,
						edge: String,
						position: String,
						offset: Object,
						relativeTo: String,
						width: Number,
						height: Number,
						timeout: Number,
						destroyOnClose: Boolean,
						closeOnClickOut: Boolean,
						closeOnEsc: Boolean,
						//modal options
						maskOptions: Object,
						//draggable options
						draggable: Boolean,
						dragHandleSelector: String,
						resizable: Boolean,
						resizeHandleSelector: String
					})
				)
			);

			if (options.mask) options.closeOnClickOut = false;

			var sw = new StickyWin.Modal(options);
			api.onCleanup(function(){
				if (!sw.destroyed) sw.destroy();
			});
			sw.addEvent('destroy', function(){
				api.cleanup(element);
			});
			return sw;
		}
	}

});


// Begin: Source/Delegators/Delegator.AddRemoveClass.js
/*
---
description: Provides methods to add/remove/toggle a class on a given target.
provides: [Delegator.ToggleClass, Delegator.AddClass, Delegator.RemoveClass, Delegator.AddRemoveClass]
requires: [Behavior/Delegator, Core/Element]
script: Delegator.AddRemoveClass.js
name: Delegator.AddRemoveClass

...
*/
(function(){

	var triggers = {};

	['add', 'remove', 'toggle'].each(function(action){

		triggers[action + 'Class'] = {
			require: ['class'],
			handler: function(event, link, api){
				var target = link;
				if (api.get('target')) {
					target = link.getElement(api.get('target'));
					if (!target) api.fail('could not locate target element to ' + action + ' its class', link);
				}
				target[action + 'Class'](api.get('class'));
			}
		};

	});

	Delegator.register('click', triggers);

})();

// Begin: Source/UI/StickyWin.Alert.js
/*
---

name: StickyWin.Alert

description: Defines StickyWin.Alert, a simple little alert box with a close button.

license: MIT-Style License

requires: [StickyWin.Modal, StickyWin.UI]

provides: [StickyWin.Alert, StickyWin.Error, StickyWin.alert, StickyWin.error]

...
*/
StickyWin.Alert = new Class({
	Implements: Options,
	Extends: StickyWin.Modal,
	options: {
		destroyOnClose: true,
		modalOptions: {
			modalStyle: {
				zIndex: 11000
			}
		},
		zIndex: 110001,
		uiOptions: {
			width: 560,
			buttons: [
				{text: 'Ok'}
			]
		},
		getWindowManager: function(){}
	},
	initialize: function(caption, message, options) {
		this.message = message;
		this.caption = caption;
		this.setOptions(options);
		this.setOptions({
			content: this.build()
		});
		this.parent(options);
	},
	makeMessage: function() {
		return new Element('p', {
			'class': 'errorMsg SWclearfix',
			styles: {
				margin: 0,
				minHeight: 10
			},
			html: this.message
		});
	},
	build: function(){
		return StickyWin.ui(this.caption, this.makeMessage(), this.options.uiOptions);
	}
});

StickyWin.Error = new Class({
	Extends: StickyWin.Alert, 
	makeMessage: function(){
		var message = this.parent();
		new Element('img', {
			src: (this.options.baseHref || Clientcide.assetLocation + '/simple.error.popup') + '/icon_problems_sm.gif',
			'class': 'bang clearfix',
			styles: {
				'float': 'left',
				width: 30,
				height: 30,
				margin: '3px 5px 5px 0px'
			}
		}).inject(message, 'top');
		return message;
	}
});

StickyWin.alert = function(caption, message, options) {
	if (typeOf(options) == "string") options = {baseHref: options};
	return new StickyWin.Alert(caption, message, options);
};

StickyWin.error = function(caption, message, options) {
	return new StickyWin.Error(caption, message, options);
};

// Begin: Source/UI/StickyWin.Confirm.js
/*
---
name: StickyWin.Confirm

description: Defines StickyWin.Conferm, a simple confirmation box with an ok and a close button.

license: MIT-Style License

requires: StickyWin.Alert

provides: [StickyWin.Confirm, StickyWin.confirm]

...
*/
StickyWin.Confirm = new Class({
	Extends: StickyWin.Alert,
	options: {
		uiOptions: {
			width: 560
		}
	},
	build: function(callback){
		this.setOptions({
			uiOptions: {
				buttons: [
					{
						text: 'OK', 
						onClick: callback || function(){
							this.fireEvent('confirm');
						}.bind(this)
					},
					{text: 'Nie, dziękuję'}
				]
			}
		});
		return this.parent();
	}
});

StickyWin.confirm = function(caption, message, callback, options) {
	return new StickyWin.Confirm(caption, message, options).addEvent('confirm', callback);
};

// Begin: Source/Delegators/Delegator.Ajax.js
/*
---
description: Provides functionality for links that load content into a target element via ajax.
provides: [Delegator.Ajax]
requires: [Behavior/Delegator, Core/Request.HTML, More/Spinner]
script: Delegator.Ajax.js
name: Delegator.Ajax
...
*/

(function(){

	Delegator.register('click', 'Ajax', {
		require: ['target'],
		defaults: {
			action: 'injectBottom'
		},
		handler: function(event, link, api){
			var target,
				action = api.get('action'),
				selector = api.get('target');
			if (selector) {
				if (selector == "self") {
					target = element;
				} else {
					target = link.getElement(selector);
				}
			}

			if (!target) api.fail('ajax trigger error: element matching selector %s was not found', selector);

			var requestTarget = new Element('div');

			var spinnerTarget = api.get('spinner-target');
			if (spinnerTarget) spinnerTarget = link.getElement(spinnerTarget);

			event.preventDefault();
			new Request.HTML(
				Object.cleanValues({
					method: 'get',
					evalScripts: api.get('evalScripts'),
					url: api.get('href') || link.get('href'),
					spinnerTarget: spinnerTarget || target,
					useSpinner: api.getAs(Boolean, 'useSpinner'),
					update: requestTarget,
					onSuccess: function(){
						//reverse the elements and inject them
						//reversal is required since it injects each after the target
						//pushing down the previously added element
						var elements = requestTarget.getChildren();
						if (api.get('filter')){
							elements = new Element('div').adopt(elements).getElements(api.get('filter'));
						}
						switch(action){
							case 'replace':
								var container = target.getParent();
								elements.reverse().injectAfter(target);
								api.fireEvent('destroyDom', target);
								target.destroy();
								api.fireEvent('ammendDom', [container, elements]);
								break;
							case 'update':
								api.fireEvent('destroyDom', target.getChildren());
								target.empty();
								elements.inject(target);
								api.fireEvent('ammendDom', [target, elements]);
								break;
							default:
								//injectTop, injectBottom, injectBefore, injectAfter
								if (action == "injectTop" || action == "injectAfter") elements.reverse();
								elements[action](target);
								api.fireEvent('ammendDom', [target, elements]);
						}
					}
				})
			).send();
		}
	});

})();




// Begin: Source/Layout/HoverGroup.js
/*
---

name: HoverGroup

description: Manages mousing in and out of multiple objects (think drop-down menus).

license: MIT-Style License

requires: [Core/Class.Extras, Core/Element.Event, More/Class.Binds]

provides: HoverGroup

...
*/

var HoverGroup = new Class({
	Implements: [Options, Events],
	Binds: ['enter', 'leave', 'remain'],
	options: {
		//onEnter: function(){},
		//onLeave: function(){},
		elements: [],
		delay: 300,
		start: ['mouseenter'],
		remain: [],
		end: ['mouseleave']
	},
	initialize: function(options) {
		this.setOptions(options);
		this.attachTo(this.options.elements);
		this.addEvents({
			leave: function(){
				this.active = false;
			},
			enter: function(){
				this.active = true;
			}
		});
	},
	elements: [],
	attachTo: function(elements, detach){
		var starters = {}, remainers = {}, enders = {};
		elements = Array.from(document.id(elements)||$$(elements));
		this.options.start.each(function(start) {
			starters[start] = this.enter;
		}, this);
		this.options.end.each(function(end) {
			enders[end] = this.leave;
		}, this);
		this.options.remain.each(function(remain){
			remainers[remain] = this.remain;
		}, this);
		if (detach) {
			elements.each(function(el) {
				el.removeEvents(starters).removeEvents(enders).removeEvents(remainers);
				this.elements.erase(el);
			}, this);
		} else {
			elements.each(function(el){
				el.addEvents(starters).addEvents(enders).addEvents(remainers);
			});
			this.elements.combine(elements);
		}
		return this;
	},
	detachFrom: function(elements){
		this.attachTo(elements, true);
	},
	enter: function(e){
		this.isMoused = true;
		this.assert(e);
	},
	leave: function(e){
		this.isMoused = false;
		this.assert(e);
	},
	remain: function(e){
		if (this.active) this.enter(e);
	},
	assert: function(e){
		clearTimeout(this.assertion);
		this.assertion = (function(){
			if (!this.isMoused && this.active) this.fireEvent('leave', e);
			else if (this.isMoused && !this.active) this.fireEvent('enter', e);
		}).delay(this.options.delay, this);
	}
});

// Begin: Source/Fx/Behavior.FxAccordion.js
/*
---
description: Creates an Fx.Accordion from any element with Accordion in its data-behavior property.  Uses the .toggle elements within the element as the toggles and the .target elements as the targets. 
provides: [Behavior.Accordion, Behavior.FxAccordion]
requires: [Behavior/Behavior, More/Fx.Accordion, Behavior/Element.Data, More/Object.Extras]
script: Behavior.Accordion.js
name: Behavior.Accordion
...
*/

Behavior.addGlobalFilter('Accordion', {
	deprecated: {
		headers:'toggler-elements',
		sections:'section-elements'
	},
	defaults: {
		// defaults from Fx.Accordion:
		display: 0,
		height: true,
		width: false,
		opacity: true,
		alwaysHide: false,
		trigger: 'click',
		initialDisplayFx: true,
		resetHeight: true,
		headers: '.header',
		sections: '.section'
	},
	setup: function(element, api){
		var options = Object.cleanValues(
			api.getAs({
				fixedHeight: Number,
				fixedWidth: Number,
				display: Number,
				show: Number,
				height: Boolean,
				width: Boolean,
				opacity: Boolean,
				alwaysHide: Boolean,
				trigger: String,
				initialDisplayFx: Boolean,
				resetHeight: Boolean
			})
		);
		var accordion = new Fx.Accordion(element.getElements(api.get('headers')), element.getElements(api.get('sections')), options);
		api.onCleanup(accordion.detach.bind(accordion));
		return accordion;
	}
});

// Begin: Source/Layout/MultipleOpenAccordion.js
/*
---
name: MultipleOpenAccordion

description: Creates a Mootools Fx.Accordion that allows the user to open more than one element.

license: MIT-Style License

requires: [Core/Element.Event, More/Fx.Reveal]

provides: MultipleOpenAccordion

...
*/
var MultipleOpenAccordion = new Class({
	Implements: [Options, Events, Chain],
	options: {
		togglers: [],
		elements: [],
		openAll: false,
		firstElementsOpen: [0],
		fixedHeight: null,
		fixedWidth: null,
		height: true,
		opacity: true,
		width: false
		//onActive: function(){},
		//onBackground: function(){}
	},
	togglers: [],
	elements: [],
	initialize: function(options){
		var args = Array.link(arguments, {options: Type.isObject, elements: Type.isElements});
		this.setOptions(args.options);
		elements = $$(this.options.elements);
		$$(this.options.togglers).each(function(toggler, idx){
			this.addSection(toggler, elements[idx], idx);
		}, this);
		if (this.togglers.length) {
			if (this.options.openAll) this.showAll();
			else this.toggleSections(this.options.firstElementsOpen, false, true);
		}
		this.openSections = this.showSections.bind(this);
		this.closeSections = this.hideSections.bind(this);
	},
	addSection: function(toggler, element){
		toggler = document.id(toggler);
		element = document.id(element);
		var test = this.togglers.contains(toggler);
		var len = this.togglers.length;
		this.togglers.include(toggler);
		this.elements.include(element);
		var idx = this.togglers.indexOf(toggler);
		var displayer = this.toggleSection.bind(this, idx);
		toggler.addEvent('click', displayer).store('multipleOpenAccordion:display', displayer);
		var mode;
		if (this.options.height && this.options.width) mode = "both";
		else mode = (this.options.height)?"vertical":"horizontal";
		element.store('moa:reveal', new Fx.Reveal(element, {
			transitionOpacity: this.options.opacity,
			mode: mode,
			heightOverride: this.options.fixedHeight,
			widthOverride: this.options.fixedWidth
		}));
		return this;
	},
	removeSection: function(toggler) {
		var idx = this.togglers.indexOf(toggler);
		var element = this.elements[idx];
		element.dissolve();
		this.togglers.erase(toggler);
		this.elements.erase(element);
		this.detach(toggler);
		return this;
	},
	detach: function(toggler){
		var remove = function(toggler) {
			toggler.removeEvent(this.options.trigger, toggler.retrieve('multipleOpenAccordion:display'));
		}.bind(this);
		if (!toggler) this.togglers.each(remove);
		else remove(toggler);
		return this;
	},
	onComplete: function(idx, callChain){
		this.fireEvent(this.elements[idx].isDisplayed()?'onActive':'onBackground', [this.togglers[idx], this.elements[idx]]);
		this.callChain();
		return this;
	},
	showSection: function(idx, useFx){
		this.toggleSection(idx, useFx, true);
	},
	hideSection: function(idx, useFx){
		this.toggleSection(idx, useFx, false);
	},
	toggleSection: function(idx, useFx, show, callChain){
		var method = show?'reveal':show != null?'dissolve':'toggle';
		callChain = [callChain, true].pick();
		var el = this.elements[idx];
		if (useFx != null ? useFx : true) {
			el.retrieve('moa:reveal')[method]().chain(
				this.onComplete.bind(this, idx, callChain)
			);
		} else {
				if (method == "toggle") el.toggle();
				else el[method == "reveal"?'show':'hide']();
				this.onComplete(idx, callChain);
		}
		return this;
	},
	toggleAll: function(useFx, show){
		var method = show?'reveal':(show!=null)?'disolve':'toggle';
		var last = this.elements.getLast();
		this.elements.each(function(el, idx){
			this.toggleSection(idx, useFx, show, el == last);
		}, this);
		return this;
	},
	toggleSections: function(sections, useFx, show) {
		last = sections.getLast();
		this.elements.each(function(el,idx){
			this.toggleSection(idx, useFx, sections.contains(idx)?show:!show, idx == last);
		}, this);
		return this;
	},
	showSections: function(sections, useFx){
		sections.each(function(i){
			this.showSection(i, useFx);
		}, this);
	},
	hideSections: function(sections, useFx){
		sections.each(function(i){
			this.hideSection(i, useFx);
		}, this);
	},
	showAll: function(useFx){
		return this.toggleAll(useFx, true);
	},
	hideAll: function(useFx){
		return this.toggleAll(useFx, false);
	}
});


// Begin: Source/Forms/Form.Validator.Tips.js
/*
---
name: Form.Validator.Tips

description: Form.Validator using StickyWin.PointyTip.

license: MIT-Style License

requires: [More/Form.Validator.Inline, Clientcide, StickyWin.PointyTip]

provides: Form.Validator.Tips
...
*/
Form.Validator.Tips = new Class({
	Extends: Form.Validator.Inline,
	options: {
		pointyTipOptions: {
			point: "left",
			width: 250
		},
		tipCaption: ''
	},
	showAdvice: function(className, field){
		var advice = this.getAdvice(field);
		if (advice && !advice.visible){
			advice.show();
			advice.position();
			advice.pointy.positionPointer();
		}
	},
	hideAdvice: function(className, field){
		var advice = this.getAdvice(field);
		if (advice && advice.visible) advice.hide();
	},
	getAdvice: function(className, field) {
		var params = Array.link(arguments, {field: Type.isElement});
		return params.field.retrieve('PointyTip');
	},
	advices: [],
	makeAdvice: function(className, field, error, warn){
		if (!error && !warn) return;
		var advice = field.retrieve('PointyTip');
		if (!advice){
			var cssClass = warn?'warning-advice':'validation-advice';
			var msg = new Element('ul', {
				styles: {
					margin: 0,
					padding: 0,
					listStyle: 'none'
				}
			});
			var li = this.makeAdviceItem(className, field);
			if (li) msg.adopt(li);
			field.store('validationMsgs', msg);
			advice = new StickyWin.PointyTip(this.options.tipCaption, msg, Object.merge(this.options.pointyTipOptions, {
				showNow: false,
				relativeTo: field,
				inject: {
					target: this.element
				}
			}));
			this.advices.push(advice);
			advice.msgs = {};
			field.store('PointyTip', advice);
			document.id(advice).addClass(cssClass).set('id', 'advice-'+className+'-'+this.getFieldId(field));			
		}
		field.store('advice-'+className, advice);
		this.appendAdvice(className, field, error, warn);
		advice.pointy.positionPointer();
		return advice;
	},
	validateField: function(field, force){
		var advice = this.getAdvice(field);
		var anyVis = this.advices.some(function(a){ return a.visible; });
		if (anyVis && this.options.serial) {
			if (advice && advice.visible) {
				var passed = this.parent(field, force);
				if (!field.hasClass('validation-failed')) advice.hide();
			}
			return passed;
		}
		var msgs = field.retrieve('validationMsgs');
		if (msgs) msgs.getChildren().hide();
		if ((field.hasClass('validation-failed') || field.hasClass('warning')) && advice) advice.show();
		if (this.options.serial) {
			var fields = this.element.getElements('.validation-failed, .warning');
			if (fields.length) {
				fields.each(function(f, i) {
					var adv = this.getAdvice(f);
					if (adv) adv.hide();
				}, this);
			}
		}
		return this.parent(field, force);
	},
	makeAdviceItem: function(className, field, error, warn){
		if (!error && !warn) return;
		var advice = this.getAdvice(field);
		var errorMsg = this.makeAdviceMsg(field, error, warn);
		if (advice && advice.msgs[className]) return advice.msgs[className].set('html', errorMsg);
		return new Element('li', {
			html: errorMsg,
			style: {
				display: 'none'
			}
		});
	},
	makeAdviceMsg: function(field, error, warn){
		var errorMsg = (warn)?this.warningPrefix:this.errorPrefix;
			errorMsg += (this.options.useTitles) ? field.title || error:error;
		return errorMsg;
	},
	appendAdvice: function(className, field, error, warn) {
		var advice = this.getAdvice(field);
		if (advice.msgs[className]) return advice.msgs[className].set('html', this.makeAdviceMsg(field, error, warn)).show();
		var li = this.makeAdviceItem(className, field, error, warn);
		if (!li) return;
		li.inject(field.retrieve('validationMsgs')).show();
		advice.msgs[className] = li;
	},
	insertAdvice: function(advice, field){
		//Check for error position prop
		var props = field.get('validatorProps');
		//Build advice
		if (!props.msgPos || !document.id(props.msgPos)) {
			switch (field.type.toLowerCase()) {
				case 'radio':
					var p = field.getParent().adopt(advice);
					break;
				default: 
					document.id(advice).inject(document.id(field), 'after');
			};
		} else {
			document.id(props.msgPos).grab(advice);
		}
		advice.position();
	}
});

if (window.FormValidator) FormValidator.Tips = Form.Validator.Tips;

// Begin: Source/3rdParty/Autocompleter.Observer.js
/*
---
name: Autocompleter.Observer

description: Observe formelements for changes

version: 1.0rc3

license: MIT-style license
author: Harald Kirschner <mail [at] digitarald.de>
copyright: Author

requires: [Core/Class.Extras, Core/Element.Event, Core/JSON]

provides: [Autocompleter.Observer, Observer]

...
 */
var Observer = new Class({

	Implements: [Options, Events],

	options: {
		periodical: false,
		delay: 1000
	},

	initialize: function(el, onFired, options){
		this.setOptions(options);
		this.addEvent('onFired', onFired);
		this.element = document.id(el) || $$(el);
		/* Clientcide change */
		this.boundChange = this.changed.bind(this);
		this.resume();
	},

	changed: function() {
		var value = this.element.get('value');
		if ($equals(this.value, value)) return;
		this.clear();
		this.value = value;
		this.timeout = this.onFired.delay(this.options.delay, this);
	},

	setValue: function(value) {
		this.value = value;
		this.element.set('value', value);
		return this.clear();
	},

	onFired: function() {
		this.fireEvent('onFired', [this.value, this.element]);
	},

	clear: function() {
		clearTimeout(this.timeout || null);
		return this;
	},
	/* Clientcide change */
	pause: function(){
		clearTimeout(this.timeout);
		clearTimeout(this.timer);
		this.element.removeEvent('keyup', this.boundChange);
		return this;
	},
	resume: function(){
		this.value = this.element.get('value');
		if (this.options.periodical) this.timer = this.changed.periodical(this.options.periodical, this);
		else this.element.addEvent('keyup', this.boundChange);
		return this;
	}

});

var $equals = function(obj1, obj2) {
	return (obj1 == obj2 || JSON.encode(obj1) == JSON.encode(obj2));
};

// Begin: Source/3rdParty/Autocompleter.js
/*
---
name: Autocompleter

description: An auto completer class from <a href=\"http://digitarald.de\">http://digitarald.de</a>.

version: 1.1.1

license: MIT-style license

author: Harald Kirschner <mail [at] digitarald.de>

copyright: Author

requires: [Core/Fx.Tween, More/Element.Shortcuts, More/Element.Forms, More/IframeShim, Observer, Clientcide]

provides: [Autocompleter, Autocompleter.Base]

...
*/
var Autocompleter = {};

var OverlayFix = IframeShim;

Autocompleter.Base = new Class({

	Implements: [Options, Events],

	options: {
		minLength: 1,
		markQuery: true,
		width: 'inherit',
		maxChoices: 10,
//		injectChoice: null,
//		customChoices: null,
		className: 'autocompleter-choices',
		zIndex: 42,
		delay: 400,
		observerOptions: {},
		fxOptions: {},
//		onSelection: function(){},
//		onShow: function(){},
//		onHide: function(){},
//		onBlur: function(){},
//		onFocus: function(){},
//		onChoiceConfirm: function(){},

		autoSubmit: false,
		overflow: false,
		overflowMargin: 25,
		selectFirst: false,
		filter: null,
		filterCase: false,
		filterSubset: false,
		forceSelect: false,
		selectMode: true,
		choicesMatch: null,

		multiple: false,
		separator: ', ',
		autoTrim: true,
		allowDupes: false,

		cache: true,
		relative: false
	},

	initialize: function(element, options) {
		this.element = document.id(element);
		this.setOptions(options);
		this.options.separatorSplit = new RegExp("\s*["+
		  this.options.separator == " " ? " " : this.options.separator.trim()+
		  "]\s*/");
		this.build();
		this.observer = new Observer(this.element, this.prefetch.bind(this), Object.merge({
			'delay': this.options.delay
		}, this.options.observerOptions));
		this.queryValue = null;
		if (this.options.filter) this.filter = this.options.filter.bind(this);
		var mode = this.options.selectMode;
		this.typeAhead = (mode == 'type-ahead');
		this.selectMode = (mode === true) ? 'selection' : mode;
		this.cached = [];
	},

	/**
	 * build - Initialize DOM
	 *
	 * Builds the html structure for choices and appends the events to the element.
	 * Override this function to modify the html generation.
	 */
	build: function() {
		if (document.id(this.options.customChoices)) {
			this.choices = this.options.customChoices;
		} else {
			this.choices = new Element('ul', {
				'class': this.options.className,
				'styles': {
					'zIndex': this.options.zIndex
				}
			}).inject(document.body);
			this.relative = false;
			if (this.options.relative || this.element.getOffsetParent() != document.body) {
				this.choices.inject(this.element, 'after');
				this.relative = this.element.getOffsetParent();
			}
			this.fix = new OverlayFix(this.choices);
		}
		if (!this.options.separator.test(this.options.separatorSplit)) {
			this.options.separatorSplit = this.options.separator;
		}
		this.fx = (!this.options.fxOptions) ? null : new Fx.Tween(this.choices, Object.merge({
			'property': 'opacity',
			'link': 'cancel',
			'duration': 200
		}, this.options.fxOptions)).addEvent('onStart', Chain.prototype.clearChain).set(0);
		this.element.setProperty('autocomplete', 'off')
			.addEvent((Browser.ie || Browser.chrome || Browser.safari) ? 'keydown' : 'keypress', this.onCommand.bind(this))
			.addEvent('click', this.onCommand.bind(this, false))
			.addEvent('focus', function(){
				this.toggleFocus.delay(100, this, [true]);
			}.bind(this));
			//.addEvent('blur', this.toggleFocus.create({bind: this, arguments: false, delay: 100}));
		document.addEvent('click', function(e){
			if (e.target != this.choices) this.toggleFocus(false);
		}.bind(this));
	},

	destroy: function() {
		if (this.fix) this.fix.dispose();
		this.choices = this.selected = this.choices.destroy();
	},

	toggleFocus: function(state) {
		this.focussed = state;
		if (!state) this.hideChoices(true);
		this.fireEvent((state) ? 'onFocus' : 'onBlur', [this.element]);
	},

	onCommand: function(e) {
		if (!e && this.focussed) return this.prefetch();
		if (e && e.key && !e.shift) {
			switch (e.key) {
				case 'enter': case 'tab':
					if (this.element.value != this.opted) return true;
					if (this.selected && this.visible) {
						this.choiceSelect(this.selected);
						this.fireEvent('choiceConfirm', this.selected);
						return !!(this.options.autoSubmit);
					}
					break;
				case 'up': case 'down':
					if (!this.prefetch() && this.queryValue !== null) {
						var up = (e.key == 'up');
						this.choiceOver((this.selected || this.choices)[
							(this.selected) ? ((up) ? 'getPrevious' : 'getNext') : ((up) ? 'getLast' : 'getFirst')
						](this.options.choicesMatch), true);
					}
					return false;
				case 'esc':
					this.hideChoices(true);
					break;
			}
		}
		return true;
	},

	setSelection: function(finish) {
		var input = this.selected.inputValue, value = input;
		var start = this.queryValue.length, end = input.length;
		if (input.substr(0, start).toLowerCase() != this.queryValue.toLowerCase()) start = 0;
		if (this.options.multiple) {
			var split = this.options.separatorSplit;
			value = this.element.value;
			start += this.queryIndex;
			end += this.queryIndex;
			var old = value.substr(this.queryIndex).split(split, 1)[0];
			value = value.substr(0, this.queryIndex) + input + value.substr(this.queryIndex + old.length);
			if (finish) {
				var space = /[^\s,]+/;
				var tokens = value.split(this.options.separatorSplit).filter(space.test, space);
				if (!this.options.allowDupes) tokens = [].combine(tokens);
				var sep = this.options.separator;
				value = tokens.join(sep) + sep;
				end = value.length;
			}
		}
		this.observer.setValue(value);
		this.opted = value;
		if (finish || this.selectMode == 'pick') start = end;
		this.element.selectRange(start, end);
		this.fireEvent('onSelection', [this.element, this.selected, value, input]);
	},

	showChoices: function() {
		var match = this.options.choicesMatch, first = this.choices.getFirst(match);
		this.selected = this.selectedValue = null;
		if (this.fix) {
			var pos = this.element.getCoordinates(this.relative), width = this.options.width || 'auto';
			this.choices.setStyles({
				'left': pos.left,
				'top': pos.bottom,
				'width': (width === true || width == 'inherit') ? pos.width : width
			});
		}
		if (!first) return;
		if (!this.visible) {
			this.visible = true;
			this.choices.setStyle('display', '');
			if (this.fx) this.fx.start(1);
			this.fireEvent('onShow', [this.element, this.choices]);
		}
		if (this.options.selectFirst || this.typeAhead || first.inputValue == this.queryValue) this.choiceOver(first, this.typeAhead);
		var items = this.choices.getChildren(match), max = this.options.maxChoices;
		var styles = {'overflowY': 'hidden', 'height': ''};
		this.overflown = false;
		if (items.length > max) {
			var item = items[max - 1];
			styles.overflowY = 'scroll';
			styles.height = item.getCoordinates(this.choices).bottom;
			this.overflown = true;
		};
		this.choices.setStyles(styles);
		if (this.fix){
			this.fix.show();
		}
	},

	hideChoices: function(clear) {
		if (clear) {
			var value = this.element.value;
			if (this.options.forceSelect) value = this.opted;
			if (this.options.autoTrim) {
				value = value.split(this.options.separatorSplit).filter(function(){ return arguments[0]; }).join(this.options.separator);
			}
			this.observer.setValue(value);
		}
		if (!this.visible) return;
		this.visible = false;
		this.observer.clear();
		var hide = function(){
			this.choices.setStyle('display', 'none');
			if (this.fix){
				this.fix.hide();
			}
		}.bind(this);
		if (this.fx) this.fx.start(0).chain(hide);
		else hide();
		this.fireEvent('onHide', [this.element, this.choices]);
	},

	prefetch: function() {
		var value = this.element.value, query = value;
		if (this.options.multiple) {
			var split = this.options.separatorSplit;
			var values = value.split(split);
			var index = this.element.getCaretPosition();
			var toIndex = value.substr(0, index).split(split);
			var last = toIndex.length - 1;
			index -= toIndex[last].length;
			query = values[last];
		}
		if (query.length < this.options.minLength) {
			this.hideChoices();
		} else {
			if (query === this.queryValue || (this.visible && query == this.selectedValue)) {
				if (this.visible) return false;
				this.showChoices();
			} else {
				this.queryValue = query;
				this.queryIndex = index;
				if (!this.fetchCached()) this.query();
			}
		}
		return true;
	},

	fetchCached: function() {
		if (!this.options.cache
			|| !this.cached
			|| !this.cached.length
			|| this.cached.length >= this.options.maxChoices
			|| this.queryValue) return false;
		this.update(this.filter(this.cached));
		return true;
	},

	update: function(tokens) {
		this.choices.empty();
		this.cached = tokens;
		if (!tokens || !tokens.length) {
			this.hideChoices();
		} else {
			if (this.options.maxChoices < tokens.length && !this.options.overflow) tokens.length = this.options.maxChoices;
			tokens.each(this.options.injectChoice || function(token){
				var choice = new Element('li', {'html': this.markQueryValue(token)});
				choice.inputValue = token;
				this.addChoiceEvents(choice).inject(this.choices);
			}, this);
			this.showChoices();
		}
	},

	choiceOver: function(choice, selection) {
		if (!choice || choice == this.selected) return;
		if (this.selected) this.selected.removeClass('autocompleter-selected');
		this.selected = choice.addClass('autocompleter-selected');
		this.fireEvent('onSelect', [this.element, this.selected, selection]);
		if (!selection) return;
		this.selectedValue = this.selected.inputValue;
		if (this.overflown) {
			var coords = this.selected.getCoordinates(this.choices), margin = this.options.overflowMargin,
				top = this.choices.scrollTop, height = this.choices.offsetHeight, bottom = top + height;
			if (coords.top - margin < top && top) this.choices.scrollTop = Math.max(coords.top - margin, 0);
			else if (coords.bottom + margin > bottom) this.choices.scrollTop = Math.min(coords.bottom - height + margin, bottom);
		}
		if (this.selectMode) this.setSelection();
	},

	choiceSelect: function(choice) {
		if (choice) this.choiceOver(choice);
		this.setSelection(true);
		this.queryValue = false;
		this.hideChoices();
	},

	filter: function(tokens) {
		return (tokens || this.tokens).filter(function(token) {
			return this.test(token);
		}, new RegExp(((this.options.filterSubset) ? '' : '^') + this.queryValue.escapeRegExp(), (this.options.filterCase) ? '' : 'i'));
	},

	/**
	 * markQueryValue
	 *
	 * Marks the queried word in the given string with <span class="autocompleter-queried">*</span>
	 * Call this i.e. from your custom parseChoices, same for addChoiceEvents
	 *
	 * @param		{String} Text
	 * @return		{String} Text
	 */
	markQueryValue: function(str) {
		if (!this.options.markQuery || !this.queryValue) return str;
		var regex = new RegExp('(' + ((this.options.filterSubset) ? '' : '^') + this.queryValue.escapeRegExp() + ')', (this.options.filterCase) ? '' : 'i');
		return str.replace(regex, '<span class="autocompleter-queried">$1</span>');
	},

	/**
	 * addChoiceEvents
	 *
	 * Appends the needed event handlers for a choice-entry to the given element.
	 *
	 * @param		{Element} Choice entry
	 * @return		{Element} Choice entry
	 */
	addChoiceEvents: function(el) {
		return el.addEvents({
			'mouseover': this.choiceOver.bind(this, el),
			'click': function(){
				var result = this.choiceSelect(el);
				this.fireEvent('choiceConfirm', this.selected);
				return result;
			}.bind(this)
		});
	}
});


// Begin: Source/3rdParty/Autocompleter.Local.js
/*
---
name: Autocompleter.Local

description: Allows Autocompleter to use an object in memory for autocompletion (instead of retrieving via ajax).

version: 1.1.1

license: MIT-style license
author: Harald Kirschner <mail [at] digitarald.de>
copyright: Author

requires: [Autocompleter.Base]

provides: [Autocompleter.Local]
...
 */
Autocompleter.Local = new Class({

	Extends: Autocompleter.Base,

	options: {
		minLength: 0,
		delay: 200
	},

	initialize: function(element, tokens, options) {
		this.parent(element, options);
		this.tokens = tokens;
	},

	query: function() {
		this.update(this.filter());
	}

});


// Begin: Source/Delegators/Delegator.FxReveal.js
/*
---
description: Provides methods to reveal, dissolve, nix, and toggle using Fx.Reveal.
provides: [Delegator.FxReveal, Delegator.Reveal, Delegator.ToggleReveal, Delegator.Dissolve, Delegator.Nix]
requires: [Behavior/Delegator, More/Fx.Reveal]
script: Delegator.FxReveal.js
name: Delegator.FxReveal

...
*/
(function(){

	var triggers = {};

	['reveal', 'toggleReveal', 'dissolve', 'nix'].each(function(action){

		triggers[action] = {
			handler: function(event, link, api){
				var target = link;
				if (api.get('target')) {
					target = link.getElement(api.get('target'));
					if (!target) api.fail('could not locate target element to ' + action, link);
				}

				var fxOptions = api.get('fxOptions');
				if (fxOptions) target.set('reveal', fxOptions);
				target.get('reveal');
				if (action == 'toggleReveal') target.get('reveal').toggle();
				else target[action]();
				event.preventDefault();
			}
		};

	});

	Delegator.register('click', triggers);

})();

// Begin: Source/3rdParty/Autocompleter.Clientcide.js
/*
---
name: Autocompleter.Clientcide

description: Adds Clientcide css assets to autocompleter automatically.

license: MIT-Style License

requires: [Autocompleter.Base]

provides: Autocompleter.Clientcide
...
*/
(function(){
	Autocompleter.Base = Class.refactor(Autocompleter.Base, {
		initialize: function(a1,a2,a3) {
			this.previous(a1,a2,a3);
			this.writeStyle();
		},
		writeStyle: function(){
			window.addEvent('domready', function(){
				if (document.id('AutocompleterCss')) return;
				new Element('link', {
					rel: 'stylesheet', 
					media: 'screen', 
					type: 'text/css', 
					href: (this.options.baseHref || Clientcide.assetLocation + '/autocompleter')+'/Autocompleter.css', 
					id: 'AutocompleterCss'
				}).inject(document.head);
			}.bind(this));
		}
	});
})();


// Begin: Source/Forms/Behavior.FormValidator.js
/*
---
description: Adds an instance of Form.Validator.Inline to any form with the class .form-validator.
provides: [Behavior.FormValidator]
requires: [Behavior/Behavior, More/Form.Validator.Inline, More/Object.Extras]
script: Behavior.FormValidator.js
name: Behavior.FormValidator
...
*/

Behavior.addGlobalFilter('FormValidator', {
	defaults: {
		useTitles: true,
		scrollToErrorsOnSubmit: true,
		scrollToErrorsOnBlur: false,
		scrollToErrorsOnChange: false,
		ignoreHidden: true,
		ignoreDisabled: true,
		useTitles: false,
		evaluateOnSubmit: true,
		evaluateFieldsOnBlur: true,
		evaluateFieldsOnChange: true,
		serial: true,
		stopOnFailure: true
	},
	setup: function(element, api) {
		//instantiate the form validator
		var validator = element.retrieve('validator');
		if (!validator) {
			validator = new Form.Validator.Inline(element, 
				Object.cleanValues(
					api.getAs({
						useTitles: Boolean,
						scrollToErrorsOnSubmit: Boolean,
						scrollToErrorsOnBlur: Boolean,
						scrollToErrorsOnChange: Boolean,
						ignoreHidden: Boolean,
						ignoreDisabled: Boolean,
						useTitles: Boolean,
						evaluateOnSubmit: Boolean,
						evaluateFieldsOnBlur: Boolean,
						evaluateFieldsOnChange: Boolean,
						serial: Boolean,
						stopOnFailure: Boolean
					})
				)
			);
		}
		//if the api provides a getScroller method, which should return an instance of
		//Fx.Scroll, use it instead
		if (api.getScroller) {
			validator.setOptions({
				onShow: function(input, advice, className) {
					api.getScroller().toElement(input);
				},
				scrollToErrorsOnSubmit: false
			});
		}
		return validator;
	}

});

// Begin: Source/Drag/Behavior.Resizable.js
/*
---
description: Creates instances of Drag for resizable elements.
provides: [Behavior.Resizable]
requires: [Behavior/Behavior, More/Drag]
script: Behavior.Resizable.js
name: Behavior.Resizable
...
*/
Behavior.addGlobalFilter('Resizable', {
	//deprecated options
	deprecated: {
		handle: 'resize-handle',
		child: 'resize-child'
	},
	deprecatedAsJSON: {
		modifiers: 'resize-modifiers'
	},
	setup: function(element, api){
		var options = {};
		if (api.get('handle')) options.handle = element.getElement(api.get('handle'));
		if (api.get('modifiers')) options.modifiers = api.getAs(Object, 'modifiers');
		var target = element;
		if (api.get('child')) target = element.getElement(api.get('child'));
		var drag = target.makeResizable(options);
		api.onCleanup(drag.detach.bind(drag));
		return drag;
	}

});


// Begin: Source/Forms/Behavior.FormRequest.js
/*
---
description: Makes form elements with a FormRequest data filter automatically update via Ajax.
provides: [Behavior.FormRequest]
requires: [Behavior/Behavior, More/Form.Request, Behavior/Element.Data]
script: Behavior.FormRequest.js
name: Behavior.FormRequest
...
*/

Behavior.addGlobalFilter('FormRequest', {
	defaults: {
		resetForm: true
	},
	setup: function(element, api){
		var updateElement,
		    update = api.get('update');
		if (update =="self") {
			updateElement = element;
		} else {
			updateElement = element.getElement(update);
		}

		if (!updateElement) api.fail('Could not find target element for form update');

		//pass null for the update element argument; JFrame does our updating for us
		var req = new Form.Request(element, updateElement, {
			requestOptions: {
				filter: api.get('filter'),
				spinnerTarget: updateElement
			},
			resetForm: api.get('resetForm') || /* noReset is deprecated: */ !element.hasClass('noReset')
		});
		api.onCleanup(req.detach.bind(req));
		return req;
	}

});


// Begin: Source/Layout/TabSwapper.js
/*
---

name: TabSwapper

description: Handles the scripting for a common UI layout; the tabbed box.

license: MIT-Style License

requires: [Core/Element.Event, Core/Fx.Tween, Core/Fx.Morph, Core/Element.Dimensions, More/Element.Shortcuts, More/Element.Measure]

provides: TabSwapper

...
*/
var TabSwapper = new Class({
	Implements: [Options, Events],
	options: {
		// initPanel: null,
		// smooth: false,
		// smoothSize: false,
		// maxSize: null,
		// onActive: function(){},
		// onActiveAfterFx: function(){},
		// onBackground: function(){}
		// cookieName: null,
		selectedClass: 'tabSelected',
		mouseoverClass: 'tabOver',
		deselectedClass: '',
		rearrangeDOM: true,
		effectOptions: {
			duration: 500
		},
		cookieDays: 999
	},
	tabs: [],
	sections: [],
	clickers: [],
	sectionFx: [],
	initialize: function(options){
		this.setOptions(options);
		var prev = this.setup();
		if (prev) return prev;
		if (this.options.initPanel != null) this.show(this.options.initPanel);
		else if (this.options.cookieName && this.recall()) this.show(this.recall().toInt());
		else this.show(0);

	},
	setup: function(){
		var opt = this.options,
		    sections = $$(opt.sections),
		    tabs = $$(opt.tabs);
		if (tabs[0] && tabs[0].retrieve('tabSwapper')) return tabs[0].retrieve('tabSwapper');
		var clickers = $$(opt.clickers);
		tabs.each(function(tab, index){
			this.addTab(tab, sections[index], clickers[index], index);
		}, this);
	},
	addTab: function(tab, section, clicker, index){
		tab = document.id(tab); clicker = document.id(clicker); section = document.id(section);
		//if the tab is already in the interface, just move it
		if (this.tabs.indexOf(tab) >= 0 && tab.retrieve('tabbered')
			 && this.tabs.indexOf(tab) != index && this.options.rearrangeDOM) {
			this.moveTab(this.tabs.indexOf(tab), index);
			return this;
		}
		//if the index isn't specified, put the tab at the end
		if (index == null) index = this.tabs.length;
		//if this isn't the first item, and there's a tab
		//already in the interface at the index 1 less than this
		//insert this after that one
		if (index > 0 && this.tabs[index-1] && this.options.rearrangeDOM) {
			tab.inject(this.tabs[index-1], 'after');
			section.inject(this.tabs[index-1].retrieve('section'), 'after');
		}
		this.tabs.splice(index, 0, tab);
		clicker = clicker || tab;

		tab.addEvents({
			mouseout: function(){
				tab.removeClass(this.options.mouseoverClass);
			}.bind(this),
			mouseover: function(){
				tab.addClass(this.options.mouseoverClass);
			}.bind(this)
		});

		clicker.addEvent('click', function(e){
			e.preventDefault();
			this.show(index);
		}.bind(this));

		tab.store('tabbered', true);
		tab.store('section', section);
		tab.store('clicker', clicker);
		this.hideSection(index);
		return this;
	},
	removeTab: function(index){
		var now = this.tabs[this.now];
		if (this.now == index){
			if (index > 0) this.show(index - 1);
			else if (index < this.tabs.length) this.show(index + 1);
		}
		this.now = this.tabs.indexOf(now);
		return this;
	},
	moveTab: function(from, to){
		var tab = this.tabs[from];
		var clicker = tab.retrieve('clicker');
		var section = tab.retrieve('section');

		var toTab = this.tabs[to];
		var toClicker = toTab.retrieve('clicker');
		var toSection = toTab.retrieve('section');

		this.tabs.erase(tab).splice(to, 0, tab);

		tab.inject(toTab, 'before');
		clicker.inject(toClicker, 'before');
		section.inject(toSection, 'before');
		return this;
	},
	show: function(i){
		if (this.now == null) {
			this.tabs.each(function(tab, idx){
				if (i != idx)
					this.hideSection(idx);
			}, this);
		}
		this.showSection(i).save(i);
		return this;
	},
	save: function(index){
		if (this.options.cookieName)
			Cookie.write(this.options.cookieName, index, {duration:this.options.cookieDays});
		return this;
	},
	recall: function(){
		return (this.options.cookieName) ? Cookie.read(this.options.cookieName) : false;
	},
	hideSection: function(idx) {
		var tab = this.tabs[idx];
		if (!tab) return this;
		var sect = tab.retrieve('section');
		if (!sect) return this;
		if (sect.getStyle('display') != 'none') {
			this.lastHeight = sect.getSize().y;
			sect.setStyle('display', 'none');
			tab.swapClass(this.options.selectedClass, this.options.deselectedClass);
			this.fireEvent('onBackground', [idx, sect, tab]);
		}
		return this;
	},
	showSection: function(idx) {
		var tab = this.tabs[idx];
		if (!tab) return this;
		var sect = tab.retrieve('section');
		if (!sect) return this;
		var smoothOk = this.options.smooth && !Browser.ie;
		if (this.now != idx) {
			if (!tab.retrieve('tabFx'))
				tab.store('tabFx', new Fx.Morph(sect, this.options.effectOptions));
			var overflow = sect.getStyle('overflow');
			var start = {
				display:'block',
				overflow: 'hidden'
			};
			if (smoothOk) start.opacity = 0;
			var effect = false;
			if (smoothOk) {
				effect = {opacity: 1};
			} else if (sect.getStyle('opacity').toInt() < 1) {
				sect.setStyle('opacity', 1);
				if (!this.options.smoothSize) this.fireEvent('onActiveAfterFx', [idx, sect, tab]);
			}
			if (this.options.smoothSize) {
				var size = sect.getDimensions().height;
				if (this.options.maxSize != null && this.options.maxSize < size)
					size = this.options.maxSize;
				if (!effect) effect = {};
				effect.height = size;
			}
			if (this.now != null) this.hideSection(this.now);
			if (this.options.smoothSize && this.lastHeight) start.height = this.lastHeight;
			sect.setStyles(start);
			var finish = function(){
				this.fireEvent('onActiveAfterFx', [idx, sect, tab]);
				sect.setStyles({
					height: this.options.maxSize == effect.height ? this.options.maxSize : "auto",
					overflow: overflow
				});
				sect.getElements('input, textarea').setStyle('opacity', 1);
			}.bind(this);
			if (effect) {
				tab.retrieve('tabFx').start(effect).chain(finish);
			} else {
				finish();
			}
			this.now = idx;
			this.fireEvent('onActive', [idx, sect, tab]);
		}
		tab.swapClass(this.options.deselectedClass, this.options.selectedClass);
		return this;
	}
});


// Begin: Source/Behaviors/Behavior.Tabs.js
/*
---
name: Behavior.Tabs
description: Adds a tab interface (TabSwapper instance) for elements with .css-tab_ui. Matched with tab elements that are .tabs and sections that are .tab_sections.
provides: [Behavior.Tabs]
requires: [Behavior/Behavior, /TabSwapper, More/String.QueryString]
script: Behavior.Tabs.js

...
*/

Behavior.addGlobalFilters({

	Tabs: {
		defaults: {
			'tabs-selector': '.tabs>li',
			'sections-selector': '.tab_sections>li',
			smooth: true,
			smoothSize: true,
			rearrangeDOM: false
		},
		setup: function(element, api) {
			var tabs = element.getElements(api.get('tabs-selector'));
			var sections = element.getElements(api.get('sections-selector'));
			if (tabs.length != sections.length || tabs.length == 0) {
				api.fail('warning; sections and sections are not of equal number. tabs: %o, sections: %o', tabs, sections);
			}
			var getHash = function(){
				return window.location.hash.substring(1, window.location.hash.length).parseQueryString();
			};

			var ts = new TabSwapper(
				Object.merge(
					{
						tabs: tabs,
						sections: sections,
						initPanel: api.get('hash') ? getHash()[api.get('hash')] : null
					},
					Object.cleanValues(
						api.getAs({
							smooth: Boolean,
							smoothSize: Boolean,
							rearrangeDOM: Boolean,
							selectedClass: String,
							initPanel: Number
						})
					)
				)
			);
			ts.addEvent('active', function(index){
				if (api.get('hash')) {
					var hash = getHash();
					hash[api.get('hash')] = index;
					window.location.hash = Object.cleanValues(Object.toQueryString(hash));
				}
				api.fireEvent('layout:display', sections[0].getParent());
			});
			element.store('TabSwapper', ts);
			return ts;
		}
	}
});


// Begin: Source/Layout/Collapsible.js
/*
---
name: Collapsible

description: Enables a dom element to, when clicked, hide or show (it toggles) another dom element. Kind of an Accordion for one item.

license: MIT-Style License

requires: [Core/Element.Event, More/Fx.Reveal]

provides: Collapsible
...
*/
var Collapsible = new Class({
	Extends: Fx.Reveal,
	initialize: function(clicker, section, options) {
		this.clicker = document.id(clicker);
		this.section = document.id(section);
		this.parent(this.section, options);
		this.boundtoggle = this.toggle.bind(this);
		this.attach();
	},
	attach: function(){
		this.clicker.addEvent('click', this.boundtoggle);
	},
	detach: function(){
		this.clicker.removeEvent('click', this.boundtoggle);
	}
});
//legacy, this class originated w/ a typo. nice!
var Collapsable = Collapsible;

// Begin: Source/Behaviors/Behavior.Collapsible.js
/*
---
name: Behavior.Collapsible
description: Instantiates a Collapsible class.
provides: [Behavior.Collapsible]
requires: [Behavior/Behavior, /Collapsible]
script: Behavior.Collapsible.js

...
*/

Behavior.addGlobalFilters({

	Collapsible: {
		defaults: {
			target: '+'
		},
		setup: function(element, api) {
			var target = element.getElement(api.get('target'));
			var col = new Collapsible(element, target);
			col.addEvent('reveal', function(){
				api.fireEvent('layout:display', target);
			});
			api.onCleanup(col.detach.bind(col));
			return col;
		},
		returns: Collapsible
	}

});

// Begin: Source/3rdParty/Autocompleter.Remote.js
/*
---
name: Autocompleter.Remote

version: 1.1.1

description: Autocompleter extensions that enable requests for JSON/XHTML data for input suggestions.

license: MIT-style license
author: Harald Kirschner <mail [at] digitarald.de>
copyright: Author

requires: [Autocompleter.Base, Core/Request.HTML, Core/Request.JSON]

provides: [Autocompleter.Remote, Autocompleter.Ajax, Autocompleter.Ajax.Base, Autocompleter.Ajax.Json, Autocompleter.Ajax.Xhtml]

...
 */

Autocompleter.Ajax = {};

Autocompleter.Ajax.Base = new Class({

	Extends: Autocompleter.Base,

	options: {
		// onRequest: function(){},
		// onComplete: function(){},
		postVar: 'value',
		postData: {},
		ajaxOptions: {}
	},

	initialize: function(element, options) {
		this.parent(element, options);
		var indicator = document.id(this.options.indicator);
		if (indicator) {
			this.addEvents({
				'onRequest': indicator.show.bind(indicator),
				'onComplete': indicator.hide.bind(indicator)
			}, true);
		}
	},

	query: function(){
		var data = Object.clone(this.options.postData);
		data[this.options.postVar] = this.queryValue;
		this.fireEvent('onRequest', [this.element, this.request, data, this.queryValue]);
		this.request.send({'data': data});
	},

	/**
	 * queryResponse - abstract
	 *
	 * Inherated classes have to extend this function and use this.parent(resp)
	 *
	 * @param		{String} Response
	 */
	queryResponse: function() {
		this.fireEvent('onComplete', [this.element, this.request, this.response]);
	}

});

Autocompleter.Ajax.Json = new Class({

	Extends: Autocompleter.Ajax.Base,

	initialize: function(el, url, options) {
		this.parent(el, options);
		this.request = new Request.JSON(Object.merge({
			'url': url,
			'link': 'cancel'
		}, this.options.ajaxOptions)).addEvent('onComplete', this.queryResponse.bind(this));
	},

	queryResponse: function(response) {
		this.parent();
		this.update(response);
	}

});

Autocompleter.Ajax.Xhtml = new Class({

	Extends: Autocompleter.Ajax.Base,

	initialize: function(el, url, options) {
		this.parent(el, options);
		this.request = new Request.HTML(Object.merge({
			'url': url,
			'link': 'cancel',
			'update': this.choices
		}, this.options.ajaxOptions)).addEvent('onComplete', this.queryResponse.bind(this));
	},

	queryResponse: function(tree, elements) {
		this.parent();
		if (!elements || !elements.length) {
			this.hideChoices();
		} else {
			this.choices.getChildren(this.options.choicesMatch).each(this.options.injectChoice || function(choice) {
				var value = choice.innerHTML;
				choice.inputValue = value;
				this.addChoiceEvents(choice.set('html', this.markQueryValue(value)));
			}, this);
			this.showChoices();
		}

	}

});


// Begin: Source/Delegators/Delegator.SubmitLink.js
/*
---
description: When the user clicks a link with this delegator, submit the target form.
provides: [Delegator.SubmitLink]
requires: [Behavior/Delegator]
script: Delegator.SubmitLink.js
name: Delegator.SubmitLink

...
*/

(function(){

	var injectValues = function(form, data){
		var injected = new Elements();
		Object.each(data, function(value, key){
			if (typeOf(value) == 'array'){
				value.each(function(val){
					injected.push(
						new Element('input', {
							type: 'hidden',
							name: key,
							value: val
						}).inject(form)
					);
				});
			} else {
				new Element('input', {
					type: 'hidden',
					name: key,
					value: value
				}).inject(form);
			}
		});
		return injected;
	};

	Delegator.register('click', {

		'submitLink': function(event, el, api){
			var formSelector = api.get('form') || '!form';
			var form = el.getElement(formSelector);
			if (!form) api.fail('Cannot find target form: "' +formSelector+ '" for submitLink delegator.');
			var rq = form.retrieve('form.request');
			var extraData = api.getAs(Object, 'extra-data');
			var injected;
			if (extraData) injected = injectValues(form, extraData);
			if (rq) rq.send();
			else form.submit();
			if (injected) injected.destroy();
		}

	});

})();

// Begin: Source/UI/StickyWin.Ajax.js
/*
---

name: StickyWin.Ajax

description: Adds ajax functionality to all the StickyWin classes.

license: MIT-Style License

requires: [Core/Request, StickyWin, StickyWin.UI]

provides: [StickyWin.Ajax, StickyWin.Modal.Ajax]

...
*/
(function(){
	var SWA = function(extend){
		return {
			Extends: extend,
			options: {
				//onUpdate: function(){},
				url: '',
				showNow: false,
				requestOptions: {
					method: 'get',
					evalScripts: true
				},
				wrapWithUi: false, 
				caption: '',
				uiOptions:{},
				handleResponse: function(response){
					var responseScript = "";
					this.Request.response.text.stripScripts(function(script){	responseScript += script; });
					if (this.options.wrapWithUi) response = StickyWin.ui(this.options.caption, response, this.options.uiOptions);
					this.setContent(response);
					this.show();
					if (this.evalScripts) Browser.exec(responseScript);
					this.fireEvent('update');
				}
			},
			initialize: function(options){
				var showNow;
				if (options && options.showNow) {
					showNow = true;
					options.showNow = false;
				}
				this.parent(options);
				this.evalScripts = this.options.requestOptions.evalScripts;
				this.options.requestOptions.evalScripts = false;
				this.createRequest();
				if (showNow) this.update();
			},
			createRequest: function(){
				this.Request = new Request(this.options.requestOptions).addEvent('onSuccess',
					this.options.handleResponse.bind(this));
			},
			update: function(url, options){
				this.Request.setOptions(options).send({url: url||this.options.url});
				return this;
			}
		};
	};
	try {	StickyWin.Ajax = new Class(SWA(StickyWin)); } catch(e){}
	try {	StickyWin.Modal.Ajax = new Class(SWA(StickyWin.Modal)); } catch(e){}
})();

// Begin: Source/Interface/Behavior.HtmlTable.js
/*
---
description: Creates instances of HtmlTable for tables with the HtmlTable filter
provides: [Behavior.HtmlTable]
requires: [Behavior/Behavior, More/HtmlTable.Sort, More/HtmlTable.Zebra, More/HtmlTable.Select, More/Object.Extras]
script: Behavior.HtmlTable.js
name: Behavior.HtmlTable
...
*/

/*
	Refactor HtmlTable.Sort functionality:
	don't detect the parsers on startup
	wait for click
	unless the option says to sort on startup
*/

HtmlTable = Class.refactor(HtmlTable, {
	detectParsers: function(){
		//if we are parsing on startup, then set ready to true
		if (this.options.sortOnStartup) this._readyToParse = true;
		//otherwise, don't parse until ready
		if (this._readyToParse) {
			this._parsed = true;
			return this.previous();
		}
	},
	headClick: function(event, el){
		//on click, if we haven't parsed, set ready to true and run the parser
		if (!this._parsed) {
			this._readyToParse = true;
			this.setParsers();
		}
		this.previous(event, el);
	},
	sort: function(index, reverse, pre){
		//don't sort if we haven't parsed; this prevents sorting on startup
		if (this._parsed) return this.previous(index, reverse, pre);
	}
});


Behavior.addGlobalFilter('HtmlTable', {

	deprecatedAsJSON: {
		resize: 'table-resize'
	},

	defaults: {
		classNoSort: 'noSort'
	},

	setup: function(element, api){
		//make all data tables sortable
		var firstSort;
		element.getElements('thead th').each(function(th, i){
			if (firstSort == null && !th.hasClass('noSort')) firstSort = i;
			if (th.hasClass('defaultSort')) firstSort = i;
		});
		api.setDefault('firstSort', firstSort);
		var multiselectable = api.getAs(Boolean, 'multiselect', element.hasClass('multiselect')) || api.getAs(Boolean, 'allowMultiSelect');
		var table = new HtmlTable(element,
			Object.cleanValues({
				parsers: api.getAs(Array, 'parsers'),
				sortOnStartup: api.getAs(Boolean, 'sortOnStartup'),
				sortIndex: api.getAs(Number, 'firstSort'),
				sortable: api.getAs(Boolean, 'sortable', /* deprecated default: */ element.hasClass('sortable')),
				classNoSort: api.get('noSort'),
				selectable: api.getAs(Boolean, 'selectable', /* deprecated default: */ element.hasClass('selectable') || multiselectable),
				allowMultiSelect: multiselectable,
				useKeyboard: api.getAs(Boolean, 'useKeybaord', /* deprecated default: */ !element.hasClass('noKeyboard'))
			})
		);
		api.onCleanup(function(){
			if (table.keyboard) table.keyboard.relinquish();
		});
		// Hack to make tables not jump around in Chrome.
		if (Browser.Engine.webkit) {
			var width = element.style.width;
			element.setStyle('width', '99%');
			(function() {
				element.style.width = width;
			}).delay(1);
		}
		return table;
	}

});

HtmlTable.defineParsers({
	//A parser to allow numeric sorting by any value.
	dataSortNumeric: {
		match: /data-sort-numeric/,
		convert: function() {
			return this.getElement('[data-sort-numeric]').getData('sort-numeric').toInt();
		},
		number: true
	},
	//A parser to allow lexicographical sorting by any string.
	dataSortString: {
		match: /data-sort-string/,
		convert: function() {
			return this.getElement('[data-sort-string]').getData('sort-string');
		},
		number: false
	}
});

// Begin: Source/Drag/Behavior.Sortable.js
/*
---
description: Creates instances of Sortable for sortable lists, optionally updating an input or element with the sort state.
provides: [Behavior.Sortable]
requires: [Behavior/Behavior, More/Sortables, More/Scroller]
script: Behavior.Sortable.js
name: Behavior.Sortable
...
*/
(function(){

Behavior.addGlobalFilter('Sortable',  {

	defaults: {
		clone: true,
		opacity: 0.6
	},
	deprecated: {
		lists: 'sort-lists',
		state: 'sort-state',
		property: 'sort-property',
		'property-child': 'property-child'
	},
	setup: function(element, api){
		//get the list selector
		var lists = api.get('lists');
		//if present, get the elements
		if (lists) lists = element.getElements(lists);
		//else the target element is the list
		else lists = element;
		//get the state target; this is the element (typically an input) to put the new state value on change
		var target = api.get('state');
		if (target) target = element.getParent().getElement(target);

		//get the property to read from each sorted element
		var property = api.get('property');
		//if the value is on a child element, a selector is specified to find it (see docs)
		var property_child = api.get('property-child');
	
		var scrollParent;
		var sort = new Sortables(lists, {
			clone: api.getAs(Boolean, 'clone'),
			opacity: api.getAs(Number, 'opacity'),
			onStart: function(element, clone){
				clone.addClass('clone');
				var scroller,
				    scrollElement = isScrollable(element) ? element : getScrollParent(element);
				if (scrollElement && scrollElement != scrollParent) {
					scroller = new Scroller(scrollElement);
					scrollElement.store('behavior:scroller', scroller);
					scrollParent = scrollElement;
				} else {
					if (scrollParent) scroller = scrollParent.retrieve('behavior:scroller');
				}
				if (scroller) scroller.attach();
			},
			onComplete: function(){
				if (target) {
					target.set(target.get('tag') == 'input' ? 'value' : 'html', sort.serialize(function(item){
						if (property_child) item = item.getElement(property_child);
						var isInput = ['input', 'textarea', 'select'].contains(item.get('tag'));
						return item.get(property || 'name') || isInput ? item.get('value') : item.get('id');
					}).join(','));
				}
				if (scrollParent) scrollParent.retrieve('behavior:scroller').detach();
			}
		});
		api.onCleanup(sort.detach.bind(sort));
		return sort;
	}

});

var isBody = function(element){
	return (/^(?:body|html)$/i).test(element.tagName);
};

var isScrollable = function(element){
	return ['scroll', 'auto'].contains(element.getStyle('overflow'));
};

var getScrollParent = function(element){
	var scrollParent,
	    parent = element.getParent();
	while (!scrollParent){
		if (isBody(parent) || isScrollable(parent)){
			scrollParent = parent;
		} else {
			parent = parent.getParent();
		}
	}
	return scrollParent;
};

})();

// Begin: Source/UI/StickyWin.Prompt.js
/*
---

name: StickyWin.Prompt

description: Defines StickyWin.Prompt, a little prompt box with an input as well as ok close buttons.

license: MIT-Style License

requires: StickyWin.Confirm

provides: [StickyWin.Prompt, StickyWin.prompt]

...
*/

StickyWin.Prompt = new Class({
	Extends: StickyWin.Confirm,
	options: {
		defaultValue: ''
	},
	initialize: function(message, header, options){
		this.addEvent('display', function(){
			this.input.select();
		}.bind(this));
		this.parent.apply(this, arguments);
	},
	makeMessage: function(){
		this.input = new Element('input', {
			value: this.options.defaultValue,
			type: 'text',
			id: 'foo',
			styles: {
				width: '100%'
			},
			events: {
				keyup: function(e) {
					if (e.key == 'enter') {
						this.fireEvent('confirm', this.input.get('value'));
						this.hide();
					}
				}.bind(this)
			}
		});
		return new Element('div').adopt(this.parent()).adopt(this.input);
	},
	build: function(){
		return this.parent(function(){
			this.fireEvent('confirm', this.input.get('value'));
		}.bind(this));
	}
});

StickyWin.prompt = function(caption, message, callback, options) {
	return new StickyWin.Prompt(caption, message, options).addEvent('confirm', callback);
};

// Begin: Source/3rdParty/Autocompleter.JSONP.js
/*
---
name: Autocompleter.JSONP

description: Implements Request.JSONP support for the Autocompleter class.

license: MIT-Style License

requires: [More/Request.JSONP, Autocompleter.Remote]

provides: [Autocompleter.JSONP]
...
*/

Autocompleter.JSONP = new Class({

	Extends: Autocompleter.Ajax.Json,

	options: {
		postVar: 'query',
		jsonpOptions: {},
//		onRequest: function(){},
//		onComplete: function(){},
//		filterResponse: function(){},
		minLength: 1
	},

	initialize: function(el, url, options) {
		this.url = url;
		this.setOptions(options);
		this.parent(el, options);
	},

	query: function(){
		var data = Object.clone(this.options.jsonpOptions.data||{});
		data[this.options.postVar] = this.queryValue;
		this.jsonp = new Request.JSONP(Object.merge({url: this.url, data: data},	this.options.jsonpOptions));
		this.jsonp.addEvent('onComplete', this.queryResponse.bind(this));
		this.fireEvent('onRequest', [this.element, this.jsonp, data, this.queryValue]);
		this.jsonp.send();
	},

	queryResponse: function(response) {
		this.parent();
		var data = (this.options.filter)?this.options.filter.apply(this, [response]):response;
		this.update(data);
	}

});
Autocompleter.JsonP = Autocompleter.JSONP;

// Begin: Source/Forms/Behavior.OverText.js
/*
---
description: Sets up an input to have an OverText instance for inline labeling. This is a global filter.
provides: [Behavior.OverText]
requires: [Behavior/Behavior, More/OverText]
script: Behavior.OverText.js
name: Behavior.OverText
...
*/
Behavior.addGlobalFilter('OverText', function(element, api){

	//create the overtext instance
	var ot = new OverText(element);
	if (element.get('class')) {
		element.get('class').split(' ').each(function(cls) {
			if (cls) ot.text.addClass('overText-'+cls);
		});
	}
	element.getBehaviors().each(function(filter){
		if (filter != "OverText") ot.text.addClass('overText-'+filter);
	});

	//this method updates the text position with a slight delay
	var updater = function(){
		ot.reposition.delay(10, ot);
	};

	//update the position whenever the behavior element is shown
	api.addEvent('layout:display', updater);

	api.onCleanup(function(){
		api.removeEvent('layout:display', updater);
		ot.destroy();
	});

	return ot;

});


// Begin: Source/Behaviors/Behavior.Autocompleter.js
/*
---
name: Behavior.Autocompleter
description: Adds support for Autocompletion on form inputs.
provides: [Behavior.Autocomplete, Behavior.Autocompleter]
requires: [Behavior/Behavior, /Autocompleter.Local, /Autocompleter.Remote]
script: Behavior.Autocomplete.js

...
*/

Behavior.addGlobalFilters({

	/*
		takes elements (inputs) with the data filter "Autocomplete" and creates a autocompletion ui for them
		that either completes against a list of terms supplied as a property of the element (dtaa-autocomplete-tokens)
		or fetches them from a server. In both cases, the tokens must be an array of values. Example:

		<input data-behavior="Autocomplete" data-autocomplete-tokens="['foo', 'bar', 'baz']"/>

		Alternately, you can specify a url to submit the current typed token to get back a list of valid values in the
		same format (i.e. a JSON response; an array of strings). Example:

		<input data-behavior="Autocomplete" data-autocomplete-url="/some/API/for/autocomplete"/>

		When the values ar fetched from the server, the server is sent the current term (what the user is typing) as
		a post variable "term" as well as the entire contents of the input as "value".

		An additional data property for autocomplete-options can be specified; this must be a JSON encoded string
		of key/value pairs that configure the Autocompleter instance (see documentation for the Autocompleter classes
		online at http://www.clientcide.com/docs/3rdParty/Autocompleter but also available as a markdown file in the
		clientcide repo fetched by hue in the thirdparty directory).

		Note that this JSON string can't include functions as callbacks; if you require amore advanced usage you should
		write your own Behavior filter or filter plugin.

	*/

	Autocomplete: {
		defaults: {
			minLength: 1,
			selectMode: 'type-ahead',
			overflow: true,
			selectFirst: true,
			multiple: true,
			separator: ' ',
			allowDupes: true,
			postVar: 'term'
		},
		setup: function(element, api){
			var options = Object.cleanValues(
				api.getAs({
					minLength: Number,
					selectMode: String,
					overflow: Boolean,
					selectFirst: Boolean,
					multiple: Boolean,
					separator: String,
					allowDupes: Boolean,
					postVar: String
				})
			);

			if (element.getData('autocomplete-url')) {
				var aaj = new Autocompleter.Ajax.Json(element, element.getData('autocomplete-url'), options);
				aaj.addEvent('request', function(el, req, data, value){
					data['value'] = el.get('value');
				});
				return aaj;
			} else {
				var tokens = api.getAs(Array, 'tokens');
				if (!tokens) {
					dbug.warn('Could not set up autocompleter; no local tokens found.');
					return;
				}
				return new Autocompleter.Local(element, tokens, options);
			}
		}
	}

});



