

$(document).ready(function()
{
	SedForm.initialize();
});

function getFormDataAsString(){
	return SedForm.readData();
}

function getFormDataFull(){
	return SedForm.readDataFull();
}

function getFormName(){
	var sFormName = $(SedForm.getRootHtmlElem()).attr("sedName");
	return sFormName;
}

function setFormData(sJsonSed){
	SedForm.writeData(Utils.escapeNewLineChars(sJsonSed), true);
	return "OK";
}

function validateData(){
	var sErrorMsg = SedForm.validate();
	return sErrorMsg;
}


//////////////////////////////////////////////////////////
//SedForm
//////////////////////////////////////////////////////////

function SedForm(){
	// we need this solely for eclipse outline to put everything under Test, 
	// otherwise we would have two items in the outline
	this._dummy = null;
}

SedForm.getRootHtmlElem = function()
{
	var rootHtmlElemSel = $("[met_sed_root]");
	
	if (rootHtmlElemSel.length == 0)
		Utils.assertFailed("Cannot find the SED form's root element (containing attribute 'met_sed_root'). This should be part of the form-template");
	
	return rootHtmlElemSel[0];
}

SedForm.initialize = function()
{
	var rootItem = Item.getGlobal();
	var initializer = new VisitorInitializer();
	rootItem.navigateTree(initializer);
	
	var rootHtmlElem = rootItem.getHtmlElem();
	
	$(rootHtmlElem).find('button[met_add_section]').click(SedForm.onClickAdd);
	$(rootHtmlElem).find('button[met_remove_section]').click(SedForm.onClickRemove);
	$(rootHtmlElem).find(".datepicker").datepicker({ dateFormat: "yy-mm-dd" });
	// does not really work. Also these are specific to the template they will need to be placed there
	// rather than here
	$(rootHtmlElem).find(".form-control datepicker").datepicker({ dateFormat: "yy-mm-dd" });
}

SedForm.readData = function()
{
	var rootItem = Item.getGlobal();
	var reader = new VisitorReadData();
	rootItem.navigateTree(reader);
	
	var serializer = new VisitorSerializeAsJson(false);
	var s = serializer.serialize(rootItem, true);
	return s;
}

SedForm.readDataFull = function()
{
	var rootItem = Item.getGlobal();
	var reader = new VisitorReadData();
	rootItem.navigateTree(reader);
	
	var serializer = new VisitorSerializeAsJson(true);
	var s = serializer.serialize(rootItem);
	return s;
}

SedForm.writeData = function(sJsonSed, bClearFirst){
	if (bClearFirst)
		SedForm.clearData();
	SedJson2Form.writeJsonString(sJsonSed);
}

SedForm.clearData = function(){
	var rootItem = Item.getGlobal();
	rootItem.navigateTree(new VisitorClearData());
}

/**
 * reads data from the HTML inputs into the item-tree and then validates it.
 * Currently it is limited to validating empty data
 * @returns null if there are no validation errors, an string error message otherwise
 */
SedForm.validate = function()
{
	var rootItem = Item.getGlobal();
	// we first read the data from the form in the item-tree
	var reader = new VisitorReadData();
	rootItem.navigateTree(reader);
	
	// the second part is the actual validation, benefiting 
	// from data read from HTML into the item-tree
	var validator = new VisitorValidateData();
	var sErrorMsg = validator.validate(rootItem, true);
		
	// we validated; now let's act on the results
	var offendingItem = validator.getOffendingItem();
	if (offendingItem != null){
		var htmlInput = SedUtils.getHtmlInput1(offendingItem);
		htmlInput.focus();		
	}
	return sErrorMsg;
}


SedForm.onClickAdd = function(e)
{
	e.preventDefault();
	// get the original sections (that belong to parent)
	var sRepeatableItemName = $(this).attr('met_add_section');
	var repeatableItemHtmlElem = $(this).parents('[met_section="' + sRepeatableItemName + '"]')[0];
	var frame = Item.getGlobal().findItem(new PredFindFrameByName(sRepeatableItemName));
	frame.cloneAndInsertAfter(repeatableItemHtmlElem);
}

SedForm.onClickRemove = function(e)
{
	e.preventDefault();
	var sRepeatableItemName = $(this).attr('met_remove_section');
	var repeatableItemHtmlElem = $(this).parents('[met_section="' + sRepeatableItemName + '"]')[0];
	var frame = Item.getGlobal().findItem(new PredFindFrameByName(sRepeatableItemName));
	frame.removeRepeatableItem(repeatableItemHtmlElem);
}

SedForm.itemTreeToString = function()
{
	var rootItem = Item.getGlobal();
	var sItemTree = rootItem.toString();
	return sItemTree;
}






//////////////////////////////////////////////////////////
// Item
//////////////////////////////////////////////////////////
// ---------------------------------
// hierarchy root - it models an abstract form-item that is read from 
// the HTML DOM. Those form-items construct a pseudo-DOM tree that is 
// fit for the typical SED-form operations (read/write data, validation, 
// hiding, showing sections, adding repetitions, etc 
//////////////////////////////////////////////////////////

function Item(sFullName, htmlElem)
{
	// used to uniquely identify the items
	this.m_nSequence = SequenceGen.getNextSequence();

	this.m_sFullName = sFullName;
	
	this.m_sName = SedUtils.lastPathName(sFullName);
	
	this.m_htmlElem = htmlElem;
	
	this.m_bHasData = false;
	
	this.accept = function(itemVisitor){
		return itemVisitor.visitItem(this);
	}
	
	this.endAccept = function(itemVisitor){
		return itemVisitor.endVisitItem(this);
	}
	
	this.getName = function(){
		return this.m_sName;
	}
	
	this.getFullName = function(){
		return this.m_sFullName;
	}
	
	this.getItems = function(){
		return null;
	}
	
	this.hasData = function(){
		return this.m_bHasData;
	}
	
	this.setHasData = function(bHasData){
		this.m_bHasData = bHasData;
		if (!bHasData)
			return;
		var parent = this.getParent();
		if (parent != null && !parent.hasData())
			parent.setHasData(true);
	}


	this.getHtmlElem = function(){
		return this.m_htmlElem;
	}
	
	this.navigateTree = function(itemVisitor)
	{
		this.accept(itemVisitor);
		
		var items = this.getItems();
		if (items == null){
			this.endAccept(itemVisitor);
			return;
		}
		itemVisitor.incLevel();
		
		for (var i = 0; i < items.length; ++i){
			var childItem = items[i];
			childItem.navigateTree(itemVisitor);
		}
		itemVisitor.decLevel();
		
		this.endAccept(itemVisitor);
	}
	
	this.findItem = function(itemFinder){
		if (itemFinder.isMatch(this))
			return this;
		
		var itemFound = null;
		var items = this.getItems();
		if (items == null)
			return null;
		for (var i = 0; i < items.length; ++i){
			var childItem = items[i];
			itemFound = childItem.findItem(itemFinder);
			if (itemFound != null)
				return itemFound;
		}
		return null;
	}
	
	this.findItemOrFail = function(itemFinder){
		var item = this.findItem(itemFinder);
		if (item == null)
			Utils.assertFailed("findItem() failed");
		return item;
	}
	
	this.findLocalItem = function(itemFinder){
		var items = this.getItems();
		if (items == null)
			return null;
		for (var i = 0; i < items.length; ++i){
			var childItem = items[i];
			if (itemFinder.isMatch(childItem))
				return childItem;
		}
		return null;
	}
	
	this.findLocalItemOrFail = function(itemFinder){
		var item = this.findLocalItem(itemFinder);
		if (item == null)
			Utils.assertFailed("findLocalItem() failed");
		return item;
	}
	
	
	this.toString = function()
	{
		var itemWriter = new VisitorPrintItem();
		this.navigateTree(itemWriter);
		return itemWriter.m_sText;
	}
	
	this.isMandatory = function(){
		var bMandatory = this.m_htmlElem.getAttribute("met_mandatory") == "true";
		return bMandatory;
	}
}
Item.rootItem = null;

Item.getGlobal = function()
{
	if (Item.rootItem != null)
		return Item.rootItem;
	
	var htmlSedRoot = SedForm.getRootHtmlElem();
	
	Item.rootItem = Item.build(htmlSedRoot, null);
	return Item.rootItem;
}

Item.create = function(itemHtmlElem,  parentItem, bAddToItems)
{
	var sFullName = $(itemHtmlElem).attr("met_sed_root");
	if (sFullName != null)
		return new RootItem(sFullName, itemHtmlElem);
	
	sFullName = $(itemHtmlElem).attr("met_section")
	if (sFullName != null)
		return new RepeatableSection(sFullName, itemHtmlElem, parentItem, bAddToItems);
	
	sFullName = $(itemHtmlElem).attr("met_field");
	if (sFullName != null)
		return new RepeatableField(sFullName, itemHtmlElem, parentItem, bAddToItems);
	
	Utils.assertFailed("The item used as input should be one of the following: met_sed_root, met_section, met_field");
	return null;
}

Item.build = function(itemHtmlElem, parentItem, bAddToItems)
{
	var item = Item.create(itemHtmlElem, parentItem, bAddToItems);	
	item.buildContent();
	return item;
}

//////////////////////////////////////////////////////////
//ContentItem
//---------------------------------
//A content item represents a form-item with the following characteristics:
//	- could sit exclusively on RepeatableSection
//	- its siblings are also ContentItems but they have distinct names 
// (note that this is contrast with RepeatableItems who are form-items that 
// could sit on Frames in list where all siblings share the same name)
//////////////////////////////////////////////////////////

