function noop(){}
function listModelSelectionAdapter(selModel, dataModel){
	this.selModel = selModel;
	this.dataModel = dataModel;
}
listModelSelectionAdapter.prototype = {
	_init: function(fireChange){
		var selModel = this.selModel,
			dataModel = this.dataModel,
			dataCount = dataModel.length;
		if (selModel.startSelectionDetection() != false){
			if (!fireChange) selModel.fireChange(false);
			for (var dataIdx=0; dataIdx<dataCount; dataIdx++){
				var record = dataModel[dataIdx];
				if (selModel.isSelected(dataIdx, record)){
					selModel.add(dataIdx, record);
				}
			}
			selModel.endSelectionDetection();
			if (!fireChange) selModel.fireChange(true);
		}
	},
	init: function(){
		this._init(false);
	},
	reset: function(){
		this._init(true);
	},
	refresh: function(){
		this.selModel.reset();
		this._init(false);
	}
}
function selectionModel(config){
	this.multiSelect = false;

	this.selItemIds = [];
	this.selItemRecords = [];
	this.selCount = 0;
	this.canFireChange = true;

	if (config.selectionRenderer){
		this.selectionRenderer = config.selectionRenderer;
	}
	if (config.multiSelect){
		this.multiSelect = config.multiSelect;
	}
	if (config.selectionChange){
		this.selectionChange = config.selectionChange;
	}
	this.startSelectionDetection = config.startSelectionDetection;
	if (!this.startSelectionDetection){
		this.startSelectionDetection = noop;
	}
	this.endSelectionDetection = config.endSelectionDetection;
	if (!this.endSelectionDetection){
		this.endSelectionDetection = noop;
	}
	this.isSelected = config.isSelected;
	if (!this.isSelected){
		this.isSelected = noop;
	}
}
selectionModel.prototype = {
	fireChange: function(fire){
		this.canFireChange = fire;
	},
	add: function(id, record){
		if (this.multiSelect){

		} else {
			var update = true;
			if (this.selCount == 1){
				if (this.selItemIds[0] == id){
					update = false;
				} else {
					this.selectionRenderer.unselect(this.selItemIds[0], this.selItemRecords[0]);
					this.selCount--;
				}
			}
			if (update){
				this.selItemIds[0] = id;
				this.selItemRecords[0] = record;
				this.selectionRenderer.select(id, record);
				this.selCount++;
				if (this.canFireChange) this.selectionChange(this.selItemIds, this.selItemRecords);
			}
		}
	},
	reset:function(){
		if (this.multiSelect){

		} else {
			if (this.selCount == 1){
				this.selectionRenderer.unselect(this.selItemIds[0], this.selItemRecords[0]);
				this.selCount=0;
			}
		}
	}
}
function setUpKeyPress(textUI, update) {
	textUI.keypress(function(e) {
		function getCaretPos(element, val) {
			var pos = -1;

			if (document.selection) {
				element.focus();
				var oSel = document.selection.createRange();
				oSel.moveStart('character', -val.length);
				pos = oSel.text.length;
			}
			else if (element.selectionStart || element.selectionStart == "0") {
				pos = element.selectionStart;
			}

			return pos;
		}

		var key = (e.which) ? e.which : e.keyCode;

		if ((e.which == 0) || (e.which == 8)) {
			setTimeout(update, 50);
			return true;
		}
		if ((e.which >= 48) && (e.which <= 57)) {
			setTimeout(update, 50);
			return true;
		}
		if (e.which == 45) {
			var text = textUI.val();
			var caretPos = getCaretPos(textUI[0], text);
			if ((caretPos == 0) && (text.indexOf("-") == -1)) {
				setTimeout(update, 50);
				return true;
			}
		}
		return false;
	});
}
function sliderUIModelAdapter(uiModel, sliderUI, textUI) {
	this.uiModel = uiModel;
	this.sliderUI = sliderUI;
	this.textUI = textUI;
	this.initFn = uiModel.init;
	this.resetFn = uiModel.reset;
	this.getMinValueFn = uiModel.getMinValue;
	this.getMaxValueFn = uiModel.getMaxValue;
	this.getStepFn = uiModel.getStep;
	this.startValueSetFn = uiModel.startValueSet;
	this.stopValueSetFn = uiModel.stopValueSet;
	this.setValueFn = uiModel.setValue;
	var me = this;

	function updateProp() {
		var newVal = textUI.val();
		if ((newVal.length == 0) || (isNaN(newVal))) {
			newVal = 0;
			textUI.val(0);
		}
		me.setValue(newVal);
	}
	setUpKeyPress(textUI, updateProp);
}
sliderUIModelAdapter.prototype = {
	init: function(){
		var result=0, fn=this.initFn;
		if (fn){
			result = fn.call(this);
			this.setUIValue(result);
		}
	},
	reset: function(){
		var result=0, fn=this.resetFn;
		if (fn){
			result = fn.call(this);
			this.setUIValue(result);
		}
	},
	getMinValue:function(){
		var result=0, fn=this.getMinValueFn;
		if (fn){
			result = fn.call(this);
		}
		return result;
	},
	getMaxValue:function(){
		var result=500, fn=this.getMaxValueFn;
		if (fn){
			result = fn.call(this);
		}
		return result;
	},
	getStep:function(){
		var result=10, fn=this.getStepFn;
		if (fn){
			result = fn.call(this);
		}
		return result;
	},
	startValueSet:function(){
		var fn = this.startValueSetFn;
		if (fn){
			result = fn.call(this);
		}
	},
	stopValueSet:function(){
		var fn = this.stopValueSetFn;
		if (fn){
			result = fn.call(this);
		}
	},
	setValue:function(value){
		var fn = this.setValueFn;
		if (fn){
			value = fn.call(this, value);
			this.sliderUI.slider('value', value);
			this.textUI.val(value);
		}
	},
	setUIValue:function(value){
		this.sliderUI.slider('value', value);
		this.textUI.val(value);
	}
}

