function linenDesigner(headline_elem, body_elem, tab_container_elem_lines, tab_container_elem_cats, swatch_container_elem, selector_id_prefix) {
	var headline = domcheck(headline_elem);
	var body = domcheck(body_elem);
	var tab_container = domcheck(tab_container_elem_lines);
	var tab_container_cats = domcheck(tab_container_elem_cats);
	var swatch_container = domcheck(swatch_container_elem);
	var typesArr = [];
	var typesObj = {};
	var selectedType = null;
	var selector_id_prefix = selector_id_prefix;
	var fills = [];
	var filled = [];
	var showProductLines = true;
	this.currentDesign = null;
	
	this.init = function() {
		var designs = [];
		 for ( var i = 0; i < 3; i++ ) {
		 	designs[i] = getCookie('design_'+(i+1));
		 	if ( designs[i] ) {
		 		fills[i+1] = true;
		 	} else {
		 		fills[i+1] = false;
		 	}
		 }
		 this.updateFills();
		 this.switchDesign(1);
		 registerEvent(tab_container, 'allchange', createDelegate(this, 'catSelectorChange'));
		 registerEvent(tab_container_cats, 'allchange', createDelegate(this, 'catSelectorChange'));
	}
	
	this.catSelectorChange = function(e) {
		var container = e.currentTarget ? e.currentTarget : e.srcElement;
		var picked = container.options[container.selectedIndex];
		if ( container == tab_container_cats && showProductLines && container.selectedIndex > 0) {
			this.showCategories();
			tab_container.selectedIndex = 0;
		} else if ( container == tab_container && !showProductLines && container.selectedIndex > 0) {
			this.showProductLines();
			tab_container_cats.selectedIndex = 0;
		}
		if ( picked.pickHandler ) {
			picked.pickHandler();
		}
	}
	
	this.showCategories = function() {
		selectedType.clearTabs();
		showProductLines = false;
		selectedType.resetTabs();
	}
	
	this.showProductLines = function() {
		selectedType.clearTabs();
		showProductLines = true;
		selectedType.resetTabs();
	}
	
	this.upload = function(input) {
		this.saveDesign();
		input = domcheck(input);
		var res = {};
		for ( var x = 1; x <= 3; x++ ) {
			this.switchDesign(x);
			var val = this.serialize(false);
			if ( val ) {
				res[x] = val;
			}
		}
		input.value = JSON.stringify(res);
		if ( input.value == '{}' ) {
			alert("You don't have any designs to send!");
			return;
		}
		input.form.submit();
	}
	
	this.switchDesign = function(num) {
		if ( this.currentDesign ) {
			callWhenReady(createDelegate(this, 'doSwitch', num));
		} else {
			this.doSwitch(num);
		}
	}
	
	this.updateFills = function() {
		for ( var x = 1; x <= 3; x++) {
			if ( fills[x] && !filled[x] ) {
				filled[x] = true;
				setClass(did(selector_id_prefix+(x)), 'filled');
			} else if ( !fills[x] && filled[x] ) {
				filled[x] = false;
				var elem = did(selector_id_prefix+(x));
	 			elem.className = elem.className.replace(/[\s]?filled/, '');
			}
		}
	}
	
	this.doSwitch = function(num) {
		if ( this.currentDesign ) {
			this.saveDesign(this.currentDesign);
			old = did(selector_id_prefix+(this.currentDesign));
			old.className = old.className.replace(/[\s]?selected/, '');
		}
		this.currentDesign = num;
		this.loadDesign(num);
		newElem = did(selector_id_prefix+(this.currentDesign));
		setClass(newElem, 'selected');
	}
	
	this.saveDesign = function(num) {
		if ( typeof(num) == 'undefined' ) {
			num = this.currentDesign;
		}
		val = this.serialize();
		if ( val ) {
			if ( !fills[num] ) {
				fills[num] = true;
				this.updateFills();
			}
		}
		createCookie('design_'+num, val, 1023);
	}
	
	this.loadDesign = function(num) {
		var val = getCookie('design_'+num);
		if ( !val ) {
			return this.reset();
		}
		this.unserialize(val);		
	}
	
	this.addType = function(name, label, description, chooser, preview, current_selection, prefix) {
		type = new linenType(name, label, description, chooser, preview, current_selection, prefix, createDelegate(this, 'saveDesign'))
		typesArr.push(type);
		typesObj[name] = type;
		type.addChooserListener(createDelegate(this, 'setSelectedType'));
		return type;
	}
	
	this.reset = function() {
		for ( var i = 0; i < typesArr.length; i++ ) {
			typesArr[i].reset();
		}
		this.setSelectedType(typesArr[0]);
		typesArr[0].selectFirstCategory();
	}
	
	this.clearDesign = function() {
		fills[this.currentDesign] = false;
		this.updateFills();
		this.reset();
		this.saveDesign();
	}
	
	
	this.redrawSelected = function() {
		headline.innerHTML = selectedType.label;
		body.innerHTML = selectedType.description;
		for ( var i = 0; i < typesArr.length; i++ ) {
			if ( typesArr[i] != selectedType ) {
				typesArr[i].deselect();
			}
		}
		selectedType.select();
	}
	
	this.serialize = function(json_encode) {
		var res = {};
		var nulled = true;
		for ( var i = 0; i < typesArr.length; i++ ) {
			var val = typesArr[i].serialize();
			if ( val ) {
				res[typesArr[i].name] = val;
				nulled = false;
			}
		}
		if ( nulled ) {
			return '';
		}
		if ( typeof(json_encode) == 'undefined' || json_encode ) {
			return JSON.stringify(res);
		}
		return res;
	}
	
	this.unserialize = function(str) {
		this.reset();
		var obj = JSON.parse(str);
		for ( var i = 0; i < typesArr; i++ ) {
			typesArr[i].reset();
		}
		for ( x in obj ) {
			if ( typesObj[x] ) {
				typesObj[x].unserialize(obj[x]);
			}
		}
	}
	
	this.setSelectedType = function(nameOrType) {
		if ( nameOrType instanceof linenType ) {
			selectedType = nameOrType;
		} else {
			selectedType = typesObj[nameOrType];
		}
		this.redrawSelected();
	}
	
	function linenType(name, label, description, chooser, preview, current_selection, prefix, updateHandler) {
		this.name = name;
		this.label = label;
		this.description = description;
		this.preview = domcheck(preview);
		this.prefix = prefix;
		this.current_selection = domcheck(current_selection);
		this.updateHandler = updateHandler;
		this.chooser = domcheck(chooser);
		this.selected = false;
		this.chooserHandler = null;
		this.selectedCategory = null;
		
		this.selectedProduct = null;
		this.originalProductImage;
		
		this.categories = [];
		this.productLines = [];
		var products = {};
		
		this.reset = function() {
			if ( this.selectedProduct ) {
				this.selectedProduct.deselect();
				this.selectedProduct = null;
			}
			if ( this.originalProductImage ) {
				this.preview.src = this.originalProductImage;
			}
				
			// Clear out the link to the product in the "Current Selection" pod
			if(this.current_selection) {
				this.current_selection.innerHTML = "None";
			}
		}
		
		this.resetTabs = function() {
			this.createTabs();
			this.selectFirstCategory();
		}
		
		this.serialize = function() {
			if ( this.selectedProduct ) {
				return this.selectedProduct.id;
			}
		}
		
		this.unserialize = function(id) {
			if ( this.selectedProduct ) {
				this.selectedProduct.deselect();
				this.selectedProduct = null;
			}
			if ( products[id] ) {
				this.prodChange(products[id]);
			} else {
				new async_rpc('designer/getProduct/'+id, createDelegate(this, 'gotProduct'));
			}
		}
		
		this.gotProduct = function(prod) {
			if ( this.selectedProduct ) {
				return;
			}
			if ( !products[prod.id] ) {
				products[prod.id] = new linenProduct(prod.id, prod.image, prod.name, prod.url, createDelegate(this, 'prodChange'));	
			}
			this.prodChange(products[prod.id]);
		}
		
		this.addChooserListener = function(handler) {
			this.chooserHandler = handler;
			registerEvent(chooser, 'click', createDelegate(this, 'chooserClick'));
		}
		
		this.chooserClick = function(event, data) {
			this.chooserHandler(this);
			return false;
		}
		
		this.select = function() {
			if ( !this.selected ) {
				this.selected = true;
				setClass(this.chooser, 'selected');
				this.createTabs();
				var cats = showProductLines ? this.productLines : this.categories;
				if ( cats.length > 0 ) {
					this.catChange(cats[0]);
				}
			}
		}
		
		this.createTabs = function() {
			var cats = this.productLines;
			if ( !showProductLines ) {
				tab_container.appendChild(this.createBlankOption());
			}
			for ( var i = 0; i < cats.length; i++ ) {
				tab_container.appendChild(cats[i].createElement());
			}
			cats = this.categories;
			if ( showProductLines ) {
				tab_container_cats.appendChild(this.createBlankOption());
			}
			for ( var i = 0; i < cats.length; i++ ) {
				tab_container_cats.appendChild(cats[i].createElement());
			}
		}
		
		this.createBlankOption = function() {
			var opt = document.createElement('option');
			opt.innerHTML = "Select...";
			this.element = opt;
			return opt;
		}
		
		this.clearTabs = function() {
			var cats = showProductLines ? this.productLines : this.categories;
			for ( var i = 0; i < cats.length; i++ ) {
				cats[i].clearElement();
			}
		}
		
		this.deselect = function() {
			if ( this.selected ) {
				if ( this.selectedCategory ) {
					this.selectedCategory.deselect();
					this.selectedCategory = null;
				}
				this.selected = false;
				this.chooser.className = this.chooser.className.replace(/[\s]?selected/, '');
				this.clearTabs();
			}
		}
		
		this.addCategory = function(id, name) {
			this.categories.push(new linenCategory(id, name, createDelegate(this, 'catChange'), createDelegate(this, 'prodChange'), products));
		}
		
		this.addProductLine = function(id, name) {
			this.productLines.push(new linenCategory(id, name, createDelegate(this, 'catChange'), createDelegate(this, 'prodChange'), products));
		}
		
		this.selectFirstCategory = function() {
			if ( showProductLines ) {
				if ( this.productLines.length > 0 ) {
					this.catChange(this.productLines[0]);
				}
			} else {
				if ( this.categories.length > 0 ) {
					this.catChange(this.categories[0]);
				}
			}
		}
		
		this.catChange = function(category) {
			if ( !this.selected ) {
				return;
			}
			if ( this.selectedCategory ) {
				this.selectedCategory.deselect();
			}
			category.select();
			this.selectedCategory = category;
		}
		
		this.prodChange = function(product) {
			if ( this.selectedProduct ) {
				this.selectedProduct.deselect();
			}
			if ( !this.originalProductImage ) {
				this.originalProductImage = this.preview.src;
			}
			this.selectedProduct = product;
			product.select();
			
			// Create the link to the product in the "Current Selection" pod
			if(this.current_selection) {
				this.current_selection.innerHTML = "";			
				var a = document.createElement('a');
				a.href = product.url;
				a.target = "_blank";
				a.appendChild(document.createTextNode(product.name));			
				this.current_selection.appendChild(a);
			}
			
			this.preview.src = product.getImageURL(this.prefix);
			this.updateHandler();
		}
		
		
	}
	
	function linenCategory(id, name, catChangeHandler, prodChangeHandler, typeProducts) {
		this.id = id;
		this.name = name;
		this.handler = catChangeHandler;
		this.prodChangeHandler = prodChangeHandler;
		
		this.element = null;
		this.registered = null;
		this.selected = false;
		this.products = null;
		this.typeProducts = typeProducts;
		
		
		this.createElement = function() {
			var opt = document.createElement('option');
			opt.innerHTML = this.name;
			this.element = opt;
			opt.pickHandler = createDelegate(this, 'catClicked');
			return opt;
		}
		
		this.catClicked = function(event) {
			if ( this.handler && !this.selected) {
				this.handler(this);
			}
			return false;
		}
		
		this.getProducts = function() {
			new async_rpc('designer/getProducts/'+this.id+'/'+selectedType.name+(showProductLines ? '/1' : ''), createDelegate(this, 'gotProducts'));
		}
		
		this.gotProducts = function(res) {
			for ( var i=0; i < res.length; i++ ) {
				if ( !this.typeProducts[res[i].id] ) {
					this.typeProducts[res[i].id] = new linenProduct(res[i].id, res[i].image, res[i].name, res[i].url, this.prodChangeHandler);
				}
				this.products.push(this.typeProducts[res[i].id]);
			}
			if ( this.selected ) {
				this.drawProducts();
			}
		}
		
		this.drawProducts = function() {
			for ( var i = 0; i < this.products.length; i++ ) {
				swatch_container.appendChild(this.products[i].createElement());
			}
		}
		
		this.clearProducts = function() {
			for ( var i = 0; i < this.products.length; i++ ) {
				this.products[i].clearElement();
			}
		}
		
		this.clearElement = function() { 
			if ( this.element ) {
				this.element.parentNode.removeChild(this.element);
				this.element = null;
				if ( this.registered ) {
					this.registered.unregister();
				}
			}
		}
		
		this.select = function() {
			if ( !this.selected ) {
				this.selected = true;
				setClass(this.element, 'selected');
				if ( !this.products ) {
					this.products = [];
					this.getProducts();
				}
				else {
					this.drawProducts();
				} 
			}
		}
		
		this.deselect = function() {
			if ( this.selected ) {
				this.selected = false;
				if ( this.element ) {
					this.element.className = this.element.className.replace(/[\s]?selected/, '');
				}
				this.clearProducts();
			}
			
		}
	}
	
	function linenProduct(id, image, name, url, handler) {
		this.id = id;
		this.image = image;
		this.name = name;
		this.url = url;
		this.handler = handler;
		this.selected = false;
		
		this.element = null;
		this.registered = null;
				
		this.createElement = function() {
			this.element = document.createElement('a');
			this.element.href = 'javascript://Switch Linen';
			var img = document.createElement('img');
			img.src = this.getImageURL('designer_thumb');
			img.width = 50;
			img.height = 50;
			img.alt = this.name;
			img.title = this.name;
			this.element.appendChild(img);
			this.registered = registerEvent(this.element, 'click', createDelegate(this, 'clicked'));
			if ( this.selected ) {
				setClass(this.element, 'selected');
			}
			return this.element;
		}
		
		this.clicked = function(event) {
			if ( this.handler ) {
				this.handler(this);
			}
			return false;
		}
		
		this.clearElement = function() {
			if ( this.element ) {
				this.element.parentNode.removeChild(this.element);
				this.registered.unregister();
				this.element = null;
			}
		}
		
		this.getImageURL = function(prefix) {
			prefix += '_';
			return 'images/products/'+this.id+'/'+prefix+this.image;
		}
		
		this.select = function() {
			if ( !this.selected ) {
				this.selected = true;
				if ( this.element ) {
					setClass(this.element, 'selected');
				}
			}
		}
		
		this.deselect = function() {
			if ( this.selected ) {
				this.selected = false;
				if ( this.element ) {
					this.element.className = this.element.className.replace(/[\s]?selected/, '');
				}
			}
		}
	}
	
	function setClass(elem, className) {
		if ( elem.className.length > 0 ) {
			elem.className += ' '+className;
		} else {
			elem.className = className;
		}
	}
	
}