function ContentItem(sFullName, htmlElem, parentRepeatableSection)
{
	Item.call(this, sFullName, htmlElem);
	
	this.m_parentRepeatableSection = parentRepeatableSection;
	
	this.m_nContentIndex = parentRepeatableSection.m_contentItems.length;
	
	parentRepeatableSection.m_contentItems.push(this);
	
	this.accept = function(itemVisitor){
		return itemVisitor.visitContentItem(this);
	}
	
	this.endAccept = function(itemVisitor){
		return itemVisitor.endVisitContentItem(this);
	}

	
	this.getItems = function(){
		return this.m_contentItems;
	}
	
	this.getContentIndex = function(){
		return this.m_nContentIndex;
	}

	this.getParentRepeatableSection = function(){
		return this.m_parentRepeatableSection;
	}
	
	this.getParent = function(){
		return this.m_parentRepeatableSection;
	}
	
	this.show = function(bShow)
	{
		this.setVisible(bShow);
		var htmlFrame = this.getHtmlElem();
	    if (bShow) 
	    	$(htmlFrame).show();
	    else 
	    	$(htmlFrame).hide();
	}
	
	this.isLastItem = function(){
		// root case
		if (this.m_parentRepeatableSection == null)
			return true;
		
		if (this.m_parentRepeatableSection.m_contentItems.length == this.m_nContentIndex + 1)
			return true;
		
		return false;
	}
}
ContentItem.prototype = Object.create(Item.prototype);
ContentItem.prototype.constructor = ContentItem;

ContentItem.createContentItem = function(htmlElem, parentRepeatableSection)
{
	var sFullName = $(htmlElem).attr("met_section_frame");
	if (sFullName != null)
		return new SectionFrame(sFullName, htmlElem, parentRepeatableSection);
	
	sFullName = $(htmlElem).attr("met_field_frame");
	if (sFullName != null)
		return new FieldFrame(sFullName, htmlElem, parentRepeatableSection);
	
	sFullName = $(htmlElem).attr("met_choice_option");
	if (sFullName != null)
		return new Choice(sFullName, htmlElem, parentRepeatableSection);
	
	return null;
}




//////////////////////////////////////////////////////////
//Choice
//--------------------------------------------------------
// When adjacent Frames (FieldFrame or SectionFrame) are marked
// as as a XOR group, the entire group of frames is prefixed with
// radio-buttons. This class models one such radio button.
//////////////////////////////////////////////////////////

function Choice(sFullName, htmlElem, parentRepeatableSection)
{
	ContentItem.call(this, sFullName, htmlElem, parentRepeatableSection);

	this.m_nChoiceIndex = 0;
	
	this.m_bLastChoiceInGroup = true;
	
	var contentItems = parentRepeatableSection.m_contentItems;
	
	if (contentItems.length > 1){
		var prevContentItemSibling = contentItems[contentItems.length - 2];
		if (Object.getPrototypeOf(prevContentItemSibling).constructor == Choice){
			this.m_nChoiceIndex = prevContentItemSibling.m_nChoiceIndex + 1;
			prevContentItemSibling.m_bLastChoiceInGroup = false;
		}
	}
		
	this.accept = function(itemVisitor){
		return itemVisitor.visitChoice(this);
	}
	
	this.endAccept = function(itemVisitor){
		return itemVisitor.endVisitChoice(this);
	}

	this.buildContent = function(){
		// deliberately empty
	}
	
	this.select = function(bSelected){
		var choiceHtmlElem = SedUtils.childHtmlElemOrFail1(this, "[met_choice_option_input]");
		choiceHtmlElem.checked = bSelected;
		
		var nContentIndex = this.getContentIndexOfLastChoice() + 1 + this.m_nChoiceIndex;
		var parentSectionItem = this.getParentRepeatableSection();
		var contentItems = parentSectionItem.getItems();
		var contentItem = contentItems[nContentIndex];
		contentItem.show(bSelected);
	}

	this.getContentIndexOfLastChoice = function(){
		var parentSectionItem = this.getParentRepeatableSection();
		var siblingContentItems = parentSectionItem.getItems();
		for (var i = this.m_nContentIndex + 1; i < siblingContentItems.length; ++i){
			var choiceCandidate = siblingContentItems[i];
			if (Object.getPrototypeOf(choiceCandidate).constructor != Choice)
				return i - 1;
		}
		Utils.assertFailed("Cannot find any content item subsequent to choices group");
	}
}
Choice.prototype = Object.create(ContentItem.prototype);
Choice.prototype.constructor = Choice;

Choice.onClick = function()
{
	var htmlElem = SedUtils.parentHtmlElemOrFail2(this, "[met_choice_option]");
	
	var rootItem = Item.getGlobal();
	var choice = rootItem.findItem(new PredFindByHtmlElem(htmlElem));
	var nChoiceIndex = choice.getContentIndex();
	var contentItems = choice.getParentRepeatableSection().getItems();
	
	for (var i = nChoiceIndex - 1; i >= 0; --i){
		var prevChoiceCandidate = contentItems[i];
		if (!(prevChoiceCandidate instanceof Choice))
			break;
		prevChoiceCandidate.select(false);
	}
	choice.select(true);
	for (i = nChoiceIndex + 1; i < contentItems.length; ++i){
		var nextChoice = contentItems[i];
		if (!(nextChoice instanceof Choice))
			break;
		nextChoice.select(false);
	}
}

//////////////////////////////////////////////////////////
//Frame
//--------------------------------------------------------
//Frames are form items that act as place-holders for repeatable items
//Thus a SectionFrame will hold a list of RepeatableSection and 
//the FieldFrame of RepeatableFields. A frame's name coincides with the 
//inner RepeatableItem-s name (one name for the container and the inner items)
//The frame has block operations such as show and hide 
//////////////////////////////////////////////////////////

function Frame(sFullName, htmlElem, parentRepeatableSection)
{
	ContentItem.call(this, sFullName, htmlElem, parentRepeatableSection);
	
	this.m_bRepetition = false;

	// visibility is relevant to section-frames paired with Choices (when for 
	// example the pairing choice is not selected, and the entire section is hidden)
	// All frames keep this attribute. When a a section-frame changes this visibility
	// all itc children frames also change (see setVisible(bVisible) )
	this.m_bVisible = true;
	
	this.m_repeatableItems = [];
	
	this.accept = function(itemVisitor){
		return itemVisitor.visitFrame(this);
	}
	
	this.endAccept = function(itemVisitor){
		return itemVisitor.endVisitFrame(this);
	}
		
	this.getItems = function(){
		return this.m_repeatableItems;
	}
	
	this.isVisible = function(){
		return this.m_bVisible;
	}
	
	this.setVisible = function(bVisible){
		this.m_bVisible = bVisible;

		// propagating recursively the bVisible value; we need that because in 
		// fact when writing JSON data in the form, it is when a non-empty data
		// is written to an "invisible" field, that we trigger the switch to "visible"
		// And we do that by going up on the parent-chain >> identifying the section 
		// with options >> identifying the pairing choice, that through its 
		// "un-selected" status is the cause of invisibility. 
		var itemSetVisiblility = new VisitorSetVisible(bVisible);
		this.navigateTree(itemSetVisiblility);
	}
	
	this.buildContent = function(){
		Utils.assertFailed("abstract call");
	}

	this.cloneAndInsertAfter = function(repeatableItemHtmlElem)
	{
		var newRepeatableItemHtmlElem = $(repeatableItemHtmlElem).clone(true)[0];
	    var newItem = Item.build(newRepeatableItemHtmlElem, this, false);
	    newItem.navigateTree(new VisitorClearData());
	    
	    var insertAfterItem = this.findLocalItemOrFail(new PredFindByHtmlElem(repeatableItemHtmlElem));
	    var nInsertPos = insertAfterItem.getIndex();
	    
	    this.m_repeatableItems.splice(nInsertPos + 1, 0, newItem);
	    for (var i = nInsertPos + 1; i < this.m_repeatableItems.length; ++i){
	    	this.m_repeatableItems[i].setIndex(i);
	    }
	    
	    $(newRepeatableItemHtmlElem).find(".datepicker").each(function(index){
	        $(this).removeAttr("id");
	        $(this).datepicker("destroy");
	        $(this).datepicker({dateFormat:"yy-mm-dd"});
	    });
	    $(newRepeatableItemHtmlElem).insertAfter(repeatableItemHtmlElem);
		return newItem;
	}
	
	this.removeRepeatableItem = function(repeatableItemHtmlElem)
	{
		if (this.m_repeatableItems.length <= 1)
			return;
		var removableItem = this.findLocalItemOrFail(new PredFindByHtmlElem(repeatableItemHtmlElem));
		$(repeatableItemHtmlElem).remove();
		var nRemoveIndex = removableItem.getIndex();
		this.m_repeatableItems.splice(nRemoveIndex, 1);
		for (var i = nRemoveIndex; i < this.m_repeatableItems.length; ++i)
			this.m_repeatableItems[i].setIndex(i);
	}
	
}
Frame.prototype = Object.create(ContentItem.prototype);
Frame.prototype.constructor = Frame;


//////////////////////////////////////////////////////////
//SectionFrame
//////////////////////////////////////////////////////////


function SectionFrame(sFullName, htmlElem, parentRepeatableSection)
{
	Frame.call(this, sFullName, htmlElem, parentRepeatableSection)
	
	this.m_bRepetition = false;
	
	this.accept = function(itemVisitor){
		return itemVisitor.visitSectionFrame(this);
	}
	
	this.endAccept = function(itemVisitor){
		return itemVisitor.endVisitSectionFrame(this);
	}
	
	this.addRepeatableSection = function(repeatableSection){
		this.m_sectionItems.push(repeatableSection);
		repeatableSection.setIndex(this.m_sectionItems.length - 1);
	}
	
	this.buildContent = function(){
		var htmlElem = this.getHtmlElem();
		this.m_bRepetition = $(htmlElem).attr("met_repetition") == "true";
		this._buildContent(htmlElem);
	}
	
	this._buildContent = function(htmlElem){
		var children = $(htmlElem).children();
		for (var i = 0; i < children.length; ++i){
			var child = children[i];
			var repeatableSection = RepeatableSection.create(child, this, true);
			if (repeatableSection == null){
				this._buildContent(child);
				continue;
			}
			repeatableSection.buildContent();
		}
	}
}
SectionFrame.prototype = Object.create(Frame.prototype);
SectionFrame.prototype.constructor = SectionFrame;


