/*==============================================================================

                                 Smart Cells 1.3
                                 ===============
                       Copyright (c) 2007 Vyacheslav Smolin


Author:
-------
Vyacheslav Smolin (http://www.richarea.com, http://html2xhtml.richarea.com,
re@richarea.com)

About the script:
-----------------
Smart Cells is 100% JavaScript Ajax-based library that allow to make any part
or web page (eg table cells) editable. The Smart Cells API allows to edit
information of various types and store it on server.
The cell types supported include: text, time, textarea, list box, checkbox,
and extending...

Requirements:
-------------
Smart Cells works in IE, Mozilla-based browsers such as Firefox, Opera 9+,
and Safari 3.0.

Usage:
------
Please see example.html.

Demo:
-----
http://www.richarea.com/demo/smart_grid/

License:
--------
Free for non-commercial using. Copyright information must stay intact.
Please contact author for permission to use it in commercial projects.


==============================================================================*/

var is_safari = navigator.userAgent.indexOf('Safari')!=-1?true:false;
var is_opera = navigator.userAgent.indexOf('Opera')!=-1?true:false;

var SmartCells = {

	// array of cells
	cells : new Array(),

	// a cell currently being edited
	active_cell : null,

	// add a new cell of type cell_type
	add_cell : function(obj, cell_type) {
		var cell = eval('new SmartCell_' + cell_type + '(obj)');
		// set event handlers for the cell
		cell.set_handlers();

		this.cells[obj.id] = cell;
	},

	// add a new smart cell
	add_smart_cell : function(cell) {
		cell.set_handlers();

		this.cells[cell.obj.id] = cell;
	},

	// delete an existing smart cell
	delete_smart_cell : function(cell) {
		if (!this.cells[cell.obj.id]) return;

		cell.unset_handlers();

		this.cells[cell.obj.id] = null;
	},

	// return array of cells
	get_cells : function() {

		// some of cells could be deleted ie set to null - do not take them
//		return this.cells;

		var i;
		var cells = new Array();

		for (i in this.cells) {
			if (this.cells[i]) cells[i] = this.cells[i];
		}
		return cells;
	},

	// return cell of object obj
	get_cell : function(obj) {
		if (!obj) return null;
//alert(obj.id + ' => ' + this.cells[obj.id].obj.id);
		return this.cells[obj.id];
	},

	// return cell of object obj with id obj_id
	get_cell_by_obj_id : function(obj_id) {
		return this.cells[obj_id];
	},

	// set all handlers necessary for the editable cells functionality
	set_handlers : function() {

		var onclick = function(e){

//var asdf = document.getElementById('asdf');
//asdf.value = asdf.value + '\n' + SmartCells.active_cell;
//alert(SmartCells.active_cell);
			// no cell is in edit mode
			if (!SmartCells.active_cell) return;

			SmartCells.active_cell.save();

		};

		sc_attach_event(document, 'click', onclick);


		var onkeypress = function(e){
			if (!SmartCells.active_cell) return;

			var event = e||window.event;
			var next_cell = SmartCells.get_cell_by_obj_id(SmartCells.active_cell.tab_next_cell_obj_id);

			if (event.keyCode == 9) {

				if (next_cell) {
					var active_cell = SmartCells.active_cell;

					if (active_cell.save_on_tab) {
						// store value of currently modified cell
	    				active_cell.save();
					}
	
					// set current active cell
    				SmartCells.active_cell = next_cell;


					// must be below SmartCells.active_cell = next_cell
					// as setting new value for active cell is not allowed!
					if (!active_cell.save_on_tab) {
						// just update cell value not saving it
						active_cell.set_value(active_cell.get_current_value());
						active_cell.show_value();

						// removes all objects created outside the cell
						active_cell.remove_external_objects();
					}


					// here "this" is not a SmartCell object but a DOM object
    				SmartCells.active_cell.edit();
	
					if (!document.all) {
						event.preventDefault();
						event.stopPropagation();
					}
				}

				return false;

			}

			if (event.keyCode == 13) {
				// store value of currently modified cell
				SmartCells.active_cell.save();

				if (!document.all) {
					event.preventDefault();
					event.stopPropagation();
				}

				return false;
			}

			// ESC
			if (event.keyCode == 27) {
				// escape edit mode
   				SmartCells.active_cell.cancel_edit();

				if (!document.all) {
					event.preventDefault();
					event.stopPropagation();
				}

				return false;
			}

//			event.cancelBubble = true;

		};

		if (document.all) {
			sc_attach_event(document, 'keydown', onkeypress);
		} else {
			sc_attach_event(document, 'keypress', onkeypress);
		}

	},


	// sets keyboard events handler for element el
	set_keyboard_handler : function(el) {
		var onkeypress = function(e){
			if (!SmartCells.active_cell) return;

			var event = e||window.event;
			var next_cell = SmartCells.get_cell_by_obj_id(SmartCells.active_cell.tab_next_cell_obj_id);

			// TAB
			if (event.keyCode == 9) {


				if (next_cell) {
					var active_cell = SmartCells.active_cell;

					if (active_cell.save_on_tab) {
						// store value of currently modified cell
	    				active_cell.save();
					}
	
					// set current active cell
    				SmartCells.active_cell = next_cell;


					// must be below SmartCells.active_cell = next_cell
					// as setting new value for active cell is not allowed!
					if (!active_cell.save_on_tab) {
						// just update cell value not saving it
						active_cell.set_value(active_cell.get_current_value());
						active_cell.show_value();

						// removes all objects created outside the cell
						active_cell.remove_external_objects();
					}


					// here "this" is not a SmartCell object but a DOM object
    				SmartCells.active_cell.edit();
	
					if (!document.all) {
						event.preventDefault();
						event.stopPropagation();
					}
				}

				event.cancelBubble = true;
				return false;

			}

			// ESC
			if (event.keyCode == 27) {
				// escape edit mode
   				SmartCells.active_cell.cancel_edit();

				if (!document.all) {
					event.preventDefault();
					event.stopPropagation();
				}

				return false;
			}

		};

		if (document.all) {
			sc_attach_event(el, 'keydown', onkeypress);
		} else {
			sc_attach_event(el, 'keypress', onkeypress);
		}
	}

};

