
/* J F I E L D */
function JField(name, caption, type, items, flags)
{
	var self = this,
		_itemCount = 0;

	this.name    = name;
	this.caption = unescape(caption);
	this.type    = (type) ? new Number(type) : ftString;
	this.items   = null;
	this.flags   = (flags) ? new Number(flags) : 0;
	this.index   = null;
  
	this.hasFlag = hasFlag;
	this.setItems = setItems;
	this.appendItems = appendItems;
	this.getItemCount = getItemCount; 

	this.setItems(items);

	function hasFlag(flag)
	{
		return utils.inSet(flag, self.flags);
	}

	function setItems(items)
	{
		self.items = [];
		appendItems(items);
	}

	function appendItems(items)
	{
		self.items = [];
		_itemCount = 0;
		if (utils.coalesce(items, "") != "")
		{
			var pairs = items;
			if (typeof items == 'string')
				pairs = unescape(items).split(scItemSep);

			pairs.forIn( function (item)
			{
				var parts = item.split(scEquals);
				self.items[parseInt(parts[0])] = unescape(parts[1]);
				_itemCount++;
			}, self);
  		}
	}

	function getItemCount()
	{
		return _itemCount;
	}
}

function JBindingManager(dataset)
{
	var self = this,
		_disposed = false,
		dset = dataset,
		bindings = [],
		working = false;

	this.bind = bind;
	this.unbind = unbind;
	this.pushData = pushData;
	this.pullData = pullData;
	this.dispose = dispose;

	dataset.oncurrentrecordchanged.attach(recChanged);

	function getObject(obj)
	{
		if (typeof obj == 'string')
			obj = document.getElementById(obj);

		return obj;
	}

	function recChanged(fieldName, orgValue, value, whoSet)
	{
		if (fieldName == null)
		{
			_pushData();
		}
		else
		{
			var rec = dset.getCurrentRecord();

			bindings.forEach(function(item)
			{
				if (item[2] == fieldName)
				{
					var fld = item[2];
					var jsObj = getObject(item[4]);

					if ((jsObj) && (jsObj.bind_setValue))
					{
						if (jsObj.bind_setValue(item[1], item[2], rec) === true)
							return;
					}

					var isNoDataField = rec.isNoData(fld);
					var isReadOnlyField = rec.isReadOnly(fld);

					if ((jsObj) && (jsObj.setIsNoData))
						jsObj.setIsNoData(isNoDataField);

					if ((jsObj) && (jsObj.setIsReadOnly))
						jsObj.setIsReadOnly(isReadOnlyField);


					var obj = getObject(item[0]);

					if (obj.bind_setValue)
						obj.bind_setValue(item[1], item[2], rec);
					else
						obj[item[1]] = utils.coalesce(value, '');

				}
			}, this);
		}
	}

	function boundObjectValueChanged()
	{
		if (working)
			return;
		working = true;

		bindings.forEach(function(item)
		{
			var obj = getObject(item[0]);

			if (obj === this)
			{
				dset.set(item[2], obj[item[1]]);
				if (item[3] != null)
				{
					eval('item[3].call(this, ' + utils.buildArgumentString(arguments) + ')');
				}
			}
		}, this);

		working = false;
	}

	function pushData()
	{
		if (working)
			return;
		working = true;
		_pushData();
		working = false;
	}

	function _pushData()
	{
		var rec = dset.getCurrentRecord();

		bindings.forEach(function(item)
		{
			if (typeof(item) == 'undefined')
				return;

			var fld = item[2];
			var jsObj = getObject(item[4]);

			if ((jsObj) && (jsObj.bind_setValue))
			{
				if (jsObj.bind_setValue(item[1], item[2], rec) === true)
					return;
			}

			var isNoDataField = false;
			var isReadOnlyField = false;

			if (rec)
			{
				isNoDataField = rec.isNoData(fld);
				isReadOnlyField = rec.isReadOnly(fld);
			}

			if ((jsObj) && (jsObj.setIsNoData))
				jsObj.setIsNoData(isNoDataField);

			if ((jsObj) && (jsObj.setIsReadOnly))
				jsObj.setIsReadOnly(isReadOnlyField);

			
			var obj = getObject(item[0]);
			var value;
			if (obj.bind_setValue)
				obj.bind_setValue(item[1], item[2], rec);
			else
			{
				value = (rec) ? rec.getText(item[2]) : '';
				if (isNoDataField)
					value = null;
				obj[item[1]] = utils.coalesce(value, '');
			}

		}, this);
	}

	function pullData()
	{
		if (working)
			return;

		working = true;
		_pullData();
		working = false;
	}

	function _pullData()
	{
		var rec = dset.getCurrentRecord();

		bindings.forEach(function(item)
		{
			var obj = getObject(item[0]);
			if (obj.bind_getValue)
				dset.setValue(item[2], obj.bind_getValue(item[1]));
			else
				dset.setValue(item[2], obj[item[1]]);
		}, this);
	}

	function bind(obj, property, field, setObjEvent, jsObj)
	{
		var orgEvent = null;
		if (setObjEvent === true)
		{
			var _obj = getObject(obj);

			if (_obj != null)
			{
				orgEvent = _obj.onchange;
				_obj.onchange = boundObjectValueChanged;
			}
		}

		bindings.push([obj, property, field, orgEvent, jsObj]);
	}

	function unbind(obj, property)
	{
		if (_disposed)
			return;

		var binding,
		len = bindings.length,
		refObj = getObject(obj);

		for (var i = len - 1; i >= 0; i--)
		{
			binding = bindings[i];
			if ((getObject(binding[0]) === refObj) && ((binding[1] == property) || (property == null)))
			{
				refObj.onchange = binding[3];
				bindings.removeAt(i);
				if (bindings.length == len)
					bindings.length--;
			}
		}
	}

	function dispose()
	{
		if (_disposed)
			return;

		if ((dset != null) && (dset.oncurrentrecordchanged != null))
		{
			dset.oncurrentrecordchanged.detach(recChanged);
		}

		var binding, len = bindings.length;
		for (var i = len - 1; i >= 0; i--)
		{
			binding = bindings[i];
			if (binding[0] != null)
				getObject(binding[0]).onchange = binding[3];

			bindings.removeAt(i);
		}

		dset = null;
		bindings = null;
		_disposed = true;
	}
}