//////////////////////////////////////////////////////////
// FieldFrame
//////////////////////////////////////////////////////////

function FieldFrame(sFullName, htmlElem, parentRepeatableSection)
{
	Frame.call(this, sFullName, htmlElem, parentRepeatableSection);
		
	this.m_bRepetition = false;
	
	this.accept = function(itemVisitor){
		return itemVisitor.visitFieldFrame(this);
	}

	this.endAccept = function(itemVisitor){
		return itemVisitor.endVisitFieldFrame(this);
	}
	
	this.buildContent = function(){
		var htmlElem = this.getHtmlElem();
		this.m_bRepetition = $(htmlElem).attr("met_repetition") == "true";
		this._buildContent(htmlElem);
	}
	
	
	this._buildContent = function(htmlElem){
		var children = $(htmlElem).children();
		for (var i = 0; i < children.length; ++i){
			var child = children[i];
			var repeatableField = RepeatableField.create(child, this, true);
			if (repeatableField == null){
				this._buildContent(child);
				continue;
			}
		}
	}
	
}
FieldFrame.prototype = Object.create(Frame.prototype);
FieldFrame.prototype.constructor = FieldFrame;


//////////////////////////////////////////////////////////
//RepeatableItem
//////////////////////////////////////////////////////////

function RepeatableItem(fullName, htmlElem, parentFrame, bAddToItems)
{
	Item.call(this, fullName, htmlElem);
	
	this.m_parentFrame = parentFrame;
	
	this.m_nIndex = -1;
	
	if (parentFrame != null && bAddToItems == undefined)
		Utils.assertFailed("bAddToItems parameter not specified");
	
	// there is the situation of the RootItem which is a RepeatableItem defined without a parentFrame
	if (parentFrame != null && bAddToItems){
		this.m_nIndex = parentFrame.m_repeatableItems.length;
		parentFrame.m_repeatableItems.push(this);
	}
	
	this.accept = function(itemVisitor){
		return itemVisitor.visitRepeatableItem(this);
	}
	
	this.endAccept = function(itemVisitor){
		return itemVisitor.endVisitRepeatableItem(this);
	}
	
	this.getParentFrame = function(){
		 return this.m_parentFrame;
	}
	
	this.getParent = function(){
		return this.m_parentFrame;
	}
	
	this.getIndex = function(){
		return this.m_nIndex;
	}
	this.setIndex = function(nIndex){
		this.m_nIndex = nIndex;
	}
	
	this.buildContent = function(){
		Utils.assertFailed("abstract call");
	}
	
	this.isLastItem = function(){
		// check if this is a root and then this is the last item
		if (this.m_parentFrame == null)
			return true;
		if (this.m_parentFrame.m_repeatableItems.length == this.m_nIndex + 1)
			return true;
		return false;
	}
	
	this.isMandatory = function(){
		return this.getParentFrame().isMandatory();
	}
	
	
}
RepeatableItem.prototype = Object.create(Item.prototype);
RepeatableItem.prototype.constructor = RepeatableItem;


//////////////////////////////////////////////////////////
//RepeatableField
//////////////////////////////////////////////////////////

function RepeatableField(sFullName, htmlElem, fieldFrame, bAddToItems)
{
	RepeatableItem.call(this, sFullName, htmlElem, fieldFrame, bAddToItems);
	
	this.m_sValue = null;
	
	this.m_bIsCheckbox = false;
	
	this.m_isEnumCodesMultiple = false;
	
	this.accept = function(itemVisitor){
		return itemVisitor.visitRepeatableField(this);
	}

	this.endAccept = function(itemVisitor){
		return itemVisitor.endVisitRepeatableField(this);
	}
	
	this.setValue = function(sValue){
		this.m_sValue = sValue;
	}
	
	this.getValue = function(){
		return this.m_sValue;
	}
	
	this.setAsCheckbox = function(){
		this.m_bIsCheckbox  = true;
	}
	
	this.setAsEnumCodesMultiple = function(){
		this.m_isEnumCodesMultiple  = true;
	}
	
	this.isCheckbox = function(){
		return this.m_bIsCheckbox;
	}
	
	this.isEnumCodesMultiple = function(){
		return this.m_isEnumCodesMultiple;
	}
	
	this.getFrame = function(){
		return this.m_parentFrame; 
	}
	
	this.buildContent = function(){
		// deliberately empty;
	}
	
	/**
	 * this validates at individual field level. It is limited to checking if it has data
	 * once the data has been pre-read from the HTML inputs into the item-tree
	 */
	this.validate = function(bFieldEffectivelyRequired)
	{
		if (!bFieldEffectivelyRequired)
			return null;
		if (!this.hasData()){
			var sErrorMsg = getName()
		}
			
	}
	
}
RepeatableField.prototype = Object.create(RepeatableItem.prototype);
RepeatableField.prototype.constructor = RepeatableField;

RepeatableField.create = function(htmlElem, fieldFrame, bAddToItems){
	var sFullName = $(htmlElem).attr("met_field");
	if (sFullName != null)
		return new RepeatableField(sFullName, htmlElem, fieldFrame, bAddToItems);
	return null;
}

RepeatableField.onClick = function()
{
	// process met_input_radio
	if(this.getAttribute("met_input_radio") != null){	
		var htmlElem = SedUtils.parentHtmlElemOrFail2(this, "[type='enumCodesSingle']");
		
		if(htmlElem != null){
			var children = $(htmlElem).children("input[met_input_radio]");
			if(children != null)
			for (var i = 0; i < children.length; ++i)
				if(children[i] !== this)
					children[i].checked = false;
		}
	}
}


//////////////////////////////////////////////////////////
//RepeatableSection
//////////////////////////////////////////////////////////

function RepeatableSection(sFullName, htmlElem, sectionFrame, bAddToItems)
{
	RepeatableItem.call(this, sFullName, htmlElem, sectionFrame, bAddToItems);
	
	this.m_contentItems = [];
	
	this.m_bHasChoices = false;
	
	this.accept = function(itemVisitor){
		return itemVisitor.visitRepeatableSection(this);
	}
	
	this.endAccept = function(itemVisitor){
		return itemVisitor.endVisitRepeatableSection(this);
	}
			
	this.getFrame = function(){
		return this.m_parentSectionFrame; 
	}
	
	this.getItems = function(){
		return this.m_contentItems;
	}
	
	this.getContentItems = function(){
		return this.m_contentItems;
	}
	
	this.buildContent = function(){
		var helem = this.getHtmlElem();
		this._buildContent(helem);
	}
	
	this.hasChoices = function(){
		return this.m_bHasChoices;
	}
	
	this._buildContent = function(helem){
		var children = $(helem).children();
		for (var i = 0; i < children.length; ++i)
		{
			var child = children[i];
			var contentItem = ContentItem.createContentItem(child, this);
			if (contentItem instanceof Choice)
				this.m_bHasChoices = true;
				
			if (contentItem != null){
				contentItem.buildContent();
				continue;
			}
			this._buildContent(child);
		}		
	}	
}
RepeatableSection.prototype = Object.create(RepeatableItem.prototype);
RepeatableSection.prototype.constructor = RepeatableSection;

RepeatableSection.create = function(htmlElem, sectionFrame, bAddToItems){
	var sFullName = $(htmlElem).attr("met_section");
	if (sFullName != null)
		return new RepeatableSection(sFullName, htmlElem, sectionFrame, bAddToItems);
	return null;
}


//////////////////////////////////////////////////////////
//RootItem
//////////////////////////////////////////////////////////

function RootItem(sFullName, htmlElem)
{
	RepeatableSection.call(this, sFullName, htmlElem, null, false);
	
	this.accept = function(itemVisitor){
		return itemVisitor.visitRootItem(this);
	}
	
	this.endAccept = function(itemVisitor){
		return itemVisitor.endVisitRootItem(this);
	}
		
	this.getFrame = function(){
		// this.m_parentSectionFrame is null; we make this explicit
		return null; 
	}
	
	this.getIndex = function(){
		return 0;
	}
	
	this.isMandatory = function(){
		return true;
	}	
}
RootItem.prototype = Object.create(RepeatableSection.prototype);
RootItem.prototype.constructor = RootItem;


//////////////////////////////////////////////////////////
//ItemVisitor
//////////////////////////////////////////////////////////

function ItemVisitor() 
{
	this.m_nLevel = 0;
	
	this.incLevel = function(){
		return ++this.m_nLevel;
	}
	
	this.decLevel = function(){
		return --this.m_nLevel;
	}
	
	this.getLevel = function(){
		return this.m_nLevel;
	}
	
	this.visitItem = function(item){
		return null;
	}
	
	this.endVisitItem = function(item){
		return null;
	}
	
	this.visitContentItem = function(contentItem){
		return this.visitItem(contentItem);
	}
	
	this.endVisitContentItem = function(contentItem){
		return this.endVisitItem(contentItem);
	}
	
	this.visitChoice = function(choice){
		return this.visitContentItem(choice);
	}
	
	this.endVisitChoice = function(choice){
		return this.endVisitContentItem(choice);
	}
	
	this.visitFrame = function(frame){
		return this.visitContentItem(frame);
	}
	
	this.endVisitFrame = function(frame){
		return this.endVisitContentItem(frame);
	}
	
	this.visitSectionFrame = function(sectionFrame){
		return this.visitFrame(sectionFrame);
	}
	
	this.endVisitSectionFrame = function(sectionFrame){
		return this.endVisitFrame(sectionFrame);
	}

	this.visitFieldFrame = function(fieldFrame){
		return this.visitFrame(fieldFrame);
	}
	
	this.endVisitFieldFrame = function(fieldFrame){
		return this.endVisitFrame(fieldFrame);
	}
	
	this.visitRepeatableItem = function(repeatableItem){
		return this.visitItem(repeatableItem);
	}
	
	this.endVisitRepeatableItem = function(repeatableItem){
		return this.endVisitItem(repeatableItem);
	}
	
	this.visitRepeatableField = function(repeatableField){
		return this.visitRepeatableItem(repeatableField);
	}
	
	this.endVisitRepeatableField = function(repeatableField){
		return this.endVisitRepeatableItem(repeatableField);
	}
	
	this.visitRepeatableSection = function(repeatableSection){
		return this.visitRepeatableItem(repeatableSection);
	}
	
	this.endVisitRepeatableSection = function(repeatableSection){
		return this.endVisitRepeatableItem(repeatableSection);
	}
	
	this.visitRootItem = function(rootItem){
		return this.visitRepeatableSection(rootItem);
	}
	
	this.endVisitRootItem = function(rootItem){
		return this.endVisitRepeatableSection(rootItem);
	}
}