function sliderPropertyAdapter(property, sliderUI, control){
	this.property = property;
	this.codec = property.codec;
	this.minValue = property.minValue || 0;
	this.maxValue = property.maxValue || 500;
	this.step = 10;
	if (control){
		this.minValue = control.minValue || this.minValue;
		this.maxValue = control.maxValue || this.maxValue;
		this.step = control.step || this.step;
	}
	property.valueChanged(function(property){
		sliderUI.slider('value', property.getValue());
	});
}
sliderPropertyAdapter.prototype = {
	init: function(){
		//if (window.console) console.log('sliderPropertyAdapter');
	},
	reset: function(){
		//todo : reset is already called through the propertyModel, we don't need to call it another time
	},
	getMinValue:function(){
		return this.minValue;
	},
	getMaxValue:function(){
		return this.maxValue;
	},
	getStep:function(){
		return this.step;
	},

	startValueSet:function(){
		//this.property.startValueSet();
	},
	stopValueSet:function(){
		//this.property.stopValueSet();
	},
	setValue:function(value){
		value = this.codec.encode(value);
		this.property.setValue(value);
	}
}
function checkboxPropertyAdapter(property, checkUI){
	var adapter = this;
	this.property = property;
	this.codec = this.property.codec;
	property.valueChanged(function(property){
		if (adapter.stopEvent){
			return;
		}
		checkUI.attr('checked', property.getValue());
	});
}
checkboxPropertyAdapter.prototype = {
	init:function(){
	},
	reset: function(){
		//todo : reset is already called through the propertyModel, we don't need to call it another time
	},
	select:function(){
		var value = this.codec.encode(true);
		this.stopEvent = true;
		this.property.setValue(value);
		this.stopEvent = false;
	},
	unselect:function(){
		var value = this.codec.encode(false);
		this.stopEvent = true;
		this.property.setValue(value);
		this.stopEvent = false;
	}
}
function checkboxUIModelAdapter(model, checkUI){
	this.uiModel = model;
	this.checkUI = checkUI;
	this.selectFn = model.select;
	this.unselectFn = model.unselect;
}
checkboxUIModelAdapter.prototype = {
	init:function(){
		var result=false;
		if (this.uiModel.init){
			result = this.uiModel.init.call(this);
			this.setUIValue(result);
		}
	},
	reset: function(){
		var result=false;
		if (this.uiModel.reset){
			result = this.uiModel.reset.call(this);
		}
		return result;
	},
	select:function(){
		if (this.selectFn){
			this.selectFn.call(this);
		}
		this.setUIValue(true);
	},
	unselect:function(){
		if (this.unselectFn){
			this.unselectFn.call(this);
		}
		this.setUIValue(false);
	},
	setUIValue:function(value){
		this.checkUI.attr('checked', value);
	}
}

function cropDialog(){
	this.bgPosregEx = /^-?(\d+)\s*px\s*-?(\d+)\s*px$/;
	this.create();
	this.offsetX=0;
	this.offsetY=25;
}
cropDialog.prototype = {
	create: function() {
		var dialog = $('<div class="CropDialog" style="display:none"><img src="T.gif" border="0"></div>');
		var cropImg = $('img', dialog);
		var dlg = this;
		this.needCompute = false;

		cropImg.load(function() {
			var src = cropImg.attr('src');
			if (src) {
				var lastIndex = src.lastIndexOf('T.gif');
				if (lastIndex == (src.length - 5)) {
					return;
				}

				if (dialog.dialog('isOpen')) {
					dialog.dialog('option', 'width', cropImg.innerWidth() + dlg.offsetX);
					dialog.dialog('option', 'height', cropImg.innerHeight() + dlg.offsetY);
					dlg.needCompute = false;
				} else {
					dlg.needCompute = true;
				}

				if (cropImg.width() && cropImg.height()) {
					if (dlg.croppedImg) {
						dlg.croppedImg.destroy();
					}
					var cropOptions = { allowResize: false,
						allowSelect: false,
						setSelect: dlg.computeSelectArea(),
						onChange: function(coords) {
							dlg.model.position.setValue('-' + coords.x + 'px -' + coords.y + 'px');
						}
					};
					dlg.croppedImg = $.Jcrop(cropImg, cropOptions);
				} else {
					function timeOutRefresh() {
						if (cropImg && cropImg.width() && cropImg.height()) {
							dialog.dialog('option', 'width', cropImg.innerWidth() + dlg.offsetX);
							dialog.dialog('option', 'height', cropImg.innerHeight() + dlg.offsetY);

							if (dlg.croppedImg) {
								dlg.croppedImg.destroy();
							}

							var cropOptions = { allowResize: false,
								allowSelect: false,
								setSelect: dlg.computeSelectArea(),
								onChange: function(coords) {
									dlg.model.position.setValue('-' + coords.x + 'px -' + coords.y + 'px');
								}
							};
							dlg.croppedImg = $.Jcrop(cropImg, cropOptions);
						}
					}
					setTimeout(timeOutRefresh, 50);
				}
			}
		});

		this.dialog = dialog;
		this.cropImg = cropImg;
	},
	computeSelectArea: function() {
		var left = 0, top = 0,
			cssWidth = 0, cssHeight = 0;
		position = null,
			model = this.model;
		if (model) {
			if (model.width) {
				cssWidth = model.width.getValue();
			}
			if (model.height) {
				cssHeight = model.height.getValue();
			}
			if (model.position) {
				position = model.position.getValue();
			}
			var pos = this.bgPosregEx.exec(position);
			if (pos && (pos.length > 2)) {
				left = parseInt(pos[1]);
				cssWidth += left;
				top = parseInt(pos[2]);
				cssHeight += top;
			}
		}
		return [left, top, cssWidth, cssHeight];
	},
	show: function(bgImgProperty, model) {
		var dlg, cropImg, options, dialog = this.dialog;

		this.bgImgProperty = bgImgProperty;
		this.model = model;
		this.cropImg.attr('src', bgImgProperty.getValue());
		cropImg = this.cropImg;
		dlg = this;

		this.bgImgProperty.valueChanged(function(property) {
			dlg.needCompute = false;
			cropImg.attr('src', property.getValue());
		});

		this.model.height.valueChanged(function(property) {
			if (dlg.croppedImg) {
				dlg.croppedImg.setSelect(dlg.computeSelectArea());
			}
		});

		options = { title: model.dialogTitle || model.title,
			autoOpen: false,
			resizable: false,
			close: function(event, ui) {
				if (dlg.croppedImg) {
					dlg.croppedImg.destroy();
					dlg.croppedImg = null;
				}
			},
			dragStart: showWebSitePreviewOverlay,
			dragStop: hideWebSitePreviewOverlay
		};

		if (dlg.needCompute) {
			options.width = cropImg.innerWidth() + this.offsetX;
			options.height = cropImg.innerHeight() + this.offsetY;
		}

		dialog.dialog(options);
		dialog.dialog('open');
	}
}

