text: /*
   * ## TEMPLATES.JS
   * Templates.js
   * getVersionId
   * This library follows the best practices enumarated here:
   * https://www.w3.org/community/webed/wiki/JavaScript_best_practices
   * 
   * **USA CASES:**
   * 
   * setup a new template using template-spintax.
   * convert the template-spintax to template-syntax to show it (dont show spyntax).
   * 
   * a blank space is font-grayed when it have the field-description.
   * a blank space is font-blacked when have a different value than the field-description.
   * the template border go green when all the blank-saces are filled with a different value then the field-description.
   * 
   * setup pre-loaded values in the blank spaces (maybe brought from either cookies or database).
   * 
   * try different contents in the same widget
   * 
   * persistance between different widgets
   * 
   * overwrite ongreen callback function
   * overwrite onblue callback function
   * 
   * commonsJs.escapeHTML
   * 
   * commonsJs.spin: convert spintax to plain text.
   * templatesJs.selectAllText
   * 
   * templatesJs.draw
   * templatesJs.isReady
   * templatesJs.getSpintax
   * templatesJs.getFieldValue
   * 
   */
  
  
  var templatesJs = {
  
  	/*
  	 * Returns the version of this commonsJs library.
  	 *  
  	 */
  	version: function() {
  		return '1.0.1';
  	},
  
  	// use this global variable to remember the values written in tempalte, even if the user go to another tamplate and then get back to this one 
  	persistance: {},
  	setPersistance: function(parent_id, field_id, field_value) {
  		if (templatesJs.persistance[parent_id] == null) { templatesJs.persistance[parent_id] = {}; }
  		if (templatesJs.persistance[parent_id][field_id] == null) { templatesJs.persistance[parent_id][field_id] = ''; }
  		templatesJs.persistance[parent_id][field_id] = field_value;
  	},
  	getPersistance: function(parent_id, field_id) {
  		var ret = null;
  		if (templatesJs.persistance[parent_id] != null) { 
  			if (templatesJs.persistance[parent_id][field_id] != null) { 
  				ret = templatesJs.persistance[parent_id][field_id]; 
  			}
  		}
  		return ret;
  	},
  	
  	/*
  	 * Do a select all thext inside a contenteditable <span>.
  	 * This operation requires custom code, because the element.select is not working for a contenteditable <span>.
  	 * 
  	 */
  	selectAllText: function(field) {
  		var range, selection;
  		if (document.body.createTextRange) {
  			range = document.body.createTextRange();
  			range.moveToElementText(field);
  			range.select();
  		} else if (window.getSelection) {
  			selection = window.getSelection();
  			range = document.createRange();
  			range.selectNodeContents(field);
  			selection.removeAllRanges();
  			selection.addRange(range);
  		}
  	},
  	
  	/*
  	 * write default value and select it if the text inside the field is all deleted.
  	 * change the font color from gray to black if the content is not the default content.
  	 * update the border color of the tempalte.
  	 * 
  	 */
  	onFieldUpdate: function(field) {
  		// persistance
  		var parent = field.parentNode.parentNode.parentNode;
  		var parent_id = parent.id;
  		var field_id = field.id;
  		var field_value = field.innerHTML;
  		templatesJs.setPersistance(parent_id, field_id, field_value);
  		
  		// write default value and select it if the text inside the field is all deleted
  		if (field.innerHTML.length == 0) {
  			field.innerHTML = $(field).data('default');
  			templatesJs.selectAllText(field);
  		}
  		
  		// change color to black if field text is different then default
  		if ( field.innerHTML != $(field).data('default') ) {
  			$(field).css('color', 'rgb(34, 177, 76)');
  			$(field).css('font-weight', 'bold');
  		} else {
  	//		$(field).css('color', 'rgb(0, 162, 232)');
  			$(field).css('color', 'gray');
  			$(field).css('font-weight', 'normal');
  		}
  		
  		// update the border color of the tempalte
  		var o = field.parentNode.parentNode;
  		if (templatesJs.isReady(o) == true) {
  			$(o).removeClass('template-blue');
  			$(o).addClass('template-green');
  		} else {
  			$(o).removeClass('template-green');
  			$(o).addClass('template-blue');
  		}
  	},
  	
  	/*
  	 * Draws the tempalte element inside a parent element
  	 * 
  	 * example:
  	 * <!benefit1!write the most attractive benefit of your service!>
  	 * 
  	 */
  	draw: function(parent, h) {
  		var FIELD_PATTERN = /\<\![^\<\>\!]*\![^\<\>\!]*\!\>/;
  	
  		if ( h['index'] == null ) { h['index'] = 0; }
  		var desc = h['versions'][h['index']];
  	
  		// convert template-spintax to tempalte
  		var s = commonsJs.spin(desc['code']);
  	
  		// reemplazo los fields
  		var match = '';
  		while (match = s.match(FIELD_PATTERN)) {
  			match = match[0];
  			parts = match.replace('<', '').replace('>', '').split('!');
  			field_id = parts[1];
  			field_default = parts[2];
  	
  			field_value = templatesJs.getPersistance(parent.id, field_id);
  			if (field_value == null) {
  				field_value = field_default;
  				if (desc['values'] != null) {
  					if (desc['values'].constructor == Object) {
  						if (desc['values'][field_id] != null) {
  							field_value = desc['values'][field_id];
  						}
  					}
  				}
  			}
  	
  			s = s.replace(match, "<span class='field' contenteditable='true' id='"+commonsJs.escapeHTML(field_id)+"' data-default='"+commonsJs.escapeHTML(field_default)+"'>"+field_value+"</span>");
  		}
  		
  		html = 
  		"<div class='template template-blue' data-code='"+commonsJs.escapeHTML(desc['code'])+"'> " +
  		"	<div class='title'> ";
  	
  		// draw select list of templates
  		if ( h['versions'].length > 1 ) {
  			html +=
  			"	<select class='versions'> ";
  	
  			i = 0;
  			h['versions'].forEach(function(item) {
  				selected = '';
  				if (h['index'] == i) { selected = 'selected'; }
  				html +=
  				"		<option class='change-version' data-index='"+i.toString()+"' data-id='"+item['id']+"' "+selected+">"+item['title']+"</option> ";
  				i++;		
  			});
  	
  			html +=
  			"	</select> ";
  		}
  
  		// draw source code icon
  		if ( commonsJs.isDefined(h['allow_code_edition']) == true ) { 
  			if (h['allow_code_edition'] == true) {// allow to edit the raw code of a template
  				html +=
  				"	<button class='edit-code off'><i class='icon-pencil icon-large' title='edit spintax'> </i></button>";
  			}
  		}
  		
  		html += 
  		"	</div> " +
  		"	<div class='code'> " +
  		s +
  		"	</div> " +
  		"	<div class='raw'> " +
  		"		<textarea class='raw-code'> </textarea> " +
  		"	</div> " +
  		"</div> ";
  	
  		// inserto el codigo html en el tag padre/contexto
  		parent.innerHTML = html;
  		
  		// change template
  		$(parent).find('.versions').change(function() {
  			op = $(this).find('option:selected');
  			h['index'] = parseInt(op.data('index'));
  			templatesJs.draw(parent, h);
  			// on_version_change callback function
  			if ( commonsJs.isDefined(h['on_version_change']) == true ) { 
  				h['on_version_change']();
  			} else {
  				// nothing by default
  			}
  		});
  		
  		// cambio 
  		$(parent).find('.edit-code').click(function() {
  			templatesJs.toggleCodeAndApplyChosenTemplate(parent);
  			// on_code_button_toggle callback function
  			if ( commonsJs.isDefined(h['on_code_button_toggle']) == true ) { 
  				h['on_code_button_toggle']();
  			} else {
  				// nothing by default
  			}
  		});
  
  		// cambio 
  		$(parent).find('textarea.raw-code').keyup(function() {
  			// on_raw_code_edition callback function
  			if ( commonsJs.isDefined(h['on_raw_code_edition']) == true ) { 
  				h['on_raw_code_edition']();
  			} else {
  				// nothing by default
  			}
  		});
  		
  		// select all text inside a field on focus
  		$('.field').focus(function() {
  			templatesJs.selectAllText(this);
  		});
  	
  		// update the field if the content changes
  		$('.field').keyup(function() {
  			templatesJs.onFieldUpdate(this);
  			// on_field_edition callback function
  			if ( commonsJs.isDefined(h['on_field_edition']) == true ) { 
  				h['on_field_edition'](this);
  			} else {
  				// nothing by default
  			}
  		});
  		
  		// actualizo los fields
  		$('.field').each(function() {
  			templatesJs.onFieldUpdate(this);
  		});
  	},
  	
  	/*
  	 * Returns true if a tempalte has been compalted by filling up all its blank spaces.
  	 * 
  	 */
  	isReady: function(parent) {
  		var res = true;
  		$(parent).find('.field').each(function() {
  			if ( this.innerHTML == $(this).data('default') ) {
  				res = false;
  			}
  		});
  		return res;
  	},
  	
  	
  	/*
  	 * Returns true if the widget is enabled to edit code.
  	 * 
  	 */
  	isCodeOn: function(parent) {
  		var ret = false;
  		if ( $(parent).find('button.edit-code').hasClass('on') == true ) {
  			ret = true;
  		} else {
  		}
  		return ret;
  	},
  
  
  	/*
  	 * Set on/off the code edition feature.
  	 * Enable/disable the viersion select list.
  	 * Set display on/of of the fillup form (div.code)
  	 * Set display on/of of the raw code textarea (div.raw)
  	 * 
  	 */
  	toggleCode: function(parent) {
  		if ( templatesJs.isCodeOn(parent) == true ) {
  			$(parent).find('button.edit-code').removeClass('on');
  			$(parent).find('button.edit-code').addClass('off');
  			$(parent).find('select.versions').prop('disabled', false);
  			$(parent).find('div.code').show();
  			$(parent).find('div.raw').hide();
  		} else {
  			$(parent).find('button.edit-code').removeClass('off');
  			$(parent).find('button.edit-code').addClass('on');			
  			$(parent).find('select.versions').prop('disabled', true);
  			$(parent).find('div.code').hide();
  			$(parent).find('div.raw').show();
  		}
  	},
  
  	/*
  	 * call to toggleCode, and then apply the spintax from the chosen template.
  	 * 
  	 */
  	toggleCodeAndApplyChosenTemplate: function(parent) {
  		//
  		templatesJs.toggleCode(parent);
  		//
  		if ( templatesJs.isCodeOn(parent) == true ) {
  			$(parent).find('div.raw textarea').text( templatesJs.getChosenVersionSpintax(parent) );
  		}
  	},
  
  	/*
  	 * Returns the code of the chosen version with the values written by the user in the blank-spaces.
  	 * 
  	 */
  	getChosenVersionSpintax: function(parent) {
  		var ret = commonsJs.unEscapeHTML( $(parent).find('.template').data('code') );
  		var regex = null;
  		templatesJs.getFieldNames(parent).forEach(function(field_name) {
  			field_value = templatesJs.getFieldValue(parent, field_name);
  			regex = new RegExp('\<\!'+field_name+'\![^\!]*\!\>');
  			ret = ret.replace(regex, field_value);
  		});
  		return ret;
  	},
  
  	/*
  	 * Returns ID of the chosen version to work with.
  	 * If the users is working with the raw code, returns null.
  	 * 
  	 */
  	getVersionId: function(parent) {
  		var ret = null;
  		if ( templatesJs.isCodeOn(parent) == false ) {
  			ret = $(parent).find('.versions').find('option:selected').data('id');
  		}
  		return ret;
  	},
  
  	/*
  	 * Change the chosen version.
  	 * The event on_version_change will be raised.
  	 * 
  	 * If the users is working with the raw code, it toggles to the template mode.
  	 * 
  	 */
  	setVersionId: function(parent, template_id) {
  		var ret = null;
  		if ( templatesJs.isCodeOn(parent) == true ) {
  			templatesJs.toggleCode(parent);
  		}
  		$(parent).find('.versions').find("option").prop('selected', false);
  		$(parent).find('.versions').find("option[data-id='"+template_id+"']").prop('selected', true);
  		$(parent).find('.versions').trigger('change');
  	},
  	
  	/*
  	 * If code button is ON, this functions returns the text inside the textarea.
  	 * If code button is OFF, this functions calls getChosenVersionSpintax and returns its value.
  	 * 
  	 */
  	getSpintax: function(parent) {
  		var ret = '';
  		codeon = templatesJs.isCodeOn(parent);
  		if ( codeon == true ) {
  			ret = $(parent).find('div.raw textarea').val();
  		} else {
  			ret = templatesJs.getChosenVersionSpintax(parent);
  		}
  		return ret;
  	},
  
  	/*
  	 * If code button is ON, this functions set the text inside the textarea.
  	 * If code button is OFF, this functions throws an exception.
  	 * 
  	 * The event on_raw_code_edition will be raised.
  	 * 
  	 */
  	setSpintax: function(parent, spintax) {
  		codeon = templatesJs.isCodeOn(parent);
  		if ( codeon == true ) {
  			$(parent).find('div.raw textarea').val(spintax);
  			$(parent).find('textarea.raw-code').trigger('keyup');
  		} else {
  			throw 'templatesJs.setSpintax is not allowed when code is off.';
  		}
  		return ret;
  	},
  	
  	/*
  	 * Returns an array with the names of the fields in the template.
  	 * 
  	 */
  	getFieldNames: function(parent) {
  		ret = [];
  		$(parent).find('.field').each(function(index) {
  			ret.push(this.id);
  		});
  		return ret;
  	},
  
  	/*
  	 * Returns the value written into a field.
  	 * 
  	 */
  	getFieldValue: function(parent, field_id) {
  		return templatesJs.getPersistance(parent.id, field_id);
  	},
  
  
  	/*
  	 * Set cursor focus in the first blank-space of the template.
  	 * 
  	 */
  	setFocus: function(parent) {
  		name = templatesJs.getFieldNames(parent)[0];
  		templatesJs.selectAllText(
  			$(parent).find('#'+name)[0]
  		);
  	}
  
  }; // var templatesJs = {