// smart cell object
function SmartCell(obj) {

	// parent object of the cell
	this.obj = obj;

	// cell value
	this.value = '';

	// default value
	this.default_value = '';

	// parameters to send to server via ajax
	this.query_params = {};

	// cell parameters (additional values)
	this.params = {};

	// cell parameters defaults (additional values)
	this.default_params = {};

	// url to post ajax query
	this.query_url = '';

	// obj id of a cell that got active on Tab key pressed
	this.tab_next_cell_obj_id = null;

	// user defined save function
	this.user_save = null;

	// cell type
	this.type = '';

	// value type used for sorting
	this.sort_type = 'string';

	// current language
	this.language = 'en';

	// use default langauge if lang data not found for the language specified
	this.default_language = 'en';

	// readonly mode
	this.readonly = false;

	// save cell value on tab key pressed (when move to next cell)
	this.save_on_tab = true;

	// cell alignment
	this.align = 'left';

	// event handlers
	this.ondblclick = null;
	this.onclick = null;

	// set all handlers necessary for the object
	this.set_handlers = function() {

		this.ondblclick = function(e){

			var obj = e.target?e.target:window.event.srcElement;
			var this_cell = SmartCells.get_cell(obj);


			if (!this_cell) {
				obj = get_previous_object(obj, 'TD');
				this_cell = SmartCells.get_cell(obj);
			}

			// this is not a cell object
			if (!this_cell) {
				return true;
			}

			// cannot change the cell
			if (this_cell.readonly) {
				return true;
			}

			// we edit this cell yet
			if (SmartCells.active_cell != this_cell) {

				// store value of currently modified cell
				if (SmartCells.active_cell) SmartCells.active_cell.save();

				// set current active cell
				SmartCells.active_cell = this_cell;

				// here "this" is not a SmartCell object but a DOM object
				SmartCells.active_cell.edit();

			} else {

			}

			var event = e||window.event;
			event.cancelBubble = true;
			return true;
		};

		sc_attach_event(this.obj, 'dblclick', this.ondblclick);
//		this.obj.ondblClick = ondblclick;

		this.onclick = function(e){

//return false;
			var obj = e.target?e.target:window.event.srcElement;
			var row = obj.parentNode;

			// need this trick to prevent context menu in Opera(sucs browser)
//			if (SmartCells.active_cell == this_cell) {
			if (row) {
				row.className = row.className.replace(/ smart_cell_temp_text/g, '');
				row.className += ' smart_cell_temp_text';
			}
//			}

			var event = e||window.event;
//			event.cancelBubble = true;


			// save current active cell if clicked somewhere outside it
//			var obj = e.target?e.target:window.event.srcElement;
    		var this_cell = SmartCells.get_cell(obj);

			if (!this_cell) return;

			// stop parsing onclick even further if cell is in edit mode
			if (SmartCells.active_cell == this_cell) {
				event.cancelBubble = true;
			}

			if (SmartCells.active_cell != this_cell) {
				if (SmartCells.active_cell) SmartCells.active_cell.save();
			}

		};

		sc_attach_event(this.obj, 'click', this.onclick);

/*
		var onselectstart = function(e){
			(e||window.event).cancelBubble = true;
			return true;
		};
		sc_attach_event(this.obj, 'selectstart', onselectstart);
*/
//this.obj.onselectstart = onselectstart;


	}


	// unset all handlers necessary for the object
	this.unset_handlers = function() {

		sc_attach_event(this.obj, 'dblclick', this.ondblclick);
		sc_attach_event(this.obj, 'click', this.onclick);

//		this.obj.removeAttribute('align');
		this.obj.removeAttribute('title');
	}


	// send cell value
	this.send = function(new_value) {

		// current active cell becomes inactive
//		SmartCells.active_cell = null;


		// restore value of class attribute that it was before editing
//		var row = this.obj.parentNode;
//		row.className = row.className.replace(/ smart_cell_temp_text/g, '');

		var sc_obj = this;
		var old_value = this.value;


		// create a new object JSHttpRequest.
		var req = new JsHttpRequest();

		// code executed automatically on loading completed
		req.onreadystatechange = function() {
			var value = old_value;
			if (req.readyState == 4) {
				if (req.responseJS) {
					var status = req.responseJS.status;

					if (status != 'ok') {
						// restore old value
						alert(status);
					} else {

						var value = new_value;

						// function called if modification was successfull
						sc_obj.post_modify_func();

					}

				}

				sc_obj.set_value(value);
				sc_obj.show_value();

			// Debug info
//			document.getElementById('debug').innerHTML = req.responseText;
			}
		}

		var query_params = this.query_params;
//		query_params.value = new_value;


		// add cell params
		var i;
		for (i in this.params) {
			query_params[i] = this.params[i];
		}

		// Enable caching
		req.caching = false;
		// Prepare object
		req.open('POST', this.query_url, true);
		// Send query data
		req.send(query_params);

	}


	// set cell value
	this.set_value = function(value) {
		this.value = value;

		// change cell hint
		this.change_title();
	};

	// set default cell value
	this.set_default_value = function(value) {
		this.default_value = value;
	};

	// show cell value in its object
	this.show_value = function() {
		// dont try to set value for active cell!!!
		if (this.is_active()) return;
/*
		if (SmartCells.active_cell &&
			SmartCells.active_cell == SmartCells.get_cell(this.obj)) {
			return;
		}
*/

		this.obj.align = this.align;

//		this.obj.innerHTML = this.value==''?'&nbsp;':this.value;
		this.obj.innerHTML = this.value==''?'&nbsp;':'<nobr>'+this.value+'</nobr>';
	};

	// turn object into edit mode
	this.edit = function() {
	};

	// turn object into default mode and save current value
	this.save = function() {

		// check if value specified is valid
		if (!this.readonly && !this.is_valid()) return;

		// restore value of class attribute that it was before editing
		var row = this.obj.parentNode;
		if (row) {
			row.className = row.className.replace(/ smart_cell_temp_text/g, '');
		}


		// current active cell becomes inactive
		SmartCells.active_cell = null;


		// non-editable cell
		if (this.readonly) {
			return;
		}


		if (this.user_save) {
			this.user_save();
		} else {
			this._save();
		}

		// removes all objects created outside the cell
		this.remove_external_objects();

	};

	// turn object into default mode without saving current value
	this.cancel_edit = function() {

		// restore value of class attribute that it was before editing
		var row = this.obj.parentNode;
		if (row) {
			row.className = row.className.replace(/ smart_cell_temp_text/g, '');
		}


		// current active cell becomes inactive
		SmartCells.active_cell = null;

		this.show_value();

		// removes all objects created outside the cell
		this.remove_external_objects();

	};

	// native save function
	this._save = function() {
	};

	// check if value typed is allowed
	this.is_valid = function() {
		return true;
	};

	// function called after changes saved
	this.post_modify_func = new Function();

	// set obj id of a cell that got active on tab key pressed
	this.set_tab_next_cell = function(obj_id) {
		this.tab_next_cell_obj_id = obj_id;
	};

	// returns current value, edit mode value has a priority
	this.get_current_value = function() {

	};

	// returns current value prepared for sorting
	this.get_sort_value = function() {
		return this.get_current_value();
	};

	// removes all objects created outside the cell
	this.remove_external_objects = function() {

	};

	// return clone of itself
	this.clone = function() {
		if (typeof(clone) == 'function') {
			return clone(this);
		}
	};

	// returns lang data in the language specified
	this.get_lang_data = function(lang) {

		var cell_lang_data = this.get_lang_array();

		if (!lang) lang = this.language;
		if (!cell_lang_data[lang]) lang = this.default_language;

		return cell_lang_data[lang];
	};


	// returns array of lang data
	this.get_lang_array = function() {

		return new Array();

	};

	// change cell hint
	this.change_title = function() {
		if (this.obj) {
			this.obj.setAttribute('title', this.value);
		}
	};

	// return true if the cell in edit mode
	this.is_active = function() {
		if (SmartCells.active_cell &&
			SmartCells.active_cell == SmartCells.get_cell(this.obj)) {
			return true;
		}
		return false;
	};

	// make the cell visible in grid if it is scrolled behind its borders
	this.make_visible_in_grid = function() {
		// make the cell visible in grid if necessary
		var grid = this.obj.grid;
		if (!grid) return;

		var grid_offset = grid.content_div.scrollLeft;

		var cell_pos = get_obj_pos(this.obj, grid.content_div);
//alert('Cell offset = ' + cell_pos[0] + ' => ' + cell_offset);
		var cell_offset = cell_pos[0];

		// cell is not visible completely (behind left border of the grid)
		if (grid_offset > cell_offset) {
			grid.header_div.scrollLeft = grid.content_div.scrollLeft = cell_offset;
		} else {
			// cell is not visible completely (behind right border of the grid)
			var next_cell_offset = cell_offset +
				Number(grid.columns[this.obj.cell_index + grid.frozen_col_num]['col_width']);
//alert('Cell offset = ' + cell_offset + ' => ' + Number(grid.columns[this.obj.cell_index + grid.frozen_col_num]['col_width']));
//alert('Next offset = ' + next_cell_offset + ' Cur offset ' + (grid_offset + grid.content_div.offsetWidth));
			var offset_diff = next_cell_offset - (grid_offset + grid.content_div.offsetWidth);
//alert('Diff = ' + offset_diff);
			if (offset_diff > 0) {
				grid.header_div.scrollLeft = grid.content_div.scrollLeft =
										Math.max(0, grid_offset+offset_diff);
//alert('Old offset = ' + grid_offset + ' New offset = ' + Math.max(0, grid_offset+offset_diff));
			}
		}
//alert('Grid: ' + grid_offset + ' => ' + cell_offset);

	};

}


