
//backwards compatable function...
function showColorPicker(el, callback, allowTransparency, transparencyCaption) {
	//try and get a color...
	var clr = null;
	if(el.style.backgroundColor != null && el.style.backgroundColor != "") {
		clr = el.style.backgroundColor;
	}
	var opts = {};
	if(allowTransparency) {
		opts.allow_transparency = true;
		if(transparencyCaption != null) {
			opts.transparency_caption = transparencyCaption;
		}
	}
	pwColorPicker.selectColor(null, el, clr, callback, opts);
}


var ColorPicker = Class.create({
	CLASSDEF: {
		name: 'ColorPicker'
	},
	
	initialize: function() {
		this.palettes = {};
		this.processes = {};
		this.bodyClickEvent = this.bodyClick.bindAsEventListener(this);
		this.allColors = {};
	},
	
	registerColor: function(color) {
		this.allColors[color.id] = color;
		
	},
	
	initDefaultPalettes: function() {
		var ids = -1;
		var p = new ColorPalette({id:-1, name:"Web Colors"}, []);
		for(var r=255;r>=0;r-=3*16) {
			for(var g=0;g<=255;g+=3*16) {
				for(var b=0;b<=255;b+=3*16) {
					var c = new ColorPaletteColor([ids--,0,0,0,0,r,g,b,"","", "rgb"]);
					convertToCMYK(c);
					p.colors.add(c);
				}
			}
		}
		this.palettes[p.id] = p;
		
		p = new ColorPalette({id:-2, name:"Named Colors"}, []);
		var namedColors = new Array('AliceBlue','AntiqueWhite','Aqua','Aquamarine','Azure','Beige','Bisque','Black','BlanchedAlmond','Blue','BlueViolet','Brown',
			'BurlyWood','CadetBlue','Chartreuse','Chocolate','Coral','CornflowerBlue','Cornsilk','Crimson','Cyan','DarkBlue','DarkCyan','DarkGoldenRod','DarkGray',
			'DarkGreen','DarkKhaki','DarkMagenta','DarkOliveGreen','Darkorange','DarkOrchid','DarkRed','DarkSalmon','DarkSeaGreen','DarkSlateBlue','DarkSlateGray',
			'DarkTurquoise','DarkViolet','DeepPink','DeepSkyBlue','DimGray','DodgerBlue','Feldspar','FireBrick','FloralWhite','ForestGreen','Fuchsia','Gainsboro',
			'GhostWhite','Gold','GoldenRod','Gray','Green','GreenYellow','HoneyDew','HotPink','IndianRed','Indigo','Ivory','Khaki','Lavender','LavenderBlush',
			'LawnGreen','LemonChiffon','LightBlue','LightCoral','LightCyan','LightGoldenRodYellow','LightGrey','LightGreen','LightPink','LightSalmon','LightSeaGreen',
			'LightSkyBlue','LightSlateBlue','LightSlateGray','LightSteelBlue','LightYellow','Lime','LimeGreen','Linen','Magenta','Maroon','MediumAquaMarine',
			'MediumBlue','MediumOrchid','MediumPurple','MediumSeaGreen','MediumSlateBlue','MediumSpringGreen','MediumTurquoise','MediumVioletRed','MidnightBlue',
			'MintCream','MistyRose','Moccasin','NavajoWhite','Navy','OldLace','Olive','OliveDrab','Orange','OrangeRed','Orchid','PaleGoldenRod','PaleGreen',
			'PaleTurquoise','PaleVioletRed','PapayaWhip','PeachPuff','Peru','Pink','Plum','PowderBlue','Purple','Red','RosyBrown','RoyalBlue','SaddleBrown',
			'Salmon','SandyBrown','SeaGreen','SeaShell','Sienna','Silver','SkyBlue','SlateBlue','SlateGray','Snow','SpringGreen','SteelBlue','Tan','Teal','Thistle',
			'Tomato','Turquoise','Violet','VioletRed','Wheat','White','WhiteSmoke','Yellow','YellowGreen');
			
		var namedColorRGB = new Array('#F0F8FF','#FAEBD7','#00FFFF','#7FFFD4','#F0FFFF','#F5F5DC','#FFE4C4','#000000','#FFEBCD','#0000FF','#8A2BE2','#A52A2A','#DEB887',
			'#5F9EA0','#7FFF00','#D2691E','#FF7F50','#6495ED','#FFF8DC','#DC143C','#00FFFF','#00008B','#008B8B','#B8860B','#A9A9A9','#006400','#BDB76B','#8B008B',
			'#556B2F','#FF8C00','#9932CC','#8B0000','#E9967A','#8FBC8F','#483D8B','#2F4F4F','#00CED1','#9400D3','#FF1493','#00BFFF','#696969','#1E90FF','#D19275',
			'#B22222','#FFFAF0','#228B22','#FF00FF','#DCDCDC','#F8F8FF','#FFD700','#DAA520','#808080','#008000','#ADFF2F','#F0FFF0','#FF69B4','#CD5C5C','#4B0082',
			'#FFFFF0','#F0E68C','#E6E6FA','#FFF0F5','#7CFC00','#FFFACD','#ADD8E6','#F08080','#E0FFFF','#FAFAD2','#D3D3D3','#90EE90','#FFB6C1','#FFA07A','#20B2AA',
			'#87CEFA','#8470FF','#778899','#B0C4DE','#FFFFE0','#00FF00','#32CD32','#FAF0E6','#FF00FF','#800000','#66CDAA','#0000CD','#BA55D3','#9370D8','#3CB371',
			'#7B68EE','#00FA9A','#48D1CC','#C71585','#191970','#F5FFFA','#FFE4E1','#FFE4B5','#FFDEAD','#000080','#FDF5E6','#808000','#6B8E23','#FFA500','#FF4500',
			'#DA70D6','#EEE8AA','#98FB98','#AFEEEE','#D87093','#FFEFD5','#FFDAB9','#CD853F','#FFC0CB','#DDA0DD','#B0E0E6','#800080','#FF0000','#BC8F8F','#4169E1',
			'#8B4513','#FA8072','#F4A460','#2E8B57','#FFF5EE','#A0522D','#C0C0C0','#87CEEB','#6A5ACD','#708090','#FFFAFA','#00FF7F','#4682B4','#D2B48C','#008080',
			'#D8BFD8','#FF6347','#40E0D0','#EE82EE','#D02090','#F5DEB3','#FFFFFF','#F5F5F5','#FFFF00','#9ACD32');	
	  for(var i=0; i < namedColors.length; i++) {
			var c = toColorPaletteColor(namedColorRGB[i]);
			c.id = ids--;
			c.name = namedColors[i];
			c.type = "rgb";
			c.tint = "";
			pwColorPicker.registerColor(c);
			p.colors.add(c);
		}
		this.palettes[p.id] = p;
		this.addProcess(null, [-1, -2], {});
	},
	
	addPalette: function(options, colors) {
		var p = new ColorPalette(options, colors);
		this.palettes[p.id] = p;
	},
	
	addProcess: function(processId, paletteIds, options) {
		this.processes[processId] = {paletteIds:paletteIds, options:options};
	},
	
	//if the process limits colors find the first color in the first palette, otherwise use the fullColorDefault
	getDefaultColor: function(processId, fullColorDefault, defaultColorId) {
		if(defaultColorId != null) {
			var color = this.allColors[defaultColorId];
			if(color != null) {
				return color.getHTMLColor();
			}
		}
		var process = this.processes[processId];
		if(process.options.limitColors != true) {
			log("getDefaultColor: " + processId + " process.options.limitColors != true");
			return fullColorDefault;
		}
		log("getDefaultColor: " + processId + " process.options.limitColors == true, finding first color in first palette");
		for(var i=0; i < process.paletteIds.length; i++) {
			var palette = this.palettes[process.paletteIds[i]];
			for(var j=0; j < palette.colors.list.size(); j++) {
				return palette.colors.list[j].getHTMLColor();
			}
		}
		log("NO COLORS AVAILABLE!");
		return "#000000";
	},
	
	selectColor: function(processId, refElement, currentColor, callback, options) {
		var paletteIds = [];
		if (processId == undefined) processId = null;
		if((this.processes[processId] == null)||(this.processes[processId].paletteIds.length == 0))  {
			if(this.processes[null] == null) {
				this.initDefaultPalettes();
			}
			//processId = null;
			paletteIds = this.processes[null].paletteIds;
		} else {
			paletteIds = this.processes[processId].paletteIds;
		}
		
		log("pwColorPicker.selectColor(" + currentColor + ")");
		log(paletteIds);
		this.callback = callback;
		if(currentColor != null) {
			this.currentColor = toColorPaletteColor(currentColor);
		} else {
			this.currentColor == null;
		}
		log(this.currentColor);
		this.currentPalette = null; //a palette will set this is it contains the currentColor 
		this.refElement = refElement;
		this.options = options;
		var process = this.processes[processId];
		this.loadPalettes(paletteIds, process.options, options);
		
		var pos = Position.cumulativeOffset(refElement);
		var height = $(refElement).getHeight();
		
		this.el.style.top = (pos[1] + height) + "px";
		this.el.style.left = pos[0] + "px";
		
		
	},
	
	loadPalettes: function(paletteIds, options, callOptions) {
		if(this.el == null) {
			
			var html = '<div class="header" id="pwColorPickerHeader" ><a href="#" onclick="pwColorPicker.close(); return false;" class="close">close</a></div><div class="content"><ol class="palette" id="pwColorPaletteTabs"></ol>';
			html += '<div class="interior" id="pwColorPanels">';
			html += '<ul class="slider" style="display: none;" id="pwCP_0_panel">' +
				'<li><label>C</label><span><b id="pwSCC" style="width:210px;"><i id="pwSSC">&nbsp;</i></b><input type="text" value="" id="pwSVC" onkeyup="pwColorPicker.setSliderValue(\'c\', this);" /></span></li>' +
				'<li><label>M</label><span><b id="pwSCM" style="width:210px;"><i id="pwSSM">&nbsp;</i></b><input type="text"  value="" id="pwSVM" onkeyup="pwColorPicker.setSliderValue(\'m\', this);" /></span></li>' +
				'<li><label>Y</label><span><b id="pwSCY" style="width:210px;"><i id="pwSSY">&nbsp;</i></b><input type="text"  value="" id="pwSVY" onkeyup="pwColorPicker.setSliderValue(\'y\', this);" /></span></li>' +
				'<li><label>K</label><span><b id="pwSCK" style="width:210px;"><i id="pwSSK">&nbsp;</i></b><input type="text"  value="" id="pwSVK" onkeyup="pwColorPicker.setSliderValue(\'k\', this);" /></span></li>' +
				'<li class="color" id="pwColorBar_0" style="background-color: #ff0000;" onclick="pwColorPicker.chooseCurrentColor();"><span><b>select</b></span></li>' +
			'</ul>';
		
			html += '<ul class="slider" style="display: none;" id="pwCP_00_panel">' +
				'<li><label>R</label><span><b id="pwSCR" style="width:210px;"><i id="pwSSR">&nbsp;</i></b><input type="text" value="" id="pwSVR" onkeyup="pwColorPicker.setSliderValue(\'r\', this);" /></span></li>' +
				'<li><label>G</label><span><b id="pwSCG" style="width:210px;"><i id="pwSSG">&nbsp;</i></b><input type="text"  value="" id="pwSVG" onkeyup="pwColorPicker.setSliderValue(\'g\', this);" /></span></li>' +
				'<li><label>B</label><span><b id="pwSCB" style="width:210px;"><i id="pwSSB">&nbsp;</i></b><input type="text"  value="" id="pwSVB" onkeyup="pwColorPicker.setSliderValue(\'b\', this);" /></span></li>' +
				'<li class="color" id="pwColorBar_1" style="background-color: #ff0000;" onclick="pwColorPicker.chooseCurrentColor();"><span><b>select</b></span></li>' +
			'</ul>';
			
			html += '</div></div><div class="footer"><span class="color_name" id="pwCPCN" class="visibility:hidden;"></span><a href="#" class="transparent" onclick="pwColorPicker.selectTransparent(); return false;" id="pwMakeTrans">make transparent</a></div>';

			
			this.el = document.createElement("DIV");
			this.el.className = "color_picker";
			this.el.style.display="none";
		
			this.el.setAttribute("stopdeselect","true");
			this.el.innerHTML = html;
			document.body.appendChild(this.el);
			
			this.transButton = $("pwMakeTrans");
			if (!callOptions.allow_transparency) {
			  this.transButton.style.display = "none";
			} else {
			  this.transButton.style.display = "";
			}
			this.tabContainer = $("pwColorPaletteTabs");
			this.paletteContainer = $("pwColorPanels");
			
			this.el.style.zIndex = 30000;
			this.el.style.display="";
			this.dragger = new Draggable(this.el, {handle: "pwColorPickerHeader", zindex: 30000});
			
			Event.observe(document.body, "mousedown", this.bodyClickEvent);
			
			
			this.loadedPalettes = {
				"pwCP_0_tab": $("pwCP_0_panel"),
				"pwCP_00_tab": $("pwCP_00_panel")
			};
			
			this.colorNameEl = $("pwCPCN");
			
			this.sliderC = new Control.Slider('pwSSC', 'pwSCC', { minimum: 0, maximum: 100, range: $R(0,100), values: $R(0,100), sliderValue:0, alignX:10, increment: 210.0/100.0, onSlide: function(v) {this.slideValue("c", v, true);}.bind(this)});
			//$("pwSVC").value = this.currentColor.c;
			this.sliderC = new Control.Slider('pwSSM', 'pwSCM', { minimum: 0, maximum: 100, range: $R(0,100), values: $R(0,100), sliderValue:0, alignX:10,  increment: 210.0/100.0, onSlide: function(v) {this.slideValue("m", v, true);}.bind(this)});
			//$("pwSVM").value = this.currentColor.m;
			this.sliderC = new Control.Slider('pwSSY', 'pwSCY', { minimum: 0, maximum: 100, range: $R(0,100), values: $R(0,100), sliderValue:0, alignX:10,  increment: 210.0/100.0, onSlide: function(v) {this.slideValue("y", v, true);}.bind(this)});
		//	$("pwSVY").value = this.currentColor.y;
			this.sliderC = new Control.Slider('pwSSK', 'pwSCK', { minimum: 0, maximum: 100, range: $R(0,100), values: $R(0,100), sliderValue:0, alignX:10,  increment: 210.0/100.0, onSlide: function(v) {this.slideValue("k", v, true);}.bind(this)});
			//$("pwSVK").value = this.currentColor.k;
			
			
			
			this.sliderR = new Control.Slider('pwSSR', 'pwSCR', { minimum: 0, maximum: 255, range: $R(0,255), values: $R(0,255), sliderValue:0, alignX:10, increment: 210.0/100.0, onSlide: function(v) {this.slideValue("r", v, false);}.bind(this)});
		//	$("pwSVR").value = this.currentColor.r;
			this.sliderG = new Control.Slider('pwSSG', 'pwSCG', { minimum: 0, maximum: 255, range: $R(0,255), values: $R(0,255), sliderValue:0, alignX:10,  increment: 210.0/100.0, onSlide: function(v) {this.slideValue("g", v, false);}.bind(this)});
		//	$("pwSVG").value = this.currentColor.g;
			this.sliderB = new Control.Slider('pwSSB', 'pwSCB', { minimum: 0, maximum: 255, range: $R(0,255), values: $R(0,255), sliderValue:0, alignX:10,  increment: 210.0/100.0, onSlide: function(v) {this.slideValue("b", v, false);}.bind(this)});
		//	$("pwSVB").value = this.currentColor.b;
			
			this.colorBars = [$("pwColorBar_0"), $("pwColorBar_1")];
				
				
			
		}
		
		this.setupPicker(paletteIds, options, callOptions);
			
	
		if(this.currentColor != null) {
			//try and find the color....
			for(var i=0; i < paletteIds.length; i++) {
				var pId = paletteIds[i];
				var palette = this.palettes[pId];
				if(palette.hasColor(this.currentColor)) {
					this.currentPalette = palette;
					break;
				}
			}
		}
			
		
		
		if(this.currentPalette != null) {
			appTabClick("color_picker", "pwCP_" + this.currentPalette.id + "_tab");
		//} else if((this.currentColor != null)&&(options.limitColors != true)) {
		//	appTabClick("color_picker", "pwCP_0_tab"); //select the color slider
		} else {
			if(paletteIds.length > 0) {
				appTabClick("color_picker", "pwCP_" + paletteIds[0] + "_tab");
			} else {
				appTabClick("color_picker", "pwCP_0_tab");
			}
		}
		
		if(this.currentColor ==null) {
			this.currentColor = new ColorPaletteColor([0,0,0,0,0,0,0,0,"", 0, options.colorMode]);
		}
		
		
		//color sliders...
		if(options.limitColors != true) {
			if(options.colorMode=="cmyk") {
				$("pwSVC").value = this.currentColor.c;
				this.setSliderValue("c", $("pwSVC"), true);
				$("pwSVM").value = this.currentColor.m;
				this.setSliderValue("m", $("pwSVM"), true);
				$("pwSVY").value = this.currentColor.y;
				this.setSliderValue("y", $("pwSVY"), true);
				$("pwSVK").value = this.currentColor.k;
				this.setSliderValue("k", $("pwSVK"), true);
			} else {
				$("pwSVR").value = this.currentColor.r;
				this.setSliderValue("r", $("pwSVR"), false);
				$("pwSVG").value = this.currentColor.g;
				this.setSliderValue("g", $("pwSVG"), false);
				$("pwSVB").value = this.currentColor.b;
				this.setSliderValue("b", $("pwSVB"), false);
			}
			//this.colorBar.style.backgroundColor = this.currentColor.getHTMLColor();
		}
		this.el.style.display="";
	},
	
	setupPicker: function(paletteIds, options, callOptions) {
		this.tabContainer.innerHTML = this.buildTabs(options, paletteIds);
		for(var k in this.loadedPalettes) {
			this.loadedPalettes[k].style.display = "none";
			log("Hidden " + k );
		}
		/*
		for(var i=0; i < paletteIds.length; i++) {
			var paletteId = paletteIds[i];
			var palette = this.palettes[paletteId];
			html += palette.buildPanelHtml(this.currentColor);
		}
		*/
		if(options.limitColors != true) {
			if(options.colorMode=="cmyk") {
				registerAppTab("color_picker", "pwCP_0_tab", "pwCP_0_panel");
				this.colorBar = this.colorBars[0];
			} else {
				registerAppTab("color_picker", "pwCP_0_tab", "pwCP_00_panel");
				this.colorBar = this.colorBars[1];
			}
		}
		var self = this;
		for(var i=0; i < paletteIds.length; i++) {
			var pId = paletteIds[i];
			registerAppTab("color_picker", "pwCP_" + paletteIds[i] + "_tab", null, { callback: function(id, cbData) { return self.beforeSelectPalette(id, cbData); }, callbackData: pId});
		}
	},
	
	buildTabs: function(options, paletteIds) {
		var html = '';
		if(options.limitColors != true) {
			html += '<li class="slider" id="pwCP_0_tab"><a href="#" onclick="pwColorPicker.selectPalette(0); return false;">slider</a></li>';
		}
		for(var i=0; i < paletteIds.length; i++) {
			var paletteId = paletteIds[i];
			var palette = this.palettes[paletteId];
			html += '<li id="pwCP_' + paletteId + '_tab"><a href="#" onclick="pwColorPicker.selectPalette(' + paletteId + '); return false;">' + palette.name + '</a></li>';
		}
		return html;
	},
	
	beforeSelectPalette: function(tabId, paletteId) {
		log("beforeSelectPalette(" + tabId + "," + paletteId + ")");
		if(this.loadedPalettes[tabId] == null) {
			var palette= this.palettes[paletteId];
			if(palette.colors.list.size() > 70) {
				new Insertion.Bottom(this.paletteContainer, '<ul class="palette_loading" id="pwCP_' + paletteId + '_panel" style="display:none;"><li>Loading...</li></ul>');
				var self = this;
				window.setTimeout(function() {
						self.loadedPalettes[tabId].replace(self.palettes[paletteId].buildPanelHtml(self.currentColor, true));
						self.loadedPalettes[tabId] = $('pwCP_' + paletteId + '_panel');
				}, 100);
			} else {
				new Insertion.Bottom(this.paletteContainer, this.palettes[paletteId].buildPanelHtml(this.currentColor));
			}
			this.loadedPalettes[tabId] = $('pwCP_' + paletteId + '_panel');
		}
		return 'pwCP_' + paletteId + '_panel';
	},
	/*
	setupPanels: function(options, paletteIds) {
		for(var i=0; i < paletteIds.length; i++) {
			var paletteId = paletteIds[i];
			var palette = this.palettes[paletteId];
			html += palette.buildPanelHtml(this.currentColor);
		}
	},*/
	
	slideValue: function(field, value, isCmyk) {
		log("slideValue:" +field + "=" + value + " isCmyk=" + isCmyk);
		$("pwSV" + field.toUpperCase()).value = value;
		this.currentColor[field] = value;
		if(isCmyk) {
			convertToRGB(this.currentColor);
		}
		this.colorBar.style.backgroundColor = this.currentColor.getHTMLColor();
		log(this.colorBar.style.backgroundColor + " " +  this.currentColor.getHTMLColor());
	},
	
	setSliderValue: function(field, el, isCmyk) {
		log("setSliderValue:" +field + "=" + el.value + " isCmyk=" + isCmyk);
		if(!isNaN(el.value)) {
			var val = parseInt(el.value);
			if((val >=0) && (!isCmyk || val <=100) && (isCmyk || val <=255)) {
				this["slider" + field.toUpperCase()].setValue(val);
				this.currentColor[field] = parseInt(el.value, 10);
				if(isCmyk) {
					convertToRGB(this.currentColor);
					this.currentColor.type="cmyk";
				} else {
				  this.currentColor.type="rgb";
				}
				this.colorBar.style.backgroundColor = this.currentColor.getHTMLColor();
				
			}
		}
	},
	
	selectPalette: function(paletteId) {
		
	},
	
	chooseColor: function(paletteId, colorId) {
		var color = this.palettes[paletteId].colors.byId[colorId];
		this.callback(color.getHTMLColor(), color);
		this.deleteElements();
	},
	
	chooseCurrentColor: function() {
		this.callback(this.currentColor.getHTMLColor(), this.currentColor);
		this.deleteElements();
	},
	
	selectTransparent: function() {
		this.callback("Transparent", null);
		this.deleteElements();
	},
	
	close: function() {
		this.deleteElements();
	},
	
	//close the picker....
	bodyClick: function(event) {
		var el = Event.element(event);
		while(el != null) {
			if(el == this.el) {
				return;
			}
			el = el.parentNode;
		}
		//if we get here we did not click in the color picker...
		this.deleteElements();
	},
	
	deleteElements: function() {
		log("deleteElements");
		//Event.stopObserving(document.body, "mousedown", this.bodyClickEvent);
		//this.dragger.destroy();
		//document.body.removeChild(this.el);
		//this.el = null;
		this.el.style.display="none";
		clearAppTabs("color_picker");
	},
	
	colorOver: function(id) {
		
		if(id == null) {
			this.colorNameEl.style.visibility = "hidden";
		} else {
			var color = this.allColors[id];
			log(color);
			if(color != null) {
				if(color.name != null && color.name != "") {
					this.colorNameEl.innerHTML =  color.name;
				} else {
					this.colorNameEl.innerHTML = color.getHTMLColor();
				}
				this.colorNameEl.style.visibility = "visible";
			} else {
				this.colorNameEl.style.visibility = "hidden";
			}
		}
	}
});