function windowManager(excludeWithinGroup){
	this.wndList = [];
	this.excludeOtherWnd = true;
	this.secondClickAction = 'moveToTop'; //'close';
}
windowManager.prototype = {
	add: function(wnd, groupName) {
		this.wndList.push({ wnd: wnd, groupName: groupName });
	},
	show: function(wnd) {
		if (wnd.dialog('isOpen')) {
			wnd.dialog(this.secondClickAction);
		} else {
			wnd.dialog('open');
		}
		if (this.excludeOtherWnd) {
			var curWnd, wndList = this.wndList, wndCount = wndList.length;
			for (var wndIdx = 0; wndIdx < wndCount; wndIdx++) {
				curWnd = wndList[wndIdx].wnd;
				if (curWnd !== wnd) {
					if (curWnd.dialog('isOpen')) {
						curWnd.dialog('close');
					}
				}
			}
		}
	}
}
function createStdRenderer(renderer){
	function createToolbarButtonUI(ctxt, btn){
		var buttonUI = $('<a href="#" class="toolbarAction"></a>'),
			divInnerToolbarAction = $('<div class="innerToolbarAction"></div>'),
			divToolbarAction = $('<div class="toolbarAction"></div>'),
			btnType = $('<div></div>');
		if (btn.id){
			buttonUI.attr('id', btn.id);
		}
		btnType.attr('class', btn.type);
		btnType.text(btn.title);
		divInnerToolbarAction.append(divToolbarAction);
		divInnerToolbarAction.append(btnType);
		buttonUI.append(divInnerToolbarAction);
		return buttonUI;
	}
	renderer.titleRenderers.slider = function(ctxt, propModel, parent){
		parent.text(propModel.title);
	};
	renderer.titleRenderers.bgimg= function(ctxt, panelConf){
		var title = panelConf.title;
		if (title){
			var panel = $('<div class="PanelTitle"></div>'),
				span = $('<span></span>');
			span.text(title);
			panel.append(span);
			parent.append(panel);
		}
	};
	var titleRenderers = renderer.titleRenderers;
	titleRenderers.font = titleRenderers.designcolors = titleRenderers.list = titleRenderers.bgimg;
	titleRenderers.checkbox = titleRenderers.select = titleRenderers.text = titleRenderers.imgSelect = titleRenderers.color = titleRenderers.slider;

	renderer.toolbarButtonRenderers.action=function(ctxt, btn, group, parent){
		var btnUI = createToolbarButtonUI(ctxt, btn);
		if (btnUI && btn.action){
			if (btn.action instanceof Function){
				btnUI.click(function(){
					btn.action.call(null, btn, ctxt.conf);
				});
			} else {
				btnUI.click(function(){
					btn.action.run(ctxt.conf);
				});
			}
		}
		parent.append(btnUI);
	};
	renderer.toolbarButtonRenderers.submenu=function(ctxt, btn, group, parent){
		var wnd = null, wndConf = btn.window;
		if (wndConf){
			wnd = this.windowRenderer(ctxt, group.name, wndConf, btn.id);
			if (wnd){
				var btnUI = createToolbarButtonUI(ctxt, btn);
				if (btnUI){
					btnUI.click(function(event){
						var position = wnd.dialog('option', 'position');
						if (position=='compute'){
							var btnContainer = btnUI.parent();
							var offset = btnContainer.offset();
							offset.top += btnContainer.height();
							wnd.dialog('option', 'position', [offset.left, offset.top]);
						}
						ctxt.windowMgr.show(wnd);
					});
				}
			}
		}
		parent.append(btnUI);
	};
	renderer.rowRenderers.checkbox=function(ctxt, propModel, titleRenderer, ctrlRenderer, parent){
		var row = $('<div class="propertyrow"></div>'),
			propRndr = $('<div class="renderer"></div>'),
			control = propModel.control,
			type = propModel.type,
			checkBoxId = this.checkboxCount || 0;

		if (type){
			row.addClass(type);
		}

		if (!propModel.control){
			control = {};
			propModel.control = control;
		}

		control.id = 'check' + checkBoxId;
		this.checkboxCount = ++checkBoxId;

		if (titleRenderer){
			title = $('<label class="title"></label>'),
			title.attr('for', control.id);
			titleRenderer.call(this, ctxt, propModel, title);
			row.append(title);
		}
		ctrlRenderer.call(this, ctxt, propModel, propRndr);
		row.append(propRndr);
		parent.append(row);
	};
	renderer.rowRenderers.button=function(ctxt, propModel, titleRenderer, ctrlRenderer, parent){
		var row = $('<div class="propertyrow"></div>'),
			control = propModel.control,
			type = control.type;
		if (type){
			row.addClass(type);
		}
		ctrlRenderer.call(this, ctxt, propModel, row);
		parent.append(row);
	};
	renderer.rowRenderers.slider= function(ctxt, propModel, titleRenderer, ctrlRenderer, parent){
		var row = $('<div class="propertyrow"></div>'),
			title,
			propRndr = $('<div class="renderer"></div>'),
			type = propModel.type;
		if (type){
			row.addClass(type);
		}
		if (titleRenderer){
			title = $('<span class="title"></span>'),
			titleRenderer.call(this, ctxt, propModel, title);
			row.append(title);
		}
		ctrlRenderer.call(this, ctxt, propModel, propRndr);
		row.append(propRndr);
		parent.append(row);
	};
	var rowRenderers = renderer.rowRenderers;
	rowRenderers.select = rowRenderers.text = rowRenderers.imgSelect = rowRenderers.crop = rowRenderers.color = rowRenderers.slider;

	renderer.itemRenderers.slider = function(ctxt, propModel, parent) {
		var sliderUI = $('<div class="slider"></div>'),
			textUI = $('<input class="text slidertext" type="text">'),
			uiModel = null,
			control = propModel.control,
			property = propModel.property,
			textSize = 3, textName, textId, textMaxLen=textSize, textClass;

		if (control) {
			if (control.width) {
				sliderUI.css('width', control.width);
			}
			if (control.text) {
				var text = control.text;
				if (text.size) {
					textSize = text.size;
				}
				if (text.name) {
					textName = text.name;
				}
				if (text.id) {
					textId = text.id;
				}
				if (text.maxLen) {
					textMaxLen = text.maxLen;
				}
				if (text.className) {
					textClass = text.className;
				}
			}
		}
		textUI.attr('size', textSize);
		textUI.attr('name', textName);
		textUI.attr('id', textId);
		textUI.attr('maxlen', textMaxLen);
		textUI.addClass(textClass);

		if (property) {
			ctxt.propertyModel.add(property);
			uiModel = new sliderPropertyAdapter(property, sliderUI, control);

			property.valueChanged(function(property) {
				textUI.val(property.getValue());
			});

			function updateProp() {
				var newVal = textUI.val();
				var curVal = property.getValue();
				if ((newVal.length == 0) || (isNaN(newVal))) {
					newVal = 0;
				}
				if (newVal != curVal) {
					property.setValue(property.codec.encode(newVal));
				}
			}
			setUpKeyPress(textUI, updateProp);
		} else {
			uiModel = new sliderUIModelAdapter(propModel.model, sliderUI, textUI);
		}
		if (uiModel) {
			ctxt.uiModel.add(uiModel);
			sliderUI.slider({
				min: uiModel.getMinValue(),
				max: uiModel.getMaxValue(),
				step: uiModel.getStep(),
				slide: function(event, ui) { uiModel.setValue(ui.value); },
				start: function(event, ui) { uiModel.startValueSet(); },
				stop: function(event, ui) { uiModel.stopValueSet(); }
			});
			parent.append(sliderUI);
		}
		parent.append(textUI);
	};
	renderer.itemRenderers.color = function(ctxt, propModel, parent) {
		if (!this.colorRenderInitDone) {
			$('<link type="text/css" href="DesignCSS/CssDesigner/ColorPicker.css" rel="stylesheet">').appendTo("head");
			this.colorRenderInitDone = true;
		}

		if (!propModel.property) {
			//if (window.console) console.log('Color property not found');
			return;
		}
		var colorUI = $('<div class="colorindicator"></div>'),
			colorbutton = $('<button type="button" class="launchcolorpicker">...</button>'),
			property = propModel.property,
			colorPickerContainer = $('<div style="padding:0;margin:0;border:none;display:none;"></div>'),
			rgbClrModel = new rgbColorModel(),
			title = propModel.title,
			dialog = propModel.dialog;

		if (dialog) {
			title = dialog.title || title;
		}
		createColorPickerUI(rgbClrModel, colorPickerContainer);
		colorPickerContainer.appendTo('body');

		colorPickerContainer.dialog({ title: title,
			autoOpen: false,
			resizable: false,
			width: jQuery.support.boxModel ? 260 : 270,
			dragStart: showWebSitePreviewOverlay,
			dragStop: hideWebSitePreviewOverlay
		});

		property.valueChanged(function(property) {
			var color = property.toString();
			//if (window.console) console.log(property.debugGetName(), 'color property.valueChanged =>', color);
			if (color) {
				colorUI.css('background-color', color);
				rgbClrModel.setRGBHex(color);
			}
		});

		rgbClrModel.addEventListener(function(event) {
			var value = property.codec.encode(event.hex);
			//if (window.console) console.log(property.debugGetName(), 'rgbClrModel value changed =>', event.hex, property.getValue());
			property.setValue(value);
		});

		ctxt.propertyModel.add(property);

		colorbutton.click(function() {
			//if (window.console) console.log('current color', property.getValue());
			if (colorPickerContainer.dialog('isOpen')) {
				colorPickerContainer.dialog('moveToTop');
			} else {
				var offset = colorbutton.offset();
				offset.top += colorbutton.outerHeight();
				colorPickerContainer.dialog('option', 'position', [offset.left, offset.top]);
				colorPickerContainer.dialog('open');
			}
		});

		parent.append(colorUI);
		parent.append(colorbutton);
	};
	renderer.itemRenderers.checkbox= function(ctxt, propModel, parent){
		var checkUI = $('<input type="checkbox" class="checkbox">'),
			uiModel = null,
			control = propModel.control,
			id = control.id,
			property = propModel.property;
		if (id){
			checkUI.attr('id', id);
		}
		if (property){
			ctxt.propertyModel.add(property);
			uiModel = new checkboxPropertyAdapter(property, checkUI);
		} else {
			uiModel = new checkboxUIModelAdapter(propModel.model, checkUI);
		}
		if (uiModel){
			ctxt.uiModel.add(uiModel);
			parent.append(checkUI);
		}
		checkUI.click(function(){
			var value = checkUI.attr('checked');
			if (value){
				uiModel.select();
			} else {
				uiModel.unselect();
			}
		});
	};
	renderer.itemRenderers.button= function(ctxt, propModel, parent){
		var buttonUI = $('<button type="button"></button>'),
			uiModel = null,
			control = propModel.control,
			id = control.id,
			action = control.action,
			title = propModel.title;
		if (title){
			buttonUI.text(title);
		}
		if (id){
			buttonUI.attr('id', id);
		}
		if (action){
			buttonUI.click(function(){
				action();
			});
			parent.append(buttonUI);
		}
	};
	renderer.itemRenderers.bgimg= function(ctxt, panelConf, parent){
		var model = panelConf.model || getDefaultImgModel(),
			property = panelConf.property,
			imgCount = model.length,
			defHeight = '250px';

		if (panelConf.control){
			var ctrl = panelConf.control;
			if (ctrl.height) {
				defHeight = ctrl.height;
			}
		}

		ctxt.propertyModel.add(property);
		var listModel ={dataModel:model,
						uiModel:{
							height: defHeight,
							columns: [{
								width:'10%',
								render: function(ctxt, record, parent){
									var img = $('<img alt="" border="0"></img>');
									img.attr('src', record.thumbnailUrl);
									parent.append(img);
								}
							}]
						},
						selectionModel:{
							selectionChange:function(selIds, selRecords){
								property.setValue(selRecords[0].url);
							},
							startSelectionDetection: function(){
								this.selImg = property.getValue();
							},
							isSelected:function(id, record){
								//if (id == 0){
								//	if (window.console) console.log('bgimg isselected', record.url, this.selImg)
								//}
								return record.url == this.selImg;
							}
						}};


		var selModelAdapter = this.listModelRenderer(ctxt, 'ImgPanel', listModel, parent);
		if (property && selModelAdapter){
			property.valueChanged(function(property){
				selModelAdapter.refresh();
			});
		}
	};
	renderer.itemRenderers.crop= function(ctxt, panelConf, parent){
		var title = panelConf.title || ctxt.conf.strRes[ctxt.conf.lang].crop;
		if (title){
			var model = panelConf.model,
				property = panelConf.property,
				buttonUI = $('<button type="button"></button>');

			buttonUI.text(title);

			ctxt.propertyModel.add(property);
			ctxt.propertyModel.add(model.width);
			ctxt.propertyModel.add(model.height);
			ctxt.propertyModel.add(model.position);

			cropDlg = new cropDialog();

			parent.append(buttonUI);
			buttonUI.click(function(event){
				cropDlg.show(property, model);
			});
		}
	};
	renderer.itemRenderers.list= function(ctxt, panelConf, parent){
		var property, keyColumn, className, listModel = panelConf.model;
		if (!listModel){
			return;
		}
		className = panelConf.className || 'ListPanel';
		property = panelConf.property;
		keyColumn = listModel.keyColumn;
		if (property && keyColumn){
			ctxt.propertyModel.add(property);
			listModel.selectionModel = {
				selectionChange:function(selIds, selRecords){
					property.setValue(selRecords[0][keyColumn]);
				},
				startSelectionDetection: function(){
					this.currentValue = property.getValue();
				},
				isSelected:function(id, record){
					if (record[keyColumn] == this.currentValue){
						return true;
					}
					return false;
				}
			};
		}
		var selModelAdapter = this.listModelRenderer(ctxt, className, listModel, parent);
		if (property && selModelAdapter){
			property.valueChanged(function(property){
				selModelAdapter.refresh();
			});
		}
	};
	renderer.itemRenderers.font= function(ctxt, panelConf, parent){
		var property = panelConf.property;
		ctxt.propertyModel.add(property);
		var listModel ={dataModel:panelConf.model,
						uiModel:{
							columns: [{
								render: function(ctxt, record, parent){
									parent.css('font-family', record.family);
									parent.text(record.title);
								}
							}]},
						selectionModel:{
							selectionChange:function(selIds, selRecords){
								property.setValue(selRecords[0].family);
							},
							startSelectionDetection: function(){
								this.currentValue = property.getValue();
							},
							isSelected:function(id, record){
								if (record.family == this.currentValue){
									return true;
								}
								return false;
							}
						}};
		var selModelAdapter = this.listModelRenderer(ctxt, 'FontPanel', listModel, parent);
		if (property && selModelAdapter){
			property.valueChanged(function(property){
				selModelAdapter.refresh();
			});
		}
	};
	renderer.itemRenderers.designcolors= function(ctxt, panelConf, parent){
		var colorModel = panelConf.model || ctxt.conf.getColorModel();
		if (!colorModel) {
			if (window.console) console.log('Exit createcolorPanel => colorModel is null');
			return;
		}
		var designColorModel = colorModel.designColorModel, designColorCount = designColorModel.length;
		var predefinedColorModel = colorModel.predefinedColorModel || getPredefinedColorModel(), predefinedColorCount = predefinedColorModel.length;
		var colorRndr;
		if (!designColorCount || !predefinedColorCount){
			if (window.console) console.log('Exit createcolorPanel => designColorCount', designColorCount, 'predefinedColorCount', predefinedColorCount);
			return;
		}
		var defHeight = '250px';
		if (panelConf.control) {
			var ctrl = panelConf.control;
			if (ctrl.height) {
				defHeight = ctrl.height;
			}
		}
		var listModel ={dataModel:predefinedColorModel,
						uiModel:{
							height: defHeight,
							columns: []
						},
						selectionModel:{
							selectionChange:function(selIds, selRecords){
								var record = selRecords[0];
								for (var colorIdx = 0; colorIdx < designColorCount; colorIdx++){
									var property = designColorModel[colorIdx].property,
										value = property.codec.encode(record.colors[colorIdx]);
									property.setValue(value);
								}
							},
							startSelectionDetection: function(){
								var selColors = [], nullCount=0;
								for (var colorIdx = 0; colorIdx < designColorCount; colorIdx++){
									var color = designColorModel[colorIdx].property.getValue();
									selColors[colorIdx] = color;
									if (color == null) nullCount++;
									//if (window.console) console.log(colorIdx, selColors[colorIdx]);
								}
								this.selColors = selColors;
								return (nullCount != designColorCount);
							},
							isSelected:function(id, record){
								var match = 0, selColors = this.selColors, currColors = record.colors;
								for (var colorIdx = 0; colorIdx < designColorCount; colorIdx++){
									if (currColors[colorIdx] == selColors[colorIdx]){
										match++;
									}
								}
								//if (window.console) console.log((match == designColorCount), currColors[0], selColors[0], '-', currColors[1], selColors[1]);
								return match == designColorCount;
							}
						}},
			columnModel = listModel.uiModel.columns;

		function createColorUIColumnModel(colorIdx){
			return {
				width:'14px',
				render: function(ctxt, record, parent){
					var div = $('<div></div>');
					div.css('background-color', record.colors[colorIdx]);
					div.html('&nbsp;');
					parent.append(div);
				}
			};
		}

		for (var colorIdx = 0; colorIdx < designColorCount; colorIdx++){
			columnModel[colorIdx] = createColorUIColumnModel(colorIdx);
		}
		columnModel[colorIdx] = {
			render: function(ctxt, record, parent){
				parent.text(record.name[ctxt.conf.lang]);
			}
		};

		var selModelAdapter = this.listModelRenderer(ctxt, 'ColorPanel', listModel, parent);
		if (false && selModelAdapter){
			for (var colorIdx = 0; colorIdx < designColorCount; colorIdx++){
				var property = designColorModel[colorIdx].property;
				property.valueChanged(function(property){
					selModelAdapter.refresh();
				});
			}
		}

		var type = 'color',
			itemRndr= this.itemRenderers[type],
			rowRenderer = this.rowRenderers[type],
			titleRenderer = this.titleRenderers[type],
			sepRenderer = this.itemRenderers['-'];
		sepRenderer.call(this, ctxt, null, parent);
		for (var colorIdx = 0; colorIdx < designColorCount; colorIdx++){
			var itemConf = designColorModel[colorIdx];
			if (itemConf){
				if (rowRenderer){
					rowRenderer.call(this, ctxt, itemConf, titleRenderer, itemRndr, parent);
				} else {
					itemRndr.call(this, ctxt, itemConf, parent);
				}
			}
		}
	};
	renderer.itemRenderers.title = function(ctxt, itemConf, parent){
		var title = itemConf.title;
		if (title){
			var div = $('<div class="Title"></div>'),
				span = $('<span></span>');
			span.text(title);
			div.append(span);
			parent.append(div);
		}
	};
	renderer.itemRenderers['-'] = function(ctxt, itemConf, parent){
		var panel = $('<div class="Separator">&nbsp;</div>');
		parent.append(panel);
	};
	renderer.itemRenderers.text= function(ctxt, itemConf, parent){
		var property = itemConf.property, 
			textUI = $('<input class="text" type="text">'),
			control = itemConf.control;
		if (control){
			if (control.name){
				textUI.attr('name', control.name);
			}
			if (control.id){
				textUI.attr('id', control.id);
			}
			if (control.size){
				textUI.attr('size', control.size);
			}
			if (control.maxLen){
				textUI.attr('maxlen', control.maxLen);
			}
		}
		if (property){
			ctxt.propertyModel.add(property);
			property.valueChanged(function(property){
				textUI.val(property.getValue());
			});
		} //else if (model){
		//}

		function updateProp(){
			var newVal = textUI.val();
			var curVal = property.getValue();
			if (newVal != curVal){
				property.setValue(property.codec.encode(newVal));
			}
		}

		textUI.keypress(function(){
			setTimeout(updateProp, 50);
		});

		parent.append(textUI);
	};
	renderer.itemRenderers.imgSelect = function(ctxt, itemConf, parent) {
		var buttonUI = $('<button type="button" class="launchImgSelect" style="padding:0;margin:0;">...</button>'),
			clearUI = $('<button type="button" class="clearImgSelect" style="padding:0;margin:0;">X</button>'),
			property = itemConf.property,
			model = itemConf.model,
			id, control = itemConf.control;
		if (control && control.id) {
			id = 'imgSelect' + control.id;
		}
		if (!id) {
			if (!this.imgSelectCount) {
				this.imgSelectCount = 1;
			}
			id = 'imgSelect' + (this.imgSelectCount++);
		}
		if (property) {
			ctxt.propertyModel.add(property);
		} //else if (model){
		//}

		function imgSelected(imgID, imgFileName) {
			var absoluteFileName = null;
			if (imgFileName.substr(0, 5) == '../0/') {
				absoluteFileName = imgFileName.substr(3);
			} else {
				absoluteFileName = ctxt.conf.account + '/' + imgFileName;
			}
			absoluteFileName = '/Files/' + absoluteFileName;
			//if (window.console) console.log('absoluteFileName', absoluteFileName);
			property.setValue(property.codec.encode(absoluteFileName));
			if (document.imgSelectCallbacks) {
				document.imgSelectCallbacks[id] = null;
			}
		}

		function WNDAPICreateSizeAttr(sAttr, nWidth, nHeight, nXOffset, nYOffset) {
			var nLeft, nTop;
			if (navigator.appName == "Netscape") {
				nLeft = window.screenX + ((window.outerWidth - nWidth) / 2);
				nTop = window.screenY + ((window.outerHeight - nHeight) / 2);
			} else {
				nLeft = (screen.width - nWidth) / 2;
				nTop = (screen.height - nHeight) / 2;
			}
			if (nXOffset != null) nLeft += nXOffset;
			if (nYOffset != null) nTop += nYOffset;
			if (sAttr.length > 0) sAttr += ","
			return (sAttr + "left=" + nLeft + ",top=" + nTop + ",width=" + nWidth + ",height=" + nHeight);
		}

		function WNDAPIOpenWindow(sWndName, sURL, sWndStyle, nWidth, nHeight, nXOffset, nYOffset) {
			if (sWndName == null) sWndName = "WNDAPI";
			if ((nWidth != null) && (nHeight != null))
				sWndStyle = WNDAPICreateSizeAttr(sWndStyle, nWidth, nHeight, nXOffset, nYOffset);
			var objWin = window.open(sURL, sWndName, sWndStyle);
			if (objWin) {
				if (objWin.focus) objWin.focus();
			}
			return objWin;
		}

		buttonUI.click(function() {
			var strURL, selectedImg = property.getValue();
			if (!selectedImg) {
				selectedImg = '';
			} else if (selectedImg.substr(0, 9) == '/Files/0/') {
				selectedImg = '..' + selectedImg.substr(6);
			} else {
				var prefix = '/Files/' + ctxt.conf.account + '/',
					prefixLen = prefix.length;
				if (selectedImg.substr(0, prefixLen) == prefix) {
					selectedImg = selectedImg.substr(prefixLen);
				} else {
					selectedImg = '';
				}
			}
			strURL = 'ImageDlg.asp?CSSDesigner=1&InpName=' + id + '&ImgFileName=' + selectedImg;
			if (!document.imgSelectCallbacks) {
				document.imgSelectCallbacks = {};
			}
			document.imgSelectCallbacks[id] = imgSelected;
			var objWin = WNDAPIOpenWindow('ImageDlg', strURL, 'scrollbars=yes,resizable=yes', 700, 550);
			if (!objWin) {
				alert('Popup Blocker');
			}
		});
		clearUI.click(function() {
			property.setValue(property.codec.encode(null));
		});
		var textRndr = this.itemRenderers['text'];
		if (textRndr) {
			textRndr.call(this, ctxt, itemConf, parent);
		}
		parent.append(buttonUI);
		parent.append(clearUI);
	};
	renderer.itemRenderers.select= function(ctxt, itemConf, parent){
		var init, property, keyColumn, className, listModel = itemConf.model;
		if (!listModel){
			return;
		}
		className = itemConf.className;
		property = itemConf.property;
		keyColumn = listModel.keyColumn;

		var select = $('<select></select>'),
			column = null,
			uiModel = listModel.uiModel,
			render = null,
			records = listModel.dataModel,
			recordCount = records.length,
			selModel = null,
			listIdx = 'l' + (this.listCount++) + '_';

		if (className){
			select.attr('class', className);
		}

		if (property && keyColumn){
			ctxt.propertyModel.add(property);
			selModel = {
				selectionChange:function(selIds, selRecords){
					//if (window.console) console.log('selectedValue on combo', selRecords[0][keyColumn]);
					property.setValue(selRecords[0][keyColumn]);
				},
				startSelectionDetection: function(){
					this.curValue = property.getValue();
					init = true;
				},
				endSelectionDetection: function(){
					init = false;
				},
				isSelected:function(id, record){
					return this.curValue == record[keyColumn];
				}
			};

			property.valueChanged(function(property){
				var selValue = property.getValue();
				for (var recIdx=0; recIdx < recordCount; recIdx++){
					if (records[recIdx][keyColumn] == selValue){
						select.get(0).selectedIndex = recIdx;
						break;
					}
				}
			});
		} else {
			selModel = listModel.selectionModel;
		}

		selModel.selectionRenderer={
			select:function(id, record){
				if (init){
					$('option:eq(' + id +')', select).attr('selected', 'selected');
				}
			},
			unselect: function(id, record){}
		};

		selModel = new selectionModel(selModel);

		ctxt.uiModel.add(new listModelSelectionAdapter(selModel, records));

		if (uiModel){
			if (uiModel.field){
				function createColumnRender(field){
					return function(ctxt, record, parent){
						parent.text(record[field]);
					}
				}
				render = createColumnRender(uiModel.field);
			} else {
				render = uiModel.render;
			}
		}

		for (var recordIdx=0; recordIdx<recordCount; recordIdx++){
			var record = records[recordIdx],
				option = $('<option></option>');
			render(ctxt, record, option);
			select.append(option);
		}
		render = null;

		select.bind('change', function(event){
			var selIdx = select.get(0).selectedIndex;
			//if (window.console) console.log('change on combo', selIdx, records[selIdx]);
			selModel.add(selIdx, records[selIdx]);
		});

		parent.append(select);
	};
	renderer.toolbarRenderers.group= function(ctxt, group, toolbarUI){
		var buttons = group.buttons, buttonCount = buttons.length,
			toolbarCell = $('<div class="toolbarCell"></div>'),
			innerToolbarCell = $('<div class="innerToolbarCell"></div>'),
			toolbarCellActions = $('<ul class="toolbarCellActions"></ul>'),
			toolbarCellTitle = $('<div class="toolbarCellTitle"></div>'),
			toolbarButtonRenderers = this.toolbarButtonRenderers;
		toolbarCellTitle.text(group.title);
		innerToolbarCell.append(toolbarCellActions);
		toolbarCell.append(innerToolbarCell);
		for (var buttonIdx=0; buttonIdx<buttonCount; buttonIdx++){
			var btn=buttons[buttonIdx], btnRenderer=toolbarButtonRenderers[btn.type];
			if (btnRenderer){
				var listItem = $('<li></li>'), renderer = btnRenderer.call(this, ctxt, btn, group, listItem);
				toolbarCellActions.append(listItem);
			}
		}
		innerToolbarCell.append(toolbarCellTitle);
		toolbarUI.append(toolbarCell);
	};
	renderer.toolbarRenderers.toolbar= function (ctxt, groups){
		var toolbarUI = $('<div class="toolbar"></div>');
		var toolbarGroupRenderer = this.toolbarRenderers.group;
		var groupCount = groups.length;
		for (var groupIdx=0; groupIdx<groupCount; groupIdx++){
			var group = groups[groupIdx];
			if (!group.name){
				group.name = 'grp' + groupIdx;
			}
			toolbarGroupRenderer.call(this, ctxt, group, toolbarUI);
		}
		toolbarUI.append($('<div class="smartskinlogo"></div>'));
		toolbarUI.prependTo('body');
	};
	renderer.windowRenderer = function(ctxt, groupName, wndConf, defaultId) {
		var wnd = $('<div class="subwindow"></div>');
		var items = wndConf.items, itemCount = items.length, wndOptions = wndConf.options;
		for (var itemIdx = 0; itemIdx < itemCount; itemIdx++) {
			var itemRndr, itemConf = items[itemIdx], type;
			if (itemConf && (type = itemConf.type)) {
				itemRndr = this.itemRenderers[type];
				if (itemRndr) {
					rowRenderer = this.rowRenderers[type];
					titleRenderer = this.titleRenderers[type];
					if (rowRenderer) {
						rowRenderer.call(this, ctxt, itemConf, titleRenderer, itemRndr, wnd);
					} else {
						itemRndr.call(this, ctxt, itemConf, wnd);
					}
				}
			}
		}

		if (!ctxt.windowMgr) {
			ctxt.windowMgr = new windowManager();
		}

		var options = { autoOpen: false,
			resizable: false,
			width: 200,
			position: 'compute',
			dragStart: showWebSitePreviewOverlay,
			dragStop: hideWebSitePreviewOverlay
		};
		if (defaultId) {
			options.dialogClass = defaultId + 'Wnd';
		}
		if (wndConf.title) {
			options.title = wndConf.title;
		}
		if (wndOptions) {
			for (var name in wndOptions) {
				options[name] = wndOptions[name];
				//if (window.console) console.log(defaultId, 'options :', name, 'available=', options[name] , ' = ', wndOptions[name]);
			}
		}
		ctxt.windowMgr.add(wnd, groupName);
		wnd.dialog(options);
		return wnd;
	};
	renderer.listModelRenderer= function(ctxt, className, listModel, parent){
		var modelContainer = $('<div style="overflow:auto;margin:0;padding:0;border:none;"></div>'),
			modelList = $('<table class="modellist" cellpadding="2" cellspacing="0"></table>'),
			column = null,
			uiModel = listModel.uiModel,
			columns = uiModel.columns,
			colCount = columns.length,
			records = listModel.dataModel,
			recordCount = records.length,
			selModel = listModel.selectionModel,
			listIdx = 'l' + (this.listCount++) + '_';

		if (className){
			modelContainer.attr('class', className);
		}
		if (uiModel.height){
			modelContainer.css('height',uiModel.height);
		}

		selModel.selectionRenderer={
			select:function(id, record){
				$('#'+listIdx+id, modelList).addClass('selected');
			},
			unselect: function(id, record){
				$('#'+listIdx+id, modelList).removeClass('selected');
			}
		};

		if (!(selModel instanceof selectionModel)){
			selModel = new selectionModel(selModel);
		}

		var adapter = new listModelSelectionAdapter(selModel, records);
		ctxt.uiModel.add(adapter);

		function createColumnRender(field){
			return function(ctxt, record, parent){
				parent.text(record[field]);
			}
		}

		for (var colIdx=0; colIdx<colCount; colIdx++){
			column = columns[colIdx];
			field = column.field;
			if (field){
				column.render = createColumnRender(field);
			}
		}

		for (var recordIdx=0; recordIdx<recordCount; recordIdx++){
			var record = records[recordIdx],
				tr = $('<tr></tr>');
			tr.attr('id',listIdx + recordIdx);
			for (var colIdx=0; colIdx<colCount; colIdx++){
				column = columns[colIdx];
				td = $('<td></td>');
				if (recordIdx == 0){
					if (column.width){
						td.attr('width', column.width);
					}
				}
				column.render(ctxt, record, td);
				tr.append(td);
			}
			modelList.append(tr);
		}

		for (var colIdx=0; colIdx<colCount; colIdx++){
			column = columns[colIdx];
			field = column.field;
			if (field){
				column.render = null;
			}
		}

		var lastEnteredTr = null;
		modelList.bind('mouseenter mouseleave mouseover click', function(event){
			var trList = $(event.target).closest('tr');
			if ((event.type == 'mouseover') || (event.type == 'mouseenter')){
				if (lastEnteredTr){
					lastEnteredTr.removeClass('hover');
					//lastEnteredTr.removeClass('ui-state-hover');
				}
				lastEnteredTr = null;
				if (trList.length) {
					lastEnteredTr = $(trList[0]);
					lastEnteredTr.addClass('hover');
					//lastEnteredTr.addClass('ui-state-hover');
				}
			} else if (event.type == 'mouseleave'){
				if (lastEnteredTr){
					lastEnteredTr.removeClass('hover');
					//lastEnteredTr.removeClass('ui-state-hover');
				}
				lastEnteredTr = null;
			} else if (event.type == 'click'){
				if (trList.length) {
					var recordIdx = 0,
						listLength = listIdx.length;
						id = $(trList[0]).attr('id');
					id = id.substr(listLength);
					recordIdx = parseInt(id);
					selModel.add(id, records[recordIdx]);
				}
			}
		});

		modelContainer.append(modelList);
		parent.append(modelContainer);
		return adapter;
	};
}

function JQueryToolbarRenderer(){
	this.titleRenderers = {};
	this.toolbarButtonRenderers = {};
	this.rowRenderers = {};
	this.itemRenderers = {};
	this.toolbarRenderers = {};
	createStdRenderer(this);
	this.listCount = 0;
}
JQueryToolbarRenderer.prototype = {
	render: function(designerConf){
		var groups = designerConf.toolbar.getGroups();
		var ctxt = {uiModel:designerConf.uiModel, propertyModel:designerConf.propertyModel, conf:designerConf};
		this.toolbarRenderers.toolbar.call(this, ctxt, groups);
	}
}
function keepSessionAlive(){
	function serverSideCall(){
		$.ajax({
			type: 'GET',
			url: 'CSSDesignFactory.asp?keepalive=1',
			cache: false,
			success: function(msg){
			},
			error:function (XMLHttpRequest, textStatus, errorThrown) {
			}
		});
	}

	setInterval(serverSideCall, 1000*60*2);
}

