Robert Ly | fd1eb13 | 2012-06-08 12:21:15 -0700 | [diff] [blame] | 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 | })(); |