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