//////////////////////////////////////////////////////////
//VisitorPrintItem
//////////////////////////////////////////////////////////

function VisitorPrintItem()
{
	ItemVisitor.call(this);
	
	this.m_sText = "";
	this.m_sPrefix = "";
	
	this.incLevel = function(){
		this.m_sPrefix += "| ";
	}

	this.decLevel = function(){
		this.m_sPrefix = this.m_sPrefix.substring(0, this.m_sPrefix.length - 2); 
	}
	
	this.visitItem = function(item){
		this.m_sText += Utils.alignString(5, item.m_nSequence + ":");
		this.m_sText += item.isMandatory() ? "M* " : "M  ";
		this.m_sText += item.hasData() ? "D* " : "D  ";
		this.m_sText += "  " + Utils.alignString(25, Utils.getClassName(item));
		
		this.m_sText += this.m_sPrefix;
		
		this.m_sText += Utils.alignString(40, item.getName());
		this.m_sText += SedUtils.getHtmlLabel(item);
		
		this.m_sText += "\r\n";
	}
	
	this.visitRepeatableField = function(item){
		this.m_sText += Utils.alignString(5, item.m_nSequence + ":");
		this.m_sText += item.isMandatory() ? "M* " : "M  ";
		this.m_sText += item.hasData() ? "D* " : "D  ";
		this.m_sText += "  " + Utils.alignString(25, Utils.getClassName(item));
		
		this.m_sText += this.m_sPrefix;
		this.m_sText += item.getName();
		this.m_sText +=	"[" + item.getIndex() + "]";
		
		this.m_sText += "\r\n";
	}
	
	this.visitRepeatableSection = function(item){
		this.m_sText += Utils.alignString(5, item.m_nSequence + ":");		
		this.m_sText += item.isMandatory() ? "M* " : "M  ";
		this.m_sText += item.hasData() ? "D* " : "D  ";
		this.m_sText += "  " + Utils.alignString(25, Utils.getClassName(item));
		
		this.m_sText += this.m_sPrefix;
		this.m_sText += item.getName();
		this.m_sText += "[" + item.getIndex() + "]";
		
		this.m_sText += "\r\n";
	}
	
}
VisitorPrintItem.prototype = Object.create(ItemVisitor.prototype);
VisitorPrintItem.prototype.constructor = VisitorPrintItem;





//////////////////////////////////////////////////////////
//VisitorInitializer
//////////////////////////////////////////////////////////

function VisitorInitializer()
{
	ItemVisitor.call(this);
	
	this.visitChoice = function(choice)
	{
		var bFirst = choice.m_nChoiceIndex == 0;
		choice.select(bFirst);
		var choiceHtmlElem = choice.getHtmlElem();
		$(choiceHtmlElem).click(Choice.onClick);
		$(choiceHtmlElem).children("_input[met_choice_option_input]").click(Choice.onClick);
	}
	
	this.visitRepeatableField = function(repeatableField)
	{
		var repeatableFieldHtmlElem = repeatableField.getHtmlElem();
		var elem = $(repeatableFieldHtmlElem).find("input[met_input_radio]");
		if(elem != null)
			elem.click(RepeatableField.onClick);
	}
}
VisitorInitializer.prototype = Object.create(ItemVisitor.prototype);
VisitorInitializer.prototype.constructor = VisitorInitializer;


//////////////////////////////////////////////////////////
//VisitorReadData
//////////////////////////////////////////////////////////
/**
 * Reads data from the HTML form in the item-tree. Meaning 
 * navigating the item-tree, reaching for each corresponding HTML input 
 * and reading the data in the Item.m_value. It reuses the Item.navigateTree()  
 * algorithm. This process is prerequisite for validation and serialization.
 */
function VisitorReadData()
{
	ItemVisitor.call(this);
	
	this.visitItem = function(item){
		// this is when reading starts for an item, before it goes to its children)
		// at this stage we reset the has-data indicator; which will be set slightly 
		// later, only if it really has data.
		if (item instanceof Choice)
			return;
		item.setHasData(false);
	}
	
	this.visitRepeatableField = function(repeatableField)
	{
		repeatableField.setHasData(false);
		SedDX.doExchage(repeatableField, SedDX.OP_READ);
	}
}
VisitorReadData.prototype = Object.create(ItemVisitor.prototype);
VisitorReadData.prototype.constructor = VisitorReadData;



//////////////////////////////////////////////////////////
//VisitorSerializeAsJson
//////////////////////////////////////////////////////////
/**
* After reading data, this serialize the data as a JSON string
*/


function VisitorSerializeAsJson(bIncludeEmptyAttributes)
{
	ItemVisitor.call(this);
	
	this.m_bIncludeEmptyAttributes = bIncludeEmptyAttributes;
	
	this.m_serializedChildren = null;
	
	this.m_sPrefix = Utils.TAB;
	
	/**
	 * A recursive algorithm that returns the serialized JSON for this "item". 
	 * It does so by navigating the tree of items, and serializing "item"'s children
	 * then it uses a double-dispatch to serialize the "item" itself, who now has available
	 * its serialized children. If the item is a FieldFrame that is invisible, 
	 * it will ignore it in the serialization
	 */
	this.serialize = function(item, bParentEffectivelyRequired)
	{
		// The CHECKBOX_DILLEMA 
		// one sophisticated problem is that of the checkboxes which if unchecked introduce the 
		// dillemma of (1) retrieving the value as "false" or (2) ignoring the value "false". 
		// Strategy (1) means that its non-mandatory parent-section has now data, entailing that 
		// mandatory siblings of the checkbox become "effectively required", which should not be 
		// the case; strategy (2) means that in mandatory parent-section the unchecked mandatory 
		// checkbox looks as if it did not supply the due data. Solving it is a real algorithmic mess.  
		var bEffectivelyRequired = bParentEffectivelyRequired
		if (item instanceof Frame)
			bEffectivelyRequired = item.hasData() || item.isMandatory() && bParentEffectivelyRequired;
		
		// indenting
		if (item instanceof Frame)
			this.m_sPrefix += Utils.TAB;

		var childrenItems = item.getItems();
		var serializedChildren = [];
		if (childrenItems != null && childrenItems.length > 0)
		{
			for (var i = 0; i < childrenItems.length; ++i)
			{
				var childItem = childrenItems[i];
				// ignore invisible frames; data should not be collected from them
				if (childItem instanceof Frame && !childItem.isVisible())
					continue;
				
				// serialize children first and accumulate the results
				var s = this.serialize(childItem, bEffectivelyRequired);
				
				if (childItem.getName() == "marginal145b"){
					var w = 0;
				}
				
				// prevent for retireving a ""false"" from a "data-less" checkbox (a unchecked checkbox) 
				if (childItem instanceof RepeatableField && childItem.isCheckbox() && !childItem.hasData() && !bEffectivelyRequired)
					continue;
					
				if (s != null)
					serializedChildren.push(s);
			}
		}
		// we reuse the member m_serializedChildren; note that its value is now a reziduu of previous recursive calls
		// so it is important that we only assign it now, after returning from recursive calls
		this.m_serializedChildren = serializedChildren;
		var s = item.accept(this);
		
		//indenting back
		if (item instanceof Frame)
			this.m_sPrefix = this.m_sPrefix.substring(0, this.m_sPrefix.length - 1); 
		return s;
	}
	
	
	this.visitRootItem = function(rootItem)
	{
		var sSerialize = "{";
		this.m_sPrefix = Utils.TAB;
		
		var sName = rootItem.getFullName();
		sSerialize += "\r\n" + this.m_sPrefix + "\"" + sName + "\":{";
		
		this.m_sPrefix += Utils.TAB;
		
		var htmlRootElem = SedForm.getRootHtmlElem();
		sSerialize += this._handleMetaAttribute(htmlRootElem, "sedPackage", ",");
		sSerialize += this._handleMetaAttribute(htmlRootElem, "sedGVer", ",");
		sSerialize += this._handleMetaAttribute(htmlRootElem, "sedVer");
		
		var s = this._serializeRepeatableSection(rootItem);
		if (s != null)
			sSerialize += "," + s;
		
		// closing the root-item
		sSerialize += "\r\n" + Utils.TAB + "}";
		
		// closing the original opening bracket
		sSerialize += "\r\n}";
		
		return sSerialize;
	}
	
	this._handleMetaAttribute = function(htmlRootElem, sMetaAttrName, sSeparator){
		var sVal = SedUtils.readMetaAttribute(htmlRootElem, sMetaAttrName, true);
		sVal = "\r\n" + this.m_sPrefix + '"' + sMetaAttrName + '":"' + sVal + '"';
		if (sSeparator != null) 
			sVal += sSeparator;
		return sVal;
	}
	
	this._serializeRepeatableSection = function(section)
	{
		var nChildCount = this.m_serializedChildren.length; 
		if (nChildCount == 0)
			return null;
		var sSerialization = "";
		
		for (var i = 0; i < nChildCount; ++i)
		{
			sSerialization +=  this.m_serializedChildren[i];
			if (i < nChildCount - 1)
				sSerialization += ",";
		}
		if (sSerialization.length == 0)
			return null;
		return sSerialization;
	}
		
	this.visitRepeatableSection = function(section)
	{
		var s = this._serializeRepeatableSection(section);
		if (s == null)
			return null;
		return "{" + s + "\r\n" + this.m_sPrefix + "}";
	}
	
	this.visitRepeatableField = function(field)
	{
		var sValue = field.getValue();
		
		if (sValue != null && sValue.length > 0)
			return "\"" + sValue + "\"";
		
		if (this.m_bIncludeEmptyAttributes)
			return "\"\"";
		
		return null;
	}
		
	this.visitFieldFrame = function(fieldFrame)
	{
		var sSerialization = this._serializeFrameContent(fieldFrame);
		return sSerialization;
	}
		
	this.visitSectionFrame = function(sectionFrame)
	{
		var sSerialization = this._serializeFrameContent(sectionFrame);
		return sSerialization;
	}
	
	this._serializeFrameContent = function(frame)
	{
		var nChildCount = this.m_serializedChildren.length; 
		if (nChildCount == 0)
			return null;
		var sName = frame.getName();
		
		var sSerialization = "\r\n" + this.m_sPrefix + "\"" + sName + "\":";
		
		if (nChildCount > 1)
			sSerialization += "[";
							 
		for (var i = 0; i < nChildCount; ++i)
		{
			sSerialization +=  this.m_serializedChildren[i];
			if (i < nChildCount - 1)
				sSerialization += ",";
		}
		if (nChildCount > 1)
			sSerialization += "]";
		
		return sSerialization;		
	}
	
}
VisitorSerializeAsJson.prototype = Object.create(ItemVisitor.prototype);
VisitorSerializeAsJson.prototype.constructor = VisitorSerializeAsJson;



