1 (function() { // anonymize 2 3 var allTags = {}; 4 var loadedResults = []; 5 6 /** 7 * Initialization code run upon the DOM being ready. 8 */ 9 $(document).ready(function() { 10 // Parse page query parameters. 11 var params = parseParams(document.location.search); 12 params.tag = params.tag ? makeArray(params.tag) : null; 13 14 // Load tag and resource dataset. 15 loadTags(); 16 loadResources(); 17 18 showResults(params); 19 20 // Watch for keypresses in the keyword filter textbox, and update 21 // search results to reflect the keyword filter. 22 $('#resource-browser-keyword-filter').keyup(function() { 23 // Filter results on screen by keyword. 24 var keywords = $(this).val().split(/\s+/g); 25 for (var i = 0; i < loadedResults.length; i++) { 26 var hide = false; 27 for (var j = 0; j < keywords.length; j++) { 28 if (!resultMatchesKeyword(loadedResults[i].result, keywords[j])) { 29 hide = true; 30 break; 31 } 32 } 33 34 loadedResults[i].node[hide ? 'hide' : 'show'](); 35 } 36 }); 37 }); 38 39 /** 40 * Returns whether or not the given search result contains the given keyword. 41 */ 42 function resultMatchesKeyword(result, keyword) { 43 keyword = keyword.toLowerCase(); 44 if (result.title && 45 result.title.en.toLowerCase().indexOf(keyword) >= 0) 46 return true; 47 else if (result.description && 48 result.description.en.toLowerCase().indexOf(keyword) >= 0) 49 return true; 50 else if (result.topicsHtml && 51 result.topicsHtml.replace(/\<.*?\>/g,'').toLowerCase().indexOf(keyword) >= 0) 52 return true; 53 return false; 54 } 55 56 /** 57 * Populates the allTags array with tag data from the ANDROID_TAGS 58 * variable in the resource data JS file. 59 */ 60 function loadTags() { 61 for (var tagClass in ANDROID_TAGS) { 62 for (var tag in ANDROID_TAGS[tagClass]) { 63 allTags[tag] = { 64 displayTag: ANDROID_TAGS[tagClass][tag], 65 tagClass: tagClass 66 }; 67 } 68 } 69 } 70 71 /** 72 * Massage the ANDROID_RESOURCES resource list in the resource data JS file. 73 */ 74 function loadResources() { 75 for (var i = 0; i < ANDROID_RESOURCES.length; i++) { 76 var resource = ANDROID_RESOURCES[i]; 77 78 // Convert the tags array to a tags hash for easier querying. 79 resource.tagsHash = {}; 80 for (var j = 0; j < resource.tags.length; j++) 81 resource.tagsHash[resource.tags[j]] = true; 82 83 // Determine the type and topics of the resource by inspecting its tags. 84 resource.topics = []; 85 for (tag in resource.tagsHash) 86 if (tag in allTags) { 87 if (allTags[tag].tagClass == 'type') { 88 resource.type = tag; 89 } else if (allTags[tag].tagClass == 'topic') { 90 resource.topics.push(tag); 91 } 92 } 93 94 // Add a humanized topics list string. 95 resource.topicsHtml = humanizeList(resource.topics, function(item) { 96 return '<strong>' + allTags[item].displayTag + '</strong>'; 97 }); 98 } 99 } 100 101 /** 102 * Loads resources for the given query parameters. 103 */ 104 function showResults(params) { 105 loadedResults = []; 106 $('#resource-browser-search-params').empty(); 107 $('#resource-browser-results').empty(); 108 109 var i, j; 110 var searchTags = []; 111 if (params.tag) { 112 for (i = 0; i < params.tag.length; i++) { 113 var tag = params.tag[i]; 114 if (tag.toLowerCase() in allTags) { 115 searchTags.push(tag.toLowerCase()); 116 } 117 } 118 } 119 120 if (searchTags.length) { 121 // Show query params. 122 var taggedWithHtml = ['Showing technical resources tagged with ']; 123 taggedWithHtml.push(humanizeList(searchTags, function(item) { 124 return '<strong>' + allTags[item].displayTag + '</strong>'; 125 })); 126 $('#resource-browser-search-params').html(taggedWithHtml.join('') + ':'); 127 } else { 128 $('#resource-browser-search-params').html('Showing all technical resources:'); 129 } 130 131 var results = []; 132 133 // Create the list of resources to show. 134 for (i = 0; i < ANDROID_RESOURCES.length; i++) { 135 var resource = ANDROID_RESOURCES[i]; 136 var skip = false; 137 138 if (searchTags.length) { 139 for (j = 0; j < searchTags.length; j++) 140 if (!(searchTags[j] in resource.tagsHash)) { 141 skip = true; 142 break; 143 } 144 145 if (skip) 146 continue; 147 148 results.push(resource); 149 continue; 150 } 151 152 results.push(resource); 153 } 154 155 // Format and show the list of resource results. 156 if (results.length) { 157 $('#resource-browser-results .no-results').hide(); 158 for (i = 0; i < results.length; i++) { 159 var result = results[i]; 160 var resultJqNode = $(tmpl('tmpl_resource_browser_result', result)); 161 for (tag in result.tagsHash) 162 resultJqNode.addClass('tagged-' + tag); 163 $('#resource-browser-results').append(resultJqNode); 164 165 loadedResults.push({ node: resultJqNode, result: result }); 166 } 167 } else { 168 $('#resource-browser-results .no-results').show(); 169 } 170 } 171 172 /** 173 * Formats the given array into a human readable, English string, ala 174 * 'a, b and c', with an optional item formatter/wrapper function. 175 */ 176 function humanizeList(arr, itemFormatter) { 177 itemFormatter = itemFormatter || function(o){ return o; }; 178 arr = arr || []; 179 180 var out = []; 181 for (var i = 0; i < arr.length; i++) { 182 out.push(itemFormatter(arr[i]) + 183 ((i < arr.length - 2) ? ', ' : '') + 184 ((i == arr.length - 2) ? ' and ' : '')); 185 } 186 187 return out.join(''); 188 } 189 190 /** 191 * Parses a parameter string, i.e. foo=1&bar=2 into 192 * a dictionary object. 193 */ 194 function parseParams(paramStr) { 195 var params = {}; 196 paramStr = paramStr.replace(/^[?#]/, ''); 197 198 var pairs = paramStr.split('&'); 199 for (var i = 0; i < pairs.length; i++) { 200 var p = pairs[i].split('='); 201 var key = p[0] ? decodeURIComponent(p[0]) : p[0]; 202 var val = p[1] ? decodeURIComponent(p[1]) : p[1]; 203 if (val === '0') 204 val = 0; 205 if (val === '1') 206 val = 1; 207 208 if (key in params) { 209 // Handle array values. 210 params[key] = makeArray(params[key]); 211 params[key].push(val); 212 } else { 213 params[key] = val; 214 } 215 } 216 217 return params; 218 } 219 220 /** 221 * Returns the argument as a single-element array, or the argument itself 222 * if it's already an array. 223 */ 224 function makeArray(o) { 225 if (!o) 226 return []; 227 228 if (typeof o === 'object' && 'splice' in o) { 229 return o; 230 } else { 231 return [o]; 232 } 233 } 234 235 })(); 236