var ColorPalette = Class.create({
	CLASSDEF: {
		name: 'ColorPalette'
	},
	
	initialize: function(options, colors) {
		this.id = options.id;
		this.name = options.name;
		this.colors = new MapList(this);
		for(var i=0; i < colors.length; i++) {
			this.colors.add(new ColorPaletteColor(colors[i]));
		}
	},
	
	buildPanelHtml: function(currentColor, visible) {
		var style = visible==true ? "" : ' style="display:none;"';
		var html = '<ul class="listing" id="pwCP_' + this.id + '_panel"' + style + '>';
		for(var i=0; i < this.colors.list.size(); i++) {
			var color = this.colors.list[i];
			html += color.buildPanelHtml(currentColor);
			if(color.equals(currentColor)) {
				pwColorPicker.currentPalette = this;
				currentColor.id = color.id;
			}
		}
		html += '</ul>';
		return html;
	},
	
	hasColor: function(color) {
		for(var i=0;i < this.colors.list.size(); i++) {
			if(this.colors.list[i].equals(color)) {
				return true;
			}
		}
		return false;
	}
});

var ColorPaletteColor = Class.create({
	CLASSDEF: {
		name: 'ColorPaletteColor'
	},
	
	initialize: function(options) {
		this.id = options[0]
		this.c = options[1];
		this.m = options[2];
		this.y = options[3];
		this.k = options[4];
		this.r = options[5];
		this.g = options[6];
		this.b = options[7];
		this.name = options[8];
		this.tint = options[9];
		this.type = options[10];
		if(this.id != null && this.id != "") pwColorPicker.registerColor(this);
	},
	
	buildPanelHtml: function() {
		return '<li style="background-color: ' + this.getHTMLColor() + ';" onclick="pwColorPicker.chooseColor(' + this.parentObject.id + ',' + this.id + ');" onmouseover="pwColorPicker.colorOver(' + this.id + ');" onmouseout="pwColorPicker.colorOver(null);" >&nbsp;</li>';
	},
	
	getHTMLColor: function() {
		return "#" + toHex(this.r) + toHex(this.g) + toHex(this.b);
	},
	equals: function(other) {
		if(other == null) return false;
		if((this.c == other.c) && (this.m == other.m) && (this.y == other.y) && (this.k == other.k)) { //do test in CMYK colorspace
			return true;
		}
		return false;
	}
});