// text cell
function SmartCell_text(obj) {

	// parent object the cell
	this.obj = obj;

	// cell type
	this.type = 'text';

	// turn object into edit mode
	this.edit = function() {

		// make cell visible in grid if it is behind its borders
		this.make_visible_in_grid();

//alert('Parent');
		// cannot change the cell
		if (this.readonly) {
			return true;
		}

		// switch object into edit mode
		if (is_safari) {
			var text_obj = document.createElement('INPUT');
		} else {
			var text_obj = document.createElement('TEXTAREA');
		}
		text_obj.setAttribute('id', this.get_text_field_id());
		text_obj.style.width = (this.obj.offsetWidth - 2) + 'px';
		text_obj.style.height = (this.obj.offsetHeight - 4) + 'px';
		text_obj.style.border = '0px';
		text_obj.wrap = 'soft';
		text_obj.style.overflow = 'hidden';
		text_obj.value = this.value;

//		this.obj.wrap = 'soft';
		this.obj.innerHTML = '';
		if (is_safari) var old_height = this.obj.offsetHeight;
		this.obj.appendChild(text_obj);
		if (is_safari) this.obj.style.height = (old_height-2)+'px';


		var onclick = function(e){
			(e||window.event).cancelBubble = true;
			return true;
		};
		sc_attach_event(text_obj, 'click', onclick);


		// move cursor to the very beginning of the text
		if (text_obj.createTextRange) {
			var range = text_obj.createTextRange();
			range.collapse(true);
			range.select();
		} else {
			if (text_obj.setSelectionRange) {
//				text_obj.focus();
				text_obj.setSelectionRange(0, 0);
			}
		}

		text_obj.focus();

	};

	// turn object into default mode and save current value
	this._save = function() {

		var sc_obj = this;
		var old_value = this.value;
		var new_value = document.getElementById(this.get_text_field_id()).value;
		// remove new line chars
		new_value = String(new_value).replace(/[\r\n]+/g, '');

//		SmartCells.active_cell = null;

		// restore value of class attribute that it was before editing
//		var row = this.obj.parentNode;
//		row.className = row.className.replace(/ smart_cell_temp_text/g, '');

		this.send(new_value);

	};


	// returns current value, edit mode value has a priority
	this.get_current_value = function() {
		var text_field = document.getElementById(this.get_text_field_id());

		if (text_field) {
			return text_field.value;
		}

		return this.value;
	};


/*
	// show cell value in its object
	this.show_value = function() {

		// dont try to set value for active cell!!!
		if (SmartCells.active_cell &&
			SmartCells.active_cell == SmartCells.get_cell(this.obj)) {
			return;
		}

		this.obj.innerHTML = this.value==''?'&nbsp;':this.value;
	};
*/


	// text cell specific functions

	// return id of text field for edit mode
	this.get_text_field_id = function() {
		return this.obj.id + '_text';
	}

}

SmartCell_text.prototype = new SmartCell;


// time cell
function SmartCell_time(obj) {

	// parent object the cell
	this.obj = obj;

	// cell type
	this.type = 'time';

	// sorting type
	this.sort_type = 'time';


	// check if value typed is allowed
	this.is_valid = function() {
		var text_obj = document.getElementById(this.get_text_field_id());
		var new_value = text_obj.value;

		// valid is an empty string or string in format NN:NN
		var valid = new_value.match(/^[\s]*$/) || new_value.match(/^[\s]*[0-9]+:[0-9]{2}[\s]*$/);

		if (!valid) {
			var lang_data = this.get_lang_data();

			alert(lang_data['wrong_format']);
			text_obj.focus();
		}

		return valid;
	};


	// returns array of lang data
	this.get_lang_array = function() {

		var cell_lang_data = new Array();

		cell_lang_data['en'] = {
'wrong_format' : 'The time value should be in NN:NN format!'
		};

		cell_lang_data['ru'] = {
'wrong_format' : 'Время должно быть задано в формате NN:NN!'
		};

		return cell_lang_data;
	};


	// text cell specific functions


}

SmartCell_time.prototype = new SmartCell_text;


