1 /** 
  2  * Template module for simple javascript templating
  3  */
  4 (function(){
  5 	"use strict";
  6 	
  7 	//This module relies on some others for simplicity
  8 	//so, if they aren't there, don't initialize the module
  9 	if($_.ajax === "undefined")
 10 	{
 11 		return;
 12 	}
 13 	
 14 	var t, _t, _p;
 15 	
 16 	
 17 	//Private object to store retrieved templates
 18 	_t = {};
 19 	
 20 	//Private object to store parsed templates
 21 	_p = {};
 22 	
 23 	
 24 	/**
 25 	 * Module for html templating. Requires ajax module.
 26 	 * 
 27 	 * @name template
 28 	 * @namespace
 29 	 * @memberOf $_
 30 	 */
 31 	t = {
 32 		/**
 33 		 * Retrieves a template
 34 		 * 
 35 		 * @memberOf $_.template
 36 		 * @name get
 37 		 * @param string name
 38 		 * @return string
 39 		 * @function
 40 		 * @type string
 41 		 */
 42 		get: function(name)
 43 		{
 44 			var res;
 45 
 46 			res = this.el.innerHTML;
 47 			
 48 			if(res === "")
 49 			{
 50 				console.log("Template is empty or cannot be found");
 51 				return;
 52 			}
 53 			
 54 			_t[name] = res;
 55 			return res;
 56 		},
 57 		/**
 58 		 * Formats a template
 59 		 * 
 60 		 * @memberOf $_.template
 61 		 * @name parse
 62 		 * @param string template_name
 63 		 * @param object replace_data
 64 		 * @return string
 65 		 * @function
 66 		 * @type string
 67 		 */
 68 		parse: function(name, data)
 69 		{
 70 			var tmp = _t[name],
 71 				pairs = [],
 72 				pair_reg = /\{([A-Z0-9_\-]+)\}(.*)\{\/\1\}/gim,
 73 				var_reg = /\{([A-Z0-9_\-]+)\}/gim,
 74 				pseudos = [], 
 75 				num_pairs = 0,
 76 				num_pseudos = 0,
 77 				i = 0,
 78 				j = 0,
 79 				var_name = '',
 80 				rep_data = {},
 81 				tmp_data = '',
 82 				data_len,
 83 				frag,
 84 				frag_section,
 85 				emptys,
 86 				x;
 87 				
 88 			tmp = String(tmp);
 89 		
 90 			//Remove newlines and tabs from template because
 91 			//those whitespace characters are extra bandwidth
 92 			tmp = tmp.replace(/\s+/gim, " ");
 93 			tmp = tmp.replace(/>\s+</gim, "><");
 94 			tmp = tmp.replace(/>\s+\{/gim, ">{");
 95 			tmp = tmp.replace(/\}\s+</gim, "}<");
 96 			
 97 			//Match all the looped sections of content
 98 			pairs = tmp.match(pair_reg);
 99 			
100 			if(pairs != null)
101 			{
102 				num_pairs = pairs.length;
103 				
104 				//Go through the template, and match the pairs
105 				for(i=0;i<num_pairs;i++)
106 				{
107 					//Put the loop in a placeholder
108 					tmp = tmp.replace(pairs[i], "{"+i+"}");
109 					
110 					//Create a place to store looped data
111 					tmp_data = "";
112 					
113 					//The replace variable is the name of the tag
114 					var_name = String(pairs[i]).match(/^\{([A-Z0-9_\-]+)\}/i);
115 					rep_data = data[var_name[1]];
116 					
117 					//Make sure there are loops
118 					if(rep_data.length > 0)
119 					{
120 						data_len = rep_data.length;
121 						
122 						//Get rid of the loop tags
123 						pairs[i] = pairs[i].replace(pair_reg, "$2");
124 						
125 						//Replace psudovariables with data
126 						for(j=0;j<data_len;j++)
127 						{
128 							//Is there a better way to do this, rather than an inline function?
129 							tmp_data += pairs[i].replace(var_reg, function(_, varName){
130 								return (rep_data[j][varName]) ? rep_data[j][varName] : ""; 
131 							});
132 						}
133 					}
134 					
135 					//Replace the looped content
136 					tmp = tmp.replace("{"+i+"}", tmp_data);
137 				}
138 			}
139 			
140 			//Replace all the rest of the psudeovariables
141 			pseudos = tmp.match(var_reg);
142 			
143 			if(pseudos != null)
144 			{
145 				num_pseudos = pseudos.length;
146 			
147 				for(i=0;i<num_pseudos;i++)
148 				{
149 					//Remove the {} from the pseudos
150 					var_name = pseudos[i].replace('{', '');
151 					var_name = var_name.replace('}', '');
152 					
153 					//Replace each pseudovariable with the value of the object
154 					//property of the same name
155 					tmp = tmp.replace(pseudos[i], data[var_name]);
156 				}
157 			}
158 			
159 			//Create an empty fragement
160 			frag = document.createDocumentFragment();
161 			
162 			//Insert the html
163 			frag.appendChild(document.createElement('section'));
164 			frag_section = frag.querySelectorAll('section')[0];
165 			frag_section.innerHTML = tmp;
166 			
167 			//Remove bad elements in the fragment, should be faster than being done live
168 			emptys = frag_section.querySelectorAll('[src=""], [href=""]');
169 			
170 			for(x in emptys)
171 			{
172 				if(emptys[x].parentNode)
173 				{
174 					emptys[x].parentNode.removeChild(emptys[x]);
175 				}
176 			}
177 			
178 			//Save the parsed template in an object for later retrieval
179 			_p[name] = tmp;
180 			
181 			return tmp;
182 		},
183 		/** 
184 		 * Inserts the formatted template into the page. If the url and data parameters
185 		 * are passed, it will retrieve a template file from the same domain, parse, 
186 		 * and insert the template into the page. 
187 		 *
188 		 * @memberOf $_.template
189 		 * @name apply
190 		 * @function
191 		 * @param string parsed_template/template_name
192 		 * @param [string] url
193 		 * @param [object] data
194 		 */
195 		apply: function(name, url, data)
196 		{
197 			//If the parsed template is supplied, just apply it to the passed selector
198 			if(typeof url === "undefined" && typeof data === "undefined")
199 			{
200 				//If the "name" variable is in the _p object, set that
201 				if(typeof _p[name] !== "undefined")
202 				{
203 					this.el.innerHTML = _p[name];
204 					return;
205 				}
206 			
207 				//Otherwise, set the passed string to the current selector
208 				this.el.innerHTML = name;
209 				return;
210 			}
211 			
212 			//Otherwise, get the template, parse it, and apply it
213 			$_.get(url, {}, function(res){
214 				var parsed;
215 			
216 				if(res === "")
217 				{
218 					console.log("Template is empty or can not be found");
219 					return;
220 				}
221 				
222 				//Cache the template in an object for later use
223 				_t[name] = res;
224 				parsed = this.parse(name, data);
225 				_p[name] = parsed;
226 				
227 				this.el.innerHTML = parsed;
228 			});
229 		}
230 	};
231 	
232 	//Add the module to the library
233 	$_.ext('template', t);
234 	
235 })();