function toHex(c) {
	var s = c.toString(16).toUpperCase();
	if(s.length == 1)
		return "0" + s;
	return s;
}

function isHtmlColorString(val, checkHash) {
	if(val == null) return false;
	if(checkHash) { //has is mandatory..
		if(val.substring(0,1) != "#") {
			log("isHtmlColorString:false (color does not start with #)");
			return false;
		}
		val = val.substring(1, val.length); //remove the #
	} else { //hash is optional
		if(val.substring(0,1) == "#") {
			//log("isHtmlColorString: stripping leading #");
			val = val.substring(1, val.length); //remove the #
		}
	}
	if((val != null)&&(val.length == 6)) {
		for(var i=0; i < 6; i++) {
			var cc = val.charCodeAt(i);
			if(!( ((cc >=48)&&(cc <=57)) || ((cc>=65)&&(cc<=70)) || ((cc>=97)&&(cc<=102)) )) {
				log("isHtmlColorString:false (char code " + cc + " at " + i + " invalid)");
				return false;
			}
		}
		return true;
	} else {
		log("isHtmlColorString:false (length (" + val.length + ") != 6)");
		return false;
	}
}

function isCMYKColorString(val) { //internal format of CMYK colors.. starts with a ^, colors are in HEX, 00 padded, ranging from 0-100
	if(val == null) return false;
	if(val.substring(0,1) != "^") {
		return false;
	}
	if(val.length == 9) {
		for(var i=1; i < 9; i++) {
			var cc = val.charCodeAt(i);
			if(!( ((cc >=48)&&(cc <=57)) || ((cc>=65)&&(cc<=70)) || ((cc>=97)&&(cc<=102)) )) {
				return false;
			}
		}
		return true;
	} else {
		return false;
	}
}

//convert a string to a ColorPaletteColor object..
function toColorPaletteColor(strVal) {
  if(typeof strVal == "object") {
    
    var opts = [
      strVal.id,
      strVal.c,
      strVal.m,
      strVal.y,
      strVal.k,
      strVal.r,
      strVal.g,
      strVal.b,
      strVal.name,
      strVal.tint,
      strVal.type
		]
		var cpc = new ColorPaletteColor(opts);
		if(cpc.r == null || cpc.r == "") convertToRGB(cpc);
		if(cpc.c == null || cpc.c == "") convertToCMYK(cpc);
		return cpc;
  } else {
    var opts = [null,0,0,0,0,0,0,0,""];
    if(isHtmlColorString(strVal, false)) {
      //log("isHtmlColorString (" + strVal + ")");
      var idx = 0;
      if(strVal.substr(0,1) == "#") {
        idx = 1;
      }
      opts[5] = parseInt(strVal.substr(idx,2), 16);
      opts[6] = parseInt(strVal.substr(idx+2,2), 16);
      opts[7] = parseInt(strVal.substr(idx+4,2), 16);
      
      var cpc = new ColorPaletteColor(opts);
      convertToCMYK(cpc);
      return cpc;
    } else if(isCMYKColorString(strVal)) {
      opts[1] = parseInt(strVal.substr(1, 2), 16);
      opts[2] = parseInt(strVal.substr(3,2), 16);
      opts[3] = parseInt(strVal.substr(5,2), 16);
      opts[4] = parseInt(strVal.substr(7,2), 16);
      
      var cpc = new ColorPaletteColor(opts);
      convertToRGB(cpc);
      return cpc;
    }
  }
	return null;
}



/*
	http://www.neuro.sfc.keio.ac.jp/~aly/polygon/info/color-space-faq.html
*/

function convertToCMYK(entry) {
	
	
	//normalise to 0 - 1
	var r = parseFloat(entry.r) / 255.0;
	var g = parseFloat(entry.g) / 255.0;
	var b = parseFloat(entry.b) / 255.0;
	
	
	var k = minf(minf(1-r,1-g) ,1-b);
	var c = k==1 ? 0 : (1-r-k)/(1-k);
	var m = k==1 ? 0 : (1-g-k)/(1-k);
	var y = k==1 ? 0 : (1-b-k)/(1-k);
	
	
	entry.c = parseInt(c * 100.0);
	entry.m = parseInt(m * 100.0);
	entry.y = parseInt(y * 100.0);
	entry.k = parseInt(k * 100.0);
}

function clip01(amt) {
  if(amt > 1.0) return 1.0;
  return amt;  
}

function convertToRGB(entry) {
  
  var nc = parseFloat(entry.c) / 100.0;
	var nm = parseFloat(entry.m) / 100.0;
	var ny = parseFloat(entry.y) / 100.0;
	var nk = parseFloat(entry.k) / 100.0;
	

	var c = clip01( nc + nk );
	var m = clip01( nm + nk );
	var y = clip01( ny + nk );
	var aw = (1-c) * (1-m) * (1-y);
	var ac = c * (1-m) * (1-y);
	var am = (1-c) * m * (1-y);
	var ay = (1-c) * (1-m) * y;
	var ar = (1-c) * m * y;
	var ag = c * (1-m) * y;
	var ab = c * m * (1-y);
	
	r = clip01(aw + 0.9137*am + 0.9961*ay + 0.9882*ar);
	g = clip01(aw + 0.6196*ac + ay + 0.5176*ag);
	b = clip01(aw + 0.7804*ac + 0.5412*am + 0.0667*ar + 0.2118*ag + 0.4863*ab);
  

	entry.r = parseInt(r * 255.0, 10);
	entry.g = parseInt(g * 255.0, 10);
	entry.b = parseInt(b * 255.0, 10);
	
	/*
	old way thet produces dayglow colors
	//normalise to 0 - 1
	var c = parseFloat(entry.c) / 100.0;
	var m = parseFloat(entry.m) / 100.0;
	var y = parseFloat(entry.y) / 100.0;
	var k = parseFloat(entry.k) / 100.0;

	var r = 1.0 - minf( 1.0, c*(1-k)+k);
	var g = 1.0 - minf( 1.0, m*(1-k)+k);
	var b = 1.0 - minf( 1.0, y*(1-k)+k);

	entry.r = parseInt(r * 255.0);
	entry.g = parseInt(g * 255.0);
	entry.b = parseInt(b * 255.0);
	*/
}

function minf(l,r) {
	if(l < r)
		return l;
	return r;
}

var pwColorPicker = new ColorPicker();



var FontCategory = Class.create({
	CLASSDEF: {
		name: 'FontCategory'
	},
	
	initialize: function(id, name) {
		this.id = id;
		this.name = name;
		this.childFonts = [new MapList(this),new MapList(this),new MapList(this),new MapList(this)]; //0 = non emb, 1 = emb
		this.childCategories = new MapList(this);
		this.cachedCodeString = [null,null, null, null];
		this.cat=null;
		this.subcat=null;
	},
	
	addCategory: function(category) {
		this.childCategories.add(category);
	},
	
	addFont: function(font) {
		if(font.normalFont) {
			//log("Adding Normal Font " + font.name + " to " + this.name);
			this.childFonts[0].add(font);
      //if(font.utf8) {
      //  this.childFonts[2].add(font);
      //}
		}
		if(font.embFont) {
			//log("Adding EMB Font " + font.name + " to " + this.name);
			this.childFonts[1].add(font);
      //if(font.utf8) {
      //  this.childFonts[3].add(font);
      //}
		}
		if(this.parentObject != null) {
			this.parentObject.addFont(font);
		}
	},
	
	filterUtf8Fonts: function(baseIndex, usedPages) {
	  this.childFonts[baseIndex+2] = new MapList(this);
	  var utfList = this.childFonts[baseIndex+2];
	  var fontList = this.childFonts[baseIndex];
	  for(var i=0; i < fontList.list.length; i++) {
	    var fontDef =  fontList.list[i];
	    if(fontDef.supportsPages(usedPages)) {
	      utfList.add(fontDef);
	    }
	  }
	  if(this.childCategories != null) {
	    if(this.cachedCategories!=null) {
	      this.cachedCategories[baseIndex+2] = null;
	    }
      for(var i=0; i < this.childCategories.list.length; i++) {
        this.childCategories.list[i].filterUtf8Fonts(baseIndex, usedPages);
      }
    }
	},
	
	firstCat: function(fontIndex) {
		for(var i=0; i < this.childCategories.list.size(); i++) {
			var cat = this.childCategories.list[i];
			if(cat.hasFonts(fontIndex)) {
				return cat;
			}
		}
	},
	
	hasFonts: function(fontIndex) {
		return (this.childFonts[fontIndex].list.size() > 0);
	},
	
	filteredCategories: function(fontIndex) {
		if(this.cachedCategories == null) {
			this.cachedCategories = {};
		}
		if(this.cachedCategories[fontIndex] != null) {
			return this.cachedCategories[fontIndex];
		}
		var result = [];
		for(var i=0; i < this.childCategories.list.size(); i++) {
			var cat = this.childCategories.list[i];
			if(cat.hasFonts(fontIndex)) {
				result.push(cat);
			}
		}
		this.cachedCategories[fontIndex] = result;
		return result;
	}
});