// date cell
function SmartCell_date(obj) {

	// parent object the cell
	this.obj = obj;

	// cell type
	this.type = 'date';

	// sorting type
	this.sort_type = 'date';

	// date format
	this.format = 'dd-mm-yyyy';

	// need time
	this.time_format = '';

	// calendar object
	this.cal_obj = null;

	// flag to check if Rich Calendar is available
	this.is_rc = (typeof(RichCalendar) == 'function')?true:false;

	// calendar skin
	this.rc_skin = '';

	// reference to parent class
	this.parent_class_obj = new SmartCell_text;


	// set cell value
	this.set_value = function(value) {
		this.value = this.get_date_converted(value);

		// change cell hint
		this.change_title();
	};

	// returns current value, edit mode value has a priority
	this.get_current_value = function() {
		var text_field = document.getElementById(this.get_text_field_id());

		if (text_field) {
			var value = text_field.value;
		} else {
			var value = this.value;
		}

		return this.get_date_converted(value);
	};


	// data cell specific functions

	// returns date converted to/from mysql format
	this.get_date_converted = function(value) {
		var cur_value;

		var date_parts = String(value).split(/[^0-9]+/);

		switch (this.format) {
			case 'dd-mm-yyyy':
				cur_value = date_parts[2]+'-'+date_parts[1]+'-'+date_parts[0];
				break;
			case 'mm-dd-yyyy':
				cur_value = date_parts[1]+'-'+date_parts[2]+'-'+date_parts[0];
				break;
			case 'yyyy-mm-dd':
				cur_value = date_parts[0]+'-'+date_parts[1]+'-'+date_parts[2];
				break;
			default:
				cur_value = value;
				break;
		}

		switch (this.time_format) {
			case 'hh:mm:ss':
				cur_value += ' '+date_parts[3]+':'+date_parts[4]+':'+date_parts[5];
				break;
			case 'hh:mm':
				cur_value += ' '+date_parts[3]+':'+date_parts[4];
				break;
			default:
				break;
		}

		return cur_value;
	};

	// returns current value prepared for sorting
	this.get_sort_value = function() {
		var cur_value = this.get_current_value();
		cur_value = cur_value.replace(/[^0-9]+/g, '');
		return cur_value;
	};

	// turn object into edit mode
	this.edit = function() {

		// make cell visible in grid if it is behind its borders
		this.make_visible_in_grid();

		// call edit method of parent element
		this.parent_class_obj.edit.apply(this, arguments);
//alert('Child');

		if (this.is_rc && !this.cal_obj) {
			var text_field = document.getElementById(this.get_text_field_id());

			this.cal_obj = new RichCalendar();
			this.cal_obj.language = this.language;
			this.cal_obj.sc_obj = this;
			this.cal_obj.auto_close = false;
			this.cal_obj.show_time = this.time_format==''?false:true;
			this.cal_obj.user_onchange_handler = this.calendar_on_change;
			this.cal_obj.user_onclose_handler = this.calendar_on_close;

			this.cal_obj.parse_date(this.get_current_value(), '%Y-%m-%d');

//*
			this.cal_obj.value_el = text_field;
			var cell_pos = get_obj_pos(this.obj);
			this.cal_obj.show(cell_pos[0], cell_pos[1] + this.obj.offsetHeight);
//*/
			// another way to position calendar
//			this.cal_obj.show_at_element(text_field, "-bottom");

			this.cal_obj.change_skin(this.rc_skin);

			// make calendar react on keys pressed
			SmartCells.set_keyboard_handler(this.cal_obj.iframe_doc);

		}

	};


	// removes all objects created outside the cell
	this.remove_external_objects = function() {
		if (this.cal_obj) {
			this.cal_obj.hide();
			this.cal_obj = null;
		}
	};


	// returns Rich Calendar date format
	this.get_rc_format = function() {

		switch (this.format) {
			case 'dd-mm-yyyy':
				return '%d-%m-%Y';
			case 'mm-dd-yyyy':
				return '%m-%d-%Y';
			case 'yyyy-mm-dd':
				return '%Y-%m-%d';
			default:
				break;
		}

	};


	// sets date to cell text field
	this.calendar_on_change = function(cal, object_code) {
		if (object_code == 'day' && cal.sc_obj) {
			var text_field = document.getElementById(cal.sc_obj.get_text_field_id());
			text_field.value = cal.get_formatted_date(cal.sc_obj.get_rc_format());

			// save date selected
			// direct call does not work in Firefox
			window.setTimeout('SmartCells.active_cell.save();', 1);
			// aternative way to save the smart cell
//			window.setTimeout(function(){cal.sc_obj.save();}, 1);
		}
	};


	// stops editing
	this.calendar_on_close = function(cal) {
		SmartCells.active_cell.cancel_edit();
		// alternative way to cancel edit mode
//		cal.sc_obj.cancel_edit();
	};


}

SmartCell_date.prototype = new SmartCell_text;


// price cell
function SmartCell_price(obj) {

	// parent object the cell
	this.obj = obj;

	// cell type
	this.type = 'price';

	// sorting type
	this.sort_type = 'number';

	// alignment
	this.align = 'right';


	// prefix
	this.prefix = '$';

	// postfix
	this.postfix = '';

	// decimal point
	this.decimal_point = '.';

	// thousands point
	this.thousands_point = ' ';


	// show cell value in its object
	this.show_value = function() {
		// dont try to set value for active cell!!!
		if (this.is_active()) return;
/*
		if (SmartCells.active_cell &&
			SmartCells.active_cell == SmartCells.get_cell(this.obj)) {
			return;
		}
*/

		var val = String(this.value);

		if (val.indexOf('.') < 0) val += '.00';
		var price_parts = val.split('.');

		var first_len = price_parts[0].length;
		if (first_len > 3) {

			var i;
			var k = 0;
			var price = '';
			for (i=first_len-1; i>=0; i--) {
				price = price_parts[0].substr(i, 1) + price;
				k++;
				if (k == 3 && i > 0) {
					price = this.thousands_point + price;
					k = 0;
				}
			}

		} else price = parseInt(price_parts[0]);

		price += this.decimal_point + price_parts[1];
		var zeros2add = Math.max(0, 2 - price_parts[1].length);
		if (zeros2add == 1) price += '0';
		if (zeros2add == 2) price += '00';

		price = this.prefix + price + this.postfix;

		this.obj.align = this.align;

		this.obj.innerHTML = this.value==''?'&nbsp;':'<nobr>'+price+'</nobr>';
	};


	// check if value typed is allowed
	this.is_valid = function() {
		var text_obj = document.getElementById(this.get_text_field_id());
		var new_value = text_obj.value;

		// valid is an empty string or string in format NN:NN
		var valid = new_value.match(/^[\s]*[0-9]*\.[0-9]{0,2}[\s]*$/);
		if (new_value == '.') valid = false;

		if (!valid) {
			var lang_data = this.get_lang_data();

			alert(lang_data['wrong_format']);
			text_obj.focus();
		}

		return valid;
	};


	// returns array of lang data
	this.get_lang_array = function() {

		var cell_lang_data = new Array();

		cell_lang_data['en'] = {
'wrong_format' : 'Wrong format of the price you typed!'
		};

		cell_lang_data['ru'] = {
'wrong_format' : 'Неверный формат стоимости!'
		};

		return cell_lang_data;
	};


	// price cell specific functions


}

SmartCell_price.prototype = new SmartCell_text;


