MootoolsDownload = new Class(
{

	// Imported scripts.json
	packages: {
		"Core": {

			"Core": {
				"deps": ["Core"],
				"desc": "The core of mootools. Is required by all the others, but has no dependancies and can be used stand-alone. Contains Basic functions and Initializers."
			}

		},

		"Class": {

			"Class": {
				"deps": ["Core"],
				"desc": "Contains the Class Function, to easily create, extend, implement reusable Classes."
			},

			"Class.Extras": {
				"deps": ["Core", "Class", "Array", "Function"],
				"desc": "Contains Extra Class methods to be implemented in your own classes, to ease the execution of the most common tasks."
			}

		},

		"Native": {

			"Array": {
				"deps": ["Core"],
				"desc": "Contains Array Prototypes, like copy, each, contains, remove."
			},

			"String": {
				"deps": ["Core"],
				"desc": "Contains String and Number Prototypes, like camelCase, capitalize, test, toInt."
			},

			"Function": {
				"deps": ["Core"],
				"desc": "Contains Function Prototypes, like create, bind, pass, delay, periodical."
			},

			"Number": {
				"deps": ["Core"],
				"desc": "Contains Number prototypes."
			},

			"Element": {
				"deps": ["Array", "String", "Function", "Number", "Class"],
				"desc": "One of the most important item of mootools, contains the dollar function, the dollars function, and an handful of cross-browser, time-saver methods to let you easily work with HTML Elements."
			}

		},

		"Element": {

			"Element.Event": {
				"deps": ["Element"],
				"desc": "Contains the Event Class, Element methods to deal with Element events, custom Events, and the Function prototype bindWithEvent."
			},

			"Element.Filters": {
				"deps": ["Element"],
				"desc": "Adds Filtering Capabilities to Element Collections."
			},

			"Element.Selectors": {
				"deps": ["Element.Filters"],
				"desc": "Allows to select elements by css selectors, at any depth."
			},

			"Element.Form": {
				"deps": ["Element"],
				"desc": "Contains Element methods to work with forms."
			},

			"Element.Dimensions": {
				"deps": ["Element"],
				"desc": "Contains Element methods to work with element size, scroll, or position in space."
			}

		},

		"Window": {

			"Window.DomReady": {
				"deps": ["Element.Event"],
				"desc": "Contains the domready event."
			},

			"Window.Size": {
				"deps": ["Element"],
				"desc": "Function to get window dimensions in a crossbrowser fashion."
			}

		},

		"Effects": {

			"Fx.Base": {
				"deps": ["Class.Extras", "Element"],
				"desc": "Contains the basic animation logic."
			},

			"Fx.CSS": {
				"deps": ["Fx.Base"],
				"desc": "Contains the CSS animation logic. Used by Styles based Effects."
			},

			"Fx.Style": {
				"deps": ["Fx.CSS"],
				"desc": "Effect to change any css property for an element."
			},

			"Fx.Styles": {
				"deps": ["Fx.CSS"],
				"desc": "Effect to change any number of css properties for an element."
			},

			"Fx.Elements": {
				"deps": ["Fx.CSS"],
				"desc": "Effect to change any number of css properties to any number of elements."
			},

			"Fx.Scroll": {
				"deps": ["Fx.Base", "Element.Event", "Element.Dimensions", "Window.Size"],
				"desc": "Effect to scroll any element, including the window."
			},

			"Fx.Slide": {
				"deps": ["Fx.Base"],
				"desc": "Effect to slide an element."
			},

			"Fx.Transitions": {
				"deps": ["Fx.Base"],
				"desc": "Contains transitions to be used with any of the above effects."
			}

		},

		"Drag": {

			"Drag.Base": {
				"deps": ["Class.Extras", "Element.Event"],
				"desc": "the basic drag class. you can use this to drag, resize and whatever you want to do when dragging the mouse."
			},

			"Drag.Move": {
				"deps": ["Drag.Base", "Element.Dimensions"],
				"desc": "Drag.Move extends Drag.Base, adding support for draggable containers and droppables."
			}

		},

		"Remote": {

			"XHR": {
				"deps": ["Class.Extras", "String"],
				"desc": "Basic XMLHttpRequest Object Class Wrapper."
			},

			"Ajax": {
				"deps": ["XHR", "Element.Form"],
				"desc": "Extends the basic XHR with additional methods and utility functions."
			},

			"Cookie": {
				"deps": ["Core", "Array", "String", "Function", "Number"],
				"desc": "Lets you Create/Read/Delete Cookies."
			},

			"Json": {
				"deps": ["Core", "Array", "String", "Function", "Number"],
				"desc": "Json encoder/decoder."
			},

			"Json.Remote": {
				"deps": ["XHR", "Json"],
				"desc": "Shortcut Class to send and receive Javascript Objects."
			},

			"Assets": {
				"deps": ["Element.Event"],
				"desc": "Contains functions to load external files on the fly: images, css, javascript."
			}

		},

		"Plugins": {

			"Hash": {
				"deps": ["Element"],
				"desc": "Object wrapper, allows to use custom methods on javascript Objects. By Christophe Beyls."
			},

			"Hash.Cookie": {
				"deps": ["Hash", "Cookie"],
				"desc": "Lets you Create/Read/Delete Cookies in Json Format."
			},

			"Color": {
				"deps": ["Element"],
				"desc": "Class for creating and manipulating colors in JavaScript. Supports HSB->RGB Conversions and vice versa."
			},

			"Scroller": {
				"deps": ["Class.Extras", "Element.Event", "Element.Dimensions"],
				"desc": "Lets you scroll the window when the mouse reaches certain boundaries."
			},

			"Slider": {
				"deps": ["Drag.Base", "Element.Dimensions"],
				"desc": "Lets you create horizontal and vertical sliders."
			},

			"SmoothScroll": {
				"deps": ["Fx.Scroll"],
				"desc": "Smooth scroll your internal links."
			},

			"Sortables": {
				"deps": ["Class.Extras", "Element.Event", "Element.Dimensions"],
				"desc": "Lets you resort list items. Supports optional ghosting & handles"
			},

			"Tips": {
				"deps": ["Class.Extras", "Element.Event", "Window.Size"],
				"desc": "The Tooltips."
			},

			"Group": {
				"deps": ["Class.Extras", "Element"],
				"desc": "The Group."
			},

			"Accordion": {
				"deps": ["Fx.Elements", "Element.Event"],
				"desc": "Does it need a description?"
			}

		}

	},
	// Internal list of compoments and their IDs
	components:{
		'Core':{id:0},
		'Class':{id:1},
		'Class.Extras':{id:2},
		'Array':{id:3},
		'String':{id:4},
		'Function':{id:5},
		'Number':{id:6},
		'Element':{id:7},
		'Element.Event':{id:8},
		'Element.Filters':{id:9},
		'Element.Selectors':{id:10},
		'Element.Form':{id:11},
		'Element.Dimensions':{id:12},
		'Window.DomReady':{id:13},
		'Window.Size':{id:14},
		'Fx.Base':{id:15},
		'Fx.CSS':{id:16},
		'Fx.Style':{id:17},
		'Fx.Styles':{id:18},
		'Fx.Elements':{id:19},
		'Fx.Scroll':{id:20},
		'Fx.Slide':{id:21},
		'Fx.Transitions':{id:22},
		'Drag.Base':{id:23},
		'Drag.Move':{id:24},
		'XHR':{id:25},
		'Ajax':{id:26},
		'Cookie':{id:27},
		'Json':{id:28},
		'Json.Remote':{id:29},
		'Assets':{id:30},
		'Hash':{id:31},
		'Hash.Cookie':{id:32},
		'Color':{id:33},
		'Scroller':{id:34},
		'Slider':{id:35},
		'SmoothScroll':{id:36},
		'Sortables':{id:37},
		'Tips':{id:38},
		'Group':{id:39},
		'Accordion':{id:40}
	},
	separator:'_',
	separator_value:'-',
	settings:{},
	version: '1.11',
	compression_type: 'packer',
	compression_types: {},

	initialize:function(){
		var params = document.location.search.parseQuery();
		// Parse checkum
		if( $defined( params.checksum ) ){
			// JS can only handle bitwise operations on numbers up to 2^32...
			// ...so checksum is split into ckunks
			checksum_parts = params.checksum.split( this.separator );
			for( var part= 0; part<checksum_parts.length; part++ ){
				// There are two checksums: selected and include dependencies
				var checksum = checksum_parts[part].split( this.separator_value );
				for( var bit = 0; bit < 32; bit++ ){
					var id = bit + ( 32 * part );
					this.settings[ id ] = { 'selected': false, 'include_dependencies':false };
					// Selected?
					if( ( parseInt( checksum[ 0 ] ) & Math.pow( 2, bit ) ) ){
						this.settings[ id ].selected = true;
					}
					// Include dependencies?
					if( ( parseInt( checksum[ 1 ] ) & Math.pow( 2, bit ) ) ){
						// If present, indicates false
						this.settings[ id ].include_dependencies = false;
					} else {
						this.settings[ id ].include_dependencies = true;
					}
				}
			}
		}
		
		if( $defined( params.compression ) ){
			this.compression_type = params.compression;
		}

		
	},
	
	/**
	 * Render a list of components
	 */
	drawComponents:function(){
		var packages_table = new Element(
			'table',
			{
				'class':'packages'
			}
		);
		for( var package in this.packages ){
			packages_table.adopt( this.createPackageElement( package, this.packages[ package ] ) );
		}
		$('packages').adopt( packages_table );
		this.updateChecksum();
		this.updateList();

		var rows = $$( '#compression tr.component' );
		for( var i = 0; i< rows.length; i++  ){
			var compression_type = rows[i].getAttribute( 'compression_type' );
			this.compression_types[ compression_type ] = 
			{
				'element':rows[i]
			};
			rows[i].addEvent(
				'click',
				function( compression_type ){
					this.setCompressionType( compression_type );
				}.pass( compression_type, this )
			);
		}
		this.setCompressionType( this.compression_type );
	},
	
	setCompressionType:function( compression_type ){
		this.compression_type = compression_type;
		for( var index in this.compression_types ){
			this.compression_types[ index ].element.removeClass('component_selected');
		}
		this.compression_types[ compression_type ].element.addClass( 'component_selected' );
		this.updateChecksum();
	},
	
	/**
	 * Create an package container
	 */
	createPackageElement:function( name, package ){
		// Header
		var package_block = new Element
		(
			'tbody',
			{
				'class':'package'
			}
		);
		package_block.adopt
		( 
			new Element
			(
				'tr',
				{
					'class':'package'
				}
			).adopt
			(
				new Element
				(
					'th',
					{
						'colspan':3
					}
				).adopt
					(
						new Element
						(
							'h2'
						).setText( name )
					)
			)
		);
		// Components
		for( var component_name in package ){
			component = this.components[ component_name ];
			component.path = name + '/' + component_name + '.js';
			component.name = component_name;
			component.desc = package[ component_name ].desc;
			component.deps = package[ component_name ].deps;
			component.package = name;
			component.selected = (typeof(this.settings[ component.id ]) != 'undefined')?this.settings[ component.id ].selected:false;
			component.include_dependencies = (typeof(this.settings[ component.id ]) != 'undefined')?this.settings[ component.id ].include_dependencies:true;
			component.include_as_dependency = false;
			component.is_dependency_for = {};
		
			var new_row = this.createComponentElement( component )

			package_block.adopt( new_row );
			component.element = new_row;
		}
		return package_block;

	},

	/**
	 * Create an element container
	 */
	createComponentElement:function( component ){

		var component_row = new Element
		(
			'tr',
			{
				'events':{
					'click':function( name ){
						this.toggleComponentSelected(name);
					}.pass( component.name, this )
				},
				'class':'component',
				'component':component.name
			}
		).adopt(
			new Element
			(
				'th'
				
			).setText( component.name )
		).adopt(
			new Element
			(
				'td',
				{
					'class':'description'
				}
			).setText( component.desc )
		);
		
		// Don't show dependencies for Core (even if it does apparently depend on itself...)
		if( component.id > 0 ){
			
			// Dependencies
			var deps_cell = new Element
			(
				'td',
				{
					'class':'dependencies'
				}
			).setHTML( '<div class="exclude include">Include dependencies</div><div class="list">' + component.deps.join( ', ' ) + '</div>' );

			// Toggle 'include dependencies'
			var deps_click_event = function(event, name){
				// Stop event bubbling up to row
				new Event(event).stopPropagation();
				this.toggleIncludeDependencies( name );
			};

			deps_cell.onclick = deps_click_event.bindAsEventListener( this, component.name );

		} else {
			// Empty cell
			var deps_cell = new Element
			(
				'td',
				{
					'class':'dependencies'
				}
			)
		}

		component_row.adopt( deps_cell );

		return component_row;
		
	},

	/**
	 * Toggle selected status of a component
	 */
	toggleComponentSelected:function(name){

		// Clicked row
		var component = this.components[ name ];

		// Toggle selected status
		component.selected = ( component.selected )?false:true;

		// Toggle appearance
		if(component.selected){
			this.setSelectedStyle( name );
			if( component.include_dependencies ){
				this.setDependencies( component.deps );
			}
		} else {
			this.updateList();
		}
		this.updateChecksum();
	},

	
	/**
	 * Toggle including of a component's dependencies
	 */
	toggleIncludeDependencies:function( name ){
		var component = this.getComponentByName( name );
		component.include_dependencies = (component.include_dependencies)?false:true;
		var component_row = this.getComponentRow( name );
		if( component.include_dependencies ){
			component_row.getElement( '[class=dependencies]' ).getElement( '[class^=exclude]' ).addClass( 'include' );
		} else {
			component_row.getElement( '[class=dependencies]' ).getElement( '[class^=exclude]' ).removeClass( 'include' );
		}
		this.updateList();
		this.updateChecksum();
	},
	
	getComponentRow:function(name){
		return this.components[ name ].element;
	},

	setSelectedStyle:function( name ){
		// Just in case
		this.getComponentRow(name).removeClass( 'component_dependency' );
		this.getComponentRow(name).addClass( 'component_selected' );
	},
	setUnselectedStyle:function( name ){
		this.getComponentRow(name).removeClass( 'component_selected' );
	},

	setDependencies:function( dependencies ){
		for( var i=0; i< dependencies.length; i++){
			this.selectComponentAsDependency( dependencies[i] );
		}
	},
	
	getComponentByName:function(name){
		return this.components[ name ];
	},
	
	selectComponentAsDependency:function( name ){
		var component = this.getComponentByName( name );
		if( !component.selected && !component.include_as_dependency ){
			component.include_as_dependency = true;
			// Toggle appearance
			this.getComponentRow(name).removeClass( 'component_selected' );
			this.getComponentRow(name).addClass( 'component_dependency' );
			if( component.include_dependencies ){
				for( var i=0; i< component.deps.length; i++){
					this.selectComponentAsDependency( component.deps[i] );
				}
			}
		}
	},
	updateChecksum:function(){
		var checksum = [];
		for( var name in this.components ){
			component = this.getComponentByName( name );
			// Is selected
			if( component.selected ){
				var index = Math.floor( component.id / 32);
				if(!$defined(checksum[ index ])){
					checksum[ index ] = [0,0];
				}
				checksum[ index ][0] += Math.pow( 2, component.id % 32 );
			}
			// Include dependencies?
			if( !component.include_dependencies ){
				var index = Math.floor( component.id / 32);
				if(!$defined(checksum[ index ])){
					checksum[ index ] = [0,0];
				}
				checksum[ index ][1] += Math.pow( 2, component.id % 32 );
			}
		}
		// Fill in blanks
		var output = '';
		for( var i = 0; i < checksum.length; i++ ){
			if(!checksum[ i ]){
				checksum[ i ] = [0,0];
			}
			checksum[ i ] = checksum[ i ].join( '-' );
		}

		// Write output
		checksum = checksum.join( this.separator );

		// Link
		var query =  document.location.search.parseQuery();
		query.checksum = checksum;
		query.version = this.version;
		query.compression = this.compression_type;
		var link = document.location.href.split( '?' )[0] + '?' + this.convertToQueryString(query);
		$( 'checksum_link' ).empty().setHTML( '<a href="' + link + '">' + link + '</a>' );
	},
	
	
	updateList:function(){

		// Clear all dependencies
		for( var name in this.components ){
			this.setUnselectedStyle(name);
			this.getComponentRow(name).removeClass( 'component_dependency' );
			this.getComponentByName( name ).include_as_dependency = false;
		}
		// Rewrite list
		for( var name in this.components ){
			component = this.getComponentByName( name );
			if( component.selected ){
				this.setSelectedStyle( name );
				if( component.include_dependencies ){
					this.setDependencies( component.deps );
				}
			}
			if( !component.include_dependencies ){
				component.element.getElement( '[class=dependencies]' ).getElement( '[class^=exclude]' ).removeClass( 'include' );
			}
			
		}
	},
	download:function(){
		var form=$('download_form');
		var count = 0;
		for( var component_name in this.components ){
			var component = this.components[ component_name ];
			if( component.selected || component.include_as_dependency ){
				count++;
				form.adopt
				(
					new Element
					(
						'input',
						{
							'type':'hidden',
							'name':'files[]',
							'value':component.path
						}
					)
				);
			}
		}
		if( !count ){
			alert( 'You must select at least one component for download.' );
		} else { 
			form.compression.value = this.compression_type;
			form.version.value = this.version;
			form.submit();
		}
	},
	convertToQueryString: function( query ) {

		var params = [];
		for( var key in query ){
			if(key){
				params.push( key + '=' + escape(query[key]) );
			}
		}
		return params.join( '&' );
	}

}
);

	//Author:
	//Aaron Newton, <aaron [dot] newton [at] cnet [dot] com>
	String.extend({

		parseQuery: function() {
			var pairs = this.match(/^\??(.*)$/)[1].split('&');
			var params = {};
			pairs.each(function(pair) {
			  pair = pair.split('=');
			  if( $defined( pair[1] ) ){
				value = pair[1];
			  } else {
				value = '';
			  }
			  params[pair[0]] = value;
			});
			return params;
		}
	});