var FontSelector = Class.create({
	CLASSDEF: {
		name: 'FontSelector',
		parent: FontCategory
	},
	
	initialize: function() {
		FontSelector.parentClass.constructor().call(this, 0, "root");
		this.allCategories = {};
		this.allFonts = {};
		this.fontSelectorDiv = [null,null,null,null];
		this.selectedFont = [null,null,null,null];
		this.selectedCat = [null,null,null,null];
		this.selectedSubCat = [null,null,null,null];
    this.utf8Pages = null;
    this.allCategories[0] = this;
	},
	
	addCategory: function(category, parentId) {
		if(parentId == null) {
      log("Adding " + category.name + " to root");
      this.childCategories.add(category);
		} else {
			var parent = this.allCategories[parentId];
			log("Adding " + category.name + " to " + parent.name);
			parent.addCategory(category);
		}
		this.allCategories[category.id] = category;
	},
	
	addFont: function(font) {
		this.allFonts[font.name] = font;
		FontSelector.parentClass.method("addFont").call(this, font);
		
	},
	
	
	selectFont: function(currentFont, defaultFont, embMode, callback) {
		log("FontSelector.selectFont");
		this.callback = callback;
		this.fontIndex = embMode ? 1 : 0;
		
    if(this.utf8Pages != null) {
      var codeString = "";
      for(var k in this.utf8Pages) {
        codeString += k + ";";
      }
      if(codeString != this.cachedCodeString[this.fontIndex + 2]) {
        this.filterUtf8Fonts(this.fontIndex, this.utf8Pages);
        this.cachedCodeString[this.fontIndex + 2] = codeString;
        if(this.fontSelectorDiv[this.fontIndex + 2] != null) {
          this.fontSelectorDiv[this.fontIndex + 2].remove();
          this.fontSelectorDiv[this.fontIndex + 2] = null;
        }
      }
      this.fontIndex += 2;
    }
		if(currentFont == null) {
			currentFont = defaultFont;
		}
		
		var font = this.allFonts[currentFont];
		if(font==null) {
			font = this.allFonts[defaultFont];
		}
		if(font==null) {
			alert("Unable to get font data");
			return;
		}
		
		if((this.fontSelectorDiv[this.fontIndex]==null)) {
			this.buildFontSelector(font);
		} else {
			log("Already Open");
      var selFont = this.childFonts[this.fontIndex].byId[font.id];
      if(selFont == null) {
        log("Selected Font (" + font.name + ") is not available in fontIndex " + this.fontIndex);
        selFont = this.childFonts[this.fontIndex].list[0];
        if(selFont == null) {
          log("no available fonts");
        } else {
          font = selFont;
        }
      }
			this.toggleFontCat(0); //font.category.id);
			//if(font.subCategory!=null) {
			//	this.selectFontSubCat(font.subCategory.id);
			//}
			this.toggleFont(font.id);
			popup("pwSelectFont_" + this.fontIndex);
		}
		//if(this.fontSelected!=null) {
			var el = $("pwFontCell_" + font.id + "_" + this.fontIndex);
			if(el != null) {
			  try {
          var offsetElement = Position.offsetParent(el);
          var offset = Position.positionedOffset(el);
          
          var scrollElement = $('pwFontListingUl_' + this.fontIndex);
          var scrollOffset = Position.positionedOffset(scrollElement);
          
          var sOffset = offset[1] - scrollOffset[1];
          if(sOffset > 100) {
            scrollElement.scrollTop = sOffset - 100;
          }
        } catch(e) {} //fail silently...
			}
		//}
	},
	
	buildFontSelector: function(font) {
		
		log("FontSelector.buildFontSelector, this.fontIndex=" + this.fontIndex);
		//<div id="add_text" class="popup font_sel clearfix">
		
		var html = '<div class="popup_box"><div class="popup_int"><h3 class="empty"><b>' + pwFontSelectorHeading + '</b></h3>';
		html += '<div class="popup_content">';
			html += '<div class="library_browser font">';
					html += '<div class="hierarchy"><h4>Font Categories</h4><div class="library_categories">';
					
					html += '<ul class="fonts" id="pwFontSelCatBox_' + this.fontIndex + '">';
						  html += '<li><span class="rootfont selected" id="pwFontCatLi_0_' + this.fontIndex + '"><a class="null" href="#">&nbsp;</a><a href="#" onclick="pwFontManager.toggleRootCat(); return false;" class="link">All</a></span><ul>'	
						
						var cats = this.filteredCategories(this.fontIndex);
						
						for(var i=0;i < cats.length;i++) {
							var cHtml = this.buildFontSelectorCat(cats[i], font);
							if(cHtml != null) {
								html += cHtml;
							}
						}
						html += '</ul></li></ul>';
					html += '</div></div>';
					html += '<div class="listing">';
						html += '<h4>Font Listing</h4><ul class="font_listing" id="pwFontListingUl_' + this.fontIndex + '">';
						
						//html += this.buildFontList(this.selectedCat[this.fontIndex],this.selectedSubCat[this.fontIndex], font);
						html += this.buildFontList(this,null, font); //load all fonts
					
						html += '</ul>';
					html +='</div>';
			html +='</div>';
		html += '</div>';
		html += '<div class="foot"><b><input type="button" value="' + pwFontSelectorCancelCaption + '" class="button cancel" onclick="closePopup(\'pwSelectFont_' + this.fontIndex + '\'); return false;"/> <input type="button" value="' + pwFontSelectorOKCaption + '" class="button" onclick="pwFontManager.selectCurrentFont(); return false;"/></b></div></div></div>';
		
		//</div>

		this.fontSelectorDiv[this.fontIndex] = document.createElement("DIV");
		this.fontSelectorDiv[this.fontIndex].className = "popup font_selector";
		this.fontSelectorDiv[this.fontIndex].id = "pwSelectFont_" + this.fontIndex;
		this.fontSelectorDiv[this.fontIndex].setAttribute("stopdeselect","true");
		this.fontSelectorDiv[this.fontIndex].innerHTML = html;
		document.body.appendChild(this.fontSelectorDiv[this.fontIndex]);
		
		popup("pwSelectFont_" + this.fontIndex);
	},

	buildFontSelectorCat: function(cat, font) {
		this.selectedFont[this.fontIndex] = font;
		var clz = "";
		var style = ' style="display:none;"';
		var cats = cat.filteredCategories(this.fontIndex);
		/*if(cat == font.category) {
			//clz = ' class="selected"';
			if(cats.length > 0) {
				style = "";
			}
			this.selectedCat[this.fontIndex] = font.category;
		}*/
		
		var html = '<li id="pwFontCatLi_' + cat.id + '_' + this.fontIndex + '"><span' + clz + '>';
		if (cats.length>0/* && cat != font.category*/) html += '<a href="#" class="open" id="int_icon_'+cat.id+'" onclick="pwFontManager.toggleFontCat(' + cat.id + '); return false;">&nbsp;</a>';
		//else if (cats.length>0 && cat == font.category) html += '<a href="#" class="close" id="int_icon_'+cat.id+'" onclick="pwFontManager.toggleFontCat(' + cat.id + '); return false;">&nbsp;</a>';
		else html += '<a href="#" class="null" id="int_icon_'+cat.id+'" onclick="pwFontManager.toggleFontCat(' + cat.id + '); return false;">&nbsp;</a>';
		html +='<a href="#" onclick="pwFontManager.toggleFontCat(' + cat.id + '); return false;" class="link">' + cat.name + '</a></span>';
		html += '<ul id="pwFontCatUl_' + cat.id + '_' + this.fontIndex + '"' + style + '>';
		
		for(var i=0; i < cats.length; i++) {
			var subCat = cats[i];
			clz = "";
			/*if(subCat == font.subCategory) {
				clz = ' class="selected"';
				this.selectedSubCat[this.fontIndex] = font.subCategory;
			}*/
			var sHtml = '<li id="pwFontCatLi_' + subCat.id + '_' + this.fontIndex + '"><span' + clz + '><a href="#" class="null">&nbsp;</a><a href="#" class="link" onclick="pwFontManager.selectFontSubCat(' + subCat.id + '); return false;" class="cat_label">' + subCat.name + '</a></span></li>';
			html += sHtml;
		}
		html += '</ul></li>';
		return html;
		
	},
	
	buildFontList: function(category, subCategory, currentFont) {
		if(category==null && subCategory==null) {
      return "";
    }
		var fontList = subCategory == null ? category.childFonts[this.fontIndex].list : subCategory.childFonts[this.fontIndex].list;
		var html = "";
		var prefix = this.fontIndex % 2 == 0 ? "" : "emb_";
		for(var i=0; i < fontList.size();i++) {
			var font = fontList[i];
			var clz = "";
			if(font == currentFont) {
				clz = ' class="alt"';
				this.selectedFont[this.fontIndex] = currentFont;
			}
			var fHtml = '<li id="pwFontCell_' + font.id + '_' + this.fontIndex + '"' + clz + ' onclick="pwFontManager.toggleFont(\'' + font.id + '\');" ondblclick="pwFontManager.toggleFont(\'' + font.id + '\'); pwFontManager.selectCurrentFont();"; onmousemove="pwSelFontMm(this);" onmouseout="pwSelFontMo(this);"><img src="/ppr/text_font/sample/' + font.id + '/' + prefix + 'sample.gif" border="0"><span>' + font.name + '</span></li>';
			html += fHtml;
		}
		return html;
	},
	
	selectCurrentFont: function() {
		if(this.callback!=null) {
			this.callback(this.selectedFont[this.fontIndex].name, this.selectedFont[this.fontIndex]);
		}
		closePopup("pwSelectFont_" + this.fontIndex);
	},
	
	toggleRootCat: function() {
	  this.toggleFontCat(0);
	},
	
	toggleFontCat: function(catId) {
		
		curr_li=$$("#pwFontCatUl_"+this.cat+"_0 li span");
		for(x=0; x<curr_li.length; x++) curr_li[x].className=null;
		
		var selCat = this.selectedCat[this.fontIndex];
		//if((selCat == null)||(selCat.id != catId)) {
			if(selCat != null) {
				//deselect existing...
				var el = document.getElementById("pwFontCatLi_" + selCat.id + "_" + this.fontIndex);
				if(selCat.id != 0) {
				  el=el.getElementsByTagName('span')[0];
				  el.className = "";
				  
				  el = document.getElementById("pwFontCatUl_" + selCat.id + "_" + this.fontIndex);
				  el.style.display = "none";
				
          //icon class change
          var fl = document.getElementById("int_icon_"+selCat.id);
          var int_class = (fl.className=="null") ? "null" : "open";
          fl.className = int_class
        } else {
          el.className = "rootfont";
        }
			}
			selCat = this.allCategories[catId];
			this.selectedCat[this.fontIndex] = selCat;
			this.selectFontSubCat(null);
			var el = document.getElementById("pwFontCatLi_" + selCat.id + "_" + this.fontIndex);
			if(catId == 0) {
			  el.className = "rootfont selected";
			} else {
        if(el != null) {
          el=el.getElementsByTagName('span')[0];
        //var el=$$("pwFontCatLi_" + selCat.id + "_" + this.fontIndex+" span")[0];
          el.className = "selected";
          
          var li = el.getElementsByTagName('li');
          for(y=0; y<li.length; y++)
            li[y].className=null;
        }
      }
      
			//icon class change
			var fl = document.getElementById("int_icon_"+selCat.id);
      if(fl != null) {
			var int_class=(fl.className=="null") ? "null" : "close";
        fl.className = int_class
			}
			if(selCat.filteredCategories(this.fontIndex).length != 0) {
				el = document.getElementById("pwFontCatUl_" + selCat.id + "_" + this.fontIndex);
        if(el != null) {
          el.style.display = "";
        }
			}
		//}
		el = document.getElementById("pwFontListingUl_" + this.fontIndex);
    if(el != null) {
      el.innerHTML = this.buildFontList(selCat, null, this.selectedFont[this.fontIndex]);
    }
    this.cat=catId;
	},
	
	selectFontSubCat: function(subCatId) {
		var selCat = this.selectedSubCat[this.fontIndex];
		if(selCat != null) {
			if(selCat.id == subCatId) {
				return;
			}
			//deselect existing...
			var el = document.getElementById("pwFontCatLi_" + selCat.id + "_" + this.fontIndex);
			el=el.getElementsByTagName('span')[0];
			el.className = "";
		}
		if(subCatId!=null) {
			selCat = this.allCategories[subCatId];
			this.selectedSubCat[this.fontIndex] = selCat;
			var el = document.getElementById("pwFontCatLi_" + selCat.id + "_" + this.fontIndex);
      if(el != null) {
			el=el.getElementsByTagName('span')[0];
        el.className = "selected";
			el.parentNode.parentNode.parentNode.getElementsByTagName('span')[0].className=null;
        el = document.getElementById("pwFontListingUl_" + this.fontIndex);
        el.innerHTML = this.buildFontList(this.selectedCat[this.fontIndex], selCat, this.selectedFont[this.fontIndex]);
      }
		} else {
			this.selectedSubCat[this.fontIndex] = null;
		}
	},
	
	toggleFont: function(fontId) {
		var selFont = this.selectedFont[this.fontIndex];
		if(selFont!=null) {
			var el = $("pwFontCell_" + selFont.id + "_" + this.fontIndex);
			if(el != null) {
				el.className = "";
			}
		}
		selFont = this.childFonts[this.fontIndex].byId[fontId];
    if(selFont != null) {
      this.selectedFont[this.fontIndex] = selFont;
      var el = $("pwFontCell_" + selFont.id + "_" + this.fontIndex);
      if(el != null) {
        el.className = "alt";
      }
    }
	}
});

var Font = Class.create({
	CLASSDEF: {
		name: 'Font'
	},
	
	initialize: function(options, category, subCategory) {
		this.id = options.id;
		this.name = options.name;
		this.bold = options.b;
		this.italics = options.i;
		this.boldItalics = options.bi;
		this.utf8 = options.u;
		this.category = category;
		this.subCategory = subCategory;
		this.normalFont = options.nf;
		this.embFont = options.ef;
    this.minSize = options.ms;
    this.languageCodes = options.lc.split(' ');
		if(subCategory != null) {
			subCategory.addFont(this);
		} else if(category != null) {
			category.addFont(this);
		}
	},
	
	getSampleURL: function(isEmb) {
		var prefix = isEmb ? "emb_" : "";
		return "/ppr/text_font/sample/" + this.id + "/" + prefix + "mp2.png";
	}, 
	
	supportsPages: function(pages) {
	  for(var i=0; i < this.languageCodes.length; i++) {
	    if(pages[this.languageCodes[i]]) return true;  
	  }
	  return false;
	}
});

var pwFontManager = new FontSelector();


function pwSelFontMo(el) {
	if(el.className != "alt") {
		el.className = "";
	}
}

function pwSelFontMm(el) {
	if(el.className != "alt") {
		el.className = "font_rollover";
	}
}


Ajax.Responders.register({
	onException: function(aj, ex) {
		try {
			log("Exception Occured\nError name: " + ex.name + "\nError message: " + ex.message, true);
			log(ex);
			if (typeof(orderManager) == "undefined") alert("Exception Occured\nError name: " + ex.name + "\nError message: " + ex.message);
			//log(ex);
		} catch(e) {}
		throw ex;
	}
});

