Resume

Hortidom

https://hortidom.com/

https://hortidom.blog/

About

Using the Hugo framework, I designed and developed a simple landing page along with a blog that includes categories, tags, and a search function. I continue to update and maintain these websites.

To prevent older blog posts from getting lost, I implemented a static search bar in the navigation bar using JavaScript. The search function scans for matching phrases in titles, content, and tags. Below is a breakdown of the script.

Search function that initializes search logic


var searchFn = function () {
    var lastSearchTerm = "";
    var stopwords = [
        "ja", "mnie", "moje", "my", "nasze", "ty", "to", "jego", "jej", "ten", "ta", "jestem", "jest", "są", "był", "być",
        "ma", "miał", "robi", "jeden", "jaka", "ale", "jeśli", "lub", "jak", "z", "przy", "dla", "z", "do",
        "potem", "nie", "tak", "też", "może", "i", "oraz"
    ];
    var normalizer = document.createElement("textarea");
    var searchLimit = 40;
    var minSearchLength = 2;
    var isSearching = false;

Normalizes input text by removing special characters and converting to lowercase


    var normalizeText = function (input) {
        normalizer.innerHTML = input;
        return " " + normalizer.value.trim().toLowerCase()
            .replace(/[^0-9a-ząćęłńóśźż ]/gi, " ")
            .replace(/\s+/g, " ") + " ";
    };

Renders search results by sorting them by weight and appending them to the page


    var renderResults = function (results) {
        results.sort((a, b) => b.weight - a.weight);
        
        for (var i = 0; i < results.length && i < searchLimit; i++) {
            var item = results[i].item;
            var resultHTML = `
                <div class="container result-box">
                    <div class="row">
                        <div class="col-8 img-center">
                            <a href="${item.permalink}" alt="${item.showTitle}">
                                <img src="${item.image}" alt="${item.showTitle}" class="rounded w-100">
                            </a>
                        </div>
                        <div class="col-12">
                            <h2>
                                <a href="${item.permalink}">${item.showTitle}</a>
                            </h2>
                        </div>
                    </div>
                </div>`;
            $("#results").append(resultHTML);
        }
    };

Calculates the relevance weight of search terms in different content fields


    var calculateWeight = function (terms, weight, targetText) {
        var totalWeight = 0;
        terms.forEach(term => {
            var index = targetText.indexOf(term.term);
            while (index !== -1) {
                totalWeight += term.weight * weight;
                index = targetText.indexOf(term.term, index + 1);
            }
        });
        return totalWeight;
    };

Executes the search by checking terms against indexed content and ranking results


    var search = function (terms) {
        var results = [];
        
        searchHost.index.forEach(item => {
            if (!item.tags) return;
            
            var weight = 0;
            terms.forEach(term => {
                if (item.title.startsWith(term.term)) {
                    weight += term.weight * 32;
                }
            });

            weight += calculateWeight(terms, 1, item.content);
            item.tags.forEach(tag => {
                weight += calculateWeight(terms, 4, tag);
            });
            weight += calculateWeight(terms, 16, item.title);
            
            if (weight) {
                results.push({ weight, item });
            }
        });

        $("#results").html(results.length ? "<p></p>" : "<p>Brak wyników.</p>");
        if (results.length) renderResults(results);
    };

Initiates the search process when the user types or clicks the search button


    var executeSearch = function () {
        if (isSearching) return;
        
        var searchTerm = normalizeText($("#searchBox").val()).trim();
        if (searchTerm === lastSearchTerm) return;
        lastSearchTerm = searchTerm;
        
        if (searchTerm.length < minSearchLength) {
            $("#btnGo").attr("disabled", true);
            return;
        }
        
        $("#btnGo").removeAttr("disabled");
        isSearching = true;
        var terms = searchTerm.split(" ");
        var searchTerms = [];
        
        terms.forEach((_, i) => {
            for (var j = i; j < terms.length; j++) {
                var weight = Math.pow(2, j - i);
                var phrase = terms.slice(i, j + 1).join(" ");
                if (phrase.length >= minSearchLength && !stopwords.includes(phrase)) {
                    searchTerms.push({ weight, term: " " + phrase + " " });
                }
            }
        });
        
        search(searchTerms);
        isSearching = false;
    };

Sets up event listeners for search input and button click actions


    var initSearch = function () {
        $("#searchBox").keyup(executeSearch);
        $("#btnGo").click(function () {
            executeSearch();
            window.location.href = window.location.href.split("#")[0] + "#resultsArea";
        });
        executeSearch();
    };

Loads search index from a JSON file and initializes search functionality


    var searchHost = {};
    $.getJSON("/index.json", function (results) {
        searchHost.index = [];
        var seenLinks = {};

        results.forEach(result => {
            if (result.tags && !seenLinks[result.permalink]) {
                searchHost.index.push({
                    showTitle: result.title,
                    title: normalizeText(result.title),
                    content: normalizeText(result.content),
                    tags: result.tags.map(tag => normalizeText(tag)),
                    permalink: result.permalink,
                    image: result.image
                });
                seenLinks[result.permalink] = true;
            }
        });

        $("#loading").hide();
        $("#btnGo").show();
        $("#searchBox").show().removeAttr("disabled").focus();
        initSearch();
    });
};

Runs the search function once the page is fully loaded

window.addEventListener("DOMContentLoaded", searchFn);