JSON=function(){function f(n){return n<10?'0'+n:n;}
Date.prototype.toJSON=function(){return this.getUTCFullYear()+'-'+
f(this.getUTCMonth()+1)+'-'+
f(this.getUTCDate())+'T'+
f(this.getUTCHours())+':'+
f(this.getUTCMinutes())+':'+
f(this.getUTCSeconds())+'Z';};var m={'\b':'\\b','\t':'\\t','\n':'\\n','\f':'\\f','\r':'\\r','"':'\\"','\\':'\\\\'};function stringify(value,whitelist){var a,i,k,l,r=/["\\\x00-\x1f\x7f-\x9f]/g,v;switch(typeof value){case'string':return r.test(value)?'"'+value.replace(r,function(a){var c=m[a];if(c){return c;}
c=a.charCodeAt();return'\\u00'+Math.floor(c/16).toString(16)+
(c%16).toString(16);})+'"':'"'+value+'"';case'number':return isFinite(value)?String(value):'null';case'boolean':case'null':return String(value);case'object':if(!value){return'null';}
if(typeof value.toJSON==='function'){return stringify(value.toJSON());}
a=[];if(typeof value.length==='number'&&!(value.propertyIsEnumerable('length'))){l=value.length;for(i=0;i<l;i+=1){a.push(stringify(value[i],whitelist)||'null');}
return'['+a.join(',')+']';}
if(whitelist){l=whitelist.length;for(i=0;i<l;i+=1){k=whitelist[i];if(typeof k==='string'){v=stringify(value[k],whitelist);if(v){a.push(stringify(k)+':'+v);}}}}else{for(k in value){if(typeof k==='string'){v=stringify(value[k],whitelist);if(v){a.push(stringify(k)+':'+v);}}}}
return'{'+a.join(',')+'}';}}
return{stringify:stringify,parse:function(text,filter){var j;function walk(k,v){var i,n;if(v&&typeof v==='object'){for(i in v){if(Object.prototype.hasOwnProperty.apply(v,[i])){n=walk(i,v[i]);if(n!==undefined){v[i]=n;}}}}
return filter(k,v);}
if(/^[\],:{}\s]*$/.test(text.replace(/\\./g,'@').replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(:?[eE][+\-]?\d+)?/g,']').replace(/(?:^|:|,)(?:\s*\[)+/g,''))){j=eval('('+text+')');return typeof filter==='function'?walk('',j):j;}
throw new SyntaxError('parseJSON');}};}();

