﻿(function() {

// Namespacing
if (!window.mvc) window.mvc = function() { };

// This is called by the code generated script
var xforms = window.mvc.xforms = {
	init: function(form) { initChildCons(form, ''); }
};

/** Classes **/
function controlBase(el, con) { }

function group(el, con) {
	initChildCons(con, el.id);
}

function repeat(el, con) {
	this.index = el.childNodes.length - 1;
	$.each(el.childNodes, function(i) {
		this.xforms = new repeatItem(this, i);
		initChildCons(con, el.id + '-' + i);
	});
}
repeat.prototype.insertItem = insertItem;
repeat.prototype.deleteItem = deleteItem;
repeat.prototype.buildTemplate = buildTemplate;

function repeatItem(el, itemIndex) {
	this.el = el;
	this.itemIndex = itemIndex;
}

/** Common functions **/
function getControl(conID) {
	var jq = $('#' + conID);
	if (jq.length > 0) return jq[0];
	// This is commented out because we may need two or more groups with the
	// same ID (even though this is technically illegal in HTML - is there a
	// better way to identify groups or xforms controls in general?)
	//else if (jq.length > 1) throw('XForms: Multiple elements found for ID = ' + conID); 
	else throw('XForms: Element not found for ID = ' + conID);
}

/** Initialization code **/
function initChildCons(con, prefix) {
	$.each(con.cons, function(i, childCon) {
		initControl(childCon, prefix);
	});
}

function initControl(con, prefix) {
	if (prefix.length > 0 && con.id.length > 0) prefix = prefix + '_';
	var conID = prefix + con.id;
	var el = getControl(conID);
	initDom(el, con);

	// Bind actions
	if (con.acts) {
		$.each(con.acts, function(i, action) {
			if (action.type == 0) bindAction(el, action);
			else if (action.type == 2) bindInsertAction(el, action);
			else if (action.type == 3) bindDeleteAction(el, action);
		});
	}
	
	// We are rebuilding, so trigger the event
	$(el).trigger('xforms-rebuild');
}

// Append the xforms information directly to the DOM
function initDom(el, con) {
	el.xforms = createDomControl(el, con);
	el.xforms.con = con;
	el.xforms.el = el;
}

// Create a script instance of an XForm control
function createDomControl(el, con) {
	if (con.type == 0) return new group(el, con);
	else if (con.type == 3) return new repeat(el, con);
	return new controlBase(el, con);
}

// Binds XForm actions to the DOM (using HTML events via jQuery bind)
function bindAction(el, action) {
	// Condition not currently supported, dont really need
	// it as we are calling a custom script function
	eval('var func = ' + action.func);
	$(el).bind(action.ev, func);
}

// Insert specific to a trigger
function bindInsertAction(el, action) {
	eval('var cond = ' + action.cond);
	var repeat = getTriggerRepeat(el, action.ns);
	$(el).bind(action.ev, function() {
		if (cond != null ? cond() : true) {
			var index = getRepeatIndex(repeat, el, action.at);
			repeat.xforms.insertItem(index, action.pos);
		}
	});
}

// Delete specific to a trigger
function bindDeleteAction(el, action) {
	eval('var cond = ' + action.cond);
	var repeat = getTriggerRepeat(el, action.ns);
	$(el).bind(action.ev, function() {
		if (cond != null ? cond() : true) {
			var index = getRepeatIndex(repeat, el, action.at);
			repeat.xforms.deleteItem(index);
		}
	});
}

// Conditionally execute a function
function actionEvent(ev) {
	if (cond != null ? cond() : true) func(ev);
}

/** Repeat functions **/
function insertItem(index, position) {
	if (index == undefined) index = this.el.childNodes.length - 1;
	if (position == undefined) position = 0;

	var template = this.buildTemplate();
	var jq = $(this.el);
	
	if (this.el.childNodes.length == 0) {
		// If repeat is empty, just add it
		jq.html(template);
	} else if (index > -1) {
		var insertItem = $(this.el.childNodes[index]);
		if (position == 1) insertItem.before(template);
		else insertItem.after(template);
	}
	
	initChildCons(this.con, this.el.id + '-' + template.xforms.itemIndex);
	jq.trigger('xforms-insert', [template, index, position]);
	return template;
};

function deleteItem(index) {
	var jq = $(this.el);
	
	if (index > -1) {
		var deleteItem = jq.children().eq(index);
		deleteItem.remove();
		jq.trigger('xforms-delete', [deleteItem[0], index]);
	}
};

function buildTemplate() {
	this.index++;
	var temJQ = $(this.con.itemTemplate);
	var tem = temJQ[0];
	tem.xforms = new repeatItem(tem, this.index);
	var itemID = this.el.id + '-' + this.index;
	reindexAttr(this, temJQ, 'id', itemID);
	reindexAttr(this, temJQ, 'name', itemID);
	reindexAttr(this, temJQ, 'for', itemID);
	return tem;
}

function reindexAttr(repeat, tem, attr, itemID) {
	$(tem).find('[' + attr + '^=' + repeat.con.templateID + ']').each(function() {
		var jqCon = $(this);
		var newID = jqCon.attr(attr).replace(repeat.con.templateID, itemID);
		jqCon.attr(attr, newID);
	});
}

function getRepeatIndex(repeat, el, at) {
	var itemLength = repeat.childNodes.length;
	if (itemLength == 0 || at == 1)
		return 0;

	if (at == 2 && itemLength > 0) {
		return itemLength - 1;
	} else if (at == 0) {
		var tmpEl = el;
		while (tmpEl.xforms == undefined || tmpEl.xforms.itemIndex == undefined) {
			tmpEl = tmpEl.parentNode;
			if (tmpEl == undefined)
				throw("At = current not valid for insert on trigger " + el.id);
		}
		var item = $(tmpEl);
		var repeatIndex = $(repeat).children().index(item);
		return repeatIndex;
	}
}

function getTriggerRepeat(el, nodeset) {
	var conID = el.xforms.con.id;
	var repeatID = el.id.substr(0, el.id.length - conID.length);
	
	if (nodeset.length > 0) repeatID = repeatID + nodeset;
	else repeatID = repeatID.substr(0, repeatID.lastIndexOf('-'));
	
	var dom = getControl(repeatID);
	return dom;
}

})();