function JRecordSelector(dataset)
{
	var self = this,
		_keys = [];

	this.dataset = dataset;
	this.add = add;
	this.remove = remove;
	this.checkKeys = checkKeys;
	this.clear = clear;
	this.contains = contains;
	this.getCount = getCount;
	this.getKeys = getKeys;
	this.toString = toString;
	this.dispose = dispose;

	this.dataset.onrecordchanged.attach(_DSChanged);

	function _getKey(recOrKey)
	{
		if (recOrKey == null)
			return;

		var key = recOrKey;
		if (typeof recOrKey == 'number')
		{
			key = self.dataset.getRecord(recOrKey).key;
		}
		else
		if (recOrKey.constructor == JRecord)
		{
			key = recOrKey.key;
		}

		return key;
	} 

	function add(recOrKey)
	{
		if (recOrKey == null)
			return;

		_keys.push(_getKey(recOrKey));
	}

	function remove(recOrKey)
	{
		if (recOrKey == null)
			return;

		_keys.remove(_getKey(recOrKey));
	}

	function clear()
	{
		_keys.clear();
	}

	function contains(recOrKey)
	{
		if (recOrKey == null)
			return;

		return _keys.contains(_getKey(recOrKey));
	}

	function getCount()
	{
		return _keys.length;
	}

	function getKeys()
	{
		return _keys.copy();
	}

	function checkKeys()
	{
		var idx = self.dataset.getRecCount();
		if ((_keys.length == 0) || (idx <= 0))
			return;

		var rec, keys = [];
		do
		{
			keys.push(self.dataset.getRecord(idx - 1).key);
		}
		while (--idx);

		_keys.copy().forEach (function (key, i)
		{
			if (keys.indexOf(key) == -1)
				_keys.remove(key);
		}, self);
	}

	function _DSChanged(fieldName, orgValue, value, whoSet)
	{
	}

	function toString()
	{
		checkKeys();

		return escape(_keys.join(scSafeSep));
	}


	function dispose()
	{
		clear();
	}
}




