1// ê²ì ê¸°ë¥ JavaScript 2$(document).ready(function () { 3 // ê²ì ì»´í¬ëí¸ ì´ê¸°í 4 initializeSearchComponents(); 5}); 6 7function initializeSearchComponents() { 8 // 기본 ê²ì ì»´í¬ëí¸ ì´ê¸°í 9 initializeSearchComponent("basic-search-input", "search-results-dropdown", "search-results-list", "full"); 10 11 // í¤ìë ê²ì ì»´í¬ëí¸ ì´ê¸°í 12 initializeSearchComponent("basic-search-keyword-input", "search-keyword-results-dropdown", "search-keyword-results-list", "keyword"); 13} 14 15function initializeSearchComponent(inputId, resultsId, listId, searchType) { 16 var searchTimeout; 17 var currentRequest = null; 18 var $searchInput = $("#" + inputId); 19 var $searchResults = $("#" + resultsId); 20 var $searchResultsList = $("#" + listId); 21 var lastKeyword = ""; 22 var cachedResults = []; // ê²ì ê²°ê³¼ ìºì ì¶ê° 23 24 // ê²ìì´ ì ë ¥ ì (ì¤ìê°) 25 $searchInput.on("input", function () { 26 var keyword = $(this).val().trim(); 27 28 clearTimeout(searchTimeout); 29 30 if (currentRequest) { 31 currentRequest.abort(); 32 } 33 34 if (keyword.length < 1) { 35 $searchResults.hide(); 36 cachedResults = []; // ìºì ì´ê¸°í 37 return; 38 } 39 40 // ê²ìì´ê° ìì ëë 결과를 ì¨ê¸°ì§ ìì 41 searchTimeout = setTimeout(function () { 42 performSearch(keyword); 43 }, 100); 44 }); 45 46 function performSearch(keyword) { 47 if (keyword === lastKeyword) { 48 return; 49 } 50 51 lastKeyword = keyword; 52 53 currentRequest = $.ajax({ 54 url: "/search/basic_search", 55 method: "GET", 56 data: { 57 keyword: keyword, 58 search_type: searchType, 59 }, 60 dataType: "json", 61 contentType: "application/x-www-form-urlencoded; charset=UTF-8", 62 success: function (response) { 63 if (response.keyword !== $searchInput.val().trim()) { 64 return; 65 } 66 67 if (response.success && response.results.length > 0) { 68 cachedResults = response.results; // ê²°ê³¼ ìºì 69 displayResults(response.results); 70 } else { 71 cachedResults = []; // ìºì ì´ê¸°í 72 if (response.success && response.results.length === 0) { 73 $searchResultsList.html('<div class="search-loading">ê²ì ê²°ê³¼ê° ììµëë¤.</div>'); 74 $searchResults.show(); 75 } else { 76 $searchResults.hide(); 77 } 78 } 79 }, 80 error: function (xhr, status, error) { 81 if (status === "abort") { 82 return; 83 } 84 85 console.error("Search error:", error); 86 console.error("Response:", xhr.responseText); 87 $searchResultsList.html('<div class="search-loading">ê²ì ì¤ ì¤ë¥ê° ë°ìíìµëë¤.</div>'); 88 }, 89 complete: function () { 90 currentRequest = null; 91 }, 92 }); 93 } 94 95 function displayResults(results) { 96 $searchResultsList.empty(); 97 var currentKeyword = $searchInput.val().trim(); 98 99 results.forEach(function (result) { 100 // íì ì ë°ë¥¸ íì í ì¤í¸ ì¤ì 101 var typeText = "ê²ìê¸"; // ê¸°ë³¸ê° 102 if (result.type === "page") { 103 typeText = "íì´ì§"; 104 } else if (result.type === "board_name") { 105 typeText = "ê²ìí"; 106 } 107 108 var titleHtml = '<div class="search-result-title" title="' + result.title + '"><span class="search-result-type ' + result.type + '">' + typeText + "</span>"; 109 110 // ê²ìê¸ì¸ ê²½ì° ê²ìí ëª ì ì 목 ìì ì¶ê° (í¤ìë ê²ìì´ ìë ëë§) 111 if (result.type === "board" && result.description && searchType !== "keyword") { 112 var boardName = result.description.split(" - ")[0]; 113 // ê²ìí ì´ë¦ìë í¤ìë íì´ë¼ì´í¸ ì²ë¦¬ 114 var highlightedBoardName = highlightKeyword(boardName, currentKeyword); 115 titleHtml += '<span class="search-result-board">[' + highlightedBoardName + "]</span> "; 116 } 117 118 // ì 목ìì í¤ìë íì´ë¼ì´í¸ ì²ë¦¬ 119 var highlightedTitle = highlightKeyword(result.title, currentKeyword); 120 titleHtml += highlightedTitle + "</div>"; 121 122 // ì¤ëª ììë í¤ìë íì´ë¼ì´í¸ ì²ë¦¬ (í¤ìë ê²ìì´ ìë ëë§, ìì
122±ì ì´ë¦ ì ì¸) 123 var highlightedDescription = ""; 124 if (result.description && searchType !== "keyword") { 125 // ìì±ì ì´ë¦ ë¶ë¶ ì ê±° (ì: "ê²ìíëª - ìì±ìëª "ìì "ê²ìíëª "ë§ ì¬ì©) 126 var descriptionParts = result.description.split(" - "); 127 if (descriptionParts.length > 1) { 128 // ê²ìí ì´ë¦ë§ ì¬ì© 129 var boardNameOnly = descriptionParts[0]; 130 highlightedDescription = '<div class="search-result-description">' + highlightKeyword(boardNameOnly, currentKeyword) + "</div>"; 131 } else { 132 // ì¤ëª ì 구ë¶ìê° ìì¼ë©´ ê·¸ëë¡ ì¬ì© 133 highlightedDescription = '<div class="search-result-description">' + highlightKeyword(result.description, currentKeyword) + "</div>"; 134 } 135 } 136 137 var $item = $("<div>") 138 .addClass("search-result-item") 139 .data("url", result.url) // URLì data ìì±ì ì ì¥ 140 .html(titleHtml + highlightedDescription); 141 142 $item.on("click", function () { 143 window.location.href = result.url; 144 }); 145 146 $searchResultsList.append($item); 147 }); 148 149 $searchResults.show(); 150 151 // 첫 ë²ì§¸ í목 ìë ì í 152 var $firstItem = $searchResultsList.find(".search-result-item").first(); 153 if ($firstItem.length > 0) { 154 $firstItem.addClass("active"); 155 } 156 } 157 158 // í¤ìë íì´ë¼ì´í¸ í¨ì 159 function highlightKeyword(text, keyword) { 160 if (!keyword || keyword.length === 0) { 161 return text; 162 } 163 164 // ëì문ì êµ¬ë¶ ìì´ ê²ìí기 ìí´ ì문ìë¡ ë³ííì¬ ë¹êµ 165 var lowerText = text.toLowerCase(); 166 var lowerKeyword = keyword.toLowerCase(); 167 168 if (lowerText.indexOf(lowerKeyword) === -1) { 169 return text; 170 } 171 172 // ì본 í ì¤í¸ìì í¤ìë ìì¹ ì°¾ê¸° 173 var result = ""; 174 var lastIndex = 0; 175 var currentIndex = 0; 176 177 while ((currentIndex = lowerText.indexOf(lowerKeyword, lastIndex)) !== -1) { 178 // í¤ìë ì´ì í ì¤í¸ ì¶ê° 179 result += text.substring(lastIndex, currentIndex); 180 // í¤ìë ë¶ë¶ì íì´ë¼ì´í¸ ì²ë¦¬ 181 result += '<strong class="search-highlight">' + text.substring(currentIndex, currentIndex + keyword.length) + "</strong>"; 182 183 lastIndex = currentIndex + keyword.length; 184 } 185 186 // ë¨ì í ì¤í¸ ì¶ê° 187 result += text.substring(lastIndex); 188 189 return result; 190 } 191 192 // ì¸ë¶ í´ë¦ ì ëë¡ë¤ì´ ë«ê¸° (ìì ) 193 $(document).on("click", function (e) { 194 // ê²ì ì ë ¥ì°½ì´ë ê²°ê³¼ ììì í´ë¦í ê²½ì°ê° ìë ëë§ ë«ê¸° 195 if (!$(e.target).closest(".basic-search-container, .basic-search-keyword-container").length) { 196 $searchResults.hide(); 197 } 198 }); 199 200 // í¤ë³´ë ë¤ë¹ê²ì´ì (ìí°í¤ - 첫 ë²ì§¸ í목 ëë ì íë í목ì¼ë¡ ì´ë) 201 $searchInput.on("keydown", function (e) { 202 var $items = $searchResultsList.find(".search-result-item"); 203 var $activeItem = $items.filter(".active"); 204 205 switch (e.keyCode) { 206 case 40: // ìë íì´í 207 e.preventDefault(); 208 // ê²ì ê²°ê³¼ê° ì¨ê²¨ì ¸ ìì¼ë©´ ë¤ì íì 209 if ($searchResults.is(":hidden") && cachedResults.length > 0) { 210 displayResults(cachedResults); 211 $items = $searchResultsList.find(".search-result-item"); // ìì´í ë¤ì ê°ì ¸ì¤ê¸° 212 } 213 214 if ($items.length === 0) return; // ìì´í ì´ ìì¼ë©´ ì¢ ë£ 215 216 if ($activeItem.length === 0) { 217 // íì± ìì´í ì´ ìì¼ë©´ 첫 ë²ì§¸ ìì´í ì í 218 $items.first().addClass("active"); 219 } else { 220 // íì¬ íì± ìì´í ì ë¤ì ìì´í ì í 221 var $nextItem = $activeItem.next(); 222 if ($nextItem.length === 0) { 223 // ë§ì§ë§ ìì´í ì´ë©´ 첫 ë²ì§¸ ìì´í ì¼ë¡ 224 $items.first().addClass("active"); 225 } else { 226 $nextItem.addClass("active"); 227 } 228 $activeItem.removeClass("active"); 229 } 230 break; 231 232 case 38: // ì íì´í 233 e.preventDefault();
234 // ê²ì ê²°ê³¼ê° ì¨ê²¨ì ¸ ìì¼ë©´ ë¤ì íì 235 if ($searchResults.is(":hidden") && cachedResults.length > 0) { 236 displayResults(cachedResults); 237 $items = $searchResultsList.find(".search-result-item"); // ìì´í ë¤ì ê°ì ¸ì¤ê¸° 238 } 239 240 if ($items.length === 0) return; // ìì´í ì´ ìì¼ë©´ ì¢ ë£ 241 242 if ($activeItem.length === 0) { 243 // íì± ìì´í ì´ ìì¼ë©´ ë§ì§ë§ ìì´í ì í 244 $items.last().addClass("active"); 245 } else { 246 // íì¬ íì± ìì´í ì ì´ì ìì´í ì í 247 var $prevItem = $activeItem.prev(); 248 if ($prevItem.length === 0) { 249 // 첫 ë²ì§¸ ìì´í ì´ë©´ ë§ì§ë§ ìì´í ì¼ë¡ 250 $items.last().addClass("active"); 251 } else { 252 $prevItem.addClass("active"); 253 } 254 $activeItem.removeClass("active"); 255 } 256 break; 257 258 case 13: // ìí°í¤ - 첫 ë²ì§¸ í목 ëë ì íë í목ì¼ë¡ ì´ë 259 e.preventDefault(); 260 if ($items.length > 0) { 261 var targetItem; 262 if ($activeItem.length > 0) { 263 // íì± ìì´í ì´ ìì¼ë©´ í´ë¹ ìì´í ì¼ë¡ ì´ë 264 targetItem = $activeItem; 265 } else { 266 // íì± ìì´í ì´ ìì¼ë©´ 첫 ë²ì§¸ ìì´í ì¼ë¡ ì´ë 267 targetItem = $items.first(); 268 } 269 270 var url = targetItem.data("url"); 271 if (url) { 272 window.location.href = url; 273 } 274 } 275 break; 276 277 case 27: // ESC 278 $searchResults.hide(); 279 break; 280 } 281 }); 282 283 // í¬ì»¤ì¤ ì ê²°ê³¼ íì (ê°ì ) 284 $searchInput.on("focus", function () { 285 var keyword = $(this).val().trim(); 286 if (keyword.length >= 1) { 287 if (cachedResults.length > 0) { 288 // ìºìë ê²°ê³¼ê° ìì¼ë©´ íì 289 displayResults(cachedResults); 290 } else if ($searchResultsList.children().length > 0) { 291 // 기존 ê²°ê³¼ê° ìì¼ë©´ íì 292 $searchResults.show(); 293 } 294 } 295 }); 296 297 // ë¸ë¬ ì íì± ìì´í ì ê±° (ìì ) 298 $searchInput.on("blur", function () { 299 // ê²ì 결과를 ì¨ê¸°ì§ ìê³ íì± ìì´í ë§ ì ê±° 300 setTimeout(function () { 301 $searchResultsList.find(".search-result-item").removeClass("active"); 302 }, 200); 303 }); 304}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.