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 				pseudos = [], 
 73 				num_pairs = 0,
 74 				num_pseudos = 0,
 75 				i = 0,
 76 				j = 0,
 77 				var_name = '',
 78 				rep_data = {},
 79 				tmp_data = '',
 80 				data_len,
 81 				frag,
 82 				frag_section,
 83 				emptys,
 84 				x;
 85 				
 86 			tmp = String(tmp);
 87 		
 88 			//Remove newlines and tabs from template because
 89 			//those whitespace characters are extra bandwidth
 90 			tmp = tmp.replace(/\s+/gim, " ");
 91 			tmp = tmp.replace(/>\s+</gim, "><");
 92 			tmp = tmp.replace(/>\s+\{/gim, ">{");
 93 			tmp = tmp.replace(/\}\s+</gim, "}<");
 94 			
 95 			//Match all the looped sections of content
 96 			pairs = tmp.match(/\{([A-Z0-9_\-]+)\}(.*)\{\/\1\}/gim);
 97 			
 98 			if(pairs != null)
 99 			{
100 				num_pairs = pairs.length;
101 				
102 				//Go through the template, and match the pairs
103 				for(i=0;i<num_pairs;i++)
104 				{
105 					//Put the loop in a placeholder
106 					tmp = tmp.replace(pairs[i], "{"+i+"}");
107 					
108 					//Create a place to store looped data
109 					tmp_data = "";
110 					
111 					//The replace variable is the name of the tag
112 					var_name = String(pairs[i]).match(/^\{([A-Z0-9_\-]+)\}/i);
113 					rep_data = data[var_name[1]];
114 					
115 					//Make sure there are loops
116 					if(rep_data.length > 0)
117 					{
118 						data_len = rep_data.length;
119 						
120 						//Get rid of the loop tags
121 						pairs[i] = pairs[i].replace(/\{([A-Z0-9_\-]+)\}(.*)\{\/\1\}/gim, "$2");
122 						
123 						//Replace psudovariables with data
124 						for(j=0;j<data_len;j++)
125 						{
126 							//Is there a better way to do this, rather than an inline function?
127 							tmp_data += pairs[i].replace(/\{([A-Z0-9 _\-]+)\}/gi, function(_, varName){
128 								return (rep_data[j][varName]) ? rep_data[j][varName] : ""; 
129 							});
130 						}
131 					}
132 					
133 					//Replace the looped content
134 					tmp = tmp.replace("{"+i+"}", tmp_data);
135 				}
136 			}
137 			
138 			//Replace all the rest of the psudeovariables
139 			pseudos = tmp.match(/\{([A-Z0-9_\-]+)\}/gim);
140 			
141 			if(pseudos != null)
142 			{
143 				num_pseudos = pseudos.length;
144 			
145 				for(i=0;i<num_pseudos;i++)
146 				{
147 					//Remove the {} from the pseudos
148 					var_name = pseudos[i].replace('{', '');
149 					var_name = var_name.replace('}', '');
150 					
151 					//Replace each pseudovariable with the value of the object
152 					//property of the same name
153 					tmp = tmp.replace(pseudos[i], data[var_name]);
154 				}
155 			}
156 			
157 			//Create an empty fragement
158 			frag = document.createDocumentFragment();
159 			
160 			//Insert the html
161 			frag.appendChild(document.createElement('section'));
162 			frag_section = frag.querySelectorAll('section')[0];
163 			frag_section.innerHTML = tmp;
164 			
165 			//Remove bad elements in the fragment, should be faster than being done live
166 			emptys = frag_section.querySelectorAll('[src=""], [href=""]');
167 			
168 			for(x in emptys)
169 			{
170 				if(emptys[x].parentNode)
171 				{
172 					emptys[x].parentNode.removeChild(emptys[x]);
173 				}
174 			}
175 			
176 			//Save the parsed template in an object for later retrieval
177 			_p[name] = tmp;
178 			
179 			return tmp;
180 		},
181 		/** 
182 		 * Inserts the formatted template into the page. If the url and data parameters
183 		 * are passed, it will retrieve a template file from the same domain, parse, 
184 		 * and insert the template into the page. 
185 		 *
186 		 * @memberOf $_.template
187 		 * @name apply
188 		 * @function
189 		 * @param string parsed_template/template_name
190 		 * @param [string] url
191 		 * @param [object] data
192 		 */
193 		apply: function(name, url, data)
194 		{
195 			//If the parsed template is supplied, just apply it to the passed selector
196 			if(typeof url === "undefined" && typeof data === "undefined")
197 			{
198 				//If the "name" variable is in the _p object, set that
199 				if(typeof _p[name] !== "undefined")
200 				{
201 					this.el.innerHTML = _p[name];
202 					return;
203 				}
204 			
205 				//Otherwise, set the passed string to the current selector
206 				this.el.innerHTML = name;
207 				return;
208 			}
209 			
210 			//Otherwise, get the template, parse it, and apply it
211 			$_.get(url, {}, function(res){
212 				var parsed;
213 			
214 				if(res === "")
215 				{
216 					console.log("Template is empty or can not be found");
217 					return;
218 				}
219 				
220 				//Cache the template in an object for later use
221 				_t[name] = res;
222 				parsed = this.parse(name, data);
223 				_p[name] = parsed;
224 				
225 				this.el.innerHTML = parsed;
226 			});
227 		}
228 	};
229 	
230 	//Add the module to the library
231 	$_.ext('template', t);
232 	
233 })();