/* J R E C O R D */
function JRecord(dataset, recState, key, buffer, flags, tvFlags, tvLevel, tvIndex, rFlags, fFlags)
{
  // private properties
	var self      = this,
		_disposed = false,
		values    = [],
		orgValues = [],
		state     = (recState == null) ? rsAdded : parseInt(recState),
		orgState  = state;

  // public properties
	this.dataset = dataset;
	this.index   = -1;
	this.flags   = parseInt(flags);
	this.fieldFlags = [];
	this.rightsFlags = [];
	this.key     = key;
	this.tvFlags = parseInt(tvFlags);
	this.tvLevel = parseInt(tvLevel);
	this.tvIndex = ((tvIndex == null) || (tvIndex == '')) ? -1 : parseInt(tvIndex);
  
  // methods
	this.clone     = clone;
	this.commit    = commit;
	this.getText   = getText;
	this.getValue  = getValue;
	this.getValues = getValues;
	this.getOriginalValue = getOriginalValue;
	this.getState  = getState;
	this.getOriginalState = getOriginalState;
	this.isDirty   = isDirty;
	this.restore   = restore;
	this.setState  = setState;
	this.setValue  = setValue;
	this.setNewValue  = setNewValue;
	this.setValues = setValues;
	this.toString  = toString;
	this.isNoData  = isNoData;
	this.isReadOnly = isReadOnly;
	this.isRequired = isRequired;
	this.isSelect   = isSelect;
	this.dispose   = dispose;

	this._cloneSetValues = _cloneSetValues; 

	var buf  = unescape(buffer);
	if (!utils.isEmpty(buf))
	{
		for (var fldName in dataset.fields)
		{
			if (Array.prototype[fldName] != null)
				continue;

			var start  = buf.indexOf(fldName + scEquals);
			if (start == -1)
				continue;

			var stop   = buf.indexOf(scSafeSep, start);
			var value  = buf.substring(start + fldName.length + scEquals.length, stop);

			var type = dataset.fields[fldName].type;
			if (utils.inList(type, ftInteger, ftFloat) && (typeof(value) == 'string'))
				value = value.replace(",", ".");
			else
			if (utils.inList(type, ftDate, ftTime, ftDateTime))
				value = formatDateTimeValue(value, type, 'mm.dd.yyyy', scDateSep)
	
			values.length++;
			values[fldName] = (value != "") ? value : null ;
			orgValues[fldName] = values[fldName];
		}
	}

	rFlags = unescape(rFlags);
	if (!utils.isEmpty(rFlags))
	{
		for (var fldName in dataset.fields)
		{
			if (Array.prototype[fldName] != null)
				continue;

			var start  = rFlags.indexOf(fldName + scEquals);
			var stop   = rFlags.indexOf(scSafeSep, start);
			var value  = rFlags.substring(start + fldName.length + scEquals.length, stop);

			//self.rightsFlags.length++;
			self.rightsFlags[fldName] = parseInt(value);
		}
	}

	fFlags = unescape(fFlags);
	if (!utils.isEmpty(fFlags))
	{
		for (var fldName in dataset.fields)
		{
			if (Array.prototype[fldName] != null)
				continue;

			var start  = fFlags.indexOf(fldName + scEquals);
			var stop   = fFlags.indexOf(scSafeSep, start);
			var value  = fFlags.substring(start + fldName.length + scEquals.length, stop);

			self.fieldFlags[fldName] = parseInt(value);
		}
	}


	function checkModified()
	{
		if (state != rsModified)
			return;

		state = orgState;
		if (self.isDirty())
			state = rsModified;
	}

	function getText(fieldname)
	{
		var ds = self.dataset,
			fields = ds.fields,
			value = getValue(fieldname),
			type  = fields[fieldname] != null ? fields[fieldname].type : '';

		if (self.isNoData(fieldname))
			return '';
		
		if (type == ftEnum)
		{
			return (utils.coalesce(fields[fieldname].items[value], ''));
		}

		if (type == ftSet)
		{
			var labels = [];
			var items = fields[fieldname].items;
			for (i in items)
			{
				if ((value & i) == i)
				{
					labels[labels.length++] = items[i];
				}
			}
			return ('[' + labels.join(', ') + ']');
		}

/*
		if (utils.inList(type, ftDate, ftDateTime))
		{
			if (value == null)
				value = '';
			else
			if (value != '')
			{
				var dt = new Date(value);
				if (type == ftDateTime)
				{
					var timePart = '';
					if ((dt.getHours() != 0) || (dt.getMinutes() != 0) || (dt.getSeconds() != 0))
						timePart = ' ' + dt.getHours().padLeft(2, '0') + ':' + dt.getMinutes().padLeft(2, '0') + ':' + dt.getSeconds().padLeft(2, '0')

					value = dt.getDate() + '.' + (dt.getMonth() + 1) + '.' + dt.getFullYear() + timePart;
				}
				else
				if (type == ftDate)
					value = dt.getDate() + '.' + (dt.getMonth() + 1) + '.' + dt.getFullYear();
				else
					value = dt.getHours().padLeft(2, '0') + ':' + dt.getMinutes().padLeft(2, '0') + ':' + dt.getSeconds().padLeft(2, '0');
				
			}

			return value;
		}
*/
		if (utils.inList(type, ftDate, ftTime, ftDateTime))
			return formatDateTimeValue(value, type)


		value = new String(utils.coalesce(value, ''));

		if ((fields[fieldname] != null) && utils.inSet(ffPassword, fields[fieldname].flags))
		{
			if (browser.isIE)
				return (String.fromCharCode(0x2022, 0x2022, 0x2022, 0x2022, 0x2022, 0x2022, 0x2022, 0x2022, 0x2022, 0x2022));
			else
				return '**********';
		}

		return (value);
	}

	function getValue(fieldName)
	{
		return values[fieldName];
	}

	function getValues()
	{
		return values.clone();
	} 

	function getOriginalValue(fieldName)
	{
		return orgValues[fieldName];
	}

	function getState()
	{
		return state;
	}

	function getOriginalState()
	{
		return orgState;
	}

	function isDirty(fieldName)
	{
		if (fieldName != null)
		{
			if ((self.dataset.fields[fieldName] != null) && (!self.dataset.fields[fieldName].hasFlag(ffNoRecStatusChange)) && (values[fieldName] != orgValues[fieldName]))
				return true;

			return false;
		}

		for (var fldName in self.dataset.fields)
		{
			if (Array.prototype[fldName] != null)
				continue;

			if ((!self.dataset.fields[fldName].hasFlag(ffNoRecStatusChange)) && (values[fldName] != orgValues[fldName]))
				return true;
		}

		return false;
	}

	function restore(fieldName)
	{
		var ds = self.dataset;

		if (fieldName == null)
		{
			ds.fields.forIn( function (item, fldName) 
			{
				setValue(fldName, orgValues[fldName]);
			}, self);

			state = orgState;
			if ((state == rsModified) && (!ds.isUpdating()))
			{
				ds.onrecordchanged.raise(self, ds.records.indexOf(self));

				if (ds.getCurrentRecord() === self)
					ds.oncurrentrecordchanged.raise(self);
			}
		}
		else
		{
			var value = getValue(fieldName);
			setValue(fieldName, orgValues[fieldName]);

			checkModified();
			if ((state == rsModified) && (!ds.isUpdating()))
			{
				ds.onrecordchanged.raise(self, ds.records.indexOf(self), fieldName, value, getValue(fieldName));

				if (ds.getCurrentRecord() === self)
					ds.oncurrentrecordchanged.raise(self, fieldName, value, getValue(fieldName));
			}
		}
	}

	function setState(newState)
	{
		if ((state == rsAdded) && (newState == rsModified))
			return;
		state = newState;
	}
	
	function setNewValue(fieldName, value){
		if ((self.isNoData(fieldName)) || (self.isReadOnly(fieldName)))
			return value;
		
		var ds = self.dataset;
		if (ds.fields[fieldName] != null)
		{
			var type = ds.fields[fieldName].type;

			if (utils.inList(type, ftInteger, ftFloat) && (typeof(value) == 'string'))
				value = value.replace(",", ".");
			else
			if (utils.inList(type, ftDate, ftTime, ftDateTime))
				value = formatDateTimeValue(value, type, 'mm.dd.yyyy', scDateSep);

			values[fieldName] = value;
			orgValues[fieldName] = value;
		}
	}

	function setValue(fieldName, value)
	{
		if ((self.isNoData(fieldName)) || (self.isReadOnly(fieldName)))
			return value;
		
		var ds = self.dataset;
		if (ds.fields[fieldName] != null)
		{
			if (values[fieldName] != value)
			{
				var orgValue = values[fieldName]; 
				var type = ds.fields[fieldName].type;

				if (utils.inList(type, ftInteger, ftFloat) && (typeof(value) == 'string'))
					value = value.replace(",", ".");
				else
				if (utils.inList(type, ftDate, ftTime, ftDateTime))
					value = formatDateTimeValue(value, type, 'mm.dd.yyyy', scDateSep);

				values[fieldName] = value;
				if (!ds.fields[fieldName].hasFlag(ffNoRecStatusChange))
				{
					setState(rsModified);
					checkModified();
				}

				if (!ds.isUpdating())
				{
					ds.onrecordchanged.raise(self, ds.records.indexOf(self), fieldName, orgValue, value);

					if (ds.getCurrentRecord() === self)
						ds.oncurrentrecordchanged.raise(self, fieldName, orgValue, value);
				}

				return orgValue;
			}
			return value;
		}
	}

	function setValues(valueArray)
	{
		if (valueArray == null)
			return;

		if (valueArray.constructor == JRecord)
		{
			self.key = valueArray.key;  
			valueArray = valueArray.getValues();
		}

		values = [];
		var changed = false;

		self.dataset.fields.forIn(function (item, name)
		{
			if (values[name] != valueArray[name])
			{
				values[name] = valueArray[name];
				changed = true;
			}
		}, self);

		if (state == rsUnchanged)
			state = rsModified;
		checkModified();
		if ((changed) && (!self.dataset.isUpdating()))
		{
			self.dataset.onrecordchanged.raise(self, self.dataset.records.indexOf(self));

		    if (self.dataset.getCurrentRecord() === self)
		      self.dataset.oncurrentrecordchanged.raise(self);
		}
	}

	function toString(separator)
	{
		if (separator == null)
			separator = scSafeSep;

		var query = [];

		self.dataset.fields.forIn(function (item, name)
		{
			if (item.hasFlag(ffLocalField))
				return;

			if ((self.isNoData(name)) || (self.isReadOnly(name)))
				return;

			var value = values[name];
			if (value == null)
				value = '';

			query.push(name + scEquals + value);
		}, self);

		return escape(query.join(separator));
	}

	function isNoData(field)
	{
		if (field == '__Tree')
			return false; 

		return ((self.rightsFlags[field]) && (self.rightsFlags[field] == rffNone));
	}

	function isReadOnly(field)
	{
		if (field == '__Tree')
			return false; 

		return ((utils.inSet(rfReadOnly, self.flags)) || 
				((field) && (self.dataset.fields[field] != null) &&
						((utils.inSet(ffReadOnly, self.dataset.fields[field].flags)) ||
						 (utils.inSet(ffReadOnly, self.fieldFlags[field])) ||
						 ((self.rightsFlags[field]) && ((self.rightsFlags[field] != rffFull))))));
	}

	function isRequired(field)
	{
		if (field == '__Tree')
			return false; 

		return ((field) && (self.dataset.fields[field] != null) &&
						((utils.inSet(ffRequired, self.dataset.fields[field].flags)) || 
						 (utils.inSet(ffRequired, self.fieldFlags[field]))));
	}

	function isSelect(field)
	{
		if (field == '__Tree')
			return false; 

		return ((field) && (utils.inSet(ffSelect, self.fieldFlags[field])));
	}
	
	function clone()
	{
		var newRec = new JRecord(self.dataset, state, self.key, null, self.flags, self.tvFlags, self.tvLevel, self.tvIndex, rFlags, fFlags);
		newRec._cloneSetValues(values, orgValues);

		return newRec;
	}

	function _cloneSetValues(vals, orgVals)
	{
		values = vals.clone();
		orgValues = orgVals.clone();
	}

	function commit(newState)
	{
		self.dataset.fields.forIn(function (item, name)
		{
			orgValues[name] = values[name];
		}, self);

		if (newState != null)
		{
			orgState = newState;
			state = newState;
		}
	}

	function dispose()
	{
		if (_disposed)
			return;

		values    = null;
		orgValues = null;
		self.dataset = null;

		_disposed = true;
	}
}