// select cell
function SmartCell_select(obj) {

	// parent object the cell
	this.obj = obj;

	// cell type
	this.type = 'select';

	// sorting type
	this.sort_type = 'number';

	// list options
	this.options = new Array();

	// list size
	this.list_size = 7;

	// turn object into edit mode
	this.edit = function() {

		// make cell visible in grid if it is behind its borders
		this.make_visible_in_grid();

		// cannot change the cell
		if (this.readonly) {
			return true;
		}

		// unselect cell content
//		this.obj.focus();

		// switch object into edit mode
		var select_obj = document.createElement('SELECT');
		select_obj.setAttribute('id', this.get_select_id());
		select_obj.parent_obj = this.obj;
//		select_obj.style.width = (this.obj.offsetWidth - 2) + 'px';
//		select_obj.style.height = (this.obj.offsetHeight - 4) + 'px';
		select_obj.size = this.list_size;

		var cell_pos = get_obj_pos(this.obj);

//alert(cell_pos);
		select_obj.style.position = 'absolute';
		select_obj.style.left = cell_pos[0] + 'px';
		select_obj.style.top = cell_pos[1] + this.obj.offsetHeight + 'px';
		select_obj.style.border = '1px solid #000000';


		// add items to the list
		var len = this.options.length;
		var i;
		for (i=0; i<len; i++) {
			var item_value = this.options[i]['value'];
			var item = new Option(this.options[i]['text'], item_value);
			if (item_value == this.value) {
//				item.selected = true;
				item.setAttribute('selected', 'selected');
			}
			select_obj.options[i] = item;
		}

/*
		this.obj.wrap = 'soft';
		this.obj.innerHTML = '';
*/

//		this.obj.appendChild(select_obj);
		document.body.appendChild(select_obj);

		// otherwise Opera does not draw first option in list (sucs browser)
//		select_obj.style.top = cell_pos[1] + this.obj.offsetHeight + 'px';

		select_obj.focus();

		var onclick = function(e){
			var select_obj = e.target?e.target:window.event.srcElement;

			if (select_obj.tagName == 'OPTION') {
			 	select_obj = select_obj.parentNode;
			}

			// get the cell obj
			var cell_obj = select_obj.parent_obj;
			var sc_obj = SmartCells.get_cell(cell_obj);

			sc_obj.save();

			(e||window.event).cancelBubble = true;
			return true;
		};
		sc_attach_event(select_obj, 'click', onclick);

	};


	// turn object into default mode and save current value
	this._save = function() {
		var select_obj = document.getElementById(this.get_select_id());
		var value = select_obj.value;

		// otherwise Opera does not hide textarea border - sucs!!!
//		select_obj.style.display = 'none';

//		document.body.removeChild(select_obj);

		this.send(value);
	}


	// show cell value in its object
	this.show_value = function() {

		// dont try to set value for active cell!!!
		if (this.is_active()) return;
/*
		if (SmartCells.active_cell &&
			SmartCells.active_cell == SmartCells.get_cell(this.obj)) {
			return;
		}
*/

		// find text of option chosen
		var cell_text = '';
		var len = this.options.length;
		var i;
		for (i=0; i<len; i++) {
			var item = this.options[i];
			if (item['value'] == this.value) {
				cell_text = item['text'];
			}
		}

		this.obj.align = this.align;

		this.obj.innerHTML = cell_text==''?'&nbsp;':cell_text;
	};


	// returns current value, edit mode value has a priority
	this.get_current_value = function() {
		var select_obj = document.getElementById(this.get_select_id());

		if (select_obj) {
			return select_obj.value;
		}

		return this.value;
	};


	// removes all objects created outside the cell
	this.remove_external_objects = function() {
		var select_obj = document.getElementById(this.get_select_id());

		document.body.removeChild(select_obj);
	};


	// select cell specific functions

	// return id of list for edit mode
	this.get_select_id = function() {
		return this.obj.id + '_select';
	}

	// add new list option
	this.add_option = function(value, text) {
		var len = this.options.length;
		this.options[len] = new Array();
		this.options[len]['value'] = value;
		this.options[len]['text'] = text;
	}

}

SmartCell_select.prototype = new SmartCell;


// textarea cell
function SmartCell_textarea(obj) {

	// parent object the cell
	this.obj = obj;

	// cell type
	this.type = 'textarea';

	// textarea size
	this.textarea_size = [200, 200];

	// turn object into edit mode
	this.edit = function() {

		// make cell visible in grid if it is behind its borders
		this.make_visible_in_grid();

		// cannot change the cell
		if (this.readonly) {
			return true;
		}

		// switch object into edit mode

		// create a span object to fill the cell
		// otherwise Opera will generate onclick event for document that will
		// cause saving the cell just after we enter edit mode - sucs browser!!!
		var span_obj = document.createElement('SPAN');
		span_obj.setAttribute('id', this.get_span_id());
		span_obj.style.width = (this.obj.offsetWidth - 2) + 'px';
		span_obj.style.height = (this.obj.offsetHeight - 4) + 'px';
		span_obj.style.border = '0px';
		span_obj.wrap = 'soft';
		span_obj.style.overflow = 'hidden';
		span_obj.value = this.value;

		this.obj.innerHTML = this.value;
		this.obj.appendChild(span_obj);


		var onclick = function(e){
			(e||window.event).cancelBubble = true;
			return true;
		};
		sc_attach_event(span_obj, 'click', onclick);

		// create a textarea field for cell note
		var textarea_obj = document.createElement('TEXTAREA');
		textarea_obj.setAttribute('id', this.get_textarea_id());
		textarea_obj.style.position = 'absolute';

		// place the textarea just below the cell
		var pos = get_obj_pos(this.obj);
		textarea_obj.style.left = pos[0] + 'px';
		textarea_obj.style.top = pos[1] + this.obj.offsetHeight + 'px';
		textarea_obj.style.width = this.textarea_size[0] + 'px';
		textarea_obj.style.height = this.textarea_size[1] + 'px';
		textarea_obj.style.border = '1px solid #000000';
		textarea_obj.value = this.value;

//		this.obj.appendChild(textarea_obj);
		document.body.appendChild(textarea_obj);

		textarea_obj.focus();

		var onclick = function(e){
			(e||window.event).cancelBubble = true;
			return true;
		};
		sc_attach_event(textarea_obj, 'click', onclick);

		var onkeypress = function(e){

			var event = e||window.event;

			if (event.keyCode == 13) {
				event.cancelBubble = true;
				return true;
			}

		};

		if (document.all) {
			sc_attach_event(textarea_obj, 'keydown', onkeypress);
		} else {
			sc_attach_event(textarea_obj, 'keypress', onkeypress);
		}

	};

	// turn object into default mode and save current value
	this._save = function() {

		var textarea_obj = document.getElementById(this.get_textarea_id());
		this.query_params.value = textarea_obj.value;

		this.send(new_value);

	};


	// returns current value, edit mode value has a priority
	this.get_current_value = function() {
		var textarea_obj = document.getElementById(this.get_textarea_id());

		if (textarea_obj) {
			return textarea_obj.value;
		}

		return this.value;
	};


	// removes all objects created outside the cell
	this.remove_external_objects = function() {
		var textarea_obj = document.getElementById(this.get_textarea_id());

		document.body.removeChild(textarea_obj);
	};



	// text cell specific functions

	// return id of span obj for edit mode
	this.get_span_id = function() {
		return this.obj.id + '_span';
	}

	// return id of textarea for edit mode
	this.get_textarea_id = function() {
		return this.obj.id + '_textarea';
	}

}

SmartCell_textarea.prototype = new SmartCell;