//////////////////////////////////////////////////////////
//VisitorClearData
//////////////////////////////////////////////////////////
function VisitorClearData()
{
	ItemVisitor.call(this);
	
	this.visitFrame = function(frame){
		frame.setHasData(false);
		this._removeRepetitions(frame);
	}
	
	this.visitItem = function(item){
		item.setHasData(false);
	}
	
	this.visitRepeatableField = function(repeatableField)
	{
		repeatableField.setHasData(false);
		SedDX.doExchage(repeatableField, SedDX.OP_CLEAR);
	}
	
	this._removeRepetitions = function(frame)
	{
		// removing repetitions items (all repeatables except the first one)
		var items = frame.getItems();
		for (var i = 1; i < items.length; ++i){
			var htmlElem = items[i].getHtmlElem();
			$(htmlElem).remove();
		}
		// keeping only the first element
		items.splice(1, items.length - 1);
	}
}
VisitorClearData.prototype = Object.create(ItemVisitor.prototype);
VisitorClearData.prototype.constructor = VisitorClearData;

//////////////////////////////////////////////////////////
//VisitorValidateData
//////////////////////////////////////////////////////////
function VisitorValidateData()
{
	ItemVisitor.call(this);
	
	// if the validation finds an offending field, then it will be placed here
	// the validation currently stops at the first offence 
	this.m_offendingItem = null;
	
	// a private var to pass bEffectivelyRequired param to the accept() calls
	this.m_bEffectivelyRequired = false;
	
	this.getOffendingItem = function(){
		return this.m_offendingItem;
	}
		
	this.validate = function(item, bParentEffectivelyRequired)
	{
		// we are confronted here with a problem resembling the CHECKBOX_DILLEMA
		// a mandatory attribute may not be effectively required because it sits on an empty
		// section which is not mandatory. Yet if the section has some data, then the field
		// becomes effectively required
		var bEffectivelyRequired = bParentEffectivelyRequired
		if (item instanceof Frame)
			bEffectivelyRequired = item.hasData() || item.isMandatory() && bParentEffectivelyRequired;
		

		// HANDLING CHILDREN
		var childrenItems = item.getItems();
		if (childrenItems != null && childrenItems.length > 0)
		{
			for (var i = 0; i < childrenItems.length; ++i)
			{
				var childItem = childrenItems[i];
				
				// ignore invisible frames; data should not be validated for them
				if (childItem instanceof Frame && !childItem.isVisible())
					continue;
				
				var sErrorMsg = this.validate(childItem, bEffectivelyRequired);
				if (sErrorMsg != null)
					return sErrorMsg;
			}
		}
		// "pushing" bEffectivelyRequired parameter given that accept() 
		// has a fix signature, evidently devised once, not aware for this param
		this.m_bEffectivelyRequired = bEffectivelyRequired;
		// making the "accept()" call and returning the result
		var sErrorMsg = item.accept(this);
		return sErrorMsg;
	}
	
	this.visitRepeatableField = function(item)
	{
		// we only validate empty required data
		if (!this.m_bEffectivelyRequired)
			return null;
		// checkboxes are allways neutral to validation
		if (item.isCheckbox())
			return null;
		// if it has data, nothing to validate
		if (item.hasData())
			return null;
		
		// we are here in an validation-offence situation 
		this.m_offendingItem = item;
		var sLabel = SedUtils.getHtmlLabel(item);
		sErrorMsg =  "Field '" + sLabel + "' is required. Please supply a value.";
		
		return sErrorMsg;
	}
	
	/**
	 * this gets called when all fields of the section have been validated 
	 * It can happen that the section has all fields non-mandatory and empty,
	 * thus they cause no validation offence. Yet if the section is required 
	 * this is an offence. Albeit it is a collective offence (section level) 
	 * rather than an individual one (field level)
	 */
	this.visitRepeatableSection = function(item)
	{
		// we only validate empty required data
		if (!this.m_bEffectivelyRequired)
			return null;
		// if it has data, nothing to validate
		if (item.hasData())
			return null;
		
		// we are here in an validation-offence situation; we register the item 
		this.m_offendingItem = item;
		var sLabel = SedUtils.getHtmlLabel(item);
		var sErrorMsg = "Section '" + sLabel + "' is a mandatory section/subsection and thus it requires at least one of its fields being populated";
		return sErrorMsg;
	}
	
}
VisitorValidateData.prototype = Object.create(ItemVisitor.prototype);
VisitorValidateData.prototype.constructor = VisitorValidateData;


function VisitorSetVisible(bVisible)
{
	ItemVisitor.call(this);
	
	this.m_bVisible = bVisible;
	
	this.visitFrame = function(frame){
		frame.m_bVisible = this.m_bVisible;
	}
}
VisitorSetVisible.prototype = Object.create(ItemVisitor.prototype);
VisitorSetVisible.prototype.constructor = VisitorSetVisible;




//////////////////////////////////////////////////////////
//PredFindByHtmlElem
//////////////////////////////////////////////////////////

function PredFindByHtmlElem(htmlElem)
{
	this.isMatch = function(item){
		return htmlElem == item.m_htmlElem;
	}
}

function PredFindFrameByName(sFullName)
{
	this.isMatch = function(item)
	{
		if (!(item instanceof Frame))
			return false;
		if (item.getFullName() !== sFullName)
			return false;
		return true;
	} 
}



//////////////////////////////////////////////////////////
//SedJson2Form
//////////////////////////////////////////////////////////
/**
 * An utility class that encapsulates the complexity populating the form
 * with the data of a SED JSON
 */
function SedJson2Form()
{
	this._dummy = null;
}

SedJson2Form.writeJsonString = function(sJsonSed)
{
	var rootItem = Item.getGlobal();
	rootItem.navigateTree(new VisitorClearData());
	
	var jsonSed = JSON && JSON.parse(sJsonSed) || $.parseJSON(sJsonSed);
	SedJson2Form.writeJsonObject(jsonSed);
}

SedJson2Form.writeJsonObject = function(jsonSed)
{
	var rootItem = Item.getGlobal();
	var i = 0;
	for (var sSedName in jsonSed){
		if (i > 0)
			Utils.assertFailed("Multiple root elements found in JSON string");
		var jsonRootContent = jsonSed[sSedName];
		SedJson2Form._writeJsonObject(rootItem, jsonRootContent);
		++i;
	}
}

SedJson2Form._writeJsonObject = function(item, jsonContent)
{
	for (var sItemName in jsonContent)
	{
		var subJson = jsonContent[sItemName];
		// it can be that this is a field with null value
		if (subJson == null)
			continue;
		
		if (typeof subJson === "string")
			SedJson2Form._writeField(item, sItemName, subJson);
		else if (subJson instanceof Date){
			SedJson2Form._writeField(item, sItemName, subJson);
		}
		else if (typeof subJson === "boolean")
			SedJson2Form._writeField(item, sItemName, subJson);
		else if (typeof subJson === "object")
			SedJson2Form._writeSection(item, sItemName, subJson);
		else
			Utils.assertFailed("unexpected kind of subJson variable = " + typeof subJson);
	}		
}

SedJson2Form._writeField = function(parentItem, sChildItemName, json)
{
	if (json.constructor === Array){
		SedJson2Form._writeFieldArray(parentItem, sChildItemName, json);
		return;
	}
	SedJson2Form._writeFirstField(parentItem, sChildItemName, json);
}


SedJson2Form._writeSection = function(parentItem, sChildItemName, jsonSection)
{
	if (jsonSection.constructor === Array)	{
		SedJson2Form._writeSectionArray(parentItem, sChildItemName, jsonSection);
		return;
	}
	SedJson2Form._writeFirstSection(parentItem, sChildItemName, jsonSection);
}