var LibraryCategory = Class.create({
	CLASSDEF: {
		name: 'LibraryCategory'
	},
	
	initialize: function(library, id, name, parent, options) {
		this.library = library;
		this.id = id;
		this.uId = library.getNextId(); //we need a unique id because the same cat can appear twice..
		this.name = name;
		this.parent = parent;
		this.children = new MapList(this);
		this.expanded = (options.expand == true);
		this.hasChildren = options.hc;
		this.assetCount = options.ac == null? 0 : options.ac;
		this.directAssetCount = options.dc == null? 0 : options.dc;
		this.parentCount = options.pc;
		this.nonDecCount = options.dl == null? 0 : options.dl;
		this.images = new MapList(this);
		this.selected = false;
		this.childrenLoadingCleared = false;
		
		library.registerCategory(this);
		this.buildElement();
		if(options.c != null) {
			
			for(var i=0; i < options.c.length; i++) {
				var catOpt = options.c[i];
				this.addCat(catOpt.id, catOpt.n, catOpt.o);
			}
		} else {
			this.childrenLoaded = false;
		}
	},
	
	addCat: function(id, name, options) {
		log("LibraryCategory.addCat(" + name + ")");
		this.addCatObject(new LibraryCategory(this.library, id, name, this, options));
	},
	
	//remove the "loading..." li..
	clearCatLoadingMessage: function() {
		if(!this.childrenLoadingCleared) {
			this.getChildrenListElement().innerHTML = "";
			this.childrenLoadingCleared = true;
		}
	},
	
	addCatObject: function(cat) {
		if(!this.childrenLoaded) {
			this.childrenLoadCalled = true;
			this.childrenLoaded = true;
		}
		if(!this.hasChildren) { //we have added children thru the interface...
			this.hasChildren = true;
			this.checkExpander();
		}
		if(this.children.byId[cat.id] != null) {
			return this.children.byId[cat.id]; 
		}
		return this.children.add(cat);
	},
	
	//check if we should be showing the expander...
	checkExpander: function() {
		if(this.expanderEl != null) {
			if(this.hasChildren) {
				//this.expanderEl.style.visibility="visible";
				this.expanderEl.className="open";
			} else {
				//this.expanderEl.style.visibility="hidden";
				//this.childrenEl.style.display="none";
        if(this.childrenEl!=null) { //this is needed to catch when the server thinks the cat has children but it actually doesnt 
          this.childrenEl.hide();
        }
				this.expanderEl.className="null";
			}
		}
	},
	
	//called from move/delete.. check if has_children still applies...assumes the element has already been removed..
	removeCatObject: function(cat) {
		this.changeImageCount(0-cat.assetCount, false); //deduct asset count 
		this.children.remove(cat.id);
		if(this.children.list.size() == 0) {
			this.hasChildren = false;
			this.checkExpander();
		}
	},
	
	buildElement: function() {
		log("buildElement: this.expanded=" + this.expanded);
		var style = this.expanded ? '' : ' style="display:none;"';
		
		//var expandElClass = this.expanded ? 'icon cat_expander_expanded' : 'icon cat_expander_shrunk';
		var expandElClass = this.expanded ? 'close' : 'open';
    var visHtml = this.isVisible() ? '' : ' style="display:none;"';
		//var expandStyle = this.hasChildren ? '' : ' style="visibility: hidden"';
		expandElClass = this.hasChildren ? expandElClass : ' null';
		
		/*var html = '<li id="cat_' + this.uId + '" class="cat_node"><img src="../../images/admin/current.gif" /><span class="' + expandElClass + '"' + expandStyle + ' id="cat_e_' + this.uId + '">&nbsp;</span><span class="cat_label" id="cat_n_' + this.uId + '">' + this.getCaption() + '</span><ul class="cat_children"' + style + ' id="cat_c_' + this.uId + '"><li><span class="cat_label">Loading...</span></li></ul></li>';*/
		
		var html = '<li id="cat_' + this.uId + '" class="cat_node"' + visHtml + '><span><a href="Javascript://" class="' + expandElClass + '" id="cat_e_' + this.uId + '">&nbsp;</a><a href="Javascript://" class="link" id="cat_n_' + this.uId + '">' + this.getCaption() + '</a></span><ul class="cat_children"' + style + ' id="cat_c_' + this.uId + '" style="padding-left: 20px;"><li><span class="link">Loading...</span></li></ul></li>';
		
		this.parent.clearCatLoadingMessage();
		new Insertion.Bottom(this.parent.getChildrenListElement(), html);
		this.liEl = $('cat_' + this.uId);
		this.expanderEl = $('cat_e_' + this.uId);
		this.nameEl = $('cat_n_' + this.uId);
		this.childrenEl = $('cat_c_' + this.uId);
		
		var self = this;
		this.nameEl.onclick = function() {
			log("LibraryCategory.nameEl.onclick");
			self.select();
      return false;
		};
		
		this.expanderEl.onclick = function() {
			self.toggleExpand();
      return false;
		};
		
		this.afterBuildElement();
	},
	
	afterBuildElement: function() {},
	
	getCaption: function() {
		var assetCount = this.assetCount == null ? '' : ' (' + this.assetCount + ')';
		return this.name + assetCount;
	},
	
	getChildrenListElement: function() {
		return this.childrenEl;
	},
	
	select: function() {
		if(this.library.selectCategory(this)) {
			//this.nameEl.className = "link ";
			this.nameEl.parentNode.className="selected";
			if(this.library.autoExpandCats && !this.expanded && this.hasChildren) {
				log("toggleExpand from select");
				this.toggleExpand();
			}
			this.getImages(1);
      this.selected = true;
		}
	},
	
	deSelect: function() {
		if(this.nameEl != null) {
			this.nameEl.className = "link";
			this.nameEl.parentNode.className=null;
		}
    this.selected = false;
	},
	
	toggleExpand: function() {
		//log("toggleExpand");
		if(this.expanderEl.className=="null") return false;
		if(this.expanded) {
			this.expanded = false;
			this.childrenEl.style.display = "none";
			//this.expanderEl.className = "icon cat_expander_shrunk";
			this.expanderEl.className="open";
		} else if (this.hasChildren) {
			if(!this.childrenLoadCalled) {
				this.childrenLoadCalled = true; //we dont want to call this twice if toggled quickly...
				this.loadChildCategories();
			}
			this.childrenEl.style.display = "";
			//this.expanderEl.className = "icon cat_expander_expanded";
			this.expanderEl.className="close";
			this.expanded = true;
		}
	},
	
	//get a list of children (loading from server if needed)
	getCategories: function(callback) {
		if(!this.childrenLoadCalled) {
			this.childrenLoadCalled = true; //we dont want to call this twice if toggled quickly...
			this.loadChildCategories(function() {
				callback(this.children);
			}.bind(this));
		}
	},
	
	loadChildCategories: function(callback) {
		callback = callback == null ? function() {} : callback
		var ajax = new Ajax.Request("/shared/library/get_children_categories?id=" + this.id + "&uid=" + this.uId + "&process=" + this.library.process, {asynchronous:true, evalScripts:true, method:'get', onComplete: callback});
	},
	
	getImagesUrl: function(page, keywords) {
		var id = this.id == null ? "0" : this.id;
		
		if (keywords != null) {
		  return "/shared/library/search_images?process=" + this.library.process + "&page=" + page + "&keywords=" + keywords;
		} else {
		  return "/shared/library/get_images?id=" + id + "&uid=" + this.uId + "&process=" + this.library.process + "&page=" + page;
		}
	},
	
	getImages: function(page, keywords) {
		log("getImages", true);
		resetImageQueue(); //stop downloading any images from another category...
		this.currentPage = page;
		var url = this.getImagesUrl(page, keywords);
		var searchIndicator = null;
		this.keywords = keywords;
		if (keywords != null) searchIndicator = $("search_box_indicator_" + this.library.process);
		var key = null;
		var ajax = new Ajax.Request(url, {asynchronous:true, evalScripts:true, method:'get', 
			onComplete: function() {
			  if (keywords != null) searchIndicator.style.display = "none";
				asyncFinish(key);
			}
		});
		if (keywords != null) searchIndicator.style.display = "";
		key = asyncStart("decoration_list_container_div_" + this.library.process);
	},
  
  refreshImages: function(onlyIfSelected) {
    if(onlyIfSelected != true || this.selected) {
      this.getImages(this.currentPage);
    }
  },
	
	paginate: function(page) {
		if(page == -1) {
			page = this.currentPage - 1;
		} else if(page == -2) {
			page = this.currentPage + 1;
		}
		this.getImages(page, this.keywords);
	},
	
	//called before addImage to (re)set the list
	initImageList: function(pages) {
		this.images = new MapList(this);
		this.pages = pages;
		this.decorationImageListHtml = "";
		
		if(this.pages.length > 1) {
			var paginatorHtml = '';
			
			if(this.currentPage > 1) {
				paginatorHtml = '<a class="prev_page" rel="prev" href="#" onclick="decorationLibrary.paginate(-1); return false;">' + ml('&laquo; Previous') + '</a>';
			} else {
				paginatorHtml = '<span class="disabled prev_page">' + ml('&laquo; Previous') + '</span>';
			}

			for(var i=0; i < pages.length; i++) {
				var page = pages[i];
				if(page == -1) {
					paginatorHtml += '<span class="disabled">...</span>';
				} else if(page == this.currentPage) {
					paginatorHtml += '<span class="current">' + page + '</span>';
				} else {
					paginatorHtml += '<a href="#" onclick="decorationLibrary.paginate(' + page + '); return false;">' + page + '</a>';
				}
			}

			var maxPage = pages[pages.length-1];
			if(this.currentPage < maxPage) {
				paginatorHtml += '<a class="next_page" rel="next" href="#" onclick="decorationLibrary.paginate(-2); return false;">' + ml('Next &raquo;') + '</a>';
			} else {
				paginatorHtml += '<span class="disabled next_page">' + ml('Next &raquo;') + '</span>';
			}
			
			this.library.paginatorPagesElement.innerHTML = paginatorHtml;
			this.library.paginatorContainerElement.style.display = "none";
		} else {
			this.library.paginatorContainerElement.style.display = "none";
		}
	},
	
	//called by server response for each image
	//doInstant= dont batch it (called after adding new image to server)
	addImage: function(options, doInstant) {
		var img = new DecorationLibraryImage(this, options);
		this.images.add(img);
		
		var html = this.getImageHtml(img);
		if(doInstant == true) {
		  if(this.selected) {
		    new Insertion.Bottom(this.library.imageListElement, html);
		    img.loadImage();
		  }
		} else {
			this.decorationImageListHtml += html;
		}
		return img;
	},
  
  getImageHtml: function(img) {
    var dims = img.scale(50);
		var m_top=(50-dims[1])/2
    var html = '<li id="dec_lib_li_' + img.uId + '" class="product">' +
		  '<div class="d_l_container">'+
        '<img src="/images/trans.gif" width="' + dims[0] + '" height="' + dims[1] + '" style="margin-top: '+m_top+'px;" id="dec_lib_img_' + img.uId + '" />'+
      '</div>'+
      '<span>&nbsp;</span>'+
    '</li>';
    return html;
  },
	
	//when a user has changed an image...
	updateImage: function(options) {
		var img = new DecorationLibraryImage(this, options);
		var oldImage = this.images.byId[img.id];
		img.uId = oldImage.uId;
		this.images.replace(img);
		img.loadImage();
		return img;
	},
	
	//called once all the addImage() calls are done...
	allImagesAdded: function() {
		
		this.library.imageListElement.innerHTML = this.decorationImageListHtml;
		for(var i=0; i < this.images.list.size(); i++) {
			var img = this.images.list[i];
			img.loadImage();
		}
		if(this.pages.length > 1) {
			this.library.paginatorContainerElement.style.display = "";
		}
		
		var noResult = $("decoration_list_no_result_" + this.library.process);
		if (this.images.list.size() == 0) {
		  if (noResult != null) {
		    noResult.style.display = "";
		  } else {
		    var dlContainer = null;
		    if (this.library.process == 0) {
		      dlContainer = $("decoration_list_container");
		    } else {
		      dlContainer = $("decoration_list_container_" + this.library.process);
		    }
		    new Insertion.Top(dlContainer, "<p id='decoration_list_no_result_" + this.library.process + "'>No results</p>");
		  }
		} else {
		  if (noResult != null) {
		    noResult.style.display = "none";
		  }
		}
	},
	
	//to track selected image
	selectImage: function(image) {
		if(this.selectedImage != null) {
			if(this.library.imageSelectionCallback != null) { //notify container we are deselecting the image 
				if(!this.library.imageSelectionCallback(false, this.selectedImage)) { //if returns false we cant deselect the image
					return false;
				}
			}
			this.selectedImage.deSelect();
		}
		this.selectedImage = image;
		if(this.selectedImage != null) {
			if(this.library.imageSelectionCallback != null) {
				this.library.imageSelectionCallback(true, image);
			}
		}
		return true;
	},
	
	changeImageCount: function(delta, direct) {
		//get all instances of the category...
		this.library.processCategory(this.id, function(cat) {
				cat.assetCount += delta;
				if(direct) {
					cat.directAssetCount += delta;
				}
				cat.setCaption();
		});
		if(this.parent!=null) {
			log(this.parent);
			this.parent.changeImageCount(delta, false);
		}
	},
	
	setCaption: function() {
		if(this.nameEl != null) {
			this.nameEl.innerHTML = this.name + " (" + this.assetCount + ")";
		}
	},
	
	//called when deleting/moving cat
	removeElement: function() {
		this.beforeRemoveElement();
		this.liEl.parentNode.removeChild(this.liEl);
		this.liEl = null;
		this.expanderEl = null;
		this.nameEl = null;
		this.childrenEl = null;
		this.library.deRegisterCategory(this);
	},
  
  isVisible: function() {
    return true;
  },
  
  toggleVisible: function(visible) {
    if(visible) {
      $("cat_" + this.uId).show();
    } else {
      $("cat_" + this.uId).hide();
    }
  },
	
	beforeRemoveElement: function() {}
});