// checkbox cell
function SmartCell_checkbox(obj) {

	// parent object the cell
	this.obj = obj;

	// cell type
	this.type = 'checkbox';

	// sorting type
	this.sort_type = 'number';

	// alignment
	this.align = 'center';

	this.checkbox_obj = null;


	// turn object into edit mode
	this.edit = function() {

		// make cell visible in grid if it is behind its borders
		this.make_visible_in_grid();

		// set current active cell
		SmartCells.active_cell = this;

		this.checkbox_obj.focus();

		return;
/*
		if (this.checkbox_obj.checked) {
			this.checkbox_obj.checked = false;
		} else {
			this.checkbox_obj.checked = true;
		}

		this.save();
*/

	};

	// turn object into default mode and save current value
	this._save = function() {

		this.send((this.checkbox_obj.checked||this.type=='radio')?1:0);

	};


	// returns current value, edit mode value has a priority
	this.get_current_value = function() {

		if (this.checkbox_obj.checked) {
			return 1;
		}

		return 0;
	};


	// show cell value in its object
	this.show_value = function() {
		// dont try to set value for active cell!!!
		if (this.is_active()) return;
/*
		if (SmartCells.active_cell &&
			SmartCells.active_cell == SmartCells.get_cell(this.obj)) {
			return;
		}
*/

		if (!this.checkbox_obj) {
			this.checkbox_obj = document.createElement('INPUT');
			this.checkbox_obj.setAttribute('type', this.type);
			this.checkbox_obj.sc_obj = this;

			if (this.type == 'radio') {
				this.checkbox_obj.setAttribute('name', this.group);
			}

			this.obj.align = this.align;

			this.obj.innerHTML = '';
			this.obj.appendChild(this.checkbox_obj);

//			this.checkbox_obj.onclick = new Function("e","this.grid.row_clicked(e||window.event)");
			var onclick = function(e) {
				// turn current active cell's edit mode off if any
   				if (SmartCells.active_cell) SmartCells.active_cell.cancel_edit();

				var src_obj = e.target?e.target:window.event.srcElement;

				// cannot change the cell
				if (src_obj.sc_obj.readonly) {
					src_obj.checked = parseInt(src_obj.sc_obj.value)?true:false;
				}

				// reset state of all radio boxes of the group
				if (src_obj.sc_obj.type == 'radio') {
					var mode = src_obj.sc_obj.readonly?'restore':'clear';
					src_obj.sc_obj.reset_radio_group(mode);

					if (!src_obj.sc_obj.readonly) {
						src_obj.checked = true;
					}
				}

				src_obj.sc_obj.save();

				(e||window.event).cancelBubble = true;
				return true;
			};
			sc_attach_event(this.checkbox_obj, 'click', onclick);


			var onkeypress = function(e){
				if (!SmartCells.active_cell) return;

				var src_obj = e.target?e.target:window.event.srcElement;
				var event = e||window.event;
//				var next_cell = SmartCells.get_cell_by_obj_id(src_obj.sc_obj.tab_next_cell_obj_id);
				var next_cell = SmartCells.get_cell_by_obj_id(SmartCells.active_cell.tab_next_cell_obj_id);

				if (event.keyCode == 9) {

					if (next_cell) {
//if (next_cell.type == 'image') alert(next_cell.type);
						// checkboxes/radio are saved on Space key pressed
						if (SmartCells.active_cell.type != 'checkbox' &&
							SmartCells.active_cell.type != 'radio') {
							// store value of currently modified cell
		    				SmartCells.active_cell.save();
						}

						// set current active cell
        				SmartCells.active_cell = next_cell;

						// here "this" is not a SmartCell object but a DOM object
        				SmartCells.active_cell.edit();

						if (next_cell.readonly) {
							src_obj.focus();
						}

						if (!document.all) {
							event.preventDefault();
							event.stopPropagation();
						}
					}
    
					event.cancelBubble = true;
					return false;
    
				}

			};

			if (document.all) {
				sc_attach_event(this.checkbox_obj, 'keydown', onkeypress);
			} else {
				sc_attach_event(this.checkbox_obj, 'keypress', onkeypress);
			}


			this.obj.align = 'middle';

		}

		// update state of other group radio boxes
		if (this.type == 'radio') {
			this.reset_radio_group('update');
		}

		// change cell hint
		this.change_title();

		if (parseInt(this.value)) {
			this.checkbox_obj.checked = true;
		} else {
			this.checkbox_obj.checked = false;
		}

	};


	// checkbox cell specific functions


}

SmartCell_checkbox.prototype = new SmartCell;


// radiobox cell
function SmartCell_radio(obj) {

	// parent object the cell
	this.obj = obj;

	// cell type
	this.type = 'radio';

	// radio group
	this.group = '';


	// (does not send any queries to server - just changes values of the boxes)
	// if mode is 'restore' - restore current states; 'update' - unset all boxes
	// except this one if its value is not 0, otherwise do 'reset' actions;
	// 'clear' - switch off all radio boxes
	this.reset_radio_group = function(mode) {
		if (!mode) reset = 'restore';
		if (mode == 'update' && !parseInt(this.value)) mode = 'restore';

		// all cells
		var smart_cells = SmartCells.get_cells();
		var i;

		for (i in smart_cells) {
			var sc = smart_cells[i];

			if (sc.type == 'radio' && sc.group == this.group &&	sc.checkbox_obj) {

				switch (mode) {
					case 'restore':
						sc.checkbox_obj.checked = parseInt(sc.value)?true:false;
						break;
					case 'update':
						if (i != this.obj.id) {
							sc.checkbox_obj.checked = false;
							sc.set_value(0);
							sc.change_title();
						}
						break;
					case 'clear':
						sc.checkbox_obj.checked = false;
						break;
					default:
						break;
				}
				
			}
		}		

	};

}

SmartCell_radio.prototype = new SmartCell_checkbox;