SedJson2Form._writeFirstField = function(parentItem, sChildItemName, json)
{
	if (json == null || json.length == 0)
		return;
	
	var childRepetableField = SedUtils.getRepeatableChildByNameMakeFrameVisible(parentItem, sChildItemName, 0);
	if (childRepetableField == null)
		return;
	if (!(childRepetableField instanceof RepeatableField))
		Utils.assertFailed("Expecting RepeatableField but got instead " + Utils.getClassName(childRepetableField));
	
	childRepetableField.setValue(json);
	childRepetableField.setHasData(true);
	SedDX.doExchage(childRepetableField, SedDX.OP_WRITE);
}


SedJson2Form._writeFirstSection = function(parentRepeatableSection, sChildItemName, jsonSection)
{
	var childRepeatableSection = SedUtils.getRepeatableChildByName(parentRepeatableSection, sChildItemName, 0);
	if (childRepeatableSection == null)
		return;	
	SedJson2Form._writeSectionContent(childRepeatableSection, jsonSection);
}

SedJson2Form._writeNextSection = function(parentRepeatableSection, sChildItemName, jsonSection)
{
	var childRepeatableSection = SedUtils.getLastRepeatableSiblingByName(parentRepeatableSection, sChildItemName);
	if (childRepeatableSection == null)
		return;
	
	var parentFrame = childRepeatableSection.getParent();
	var htmlLastSection = childRepeatableSection.getHtmlElem();
	var newSection = parentFrame.cloneAndInsertAfter(htmlLastSection);
	
	SedJson2Form._writeSectionContent(newSection, jsonSection);
}

SedJson2Form._writeSectionContent = function(repeatableItem, jsonSection)
{
	for (var sSectionOrFieldName in jsonSection)
	{
		var subJson = jsonSection[sSectionOrFieldName];
		// it can be that this is a field with null value
		if (subJson == null)
			continue;
		
		if (typeof subJson === "string")
			SedJson2Form._writeField(repeatableItem, sSectionOrFieldName, subJson);
		else if (subJson instanceof Date)
			SedJson2Form._writeField(repeatableItem, sSectionOrFieldName, subJson);
		else if (typeof subJson === "boolean")
			SedJson2Form._writeField(repeatableItem, sSectionOrFieldName, subJson);
		else if (typeof subJson === "object")
			SedJson2Form._writeSection(repeatableItem, sSectionOrFieldName, subJson);
		else 
			Utils.assertFailed("unexpected kind of subJson variable = " + typeof subJson);
	}
}

SedJson2Form._writeSectionArray = function(parentRepeatableSection, sSectionName, jsonArray)
{
	var htmlPrevSection = SedJson2Form._writeFirstSection(parentRepeatableSection, sSectionName, jsonArray[0]);
	for (var i = 1; i < jsonArray.length; ++i)
	{
		htmlPrevSection = SedJson2Form._writeNextSection(parentRepeatableSection, sSectionName, jsonArray[i]);
	}
}

SedJson2Form._writeFieldArray = function(parentItem, sChildItemName, jsonArray)
{
	// TODO - NEVER ACTUALLY USED SO FAR
	
//	var childFrame = SedUtils.getChildFrameByName(parentRepeatable, sName);
//	if (childFrame == null)
//		return;
//	
//	var repeatables = childFrame.getItems();
//	if (repeatables == null || repeatables.length != 1)
//		Utils.assertFailed("expecting a list with one repeatable only");
//	
//	var childRepeatableField = repeatables[i];
//	childRepeatableField.setValue(jsonArray[0])
//	SedDX.doExchage(childRepeatableField, SedDX.OP_WRITE);
//	
//	for (var i = 1; i < jsonArray.length; ++i){
//		var newItem = childFrame.cloneAndInsertAfter(htmlElem);
//		htmlElem = newItem.getHtmlElem();
//		SedDX.doExchage(newItem, htmlElem, jsonArray[i]);
//	}
}

//////////////////////////////////////////////////////////
//SedDX
//////////////////////////////////////////////////////////
/**
 * basic HTML form <-> item-tree data exchange routines
 */
function SedDX()
{
	this._dummy = null;
}

SedDX.OP_READ = 1;

SedDX.OP_WRITE = 2;

SedDX.OP_CLEAR = 3;


SedDX.doExchage = function(repeatableField, sedDXOP)
{
	///////////////////////////////
	// verification of paramters

	if (sedDXOP != SedDX.OP_READ && sedDXOP != SedDX.OP_WRITE && sedDXOP != SedDX.OP_CLEAR)
		Utils.assertFailed("Expecting one of OP_WRITE|OP_READ|OP_CLEAR but got a different value instead");
	
	// the case of writing an empty value 
	if (sedDXOP == SedDX.OP_WRITE && !repeatableField.hasData())
		return null;
	
	if (!(repeatableField instanceof RepeatableField))
		Utils.assertFailed("Expecting an instance of RepeatableField but got instead " + Utils.getClassName(repeatableField));
	
	if (!repeatableField.getHtmlElem().hasAttribute("met_field"))
		Utils.assertFailed("Expecting that RepeatableField corresponds to an html-elem that is marked with attribute met_field, but it is not");
	
	var controlHtmlElem = SedUtils.childHtmlElemOrFail1(repeatableField, "[met_input]");
	var sType = controlHtmlElem.type;
	var sName = controlHtmlElem.name;
	// get special type and name when they are outside of the types covered in htmlType
	if (sType == null)
		sType = controlHtmlElem.getAttribute("type");
	if (sName == null)
		sName = controlHtmlElem.getAttribute("name");
	
	// the actual HTML input should have met_input attribute; it requires the "type" attribute
	if (sType == null)
		Utils.assertFailed("Control expecting to have an attribute type, but it does not");
	
	// the HTML input should have the same name as the repeatableField (met_field attribute)
	// This rigidity is a legacy, but there is no longer any real reason behind it 
	if (sedDXOP != SedDX.OP_CLEAR && repeatableField.getFullName() != sName)
		Utils.assertFailed("Expecting name match but got distinct names <" + 
				repeatableField.getFullName() + 
				"> != <" + 
				sName + 
				">");
	
	if (sType == "checkbox"){
		return SedDX._checkbox(repeatableField, controlHtmlElem, sedDXOP);
	}
	if (sType == "date"){
		return SedDX._date(repeatableField, controlHtmlElem, sedDXOP);
	}
	if (sType == "enumCodesMultiple"){
		return SedDX._enumCodesMultiple_checkboxes(repeatableField, controlHtmlElem, sedDXOP);
	}
	if (sType == "select-multiple"){
		return SedDX._enumCodesMultiple_select(repeatableField, controlHtmlElem, sedDXOP);
	}
	if (sType == "enumCodesSingle"){
		return SedDX._enumCodesSingle_radiobuttons(repeatableField, controlHtmlElem, sedDXOP);
	}
	return SedDX._input(repeatableField, controlHtmlElem, sedDXOP);
}

//Version of Single EnumCodes Values with radiobuttons
SedDX._enumCodesSingle_radiobuttons = function(repeatableField, htmlElem, sedDXOP)
{
	if (sedDXOP == SedDX.OP_WRITE){
		var m_value = repeatableField.getValue();
		var children = $(htmlElem).children("input[met_input_radio]");
		
		if(m_value != null)
			for (var i = 0; i < children.length; ++i)
				// if there is a child with the same value -> check it also in the form and break because it should be only one value selected from 
				// the list of radio buttons
				if(children[i].value == m_value){
					children[i].checked = "true";
					break;
				}
	}
	else if (sedDXOP == SedDX.OP_READ){
		var children = $(htmlElem).children("input[met_input_radio]");
		var sValue = "";
		
		if(children != null)
		for (var i = 0; i < children.length; ++i)
			if(children[i].checked) {
				// set value and break because it should be only one value selected from 
				// the list of radio buttons
				sValue = children[i].value;
				break;
			}
		repeatableField.setValue(sValue);
		// if we have a button checked, then we mark the field as having data
		sValue != "" ? repeatableField.setHasData(true) : repeatableField.setHasData(false);
	}
	else {
		var children = $(htmlElem).children("input[met_input_radio]");
		if(children != null)
			for (var i = 0; i < children.length; ++i)
				children[i].checked = false;
	}
		
	return repeatableField;
	
}

// Version of Multiple EnumCodes Values with checkboxes
SedDX._enumCodesMultiple_checkboxes = function(repeatableField, htmlElem, sedDXOP)
{
	// type enumCheckboxes is a special case of repeatableField ... it should have multiple children that are checkboxes 
	if (sedDXOP == SedDX.OP_WRITE){
		// get the individual values of the overall field
		m_values = repeatableField.getValue().split("|");
		var children = $(htmlElem).children("input");
		
		if(m_values != null)
			for (var i = 0; i < m_values.length; ++i)
				for (var j = 0; j < children.length; ++j)
					// if there is a child with the same value as one that is setted in the overall field -> check it also in the form
					if(children[j].value == m_values[i]){
						children[j].checked = "true";
						break;
					}
	}
	else if (sedDXOP == SedDX.OP_READ){
		
		var children = $(htmlElem).children("input");
		var sValue = "";
		
		// complete the overall field value with the values of the checked boxes separated by "|"
		if(children != null)
		for (var i = 0; i < children.length; ++i)
			if(children[i].checked)
				sValue == "" ? sValue = sValue.concat(children[i].value) : sValue = sValue.concat("|",children[i].value);
		repeatableField.setValue(sValue);
		// if we have at least one checkboxes checked, then we mark the field as having data
		sValue != "" ? repeatableField.setHasData(true) : repeatableField.setHasData(false);
		// we mark the repeatableField as being a enumCheckBoxes
		repeatableField.setAsEnumCodesMultiple();
	}
	else {
		var children = $(htmlElem).children("input");
		if(children != null)
			for (var i = 0; i < children.length; ++i)
				children[i].checked = false;
	}
		
	return repeatableField;
}