var RootCategory = Class.create({
	CLASSDEF: {
		name: 'RootCategory',
		parent: LibraryCategory
	},
	
	getCaption: function() {
		return this.name;
	},
	
	changeImageCount: function(delta, direct) {
		//do nothing
	}
});

var UserCategory = Class.create({
	CLASSDEF: {
		name: 'UserCategory',
		parent: RootCategory
	},
	
	initialize: function(library, name, options) {
    this.forProduct = options.for_product;
    this.hidden = options.hidden;
    this.forDigitize = (options.forDigitize == true);
    this.userId = options.uid;
    this.om = options.om;
		UserCategory.parentClass.constructor().call(this, library, "user_images", name, library, options);
	},
	
	getImagesUrl: function(page) {
		return "/shared/library/get_user_images?process=" + this.library.process + "&page=" + page + "&uid=" + this.uId + "&for_product=" + this.forProduct + "&for_digitize=" + this.forDigitize + "&user_id=" + this.userId + "&om=" + this.om;
	},
  
  isVisible: function() {
    return (this.hidden != true);
  }
});

var StoreRootCategory = Class.create({
	CLASSDEF: {
		name: 'StoreRootCategory',
		parent: RootCategory
	},
	
	initialize: function(library, name, options) {
		StoreRootCategory.parentClass.constructor().call(this, library, "store_images", name, library, options);
	},
	
	getImagesUrl: function(page) {
		return "/shared/library/get_store_images?process=" + this.library.process + "&page=" + page + "&uid=" + this.uId;
	}
});

var StoreCategory = Class.create({
	CLASSDEF: {
		name: 'StoreCategory',
		parent: LibraryCategory
	},
	
	getImagesUrl: function(page) {
		return "/shared/library/get_store_images?cat=" + this.id + "&process=" + this.library.process + "&page=" + page + "&uid=" + this.uId;
	}
});

//used when the library co-exists with other categories (designer)
var DecorationLibraryRoot = Class.create({
	CLASSDEF: {
		name: 'DecorationLibraryRoot',
		parent: RootCategory
	},

	initialize: function(library, name, options) {
		DecorationLibraryRoot.parentClass.constructor().call(this, library, 0, name, library, options); 
	},
	
	getImagesUrl: function(page) {
		var id = this.id == null ? "0" : this.id;
		return "/shared/library/get_images?id=0&uid=" + this.uId + "&process=" + this.library.process + "&page=" + page;
	}
});

var DecorationLibrary = Class.create({
	CLASSDEF: {
		name: 'DecorationLibrary',
		parent: RootCategory
	},
	
	initialize: function(categoryListElement, imageListElement, paginatorContainerElement, paginatorPagesElement, displayRoot, process, manager, searchElement) {
		this.displayRoot = displayRoot;
		this.id = 0;
		this.uId = (manager == null)? 0 : manager.getNextId();
		this.manager = manager; //when used from designer.. handles multiple processes
		this.name = "Root";
		this.hasChildren = false; //this will stop the expander from showing...
		this.childrenLoaded = true; //stop check of expand info....
		//this.expanded = true;
		this.children = new MapList(this);
		this.categoryListElement = categoryListElement;
		this.imageListElement = imageListElement;
		this.paginatorContainerElement = paginatorContainerElement;
		this.paginatorPagesElement = paginatorPagesElement;
		this.searchElement = searchElement;
		
		this.library = this;
		this.categories = {};
		this.categoriesById = {}; //handle multiple instances of same cat....
		this.nextId = -1;
		this.process = process;
		this.categories[0] = this;
		this.categoriesById[0] = [this];
		this.imageSelectionCallback = null;
		this.imageUploadedCallback = null;
		this.categorySelectionCallback = null;
		this.doubleClickImageCallback = null;
		this.autoExpandCats = false; //when a cat is selected expand it
		
		this.childrenLoadCalled = true;
		
		
		if(displayRoot) {
			this.parent = new DecorationLibraryParent(this.categoryListElement);
			this.buildElement();
			this.getChildrenListElement().innerHTML = ""; //remove the Loading....
			this.select();
		}
	},
	
	getNextId: function() {
		if(this.manager != null) {
			return this.manager.getNextId();
		} else {
			return this.nextId--;
		}
	},
	
	getChildrenListElement: function() {
		if(this.displayRoot) {
			return this.childrenEl;
		} else {
			return this.categoryListElement;
		}
	},
	
	//track currenctly selected category
	selectCategory: function(libraryCategory) {
		
		if(this.selectedCat != null) {
			if(this.categorySelectionCallback != null) {
				if(!this.categorySelectionCallback(false, this.selectedCat)) {
						return false;
				}
			}
			this.selectedCat.deSelect();
		}
		this.selectedCat = libraryCategory;
		if(this.categorySelectionCallback != null) {
			this.categorySelectionCallback(true, libraryCategory);
		}
		return true;
	},
	
	//allow us to lookup a cat directly
	registerCategory: function(category) {
		this.categories[category.uId] = category;
		if(this.categoriesById[category.id] == null) {
			this.categoriesById[category.id] = [];
		}
		this.categoriesById[category.id].push(category);
	},
	
	deRegisterCategory: function(category) {
		var cats = this.categoriesById[category.id];
		var catIdx = -1;
		for(var i=0; i < cats.length; i++) {
			if(cats[i].uId == category.uId) {
				catIdx = i;
				break;
			}
		}
		if(catIdx != -1) {
			this.categoriesById[category.id].splice(catIdx,1);
		} else {
			log("ERROR: unable to deRegisterCategory");
		}
	},
	
	//run a callback on each instance of a category
	processCategory: function(id, callback) {
		var cats = this.categoriesById[id];
		//make a copy in case the array gets changed from the callback
		var cats2 = [];
		for(var i=0; i < cats.length; i++) {
			cats2.push(cats[i]);
		}
		for(var i=0; i < cats2.length; i++) {
			var cat = cats2[i];
			callback(cat);
		}
	},
	
	search: function() {
	  if(this.selectedCat != null) {
			if(this.categorySelectionCallback != null) {
				if(!this.categorySelectionCallback(false, this.selectedCat)) {
						return false;
				}
			}
			this.selectedCat.deSelect();
		}
		this.selectedCat = this;
		this.getImages(1, this.searchElement.value);
	},
	
	paginate: function(page) {
		if(this.selectedCat == this) {
			DecorationLibrary.parentClass.method("paginate").call(this, page);
		} else {
			this.selectedCat.paginate(page);
		}
	},
  //after making a change to an image it may need processing... lets track that... 
  startTrackingProcessing: function(imageId, isNew, catUid, inDesigner) {
    this.processTrackingImageId = imageId;
    this.processTrackingIsNew = isNew;
    this.processTrackingCatUid = catUid;
    this.failureCount = 0;
    this.processTrackingInDesigner = (inDesigner == true)? 1 : 0;
    this.updateTrackedProcess(imageId, "Processing....", 0);
    popup("process_status");
  },
  
  updateTrackedProcess: function(imageId, caption, percent) {
    
    if(imageId == this.processTrackingImageId) {
      var totalWidth = parseInt($("process_status_bar").style.width);
      var barWidth = totalWidth * percent / 100;
      $("process_status_indicator").style.width = parseInt(barWidth) + "px";
      $("process_status_message").innerHTML = caption;
      var self = this;
      if(percent >= 100) {
        window.setTimeout( function() {
            self.finishTrackingProcess(true);
        }, 200);
      } else {
        window.setTimeout( function() {
          var ajax = new Ajax.Request("/shared/library/processing_status?image_id=" + imageId + "&ts=" + new Date().getTime() , {asynchronous:true, evalScripts:true, method:'get', 
            onFailure: function() {
              self.failureCount ++;
              if(self.failureCount > 10) {
                self.finishTrackingProcess(false);
              } else {
                self.updateTrackedProcess(imageId, caption, percent);
              }
            }
          });
        }, 500);
      }
    }
  },
  
  finishTrackingProcess: function(allOk) {
    if(!allOk) {
      alert("A Server error occured while tracking file processing.");
      closePopup("process_status");
    } else {
      var ajax = new Ajax.Request("/shared/library/get_details?id=" + this.processTrackingImageId + "&is_new=" + this.processTrackingIsNew + "&category_uid=" + this.processTrackingCatUid +  "&must_be_ok=1&ts=" + new Date().getTime()+ "&in_designer=" + this.processTrackingInDesigner, {asynchronous:true, evalScripts:true, method:'get', 
        onComplete: function() {
          closePopup("process_status");
        }
      });
    }
  }	
});

//placeholder class to hold a container el when root is shown
var DecorationLibraryParent = Class.create({
	CLASSDEF: {
		name: 'DecorationLibraryParent'
	},
	
	initialize: function(listElement) {
		this.listElement = listElement;
	},
	
	getChildrenListElement: function() {
		return this.listElement;
	},
	
	clearCatLoadingMessage: function() {
	}
});


var DecorationLibraryImage = Class.create({
	CLASSDEF: {
		name: 'DecorationLibraryImage'
	},
	
	initialize: function(libraryCategory, options) {
		this.libraryCategory = libraryCategory;
		this.id = options.id;
		this.sid = options.sid;
		this.uId = libraryCategory.library.getNextId(); //we need a unique id because the same cat can appear twice in designer
    
    this.urlOptions = {
       bURL: options.bURL,
       tURL: options.tURL,
       sURL: options.sURL,
       url: options.url
    };
    this.baseUrl = options.bURL; //base url
    
		this.thumbUrl = this.baseUrl + "/" + options.tURL;
		this.url = this.baseUrl + "/" + options.url;
    this.smallUrl = this.baseUrl + "/" + options.sURL;
		this.width = options.width;
		this.height = options.height;
		this.aspectRatio = parseFloat(this.width) / parseFloat(this.height);
		this.type = options.type;
		this.usesTransparency = options.trans;
		this.name = options.name;
		this.colors = options.c;
		this.colorWays = options.cw;
		this.defaultColorway = options.dcw;
		this.threadCount = options.tc;
		this.processes = options.p;
		this.ownerType = options.ot;
		this.imageType = options.it;
		this.percentOpaque = options.po;
		this.defaultColorCount = options.dcc;
		this.metaData = options.meta;
		this.decorationLibraryId = options.dl;
    this.digitizedProcessId = options.dp;
    this.digitizedState = options.dps;
    this.hasColorsAlt = options.hc;
    this.templated = options.t;
    this.metaTitle = options.mt;
    this.overrideMetaTitle = options.omt;
    this.metaKeywords = options.mk;
    this.overrideMetaKeywords = options.omk;
    this.metaDescription = options.md;
    this.overrideMetaDescription = options.omd;
	},
	
	//return width/height of thumb that fits in square of side 
	scale: function(side) {
		
		if(this.aspectRatio > 1) { //width is greater...
			side = (this.width < side) ? this.width : side;
			return [side, parseInt(parseFloat(side) / this.aspectRatio)];
		} else {
			side = (this.height < side) ? this.height : side;
			return [parseInt(parseFloat(side) * this.aspectRatio), side];
		}
	},
	
	loadImage: function() {
		log("loadImage");
		var self =this;
		if(this.el == null) {
			this.el = $("dec_lib_img_" + this.uId);
			this.el.className="protect";
			myqueue.add(this.el);
			
			this.liEl = $("dec_lib_li_" + this.uId);
			
			this.liEl.onmousemove = function() {
				if(this.liEl.className != "alt") {
					//this.liEl.className = "product over";
				}
			}.bind(this);
			this.liEl.onmouseleave = function() {
				if(this.liEl.className != "alt") {
					//this.liEl.className = "product";
				}
			}.bind(this);
			this.liEl.onclick = function(el) {
				self.select(el);
        return false;
			};
			this.liEl.ondblclick = function(el) {
				if(self.libraryCategory.library.doubleClickImageCallback != null) {
					self.libraryCategory.selectImage(null); //not tracking selected image.. make sure state reflects this
					self.libraryCategory.library.doubleClickImageCallback(self);
				} else {
					self.select(el);
				}
        return false;
			};
			
		}
		var dims = this.scale(50);
		
		queueImageLoading(this.el,this.thumbUrl,dims[0],dims[1], function() {self.el.alt = "Double Click To Add To Design"});
	},
	
	select: function(el) {
    if(el != null) {
      Event.stop(el);
    }
		if(this.libraryCategory.selectImage(this)) {
			//this.liEl.className = "product alt";
      this.selected=true;
		}
	},
	
	deSelect: function() {
		//this.liEl.className = "product";
    this.selected=false;
	},
	
	uploadFinished: function() {
		var result = {action:"select"};
		if(this.libraryCategory.library.imageUploadedCallback != null) {
			this.libraryCategory.library.imageUploadedCallback(this);
		} else {
			this.select();
		}
	},
	
	allowsProcess: function(processId) {
		for(var i=0; i < this.processes.length; i++) {
			if(this.processes[i] == processId) {
				return true;
			}
		}
		return false;
	},
	
	hasColors: function() {
		//if((this.colors == null || this.colors.length == 0)&&(this.defaultColorCount ==0)) {
		//	return false;
		//}
		//return true;
		return this.hasColorsAlt;
	},
	
	isTemplate: function() {
	  return this.templated;
	},
  
  isVector: function() {
    if(this.imageType == 1) return true;
    if(this.imageType == 3 || this.imageType == 4) { //are all the components vector? 
      if(this.metaData != null && this.metaData.type == "vector") return true;
    }
    return false;
  },
	
	//called after delete image
	remove: function() {
    if(this.selected) {
      this.libraryCategory.selectImage(null);
    }
    if(this.liEl != null) {
      this.liEl.parentNode.removeChild(this.liEl);
    }
		this.libraryCategory.images.remove(this.id);
    
	},
	
	//get info a hash to init Asset
	assetOptions: function() {
		return {
			id: this.id,
			sid: this.sid,
      bURL: this.urlOptions.bURL,
      tURL: this.urlOptions.tURL,
      sURL: this.urlOptions.sURL,
      url: this.urlOptions.url,
			width: this.width,
			height: this.height,
			type: this.type,
			trans: this.usesTransparency,
			name: this.name,
			c: this.colors,
			cw: this.colorWays,
			tc: this.threadCount,
			p: this.processes,
			ot: this.ownerType,
			it: this.imageType,
			po: this.percentOpaque,
			dcc: this.defaultColorCount,
			meta: this.metaData,
			dl: this.decorationLibraryId,
      dp: this.digitizedProcessId,
      dps: this.digitizedState,
      hc: this.hasColorsAlt,
      t: this.templated
		};
	}
});