// image cell
function SmartCell_image(obj) {

	// parent object the cell
	this.obj = obj;

	// cell type
	this.type = 'image';

	// subfolder to store images to (relative to some server-side folder)
	// images are stored in 000007.[gif|jpg|png] format
	this.folder = '/images';

	// number to generate an unique image name
//	this.image_id = 0;

	// preview size (max width or height dimension)
	this.preview_size = 200;

	// script generating thumbnails (and hidding actual image folder)
	this.preview_file = 'sc_thumbnail.php';

	// uploading handler (file to submit uploading form to)
	this.upload_file = 'sc_image_upload.php';

	// real size of the last image loaded into cell
	// (will use it to calc size of preview image - if it is not the same as
	// the size of image in cell then this means that we loaded full image, not
	// its preview [eg this happens with gifs])
	this.last_width = 0;
	this.last_height = 0;


	// default size of image cell (use it if could not determine cell size -
	// this happens in IE when page is not loaded completely
	this.default_max_size = [20, 20];

	// temporary image used to load cell image (to determine if it is exists
	// and its size if yes)
	// div element used to determine image size
	this.temp_img_obj = document.createElement('IMG');
	this.temp_img_obj.style.visibility = 'hidden';
	this.temp_img_obj.style.position = 'absolute';
	this.temp_img_obj.sc_obj = this;

	// this causes IE output a message that connection lost (???) and
	// does not allow to load page
	// but anyway is not necessary to append the image to any object
	// so that onload event appears
//	document.body.appendChild(this.temp_img_obj);

	var onload = function(){
//	alert('Temp image loaded: ' + this.getAttribute('src') + ' ' +
//			this.width + ' ' + this.height);

		var img_id = this.sc_obj.get_img_id();
		var img_obj = document.getElementById(img_id);

		var max_size = this.sc_obj.max_size();

		// could not determine cell size
		if (max_size[0] <= 0 || max_size[1] <= 0) {
			max_size = this.sc_obj.default_max_size;
		}

		var max_width = max_size[0];
		var max_height = max_size[1];

		// create the image object if not exists
		if (!img_obj) {
			this.sc_obj.obj.innerHTML = '';
			img_obj = document.createElement('IMG');
			img_obj.setAttribute('id', img_id);
			img_obj.setAttribute('width', max_width);
			img_obj.setAttribute('height', max_height);
			img_obj.sc_obj = this.sc_obj;
			this.sc_obj.obj.appendChild(img_obj);
		}


		// actual size the image loaded
		var width = this.width;
		var height = this.height;

//alert('Loaded: ' + this.src + ' ' + width + ' ' + height);
		// store size of last loaded image
		this.sc_obj.last_width = this.width;
		this.sc_obj.last_height = this.height;


		if (width > max_width) {
			height = parseInt(height*max_width/width);
			width = max_width;
		}

		if (height > max_height) {
			width = parseInt(width*max_height/height);
			height = max_height;
		}


//alert(this.getAttribute('src') + ' => ' + width + ' => ' + height);
		img_obj.setAttribute('width', width);
		img_obj.setAttribute('height', height);
		img_obj.setAttribute('src', this.getAttribute('src'));

	};
	this.temp_img_obj.onload = onload;


	// turn object into edit mode
	this.edit = function() {

		// make cell visible in grid if it is behind its borders
		this.make_visible_in_grid();

		// cannot change the cell
		if (this.readonly) {
			return true;
		}

		// switch object into edit mode

		var min_width = 250;
		var min_height = 60;

		// create a span object to fill the cell
		// otherwise Opera will generate onclick event for document that will
		// cause saving the cell just after we enter edit mode - sucs browser!!!
		var span_obj = document.createElement('SPAN');
		span_obj.setAttribute('id', this.get_span_id());
		span_obj.style.width = (this.obj.offsetWidth - 2) + 'px';
		span_obj.style.height = (this.obj.offsetHeight - 4) + 'px';
		span_obj.style.border = '0px';
		span_obj.wrap = 'soft';
		span_obj.style.overflow = 'hidden';
		span_obj.value = this.value;

		// check if image exists (do it before remove cell content!)
		var img_obj = document.getElementById(this.get_img_id());
		if (img_obj) {
			var cur_width = img_obj.getAttribute('width');
			var cur_height = img_obj.getAttribute('height');
			var is_image = true;
		} else {
			var is_image = false;
		}

		this.obj.innerHTML = '&nbsp;';
		this.obj.appendChild(span_obj);

		var onclick = function(e){
			(e||window.event).cancelBubble = true;
			return true;
		};
		sc_attach_event(span_obj, 'click', onclick);


		// create a textarea field for cell note
		var iframe_obj = document.createElement('IFRAME');
		iframe_obj.setAttribute('id', this.get_iframe_id());
		iframe_obj.setAttribute('scrolling', 'no');
		iframe_obj.style.position = 'absolute';
		iframe_obj.style.display = 'none';

		// place the textarea just below the cell
		var pos = get_obj_pos(this.obj);
		iframe_obj.style.left = pos[0] + 'px';
		iframe_obj.style.top = pos[1] + this.obj.offsetHeight + 'px';
		iframe_obj.style.border = '1px solid #000000';
		iframe_obj.value = this.value;

		document.body.appendChild(iframe_obj);

		var iframe_content = '' +
'<html><body style="margin:0;background:#FFFFFF">' +
'<form id="' + this.get_form_id() + '" action="' + this.upload_file + '" method="POST" enctype="multipart/form-data">' +
		'';

		if (is_image) {

			// if current size is not the same as loaded image has then we
			// probably cannot resize images => calc width and height of the
			// image, otherwise it could be too big
//alert(this.last_width + " != " + cur_width + " != " + this.last_height + " != " + cur_height);
			if (this.last_width != cur_width ||
				this.last_height != cur_height) {

				var width = this.last_width;
				var height = this.last_height;
      
				if (width > this.preview_size) {
					height = parseInt(height*this.preview_size/width);
					width = this.preview_size;
				}
      
				if (height > this.preview_size) {
					width = parseInt(width*this.preview_size/height);
					height = this.preview_size;
				}

				var width_code = ' width="' + width + '"';
				var height_code = ' height="' + height + '"';

				var onload = '';
			} else {
				var width = min_width;
				var height = min_height;

				var width_code = '';
				var height_code = '';

				var onload = ' onload="resize();"';
			}

			iframe_content += '<center><img' + onload + ' src="' +
						 this.image_url(this.preview_size, this.preview_size) +
						 '"' + width_code + height_code + ' /></center>';
		}

		var lang_data = this.get_lang_data();

		iframe_content += '' +
'<script language="JavaScript">' +
'function resize() {' +
'var parent_win = window.parent;' +
'var cell_obj = parent_win.document.getElementById("' + this.obj.id + '");' +
'var sc_obj = parent_win.SmartCells.get_cell(cell_obj);' +
'var iframe_obj = parent_win.document.getElementById(sc_obj.get_iframe_id());' +
'var obj = iframe_obj.contentWindow.document.body;' +
'iframe_obj.style.width = obj.scrollWidth;' +
'iframe_obj.style.height = obj.scrollHeight+(document.all?10:0);' +
'}' +
'</script>' +
'<input type="hidden" name="sc_cell" value="' + this.obj.id + '" />' +
'<input type="hidden" name="id" value="' + this.value + '" id="value" />' +
'<input type="hidden" name="folder" value="' + this.folder + '" />' +

'<input type="file" id="image" name="image"><br>';

		if (is_image) {
			iframe_content += '<span style="font-family:Times;font-size:13px"><nobr>' +
'<input type="radio" name="image_action" value="delete" id="delete"><label for="delete">' + lang_data['delete'] + '</label>&nbsp;' +
'<input type="radio" name="image_action" value="update" id="update"><label for="update">' + lang_data['update'] + '</label>&nbsp;' +
'<input type="radio" name="image_action" value="none" id="none" checked><label for="none">' + lang_data['none'] + '</label>' +
			'</span></nobr>';
		}

		iframe_content += '' +
'</form>' +
'</body></html>' +
		'';


		iframe_obj.style.width = Math.max(is_image?width:0, min_width);
		iframe_obj.style.height = (is_image?height:0) + min_height;
		iframe_obj.style.display = '';

		var iframe_doc = iframe_obj.contentWindow.document;
		iframe_doc.open();
		iframe_doc.write(iframe_content);
		iframe_doc.close();

		var onclick = function(e){
			(e||window.event).cancelBubble = true;
			return true;
		};
		sc_attach_event(iframe_doc, 'click', onclick);
//		iframe_doc.onclick = onclick;


		// make image form react on keys pressed
		SmartCells.set_keyboard_handler(iframe_doc);

		var none_checkbox = iframe_doc.getElementById('none');
		if (none_checkbox) none_checkbox.focus();

	};

	// turn object into default mode and save current value
	this._save = function() {
		var iframe_obj = document.getElementById(this.get_iframe_id());
		var iframe_doc = iframe_obj.contentWindow.document;

		// submit iframe form
		var form_obj = iframe_doc.getElementById(this.get_form_id());
		if (form_obj) {
//alert('Form submit');
			form_obj.submit();
		} else {
			if (SmartCells.active_cell) SmartCells.active_cell.cancel_edit();
		}
		iframe_obj.style.visibility = 'hidden';

//		this.send(this.value);

	};


	// returns current value, edit mode value has a priority
	this.get_current_value = function() {
		return this.value;
	};


	// removes all objects created outside the cell
	this.remove_external_objects = function() {
		// cannot delete iframe here as uploding may not be finished yet!!!
//		var iframe_obj = document.getElementById(this.get_iframe_id());

//		document.body.removeChild(iframe_obj);
	};


	// show cell value in its object
	this.show_value = function() {

		// dont try to set value for active cell!!!
		if (this.is_active()) return;

		// delete iframe if it exists yet
		var iframe_obj = document.getElementById(this.get_iframe_id());
//alert(iframe_obj);
		if (iframe_obj) {
			// if delete here the rest of the JS code does not work in Opera!!!
			// so, delete the iframe_obj at the very end of the function
//			iframe_obj.parentNode.removeChild(iframe_obj);
//			document.body.removeChild(iframe_obj);
			var edit_mode = true;
		} else {
			var edit_mode = false;
		}
/*
		if (SmartCells.active_cell &&
			SmartCells.active_cell == SmartCells.get_cell(this.obj)) {
			return;
		}
*/

		// remove current content
		this.obj.innerHTML = '&nbsp;';

		this.obj.align = this.align;

		var max_size = this.max_size();
		var max_width = max_size[0];
		var max_height = max_size[1];

		var img_src = this.image_url(max_width, max_height);

		this.temp_img_obj.setAttribute('src', img_src);


		// as this function is called after uploading, we should call
		// post_modify_func here
		if (edit_mode) this.post_modify_func();

		if (iframe_obj) iframe_obj.parentNode.removeChild(iframe_obj);

	};


	// returns image url
	this.image_url = function (max_width, max_height) {
		return this.preview_file + '?id=' + this.value +
					'&folder=' + this.folder +
					'&max_width=' + max_width +
					'&max_height=' + max_height + '&' + (new Date().getTime());
	}


	// returns max width and height allowed for images
	this.max_size = function() {
		return [this.obj.offsetWidth, this.obj.offsetHeight - 2];
	};


	// returns array of lang data
	this.get_lang_array = function() {

		var cell_lang_data = new Array();

		cell_lang_data['en'] = {
'delete' : 'Delete',
'update' : 'Change',
'none' : 'Do not change'
		};

		cell_lang_data['ru'] = {
'delete' : 'Удалить',
'update' : 'Изменить',
'none' : 'Не изменять'
		};

		return cell_lang_data;
	};


	// global clone does not work as too many recursion entries happen
	this.clone = function() {
		var sc = new SmartCell_image();

		// copy non-objects
		for (var i in this) {
			if (typeof(this[i]) != 'object' && typeof(this[i]) != 'function') {
				sc[i] = this[i];
			}
		}

		// copy objects
		if (typeof(clone) == 'function') {
			sc.query_params = clone(this.query_params);
			sc.params = clone(this.params);
			sc.default_params = clone(this.default_params);
		} else {
			sc.query_params = {};
			sc.params = {};
			sc.default_params = {};
		}

		// copy functions
//		sc.post_modify_func = this.post_modify_func;

		return sc;
	};


	// set cell value
	this.set_value = function(value) {
		this.value = value;

		// change cell hint
		this.change_title();

		var iframe_obj = document.getElementById(this.get_iframe_id());
		// update value in image submit form if we are in edit mode
		if (iframe_obj) {
			var iframe_doc = iframe_obj.contentWindow.document;
			var value_field = iframe_doc.getElementById('value');
			value_field.value = value;
		}
	};


	// image cell specific functions

	// return id of temp image obj for edit mode
	this.get_img_id = function() {
		return this.obj.id + '_img';
	};

	// return id of span obj for edit mode
	this.get_span_id = function() {
		return this.obj.id + '_span';
	};

	// return id of iframe for edit mode
	this.get_iframe_id = function() {
		if (!this.obj) return '';
		return this.obj.id + '_iframe';
	};

	// return id of iframe form for edit mode
	this.get_form_id = function() {
		return this.obj.id + '_form';
	};

}