// Version of Multiple EnumCodes Values with multiselect
SedDX._enumCodesMultiple_select = function(repeatableField, htmlElem, sedDXOP)
{
	// type enumCodesMultiple is a special case of repeatableField ... it should have multiple children that are option values for a multi-choice select 
	if (sedDXOP == SedDX.OP_WRITE){
		// get the individual values of the overall field
		m_values = repeatableField.getValue().split("|");
		var children = $(htmlElem).children("option");
		
		if(m_values != null)
			for (var i = 0; i < m_values.length; ++i)
				for (var j = 0; j < children.length; ++j)
					// if there is a child with the same value as one that is setted in the overall field -> check it also in the form
					if(children[j].value == m_values[i]){
						children[j].selected = "true";
						break;
					}
	}
	else if (sedDXOP == SedDX.OP_READ){
		
		var children = $(htmlElem).children("option");
		var sValue = "";
		
		// complete the overall field value with the values of the checked boxes separated by "|"
		if(children != null)
		for (var i = 0; i < children.length; ++i)
			if(children[i].selected)
				sValue == "" ? sValue = sValue.concat(children[i].value) : sValue = sValue.concat("|",children[i].value);
		repeatableField.setValue(sValue);
		// if we have at least one checkboxes checked, then we mark the field as having data
		sValue != "" ? repeatableField.setHasData(true) : repeatableField.setHasData(false);
		// we mark the repeatableField as being a enumCheckBoxes
		repeatableField.setAsEnumCodesMultiple();
	}
	else {
		var children = $(htmlElem).children("option");
		if(children != null)
			for (var i = 0; i < children.length; ++i)
				children[i].selected = false;
	}
		
	return repeatableField;
}

SedDX._checkbox = function(repeatableField, htmlElem, sedDXOP)
{
	if (sedDXOP == SedDX.OP_WRITE)
	{
		var bChecked = repeatableField.getValue() == "true";
		if (!bChecked)
			bChecked = repeatableField.getValue() == true;
		htmlElem.checked = bChecked;
	}
	else if (sedDXOP == SedDX.OP_READ){
		var sValue = htmlElem.checked ? "true" : "false";
		repeatableField.setValue(sValue);
		// we mark the repeatableField as being a checkbox
		repeatableField.setAsCheckbox();
		// we do not mark as having data unless checkbox is checked
		if (htmlElem.checked)
			repeatableField.setHasData(true);
		
		
	}
	else 
		htmlElem.checked = false;
	return repeatableField;
}



/**
* this method is correlated with the use of lib/dateJSONExtension.js which overrides JSON.parse
* and intercepts _date-strings if formatted as ISO8601 UTC "2015-03-01T20:17:23Z" and convert them to 
* a Date object. In doing so we in fact specify that JSON/XML files should use that format.
* @param repeatableField
* @param htmlControl
* @param sedDXOP
* @returns {___anonymous2397_2404}
*/
SedDX._date = function(repeatableField, htmlElem, sedDXOP)
{
	// WRITING FROM repeatableField >> HTML-input-control
	if (sedDXOP == SedDX.OP_WRITE){
		$(htmlElem).datepicker("setDate", repeatableField.getValue());
	}
	// READING FROM HTML-input-control >> repeatableField
	else if (sedDXOP == SedDX.OP_READ){
		var dValue = $(htmlElem).datepicker("getDate");
		var sValue = dValue != null ? dValue.toISOString() : "";	// obtains a string formatted as "2015-03-01T20:17:23Z";
		repeatableField.setValue(sValue);
		if (!Utils.isEmptyString(sValue))
			repeatableField.setHasData(true);
	 	
	}
	else // (if sedDXOP == DX_CLEAR)
		htmlElem.value = null;
	return repeatableField;
}

SedDX._input = function(repeatableField, htmlElem, sedDXOP)
{
	var w = 0;
	if (sedDXOP == SedDX.OP_WRITE){
		htmlElem.value = repeatableField.getValue();
	}
	else if (sedDXOP == SedDX.OP_READ){
		sValue = Utils.noNull(htmlElem.value);
		// Escape the \n in the JSON
		sValue = Utils.escapeNewLineChars(sValue);
		repeatableField.setValue(sValue);
		if (!Utils.isEmptyString(sValue))
			repeatableField.setHasData(true);
	}
	else{
		htmlElem.value = "";
	}
	return repeatableField;
}

//////////////////////////////////////////////////////////
//SedUtils
//////////////////////////////////////////////////////////

function SedUtils(){
	this._dummy = null;
}

SedUtils.lastPathName = function(sFullName)
{
	for (var i = sFullName.length - 1; i>=0; --i)
	{
		if (sFullName.charAt(i) == '/')
			return sFullName.substr(i + 1, sFullName.length - i - 1);
	}
	return sFullName;
}

SedUtils.readMetaAttribute = function(rootHtmlElem, sMetaAttrName, bFailIfNotFound)
{
	var sVal = $(rootHtmlElem).attr(sMetaAttrName);
	sVal = Utils.noNull(sVal);
	if (sVal.length == 0 && bFailIfNotFound)
		Utils.assertFailed("Cannot read required form element named '" + sMetaAttrName + "'");
	return sVal;
}

SedUtils.getChildFrameByName = function(parentRepeatable, sName)
{
	var children = parentRepeatable.getItems();
	for (var i = 0; i < children.length; ++i){
		var childFrame = children[i];
		var sChildName = childFrame.getName();
		if (sChildName == sName){
			if (childFrame instanceof Choice)
				continue;
			if (!(childFrame instanceof Frame))
				Utils.assertFailed("Expecting a Frame instance but got instead a " + Utils.getClassName(childFrame));
			return childFrame;
		}
	}
	return null;	
}


SedUtils.getRepeatableChildByName = function(parentRepeatable, sName, nIndex)
{
	var childFrame = SedUtils.getChildFrameByName(parentRepeatable, sName);
	if (childFrame == null)
		return null;
	var repeatables = childFrame.getItems();
	return SedUtils.getRepeatableSiblingByIndex(repeatables, nIndex);
}

SedUtils.getRepeatableChildByNameMakeFrameVisible = function(parentRepeatable, sName, nIndex)
{
	var childFrame = SedUtils.getChildFrameByName(parentRepeatable, sName);
	if (childFrame == null)
		return null;
	
	// if the childFrame is marked invisible it is because at least one of its parent-section 
	// is paired with choices and the pair-choice is unselected. Thus we must position any 
	// pair-choice on the parent chain as if selected. Note that this method is triggered when 
	// writing non-empty JSON data into a field of the respective section 
	if (!childFrame.isVisible())
		SedUtils.setParentChoicesAsSelected(childFrame);
	
	if (childFrame == null)
		return null;
	var repeatables = childFrame.getItems();
	return SedUtils.getRepeatableSiblingByIndex(repeatables, nIndex);
}

	
SedUtils.getRepeatableSiblingByIndex = function(repeatables, nIndex)
{
	if (repeatables == null || repeatables.length == 0)
		Utils.assertFailed("Frame <" + sName + "> has no repeatable items ");

	if (repeatables.length <= nIndex)
		Utils.assertFailed("Requested index (=" + nIndex + ") exceeds count of Frame <" + sName + ">'s repeatables (=" + repeatables.length + ")");
	
	var childRepeatableItem = repeatables[nIndex];
	
	if (!(childRepeatableItem instanceof RepeatableItem))
		SedUtils.assertFailed("Expecting a RepeatableItem instance but got instead a " + Utils.getClassName(childRepeatableItem));
	
	return childRepeatableItem;
}

SedUtils.getLastRepeatableSiblingByName = function(parentRepeatable, sName)
{
	var childFrame = SedUtils.getChildFrameByName(parentRepeatable, sName);
	if (childFrame == null)
		return;
	
	var repeatables = childFrame.getItems();

	if (repeatables == null || repeatables.length == 0)
		Utils.assertFailed("Frame <" + sName + "> has no repeatable items ");
	
	var nLast = repeatables.length - 1;
	return SedUtils.getRepeatableSiblingByIndex(repeatables, nLast);
}


SedUtils.getHtmlInput1 = function(item)
{
	var htmlElem = item.getHtmlElem();
	var htmlInput = SedUtils.getHtmlInput2(htmlElem);
	return htmlInput;
}

SedUtils.getHtmlInput2 = function(htmlElem)
{
	htmlElem = SedUtils.childHtmlElem2(htmlElem, "[met_input]");
	return htmlElem;
}

/**
 * It produces the HTML label shown in the form for the respective item
 * This is typically used for constructing validation-offence messages that 
 * should be related to the form's label of the offending element.
 * The element could be a repeatable-section of a repeatable-field
 * @param item
 */
SedUtils.getHtmlLabel = function(item)
{
	var labelHtmlElem = SedUtils.childHtmlElemOrFail1(item, "[met_label]");
	var sLabel = $(labelHtmlElem).html();
	return sLabel;
}


SedUtils.getChoiceChildByName = function(repeatableSection, sChildFieldFullName)
{
	var children = repeatableSection.getItems();
	for (var i = 0; i < children.length; ++i){
		child = children[i];
		if (child instanceof Choice && sChildFieldFullName.indexOf(child.getFullName()) === 0)
			return child;
	}
	return null;
}

/**
 * works for any Item, if the "item" is a RepeatableSection with choices it 
 * will NOT go to a parent but use it as a return.
 * @param item
 * @returns
 */
SedUtils.findParentRepeatableSectionWithChoices = function(item)
{
	while(item != null){
		if (item instanceof RepeatableSection && item.hasChoices())
			return item;
		item = item.getParent();
	}
	return null;
}

/**
 * given an "item" it looks up on the "parent" chain to find out 
 * a RepetatableSection with choices, then it "activates" the choice
 * corresponding to the "item" full-name (it selects it among its 
 * siblings). The process continues as it could be a case of cascading 
 * choices  
 * @param item
 */
