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 })();