SmartCell_image.prototype = new SmartCell;


function sc_attach_event(obj, event, handler) {

	if (obj.addEventListener) {
		obj.addEventListener(event, handler, false);
	} else {
		if (obj.attachEvent) {
			obj.attachEvent('on'+event, handler);
		}
	}
}


function sc_detach_event(obj, event, handler) {

	if (obj.removeEventListener) {
		obj.removeEventListener(event, handler, false);
	} else {
		if (obj.detachEvent) {
			obj.detachEvent('on'+event, handler);
		}
	}
}


// return object of the event appeared
function sc_get_event_obj(e) {

	try {
		var obj = e.target; // Firefox & Opera
	} catch(error) {
		var obj = e.srcElement; // IE
	}

	return obj;
}


// search among parents an object with tag tag_name
function get_previous_object(obj, tag_name) {
	if (obj) {

		// to prevent search above BODY element
		if (obj.tagName == "BODY") return null;

		if (obj.tagName != tag_name){
			obj = get_previous_object(obj.parentNode, tag_name);
		}
	}
	return(obj);
}


// return position of object obj or this.obj if obj not set
function get_obj_pos(obj, stop_obj){
	var pos = Array(0,0);

	if (!obj) return pos;

	while (obj && stop_obj != obj) {

		//absolute positioned element => do not count its offset values
		if (obj.currentStyle) {
			if (obj.currentStyle.position == 'absolute') break;
		} else {
			if (document.defaultView.getComputedStyle(obj, '').getPropertyValue('position') == 'absolute') break;
		}

		var is_div = obj.tagName.toUpperCase() == "DIV" ? true : false;
		pos[0] += obj.offsetLeft - (is_div?obj.scrollLeft:0);
		pos[1] += obj.offsetTop - (is_div?obj.scrollTop:0);

/*
		// Opera returns negative offsets (but we took it into account
		// when substitute scrollLeft/Top of the parent elements)
		// so we use Math.max to prevent double reducing of the positions
		pos[0] += Math.max(obj.offsetLeft, 0) - obj.scrollLeft;
		pos[1] += Math.max(obj.offsetTop, 0) - obj.scrollTop;
*/
		obj = obj.offsetParent;
	}

	if (0&&!stop_obj) {
		pos[0] += document.body.scrollLeft// + document.documentElement.scrollLeft;
		pos[1] += document.body.scrollTop// + document.documentElement.scrollTop;
    
		// Opera has the same value in these vars
		if (document.body.scrollLeft != document.documentElement.scrollLeft) {
			pos[0] += document.documentElement.scrollLeft;
		}
		if (document.body.scrollTop != document.documentElement.scrollTop) {
			pos[1] += document.documentElement.scrollTop;
		}
	}

	return pos;
}