SedUtils.setParentChoicesAsSelected = function(item)
{
	var repeatableSection = SedUtils.findParentRepeatableSectionWithChoices(item);
	if (repeatableSection == null)
		return;
	var sFieldFullName = item.getFullName();
	
	while(repeatableSection != null){
		var choice = SedUtils.getChoiceChildByName(repeatableSection, sFieldFullName);
		if (choice != null)
			SedUtils.selectChoiceUnselectSiblings(choice);
		repeatableSection = SedUtils.findParentRepeatableSectionWithChoices(repeatableSection.getParent());
	}
}

SedUtils.selectChoiceUnselectSiblings = function(choice)
{
	if (choice == null)
		Utils.assertFailed("expecing parementer <choice>");
	
	if (!(choice instanceof Choice))
		Utils.assertFailed("Expecting <Choice> instance but got instead <" +Utils.getClassName(choice)+ "> instance");

	var nChoiceIndex = choice.getContentIndex();
	var contentItems = choice.getParentRepeatableSection().getItems();
	
	for (var i = nChoiceIndex - 1; i >= 0; --i){
		var prevChoiceCandidate = contentItems[i];
		if (Object.getPrototypeOf(prevChoiceCandidate).constructor != Choice)
			break;
		prevChoiceCandidate.select(false);
	}
	choice.select(true);
	for (i = nChoiceIndex + 1; i < contentItems.length; ++i){
		var nextChoice = contentItems[i];
		if (Object.getPrototypeOf(nextChoice).constructor != Choice)
			break;
		nextChoice.select(false);
	}
}

/**
 * Finds the html elem matching the JQuery selector, starting with the 
 * item's pairing html-elem (included) and continuing with its children 
 * @param item
 * @param sJQuerySelector
 * @returns 
 */
SedUtils.childHtmlElem1 = function(item, sJQuerySelector)
{
	var htmlElem = item.getHtmlElem();
	var htmlElemSought = childHtmlElem2(htmlElem, sJQuerySelector);
	return htmlElemSought;
	
}

/**
 * Finds the html elem matching the JQuery selector, starting with the given 
 * html-elem (included) and continuing with its children 
 */
SedUtils.childHtmlElem2 = function(htmlElem, sJQuerySelector)
{
	if ($(htmlElem).is(sJQuerySelector))
		return htmlElem;
		
	var htmlChild = $(htmlElem).find(sJQuerySelector).first();
	if (htmlChild.length == 0)
		return null;
	
	return htmlChild[0];
}


/**
 * Finds the html elem matching the JQuery selector, starting with the 
 * item's pairing html-elem (included) and continuing with its children
 * If it cannot find it, it will assert  
 * @param item
 * @param sJQuerySelector
 * @returns 
 */
SedUtils.childHtmlElemOrFail1 = function(item, sJQuerySelector)
{
	var htmlElem = item.getHtmlElem();
	var htmlElemSought = SedUtils.childHtmlElemOrFail2(htmlElem, sJQuerySelector);
	return htmlElemSought;
}

/**
 * Finds the html elem matching the JQuery selector, starting with the 
 * given html-elem (included) and continuing with its children
 * If it cannot find it, it will assert  
 * @param item
 * @param sJQuerySelector
 * @returns 
 */

SedUtils.childHtmlElemOrFail2 = function(htmlElem, sJQuerySelector)
{
	var htmlElemSought = SedUtils.childHtmlElem2(htmlElem, sJQuerySelector);
	if (htmlElemSought == null)
		Utils.assertFailed("cannot find child html-elem matching '" + sJQuerySelector + "'" +
				"\n" + htmlElem.outerHTML);
		
	return htmlElemSought;
}

/**
 * Finds the html elem matching the JQuery selector, starting with the 
 * item's pairing html-elem (included) and continuing on its parent chain
 * @param item
 * @param sJQuerySelector
 * @returns 
 */
SedUtils.parentHtmlElem1 = function(item, sJQuerySelector)
{
	var htmlElem = item.getHtmlElem();
	var htmlElemSought = parentHtmlElem2(htmlElem, sJQuerySelector);
	return htmlElemSought;
	
}

/**
 * Finds the html elem matching the JQuery selector, starting with the given 
 * html-elem (included) and continuing on its parent chain
 */
SedUtils.parentHtmlElem2 = function(htmlElem, sJQuerySelector)
{
	if ($(htmlElem).is(sJQuerySelector))
		return htmlElem;
		
	var htmlChild = $(htmlElem).parent(sJQuerySelector).first();
	if (htmlChild.length == 0)
		return null;
	
	return htmlChild[0];
}


/**
 * Finds the html elem matching the JQuery selector, starting with the 
 * item's pairing html-elem (included) and continuing with its parent chain
 * If it cannot find it, it will assert  
 * @param item
 * @param sJQuerySelector
 * @returns 
 */
SedUtils.parentHtmlElemOrFail1 = function(item, sJQuerySelector)
{
	var htmlElem = item.getHtmlElem();
	var htmlElemSought = SedUtils.parentHtmlElemOrFail2(htmlElem, sJQuerySelector);
	return htmlElemSought;
}

/**
 * Finds the html elem matching the JQuery selector, starting with the 
 * given html-elem (included) and continueing with its parent chain
 * If it cannot find it, it will assert  
 * @param item
 * @param sJQuerySelector
 * @returns 
 */

SedUtils.parentHtmlElemOrFail2 = function(htmlElem, sJQuerySelector)
{
	var htmlElemSought = SedUtils.parentHtmlElem2(htmlElem, sJQuerySelector);
	if (htmlElemSought == null)
		Utils.assertFailed("cannot find parent html-elem matching '" + sJQuerySelector + "'" +
				"\n" + htmlElem.outerHTML);
	return htmlElemSought;
}



//////////////////////////////////////////////////////////
//Utils
//////////////////////////////////////////////////////////

function Utils(){
	this._dummy = null;
}

Utils.assertFailed = function(sMsg){
	alert(sMsg);
	throw sMsg;
}

Utils.noNull = function(sValue)
{
	if (sValue == null)
		return "";
	else 
		return sValue;
}

Utils.TAB = "\t";

Utils.getClassName = function(object)
{
	if (object != null){
	   var funcNameRegex = /function (.{1,})\(/;
	   return (funcNameRegex).exec(object.constructor.toString())[1];
	}
	return "null";
}

Utils.isEmptyString = function(sValue)
{
	if (sValue == null)
		return true;
	if (sValue.length == 0)
		return true;
	return false;
}

/**
 * pads the string with white-spaces (WS) so it achieves the length specified in nRightPos, 
 * which thus become the right-alignment pos
 * @param nRightPos - right alignment position (the length of the string after WS padding 
 * @param sValue - the string that is to be padded
 */
Utils.alignString = function(nRightPos, sValue)
{
	var nCurrentLen = 0;
	if (sValue != null)
		nCurrentLen = sValue.length;
	var nPadSize = nRightPos - nCurrentLen;
	if (nPadSize <= 0)
		Utils.assertFailed("Cannot pad-align string to pos = " + 
				nRightPos + 
				" because string is longer already (length = " +
				sValue.length +
				"; text = '" + 
				sValue + 
				"')");
	if (nPadSize >= Utils.ms_wsPadsStringArray.length)
		Utils.assertFailed("Cannot pad-align string to pos = " + 
				nRightPos + 
				" because string is to short and exceeds padding capacity (padding capacity is = " +
				Utils.ms_wsPadsStringArray.length + 
				"; text length = " +
				nCurrentLen +
				"; text = " +
				"'" + 
				Utils.noNull(sValue) + 
				"')");
	sValue = Utils.noNull(sValue) + Utils.ms_wsPadsStringArray[nPadSize];
	return sValue;	
}

/**
*  Escape newline chars for JSON compatibility 
**/
Utils.escapeNewLineChars = function(valueToEscape) {
    if (valueToEscape != null && valueToEscape != "") {
        return valueToEscape.replace(/\n/g, "\\n");
    } else {
        return valueToEscape;
    }
}

/**
*  Unescape newline chars for JSON compatibility 
**/
Utils.unescapeNewLineChars = function(valueToUnEscape) {
    if (valueToUnEscape != null && valueToUnEscape != "") {
        return valueToUnEscape.replace(/\\n/g, "\n");
    } else {
        return valueToUnEscape;
    }
}


/**
 * this is used to pad strings in order to achieve righ alignment
 * The approach is favoring speed (one string concatenation) but it gives
 * limmited padding capacity that can align right only N more char-positions
 * Here N is 10 = number of pads in the array (there are 11 with the 0 pos 
 * which is not used   
 */
Utils.ms_wsPadsStringArray = [
	 "", 
	 " ", 
	 "  ", 
	 "   ", 
	 "    ", 
	 "     ",
	 "      ",
	 "       ",
	 "        ",
	 "         ",
	 "          ",
	 "           ",
	 "            ",
	 "             ",
	 "              ",
	 "               ",
	 "                ",
	 "                 ",
	 "                  ",
	 "                   ",
	 "                    ",
	 "                     ",
	 "                      ",
	 "                       ",
	 "                        ",
	 "                         ",
	 "                          ",
	 "                           ",
	 "                            ",
	 "                             ",
	 "                              ",
	 "                               ",
	 "                                ",
	 "                                 ",
	 "                                  ",
	 "                                   ",
	 "                                    ",
	 "                                     ",
	 "                                      ",
	 "                                       ",
	 "                                        "];
	 


//////////////////////////////////////////////////////////
//SequenceGen
//////////////////////////////////////////////////////////
/**
 * used for allocating an unique sequence-id to any Item object that 
 * is created. This is useful in debugging ensuring a immutable reference
 * of an object (a role hold by an mem-address in C/C++) 
 */
function SequenceGen()
{
	this._dummy = null;
}

SequenceGen.ms_nSequence = 0;

SequenceGen.getNextSequence = function(){
	return ++SequenceGen.ms_nSequence;
}