function JDataset(owner, parent, name, key, sort, flags, masterDS, masterFields, dsid, dspf)
{
	// private properties
	var self  = this,
		_disposed = false,
		_recno = null,
		_recCount = 0,
		_updating = [],
		_defBindingManager,
		_defRecSelector,
		_masterDS = masterDS,
		_masterFields = masterFields,
		_dsid = dsid,
		_dspf = dspf,
		_lastAction = null;

	// public properties
	this.owner    = owner;
	this.parent   = parent;
	this.name     = name;
	this.key      = key;
	this.sort     = utils.coalesce(sort, "");
	this.flags    = parseInt(flags);
	this.methods  = (cmNone);
	this.fields   = [];
	this.records  = [];
	this.pageNo   = 0;
	this.pageSize = 0;
	this.bindingManager = null;
	this.recordSelector = null;
	this.dsid = dsid;
	this.dspf = dspf;
	this.isLocal = false;

	// events
	this.onbeforerecordsave = new JEvent();
	this.oninfochanged     = new JEvent();
	this.oncleared         = new JEvent();
	this.oncurrentrecordchanged = new JEvent();
	this.onpositionchanged = new JEvent();
	this.onrecordadded     = new JEvent();
	this.onrecordchanged   = new JEvent();
	this.onrecorddeleted   = new JEvent();

	//methods
	this.addField		= addField;
	this.beginUpdate	= beginUpdate;
	this.clear			= clear;
	this.deleteRecord	= deleteRecord;
	this.endUpdate		= endUpdate;
	this.findByKey		= findByKey;
	this.get			= get;
	this.getDefaultBindingManager = getDefaultBindingManager;
	this.getCurrentRecord	= getCurrentRecord;
	this.getRealRecCount	= getRealRecCount;
	this.getRecCount	= getRecCount;
	this.getRecNo		= getRecNo;
	this.getRecord		= getRecord;
	this.getRowState	= getRowState;
	this.getText		= getText;
	this.gotoRow		= gotoRow;
	this.checkSync		= checkSync;
	this.insertRecord	= insertRecord;
	this.isDirty		= isDirty;
	this.isDisposed		= isDisposed;
	this.isEmpty		= isEmpty;
	this.isEOF			= isEOF;
	this.isMemVars		= isMemVars;
	this.isReadOnly		= isReadOnly;
	this.isRequired		= isRequired;
	this.isInvisible    = isInvisible;
	this.isUpdating		= isUpdating;
	this.loadRecord		= loadRecord;
	this.moveNext		= moveNext;
	this.movePrev		= movePrev;
	this.moveToPage		= moveToPage;
	this.refreshData    = refreshData;
	this.saveAll		= saveAll;
	this.saveRecord		= saveRecord;
	this.set			= set;
	this.setFieldReadOnly	= setFieldReadOnly;
	this.setInfo		= setInfo;
	this.toggleNode		= toggleNode;
	this.dispose		= dispose;
	this.set_flags		= set_flags;

	// initialization
	_defBindingManager = new JBindingManager(this);
	this.bindingManager = _defBindingManager;

	_defRecSelector = new JRecordSelector(this);
	this.recordSelector = _defRecSelector; 

	if (! utils.isEmpty(parent))
	{
		var prnt = this.owner.getObject(parent);

		if (prnt.addDataset)
			prnt.addDataset(this);

		if ((prnt.dataset == null) || ((prnt.dataset != null) && (utils.isEmpty(prnt.dataset.dsid)) && (utils.isEmpty(prnt.dataset.dspf))))
			prnt.dataset = this;
	}

	if (this.name.toLowerCase().indexOf('memvars') > -1)
		this.isLocal = true;


  // implementation
	function addField(field)
	{
		field.index = self.fields.length++;
		self.fields[field.name] = field;
	}

	function clear(noSave)
	{
		if (!utils.parseBool(noSave))
			self.saveAll(true);

		_disposeRecords();
		self.records.length = 0;
		self.pageNo = 0;
		_recno = null;

		self.oncleared.raise(self);
	}

	function deleteRecord(row)
	{
		var rec = (row != null) ? row : _recno;
		if (self.records[rec] == null)
			return;

		var query = new Postback(raOperateDataset);

		query.set("dop", doDelete);
		query.set("obj", self.name);
		query.set("rec", self.records[rec].key);

		self.owner.post(raOperateDataset, query);
	}

	function findByKey(key)
	{
		var recs = self.records;
		var recCount = recs.length;
		for (var i = 0; i < recCount; i++)
		{
			if (recs[i].key == key)
			{
				return i;
			}
		}
		return null;
	}

	function gotoRow(row, ignoreMaster, noEvent)
	{
		if ((_disposed) || (row == _recno) || (self.getRecCount() == 0))
			return;

		if ((row != null) && (row.toString().indexOf(';') > -1))
			row = findByKey(row);

		if (row == null)
			row = 0;

		var currRec = _recno;
		_recno = Math.max(Math.min(row, self.getRecCount() - 1), 0);
		if (currRec == _recno)
		{
			if (_recno == 0)
			{
				self.moveToPage(-1, true);
				_lastAction = "movePrev";
			}
			else
			if (_recno == self.getRecCount() - 1)
			{
				self.moveToPage(1, true);
				_lastAction = "moveNext";
			}

			return;
		}

		if (noEvent != true)
		{
			self.onpositionchanged.raise(self, currRec, _recno);
			self.oncurrentrecordchanged.raise(self, null, currRec, _recno);
		}

		if ((ignoreMaster != true) && utils.inSet(dfMaster, self.flags))
		{
			var query = new Postback();

			if ((currRec != null) && (self.getRowState(currRec) == rsModified))
			{
				query.set("srec", self.records[currRec].key);
				query.set("sdat", escape(self.records[currRec].toString()));
			}

			query.set("dop", doGotoRow);
			query.set("obj", self.name);
			query.set("rec", self.records[_recno].key);
			query.set("mst", 1);

			self.owner.post(raOperateDataset, query, false, true);
		}
	}
	
	function get(fieldName, row)
	{
		var rec = self.records[row != null ? row : _recno];
		if (rec == null)
			return null;

		if (fieldName == null)
			return rec;

		return rec.getValue(fieldName);
	}

	function getCurrentRecord()
	{
	    if ((_recno == null) || (_recno < 0) || (_recno >= self.records.length))
	        return null;

		return self.records[_recno];
	}

	function getDefaultBindingManager()
	{
		return _defBindingManager;
	}

	function getRealRecCount()
	{
	  return _recCount;
	}

	function getRecCount()
	{
	  return self.records.length;
	}

	function getRecNo()
	{
	  return _recno;
	}

	function getRecord(row)
	{
		var curr = (row != null) ? row : _recno;
		return self.records[curr];
	}

	function getRowState(row)
	{
		var record = getRecord(row);

		if (record == null)
			return rsUnchanged;

		return record.getState();
	}

	function getText(fieldName, row)
	{
		var record = getRecord(row);

		if (record == null)
			return '';

		return record.getText(fieldName);
	}

	function checkSync()
	{
		if (utils.isEmpty(_masterDS))
			return;

		var masterDS = self.owner.getObject(_masterDS);
		if (masterDS.parent == self.parent)
			return;

		var dic, du, mic, mu;

		var splitF = _masterFields.split('=');
		var re = /id/g;
		dic = splitF[0].replace(re, 'ic'); 
		du  = splitF[0].replace(re, 'u');

		if (_masterFields != '')
		{
			mic = splitF[1].replace(re, 'ic'); 
			mu  = splitF[1].replace(re, 'u');
		}
		else
		{
			mic = 'ic';
			mu  = 'u';
		}

		var recNo = _recno;
		var doGetData = false;

		if (recNo >= self.getRecCount())
			doGetData = true;
		else
		{
			while (self.getRecord(recNo).key == scItemSep)
			{
				recNo++;
				if (recNo >= self.getRecCount())
				{
					doGetData = true;
					break;
				}
			}
		}

		if (! doGetData)
		{
			if ((self.get(dic, recNo) != masterDS.get(mic)) || (self.get(du, recNo) != masterDS.get(mu)))
			{
				doGetData = true;
			}
		}

		if (doGetData)
		{
			refreshData(0);
		} 
	}

	function refreshData(page)
	{
		var query = new Postback(raGetData);

		query.set("obj", self.name);
		query.set("dpg", page);

		self.owner.post(raGetData, query);
	}


	function isDirty(includeAdded)
	{
	    if (_disposed)
	        return false;

		var rec = getCurrentRecord();
		if ((rec != null) && ((rec.isDirty()) || ((includeAdded == true) && (rec.getState() == rsAdded))))
			return true;

		for (var i = 0; i < self.records.length; i++)
		{
			rec = self.records[i];
			if ((rec.isDirty()) || ((includeAdded == true) && (rec.getState() == rsAdded)))
				return true;
		}

		return false;
	}

	function isEmpty()
	{
		return self.records.length == 0;
	}
	function isEOF()
	{
		return _recno == self.records.length - 1;
	}

	function isMemVars()
	{
		return (self.name.indexOf('MemVars') > -1);
	}

	function isReadOnly(fieldName, row)
	{
		if (self.fields.length == 0)
			return true;
		else
		if ((self.owner.isPortalVersion()) && (!self.name.startsWith('__')) && (!self.name.endsWith('_local')) && (!self.isLocal))
			return true;
		else
		if (fieldName == '__Tree')
			return false; 
		else
		if (utils.inSet(dfReadOnly, self.flags))
			return true;
		else
		if ((!utils.isEmpty(row)) && (self.records.length > row) && (self.records[row].isReadOnly(fieldName)))
			return true;
		else
		if ((!utils.isEmpty(fieldName)) && (self.fields[fieldName] != null))
			return (utils.inSet(ffReadOnly, self.fields[fieldName].flags));

	  return false;
	}

	function isRequired(fieldName, row)
	{
		var isRequired = false;

		if (fieldName == '__Tree')
			return false; 
		
		if ((!utils.isEmpty(fieldName)) && (self.fields[fieldName] != null))
			isRequired = utils.inSet(ffRequired, self.fields[fieldName].flags);

		if (isRequired)
			return true;

		if ((!utils.isEmpty(row)) && (self.records.length > row) && (self.records[row].isRequired(fieldName)))
			return true;

		var rec = self.getCurrentRecord();
		if (rec)
			return rec.isRequired(fieldName);

		return false;
	}

	function isInvisible(field)
	{
		if (field == '__Tree')
			return false; 

		return ((!utils.isEmpty(field)) && (utils.inSet(ffInvisible, self.fields[field].flags)));
	}
	
	function insertRecord(query)
	{
		if (query == null)
			query = new Postback(raOperateDataset);

		query.set("dop", doInsert);
		query.set("obj", self.name);

		var record = getCurrentRecord();
		if (record != null)
		{
			query.set("rec", record.key);
			query.set("tvIdx", record.tvIndex);
		}

		self.owner.post(raOperateDataset, query);
	}


	function set(fieldName, value, row, whoSet)
	{
		if ((fieldName == null) || (self.fields[fieldName] == null) || (self.records.length == 0))
			return;

		var currRec = row ? row : _recno;
		if (currRec == null)
			return;

		var rec = self.records[currRec];

		if (currRec != null)
			rec.setValue(fieldName, value);
	}

	function loadRecord(reason, keyOrRec, buffer, state, flags, tvFlags, tvLevel, tvIndex, gotoRow, rFlags, fFlags)
	{
		var record;

		if (keyOrRec.constructor == JRecord)
			record = keyOrRec;
		else
			record = new JRecord(self, state, keyOrRec, buffer, flags, tvFlags, tvLevel, tvIndex, rFlags, fFlags);

		var key = record.key;
		var newKey = key;

		var currRec = getCurrentRecord();
		
		// pokud uz mame novy zaznam (napr. vznikly pri otevreni formulare)
		// budeme zaznam pouze editovat, namisto pridani dalsiho
		// toto vsak neplati, pokud je zobrazen strom (koren nema idcka)
		if ((reason == 'add') && (currRec != null) && (currRec.key == ';')
			&& ((tvIndex == undefined) || (tvIndex == "-1")))
		{
			reason = 'modify';
			key = ';';
		}

		switch (reason)
		{
			case "add" :
/*
				var currRec = getCurrentRecord();
				if ((currRec != null) && (currRec.key == ';'))
				{
					loadRecord('remove', currRec)
				}
*/
				var rec = self.findByKey(key);
				if (rec != null)
				{
					self.records[rec].commit(rsUnchanged);
					return;
				}

				self.records.push(record);
				if (!self.isUpdating())
					self.onrecordadded.raise(self, self.records.length - 1, record);

				if (utils.parseBool(gotoRow) == true)
				{
					_recno = null;
					self.gotoRow(self.records.length - 1, true);
				}

				break;
    
			case "insert":
				if (_recno == null)
					_recno = 0;

				self.records.insertAt(_recno, record);

				if (!self.isUpdating())
					self.onrecordadded.raise(self, _recno, record);

				if (utils.parseBool(gotoRow) == true)
				{
					var rec = _recno;
					_recno = null;
					self.gotoRow(rec, true);
				}

				break;

			case "insertLevel":
				var orgRecNo = _recno;

				self.gotoRow(_recno + 1, true, true);

				self.records.insertAt(_recno, record);
				if (!self.isUpdating())
					self.onrecordadded.raise(self, _recno, record);

				if (utils.parseBool(gotoRow) == true)
				{
					var rec = _recno;
					_recno = null;
					self.gotoRow(rec, true);
				}
				else
					_recno = orgRecNo;

				break;

			case "modify" :
				var rec = null;
				if ((key == ";") && (self.records.length > 0) && (self.records[_recno].key == key))
					rec = _recno;
				else
				if ((self.records.length > 0) && (self.updateRecno != null) && (self.records[self.updateRecno].key == ";"))
					rec = self.updateRecno;
				else
				{
					rec = self.findByKey(key);
					if (rec == null)
						rec = self.findByKey(";");
					}

				if ((rec == null) && (self.records.length == 0))
				{
					self.records.push(record);
				}
				else
				{
					if ((rec == null) && (_recno != null))
						rec = _recno;

					self.records[rec].setValues(record);
					if (record.getState() == rsUnchanged)
						self.records[rec].commit(rsUnchanged);
					else
						self.records[rec].setState(record.getState());

					if (!self.isUpdating())
					{
						self.onrecordchanged.raise(self, rec);
						if (rec == _recno)
							self.oncurrentrecordchanged.raise(self);
					}
				}
				break;
    
			case "remove" :
				var row = self.findByKey(key);
				if (row != null)
				{
					var rec = self.records[row];

					self.records.removeAt(row);

					if (!self.isUpdating())
					{
						self.onrecorddeleted.raise(self, row, rec);
						if (row == _recno)
							self.oncurrentrecordchanged.raise(self);
					}

					rec.dispose();

					if (_recno == row)
					{
						if ((_recno == 0) && (!self.isEmpty()))
							self.gotoRow(0);
						else
							self.gotoRow(_recno-1);
					}
				}
				break;
		}

		if (self.isEmpty())
			_recno = null;
		else
		if (_recno == null)
			_recno = 0;

	}

	function moveNext()
	{
		self.gotoRow(_recno + 1);
	}

	function movePrev()
	{
		self.gotoRow(_recno - 1);
	}

	function moveToPage(page, relative)
	{
		_lastAction = null;
		var pg = parseInt(self.pageNo);
		var pageCount = Math.floor(self.getRealRecCount() / self.pageSize);

		if (relative)
		{
			if ((pg + page < 0) || (pg + page > pageCount))
				return;

			page = pg + page;
		}
		else
		{
			if (page < 0)
				return;
		}

		if (isNaN(pageCount))
			return;

		if (pg != null) 
		{
			self.refreshData(page);
		}
	}

	function saveAll(askForSaving)
	{
		if ((_disposed) || (self.isLocal) || (!self.isDirty(true)) || (self.isMemVars()) || (utils.isEmpty(self.owner.session)))
			return;

		var doSave = true;
		if ((askForSaving != null) && (askForSaving == true))
			doSave = self.owner.askForSaving(self);

		if (doSave)
		{
			self.records.forEach( function (record, row)
			{
				self.saveRecord(row);
			}, self);
		}
	}

	function saveRecord(row)
	{
	    if ((_disposed) || (self.records == null))
	        return;

		var curr = (row != null) ? row : _recno;
		var currRec = self.records[curr];

		if ((currRec == null) || (! utils.inList(currRec.getState(), rsModified, rsAdded)))
			return;

		self.onbeforerecordsave.raise(self, row);

		var query = new Postback(raPost);
		query.set('obj', self.name);
		query.set('afm', self.parent);
		query.set('ads', self.name);
		query.set('rec', currRec.key);
		query.set('dat', escape(currRec.toString()));

		self.owner.post(raPost, query);
	}

	function setFieldReadOnly(fieldName, value)
	{
		self.fields[fieldName].flags -= (self.fields[fieldName].flags & ffReadOnly) - value;
	}

	function setInfo(records, size, page)
	{
		_recCount = parseInt(records);
		if (page != null)
			self.pageSize = parseInt(size);
		if (page != null)
			self.pageNo = parseInt(page);

		if (!utils.isEmpty(parent))
		{
			var prnt = this.owner.getObject(parent);
			if (prnt.getForm)
			{
				var form = prnt.getForm();
				if (form != null)
					form.setActiveControl(form.activeControl);
			}
		}

	  self.oninfochanged.raise(self, _recCount, self.pageSize, self.pageNo);
	}

	function beginUpdate()
	{
		_updating.push({recNo: _recno, record: self.getCurrentRecord()});
	}

	function endUpdate()
	{
		if (_updating.length == 0)
			return;

		var rec = _updating.pop();

		if (_updating.length == 0)
		{
			if (_lastAction != null)
			{
				if (_lastAction == "movePrev")
					self.gotoRow(getRecCount() - 1, false, true);

				_lastAction = null;
			}

			self.onrecordchanged.raise(self);
			if ((rec.recNo != _recno) ||  !(self.records[rec.recNo] === rec.record))
				self.oncurrentrecordchanged.raise(self);
		}
	}

	function isUpdating()
	{
		return (_updating.length > 0);
	}

	function toggleNode(row)
	{
		var _row = (row != null) ? row : _recno;
		var record = self.records[_row];

		if (record == null)
			return;

		var query = new Postback();

		if (getRowState(_row) == rsModified)
		{
			query.set("srec", record.key);
			query.set("sdat", escape(record.toString()));
		}

		query.set("dop", doToggleNode);
		query.set("obj", self.name);
		query.set("rec", record.key);
		query.set("tvIdx", record.tvIndex);

		self.owner.post(raOperateDataset, query);
	}

	function set_flags(flag)
	{
	        self.flags = self.flags | parseInt(flag);
	}

	function _disposeRecords()
	{
  		self.records.forEach( function (item, i)
  		{
  			if (item == null)
  				return;

  			item.dispose();
  			self.records[i] = null;
  		}, self);
	}

	function isDisposed()
	{
		return _disposed;
	}

	function dispose()
	{
		if (_disposed)
			return;

		self.onbeforerecordsave.dispose();
		self.oninfochanged.dispose();
		self.oncleared.dispose();
		self.oncurrentrecordchanged.dispose();
		self.onpositionchanged.dispose();
		self.onrecordadded.dispose();
		self.onrecordchanged.dispose();
		self.onrecorddeleted.dispose();
		_defBindingManager.dispose();
		_defRecSelector.dispose();

        self.onbeforerecordsave = null;
		self.oninfochanged = null;
		self.oncleared = null;
		self.oncurrentrecordchanged = null;
		self.onpositionchanged = null;
		self.onrecordadded = null;
		self.onrecordchanged = null;
		self.onrecorddeleted = null;
		

		_defBindingManager = null;
		self.bindingManager = null;
		_defRecSelector = null
		self.recordSelector = null;

		self.fields = null;
		_disposeRecords();

  		self.records = null;
  		self.owner = null;
  		self.parent = null;

		_disposed = true;		
	}

}

if (typeof(loadNextScript) != 'undefined')
	loadNextScript();