var Pagination = Class.create({
  CLASSDEF: {
      name: 'Pagination'
  },
  
  initialize: function PG_initialize(parent, divId, callback, showEntriesInfo, entryName, pluralizedEntryName, prevLinkName, nextLinkName) {
    this.parent = parent;
    this.container = $(divId);
    this.totalPages = 0;
    this.totalEntries = 0;
    this.perPage = 0;
    this.currentPage = 1;
    this.offset = 0;
    this.length = 0;
    this.callback = callback;
    this.showEntriesInfo = showEntriesInfo;
    this.entryName = (entryName) ? entryName : ml("item");
    this.pluralizedEntryName = (pluralizedEntryName) ? pluralizedEntryName : ml("items");
    this.prevLinkName = (prevLinkName) ? prevLinkName : "&laquo; Previous";
    this.nextLinkName = (nextLinkName) ? nextLinkName : "Next &raquo;";
    this.id = nextPaginationId;
    nextPaginationId ++;
    
    this.update();
  },
  
  reset: function PG_reset(totalPages, totalEntries, perPage, currentPage, offset, length) {
    this.setTotalPages(totalPages);
    this.setTotalEntries(totalEntries);
    this.setPerPage(perPage);
    this.setCurrentPage(currentPage);
    this.setOffset(offset);
    this.setLength(length);
    
    this.update();
  },
  
  hide: function PG_hide() {
    this.container.hide();
  },
  
  setTotalPages: function PG_setTotalPages(totalPages) {
    this.totalPages = (totalPages) ? totalPages : 0;
  },
  
  setTotalEntries: function PG_setTotalEntries(totalEntries) {
    this.totalEntries = (totalEntries) ? totalEntries : 0;
  },
  
  setPerPage: function PG_setPerPage(perPage) {
    this.perPage = (perPage) ? perPage : 0;
  },
  
  setCurrentPage: function PG_setCurrentPage(currentPage) {
    this.currentPage = (currentPage) ? currentPage : 1;
  },
  
  setOffset: function PG_setOffset(offset) {
    this.offset = (offset) ? offset : 0;
  },
  
  setLength: function PG_setLength(length) {
    this.length = (length) ? length : 0;
  },
  
  setEntryName: function PG_setEntryName(name) {
    this.entryName = name;
  },
  
  setPluralizedEntryName: function PG_setPluralizedEntryName(name) {
    this.pluralizedEntryName = name;
  },
  
  getTotalPages: function PG_getTotalPages() {
    return this.totalPages;
  },
  
  getTotalEntries: function PG_getTotalEntries() {
    return this.totalEntries;
  },
  
  getPerPage: function PG_getPerPage() {
    return this.perPage;
  },
  
  getCurrentPage: function PG_getCurrentPage() {
    return this.currentPage;
  },
  
  nextPage: function PG_nextPage() {
    if (this.totalPages > this.currentPage) {
      if (this.callback(this.parent, this.currentPage + 1)) {
        this.currentPage += 1;
        this.update();
      }
    }
    return false;
  },
  
  prevPage: function PG_prevPage() {
    if (this.currentPage > 1) {
      if (this.callback(this.parent, this.currentPage - 1)) {
        this.currentPage -= 1;
        this.update();
      }
    }
    return false;
  },
  
  toPage: function PG_toPage(page) {
    if (typeof page != "number") {
      var element = Event.element(page);
      page = parseInt(element.innerHTML);
    }
    if (page > 0 && page <= this.totalPages) {
      if (this.callback(this.parent, page)) {
        this.currentPage = page;
        this.update();
      }
    }
    return false;
  },
  
  update: function PG_update() {
    if (this.totalPages == 0) {
      this.container.style.display = "none";
    } else {
      var html = "";
      var links = {};
    
      // page entries info
      if (this.showEntriesInfo) {
        html += '<span class="page_entries_info">';
        if (this.totalPages < 2) {
          switch(this.totalEntries) {
            case 0:
              html += ml("No %s found", this.pluralizedEntryName);
              break;
            case 1:
              html += ml("Displaying <b>1<\/b> %s", this.entryName);
              break;
            default:
              html += ml("Displaying <b>all %1s<\/b> %2s", [this.totalEntries, this.pluralizedEntryName]);
          }
        } else {
          html += ml("Displaying <b>%1s<\/b> to <b>%2s<\/b> of <b>%3s<\/b> %4s", [this.offset + 1, this.offset + this.length, this.totalEntries, this.pluralizedEntryName]);
        }
        html += '</span>';
      }
      
      // prev link
      if (this.currentPage != 1) {
        html += '<a rel="prev" class="prev_page pagination_' + this.id + '" href="#" onclick="return false;">' + this.prevLinkName + '</a>';
      } else {
        html += '<span class="disabled prev_page">' + this.prevLinkName + '</span>';
      }
      
      // pages
      var startPage = (this.currentPage - 4 < 1) ? 1 : this.currentPage - 4;
      var endPage = (startPage + 8 > this.totalPages) ? this.totalPages : startPage + 8
      
      var preEndPage = (startPage - 1 < 2) ? startPage - 1 : 2;
      for (var i=1; i<=preEndPage; i++) {
        html += '<a href="#" class="pagination_' + this.id + '" onclick="return false;">' + i + '</a>';
      }
      if (startPage > 3) html += '<span class="gap">…</span>';
      
      for (var i=startPage; i<=endPage; i++) {
        if (i == this.currentPage) {
          html += '<span class="current">' + i + '</span>';
        } else {
          html += '<a href="#" class="pagination_' + this.id + '" onclick="return false;">' + i + '</a>';
        }
      }
      
      if (this.totalPages - endPage > 2) html += '<span class="gap">…</span>';
      startPage = (this.totalPages - 2 > endPage) ? this.totalPages - 1 : endPage + 1;
      for (var i=startPage; i<=this.totalPages; i++) {
        html += '<a href="#" class="pagination_' + this.id + '" onclick="return false;">' + i + '</a>';
      }
      
      // next link
      if (this.currentPage != this.totalPages) {
        html += '<a rel="next" class="next_page pagination_' + this.id + '" href="#" onclick="return false;">' + this.nextLinkName + '</a>';
      } else {
        html += '<span class="disabled next_page">' + this.nextLinkName + '</span>';
      }
      this.container.innerHTML = html;
      this.container.style.display = "";
      
      // bind events
      var self = this;
      $$('.pagination_' + this.id).each(function(item) {
          if (item.hasClassName("prev_page")) {
            Event.observe(item, "click", self.prevPage.bindAsEventListener(self));
          } else if (item.hasClassName("next_page")) {
            Event.observe(item, "click", self.nextPage.bindAsEventListener(self));
          } else {
            Event.observe(item, "click", self.toPage.bindAsEventListener(self));
          }
      });
    }
  }
});

var nextPaginationId = 1;


var PikiSearch = Class.create({
	CLASSDEF: {
		name: 'PikiSearch'
	},
	
	initialize: function(divId, caption, mode, selectedButtonCaption, callback, translate, filter) {
		this.divId = divId;
		this.caption = caption;
		this.mode = mode;
		this.callback = callback;
		this.initDone = false;
		this.id = nextPikiSearchId;
		this.params = {};
		this.page = 1;
		this.sortKey = null;
		this.sortOrder = null;
		this.selectedItems = {};
		this.selectedCount = 0;
		this.selectedButtonCaption = selectedButtonCaption;
		this.currentSelectorIds = [];
		this.existingSelectedIds = null;
		this.translate = translate;
		this.filter = filter; // should be an array [filter_type, filter_id]
		nextPikiSearchId ++;
	},
	
	showPopup: function(forceClear, existingSelectedIds) {
		currentPikiSearch = this;
		this.existingSelectedIds = existingSelectedIds;
		if(!this.initDone || forceClear) {
			$(this.divId).innerHTML = "Loading....";
			popup(this.divId);
			var self = this;
			var t2 = new Ajax.Updater(this.divId, this.getInitUrl(), {asynchronous:true, evalScripts:true, 
				onComplete: function() { 
					repositionPopup(self.divId);
					self.initDone = true;
				}
			});
		} else {
			this.clearSelected();
			popup(this.divId);
		}
	},
	
	clearSearch: function(fieldToKeep, includeCb) {
		var inputs = $("site_search_form_" + this.id).getElementsByTagName("input");
		
		for(var i =0; i < inputs.length;i++) {
			var input = inputs[i];
			if(input.name != fieldToKeep) {
				if(input.type == "text") {
					input.value = "";	
				} else if(includeCb == true && input.type == "checkbox") {
					input.checked = false;
				}
			} 
		}
	},
	
	setSortState: function(sortKey, sortOrder) {
		this.sortKey = sortKey;
		this.sortOrder = sortOrder;
	},
	
	sortResults: function(sortOrder, sortKey) {
		this.sortKey = sortKey;
		this.sortOrder = sortOrder;
		this.page = 1;
		this.callSearch(this.params);
	},
	
	resetSearch: function() {
		this.clearSearch(null, true);
		this.page = 1;
		this.search();
	},
	
	search: function() {
		this.page = 1;
		this.params = this.getSearchParams();
		this.callSearch(this.params);
	},
	
	callSearch: function(params) {
		this.currentSelectorIds = [];
		params = hashClearEmpty(params); //copy (so we can add params below) as well as clearing empty params
		if(hashSize(params)==0) {
			$("search_clear_" + this.id ).style.display="none";
		} else {
			$("search_clear_" + this.id ).style.display="";
		}
		params["page"] = this.page;
		params["s"] = this.sortKey;
		params["d"] = this.sortOrder;
		
		var self = this;
		var aKey = null;
		var t2 = new Ajax.Updater("search_results_" + this.id, this.getSearchUrl(), {asynchronous:true, evalScripts:true, parameters:params, 
			onComplete: function() { 
				asyncFinish(aKey); 
				repositionPopup(self.divId);
			}
		});
		aKey = asyncStart($("search_results_" + this.id));
	},
	
	addSelected: function(id, value) {
		if(this.selectedItems[id]== null) {
			this.selectedCount ++;
			this.selectedItems[id] = value;
			this.updateSelectedButton();
		}
	}, 
	
	removeSelected: function(id) {
		if(this.selectedItems[id]!= null) {
			this.selectedCount --;
			delete this.selectedItems[id];
			this.updateSelectedButton();
		}
	},
	
	registerSelector: function(id, elId) {
		this.currentSelectorIds.push([id, elId]);
	},
	
	isExistingSelected: function(id) {
		if(this.existingSelectedIds!=null && this.existingSelectedIds[id] != null) {
			return true;
		}
		return false;
	},
	
	isSelected: function(id) {
		return (this.selectedItems[id]!= null);
	},
	
	clearSelected: function() {
		this.selectedCount = 0;
		this.selectedItems = {};
		this.updateSelectedButton();
		for(var i=0; i < this.currentSelectorIds.size(); i++) {
			var id = this.currentSelectorIds[i][0]
			var el = $(this.currentSelectorIds[i][1]);
			if(el != null) {
				if(this.isExistingSelected(id)) {
					el.checked = true;
					el.disabled = true;
					//el.parentNode.className = "results_on_already";
				} else {
					el.checked = false;
					el.disabled = false;
				}
			}
		}
	}, 
	
	updateSelectedButton: function() {
		if(this.selectedCount==0) {
			$("search_add_selected_" + this.id).style.display="none";
		} else {
			$("search_add_selected_" + this.id).style.display="";
			$("search_add_selected_" + this.id).value = this.selectedButtonCaption.replace("%d", this.selectedCount);
		}
	},
	
	paginate: function(page) {
		this.page = page;
		this.callSearch(this.params);
	},
	
	close: function() {
		closePopup(this.divId);
	},
	
	addSelectedItems: function() {
		closePopup(this.divId);
		this.callback(this.selectedItems);
	}
	
	
});

var PikiSiteSearch = Class.create({
	CLASSDEF: {
		name: 'PikiSiteSearch',
		parent: PikiSearch
	},
	
	initialize: function(divId, caption, mode, selectedButtonCaption, callback, translate, filter) {
		PikiSiteSearch.parentClass.constructor().call(this, divId, caption, mode, selectedButtonCaption, callback, translate, filter);
	},
	
	getInitUrl: function() {
	  var filterParams = (this.filter) ? "&ft=" + this.filter[0] + "&fi=" + this.filter[1] : "";
		return "/manage/search/select_sites?caption=" + encodeURIComponent(this.caption) + "&sbid=" + this.id + "&translate=" + this.translate + "&mode=" + this.mode + "&sl=" + encodeURIComponent(this.selectedButtonCaption) + filterParams;
	},
	
	getSearchUrl: function() {
	  var filterParams = (this.filter) ? "&ft=" + this.filter[0] + "&fi=" + this.filter[1] : "";
		return "/manage/search/update_search?sbid=" + this.id + "&mode=" + this.mode + "&sl=" + encodeURIComponent(this.selectedButtonCaption) + filterParams;
	},
	
	getSearchParams: function() {
		return Form.serialize("site_search_form_" + this.id, true);
	},
	
	searchSiteSelect: function(siteId, siteDomain, name, el) {
		
		if(el.checked) {
			this.addSelected(siteId, {
					id:siteId,
					domain: siteDomain,
          name: name
			});
		} else {
			this.removeSelected(siteId);
		}
	},
	
	selectSite: function(siteId, siteDomain, name) {
	  closePopup(this.divId);
	  this.callback({
        id:siteId,
        domain: siteDomain,
        name: name
    });
	}
	
});

var PikiGroupSearch = Class.create({
	CLASSDEF: {
		name: 'PikiGroupSearch',
		parent: PikiSearch
	},
	
	initialize: function(divId, caption, mode, selectedButtonCaption, callback, translate, filter) {
		PikiGroupSearch.parentClass.constructor().call(this, divId, caption, mode, selectedButtonCaption, callback, translate, filter);
	},
	
	getInitUrl: function() {
	  var filterParams = (this.filter) ? "&ft=" + this.filter[0] + "&fi=" + this.filter[1] : "";
		return "/manage/search/select_groups?caption=" + encodeURIComponent(this.caption) + "&sbid=" + this.id + "&translate=" + this.translate + "&mode=" + this.mode + "&sl=" + encodeURIComponent(this.selectedButtonCaption) + filterParams;
	},
	
	getSearchUrl: function() {
	  var filterParams = (this.filter) ? "&ft=" + this.filter[0] + "&fi=" + this.filter[1] : "";
		return "/manage/search/update_group_search?sbid=" + this.id + "&mode=" + this.mode + "&sl=" + encodeURIComponent(this.selectedButtonCaption) + filterParams;
	},
	
	getSearchParams: function() {
		return Form.serialize("group_search_form_" + this.id, true);
	},
	
	searchGroupSelect: function(groupId, name, el) {
		
		if(el.checked) {
			this.addSelected(groupId, {
					id:groupId,
          name: name
			});
		} else {
			this.removeSelected(groupId);
		}
	},
	
	selectGroup: function(groupId, name) {
	  closePopup(this.divId);
	  this.callback({
        id:groupId,
        name: name
    });
	},
	
	clearSearch: function(fieldToKeep, includeCb) {
		var inputs = $("group_search_form_" + this.id).getElementsByTagName("input");
		
		for(var i =0; i < inputs.length;i++) {
			var input = inputs[i];
			if(input.name != fieldToKeep) {
				if(input.type == "text") {
					input.value = "";	
				} else if(includeCb == true && input.type == "checkbox") {
					input.checked = false;
				}
			} 
		}
	}
	
});


var currentPikiSearch = null;
var nextPikiSearchId = 1;
		

var StoreAvailability = Class.create({
	CLASSDEF: {
		name: 'StoreAvailability'
	},
	
	initialize: function(divId, caption, availability, popupContainerId, translate) {
		this.div = $(divId);
		this.caption = caption;
		this.availability = availability;
		this.popupContainer = $(popupContainerId);
		this.groupSearch = null;
		this.storeSearch = null;
		this.translate = translate;
		
		this.initContainer();
	},
	
	initContainer: function() {
	  var html;
	  html = '<!-- Store Availability Header -->' +
      '<div class="box_head">' +
        '<h3>' + this.caption + '</h3>' +
      '</div>' +
      
      '<div>' +
        '<!-- All Store Default Permission -->' +
        '<table class="base hundred">' +
          '<tbody id="af_permissions">' +
            '<tr>' +
              '<td><strong>' + this.availability.all_stores[0] + '</strong></td>' +
              '<td style="width:160px;">' +
                '<input type="radio" name="availability[all_stores]" value="1"' + ((this.availability.all_stores[1])? 'checked' : '') + ' />' + ml("Allow") +
                '<input type="radio" name="availability[all_stores]" value="0"' + ((!this.availability.all_stores[1])? 'checked' : '') + ' />' + ml("Deny") +
              '</td>' +
              '<td style="width:60px;"></td>' +
            '</tr>' +
          '</tbody>' +
        '</table>' +
        '</div>'+
        '<div class="box_head">'+
			'<h3>' + ml('Group Permission Exclusions') + '</h3>'+
			'<b class="action"><a href="#" class="button" onclick="storeAvailability.addAvailableGroup(); return false;">' + ml("add group") + '</a></b>' +
		'</div>'+
		'<div>'+
			'<!-- Group Permission -->' +
			'<table class="base hundred">' +
			  '<tbody id="group_permissions">' +
			  '</tbody>' +
			'</table>' +
        '</div>'+
        '<div class="box_head">'+
			'<h3>' + ml('Store Permission Exclusions') + '</h3>'+
			 '<b class="action"><a href="#" class="button" onclick="storeAvailability.addAvailableStore(); return false;">' + ml("add store") + '</a></b>' +
		'</div>'+
		'<div>'+
        '<!-- Store Permission -->' +
        '<table class="base hundred">' +
          '<tbody id="store_permissions">' +
          '</tbody>' +
        '</table>' +
      '</div>';
      
    this.div.innerHTML = html;
    
    var groups = this.availability.groups;
    for (var i in groups) {
      this.addGroupRow(i, groups[i][0], groups[i][1]);
    }
    this.checkNoGroupMessage();
    
    var stores = this.availability.stores;
    for (var i in stores) {
      this.addStoreRow(i, stores[i][0], stores[i][1], stores[i][2]);
    }
    this.checkNoStoreMessage();
	},
	
	addStoreRow: function(id, name, domain, allow) {
	  html =
	  '<tr id="sp_' + id + '">' +
      '<td><strong>' + name + '</strong></td>' +
      '<td>' + domain + '</td>' +
      '<td style="width:160px;">' +
        '<input type="radio" name="availability[stores][' + id + ']" value="1"' + ((allow)? 'checked' : '') + ' />' + ml("Allow") +
        '<input type="radio" name="availability[stores][' + id + ']" value="0"' + ((!allow)? 'checked' : '') + ' />' + ml("Deny") +
      '</td>' +
      '<td style="width:60px;"><a href="#" onclick="storeAvailability.removeAvailableStore(' + id + '); return false;" class="icon delete">' + ml("remove") + '</a></td>' +
    '</tr>';
    
    new Insertion.Bottom("store_permissions", html);
	},
	
	addGroupRow: function(id, name, allow) {
	  html =
	  '<tr id="gp_' + id + '">' +
      '<td><strong>' + name + '</strong></td>' +
      '<td style="width:160px;">' +
        '<input type="radio" name="availability[groups][' + id + ']" value="1"' + ((allow)? 'checked' : '') + ' />' + ml("Allow") +
        '<input type="radio" name="availability[groups][' + id + ']" value="0"' + ((!allow)? 'checked' : '') + ' />' + ml("Deny") +
      '</td>' +
      '<td style="width:60px;"><a href="#" onclick="storeAvailability.removeAvailableGroup(' + id + '); return false;" class="icon delete">' + ml("remove") + '</a></td>' +
    '</tr>';
    
    new Insertion.Bottom("group_permissions", html);
    
	},
	
	addAvailableStore: function() {
	  if(this.storeSearch == null) {
      var div = document.createElement("DIV");
      div.className = "popup";
      div.style.display="none";
      div.style.width = "720px";
      div.id = "store_search";
      this.popupContainer.appendChild(div);
      
      this.storeSearch = new PikiSiteSearch("store_search", ml("Select Stores"), 0, ml("Add Selected Stores(%d)"), function(ss) { storeAvailability.selectStores(ss);}, this.translate, this.availability.filter);
    }
    this.storeSearch.showPopup(false, this.availability.stores);
	},
	
	addAvailableGroup: function() {
	  if(this.groupSearch == null) {
      var div = document.createElement("DIV");
      div.className = "popup";
      div.style.display="none";
      div.style.width = "350px";
      div.id = "group_search";
      this.popupContainer.appendChild(div);
      
      this.groupSearch = new PikiGroupSearch("group_search", ml("Select Groups"), 0, ml("Add Selected Groups(%d)"), function(sg) { storeAvailability.selectGroups(sg);}, this.translate, this.availability.filter);
    }
    this.groupSearch.showPopup(false, this.availability.groups);
	},
	
	selectStores: function(selectedStores) {
	  var stores = this.availability.stores;
	  
    for(s in selectedStores) {
      if(stores[s] == null) {
        var domain = selectedStores[s].domain;
        var name = selectedStores[s].name;
        stores[s] = [name, domain, true];
        
        this.addStoreRow(s, name, domain, true);
      }
    }
    this.checkNoStoreMessage();
	},
	
	selectGroups: function(selectedGroups) {
	  var groups = this.availability.groups;
	  
    for(g in selectedGroups) {
      if(groups[g] == null) {
        var name = selectedGroups[g].name;
        groups[g] = [name, true];
        
        this.addGroupRow(g, name, true);
      }
    }
    this.checkNoGroupMessage();
	},
	
	removeAvailableStore: function(id) {
	  delete this.availability.stores[id];
	  var row = $("sp_" + id);
    row.parentNode.removeChild(row);
    this.checkNoStoreMessage();
	},
	
	removeAvailableGroup: function(id) {
	  delete this.availability.groups[id];
	  var row = $("gp_" + id);
	  row.parentNode.removeChild(row);
	  this.checkNoGroupMessage();
	},
	
	checkNoStoreMessage: function() {
	  if(hashEmpty(this.availability.stores)) {
	    if($('availability_no_stores_message') == null) {
	       html =
        '<tr id="availability_no_stores_message">' +
          '<td colspan="4">' + ml('You have not overriden the default or group availability by adding a store.') + '</td>' +
        '</tr>';
        new Insertion.Bottom("store_permissions", html);
	    }
	  } else if($('availability_no_stores_message') != null) {
	    $('availability_no_stores_message').remove();
	  }
	},
	
	checkNoGroupMessage: function() {
	  if(hashEmpty(this.availability.groups)) {
	    if($('availability_no_groups_message') == null) {
	       html =
        '<tr id="availability_no_groups_message">' +
          '<td colspan="4">' + ml('You have not overriden the default availability by adding a group.') + '</td>' +
        '</tr>';
        new Insertion.Bottom("group_permissions", html);
	    }
	  } else if($('availability_no_groups_message') != null) {
	    $('availability_no_groups_message').remove();
	  }
	}
});


