Known Issues

Displaying index limit message for Sitefinity versions 13.2.7523 and above

Goal
The aim of this article is to demonstrate how to display an error message when the index limit is hit in Sitefinity version 13.2.7523 and above.

Overview
In Sitefinity 13.2.7523 a breaking change was introduced to the way the indexing progress is displayed. Therefore in order for an error message to be displayed correctly when index limit is hit, a couple of changes need to be made to the script handling the indexing progress.

Add custom indexing script

  1. In your Sitefinity project create a folder e.g. Scripts
  2. In it create a new Javascript file with the name PublishingMasterExtensions
  3. Add the following code:
var sidebar;
var contextBar;
var filteredByLibrary = false;
var itemsGrid;
var actionCommandPrefix = "sf_binderCommand_";
var checkProgress = null;
var currentlyReindexedPoints = {};
var serviceBaseUrl;
var clientManager;
var itemToRowMapping = {};

function OnMasterViewLoaded(sender, args) {
    sidebar = sender.get_sidebar();
    contextBar = sender.get_contextBar();
    var binder = sender.get_binder();
    binder.add_onDataBound(handleBinderDataBound);
    binder.add_onItemDataBound(handleBinderItemDataBound);
    binder.add_onItemCommand(handleBinderItemCommand);
    binder.set_unescapeHtml(true);
    serviceBaseUrl = binder.get_serviceBaseUrl();
    var index = serviceBaseUrl.indexOf(".svc");
    serviceBaseUrl = serviceBaseUrl.substring(0, index + 4);
    clientManager = binder.get_manager();
    itemsGrid = sender.get_currentItemsList();
    itemsGrid.add_command(handleItemsGridCommand);
    startMonitoringReindexProgress();
}

function handleItemsGridCommand(sender, args) {
    switch (args.get_commandName()) {
        case "activeFeeds": itemsGrid.applyFilter("IsActive == true");
            break;
        case "inactiveFeeds": itemsGrid.applyFilter("IsActive == false");
            break;
        case "filterContent": var cArg = args.get_commandArgument();
            itemsGrid.applyFilter('ContentType == "' + cArg.value + '"');
            break;
        default: break;
    }
}

async function handleBinderItemCommand(sender, args) {
    var commandName = args.get_commandName();
    var dataItem = args.get_dataItem();
    var itemElement = args.get_itemElement();
    switch (commandName) {
        case "startUpdating": case "stopUpdating": var setActive = commandName === "startUpdating";
            serviceUrl = serviceBaseUrl + "/setactive/" + dataItem.Id + "/";
            var urlParams = {};
            urlParams.setActive = setActive;
            clientManager.InvokePut(serviceUrl, urlParams, {}, {}, successCallback, failureCallback, sender);
            break;
        case "runPipes": var serviceUrl = serviceBaseUrl + "/runpipes/" + dataItem.Id + "/";
            clientManager.InvokePut(serviceUrl, {}, {}, {}, successCallback, failureCallback, sender);
            break;
        case "reindex": var serviceUrl = serviceBaseUrl + "/reindex/" + dataItem.Id + "/";
            var dateRowId = itemElement.attributes.id.nodeValue;
            monitorReindexingProgress();
            var context = { itemID: dateRowId };
            showProcessingLoader(dateRowId, -1, "Indexing...");
            var isValid = await validateIndexes(dataItem.Title);

            if (isValid) {
                clientManager.InvokePut(serviceUrl, {}, {}, {}, successCallback, failureCallback, sender, false, null, context);
            }
            else {
                hideProcessingLoader(dateRowId);
                displayErrorMessage(dateRowId);
            }
            break;
        case "deactivate": var serviceUrl = serviceBaseUrl + "/setactive/" + dataItem.Id + "/?&setActive=false";
            var dateRowId = itemElement.attributes.id.nodeValue;
            var context = { itemID: dateRowId };
            clientManager.InvokePut(serviceUrl, {}, {}, {}, successCallback, failureCallback, sender, false, null, context);
            break;
        default: break;
    }
}

function successCallback(caller, data, request, context) {
    itemsGrid.dataBind();
}

function failureCallback(error, caller, context) {
    alert(error.Detail);
}

function handleBinderItemDataBound(sender, args) {
    var dataItem = args.get_dataItem();
    var pipes = dataItem.OutputPipes;
    var elem = args.FindControl("publishedAs");
    var list = jQuery("<ul class='sfPipeContentList' />").appendTo(jQuery(elem));
    jQuery.each(pipes, function (i, pipe) {
        jQuery("<li />").appendTo(list).addClass("sfPipeTitle sfPipe" + pipe.PipeName).html(pipe.UIName);
    });
    var command = dataItem.IsActive ? "startUpdating" : "stopUpdating";
    itemsGrid.removeActionsMenuItems([actionCommandPrefix + command], args.get_itemElement());
    if (dataItem.IsBackend) {
        itemsGrid.removeActionsMenuItems([actionCommandPrefix + "delete"], args.get_itemElement());
        if (!dataItem.IsActive) {
            itemsGrid.removeActionsMenuItems([actionCommandPrefix + "deactivate"], args.get_itemElement());
        }
    } else {
        itemsGrid.removeActionsMenuItems([actionCommandPrefix + "deactivate"], args.get_itemElement());
    } $("#" + args._itemElement.id).find(".sfProgressTaskCommand").click(function () {
        hideProcessingLoader(args._itemElement.id);
        itemsGrid.dataBind();
    });
    itemToRowMapping[dataItem.Id] = args._itemElement.id;
}

function handleBinderDataBound(sender, args) {
    monitorReindexingProgress();
}

function showProcessingLoader(itemID, progress, statusMessage) {
    var itemEl = jQuery("#" + itemID + " .sfDateAuthor");
    var actionsEl = jQuery("#" + itemID + " .sfMoreActions .actionsMenu");
    var progressStatus = progress < 0 ? itemEl.find(".resPending").html() : progress >= 100 ? itemEl.find(".resDone").html() : itemEl.find(".resInProgress").html();
    if (progress >= 100) {
        statusMessage = itemEl.find(".resIndexingContent").html();
    } itemEl.find(".sfLine").addClass("sfDisplayNoneImportant");
    var wrapper = itemEl.find(".sfProgress");
    wrapper.find(".sfProgressTaskDescription").html(statusMessage);
    wrapper.find(".sfMoveProgressWrp .sfProgressPercentage").html((progress < 0 ? 0 : progress) + "%");
    wrapper.find(".taskStatus").html(progressStatus);
    var innerDiv = wrapper.find(".sfProgressBarIn");
    var width = progress > 0 ? progress : 3;
    innerDiv.attr("style", "width : " + width + "%;");
    var doneBtn = wrapper.find(".sfProgressTaskCommand");
    if (progress >= 100) {
        doneBtn.show();
    } else {
        doneBtn.hide();
    } actionsEl.hide();
    wrapper.show();
}

function hideProcessingLoader(itemID) {
    var itemEl = jQuery("#" + itemID + " .sfDateAuthor");
    var actionsEl = jQuery("#" + itemID + " .sfMoreActions .actionsMenu");
    itemEl.find(".sfLine").removeClass("sfDisplayNoneImportant");
    itemEl.find(".sfProgress").hide();
    actionsEl.show();
}

function startMonitoringReindexProgress() {
    monitorReindexingProgress();
    checkProgress = setInterval(monitorReindexingProgress, 2000);
}

function stopMonitoringReindexProgress() {
    clearInterval(checkProgress);
    checkProgress = null;
}

function monitorReindexingProgress() {
    var serviceUrl = serviceBaseUrl + "/getReindexStatus/";
    clientManager.InvokeGet(serviceUrl, {}, {}, reindexMonitorSuccessCallback);
}

function reindexMonitorSuccessCallback(caller, data) {
    if (!Array.isArray(data)) {
        return;
    } var reindexedPointsToRemove = Object.keys(currentlyReindexedPoints);
    currentlyReindexedPoints = {};
    for (var i = 0;
        i < data.length;
        i++) {
        var item = data[i].Key;
        var status = data[i].Value;
        showProcessingLoader(itemToRowMapping[item], status.Progress, status.StatusMessage);
        currentlyReindexedPoints[item] = true;
    } for (var i = 0;
        i < reindexedPointsToRemove.length;
        i++) {
        var itemToRemove = reindexedPointsToRemove[i];
        if (!currentlyReindexedPoints.hasOwnProperty(itemToRemove)) {
            showProcessingLoader(itemToRowMapping[itemToRemove], 100);
            shouldRebind = true;
        }
    }
}

function displayErrorMessage(itemID) {
    var itemEl = jQuery("#" + itemID + " .sfDateAuthor");
    itemEl.find(".sfLine").addClass("sfDisplayNoneImportant");
    itemEl.append("<span class='sfLine' style='color:red'>The indexing operation was terminated because the maximum index limit in Hawksearch was exceeded!</span>");
}

function validateIndexes(title) {
    const url = `${location.protocol}//${location.hostname}${location.port ? ':' + location.port : ''}`;
    return $.ajax({
        url: url + '/hawk/validate/indexes',
        type: "POST",
        data: JSON.stringify({ name: title }),
        dataType: "json",
        contentType: "application/json; charset=utf-8",
    });
}
  1. Right click the file and choose Properties

  2. Under Build Action choose Embedded resource

  3. Rebuild your project

Register custom script in Sitefinity

  1. Open the backend of your Sitefinity instance
  2. Navigate to Administration → Settings → Advanced (your-site-domain/Sitefinity/Administration/Settings/Advanced)
  3. Locate the Search configuration
  4. Under Search → Controls → SearchBackend → Views → SearchBackendList → Scripts you will find the registration for the script that handles the indexing.
  5. Open it and under Script location enter the namespace of the script and the Assembly in which it is. e.g.
SitefinityWebApp.Scripts.PublishingMasterExtensions.js, SitefinityWebApp
  1. Under Name of the load method should be OnMasterViewLoaded

🚧

If you Assembly has whitespace or special characters in its name, that should be reflected in the script location. You can find the namespace of your assembly when you right click it and choose Properties.

Indexing Pages when a language is deleted

Goal
The purpose of this article is to help you haldle a bug when indexing pages that have been translated to a language that has been deleted.

🚧

This bug might appear on Sitefinity version 13.0 and earlier. From 13.1 multilinual is handled in a different way and this problem should not occur.

Problem explanation
In Sitefinity various languages can be added. And all kinds of content could be created in these languages or content could be translated into these chosen languages. When a site has more thаn two languages (ex: English, Spanish, German) and page is created in a language, Spanish for example, but afterwards the Spanish language is deleted, this page is automatically translated into the default language. Actually, it is not translated but fallbacks to an invariant culture, which is the default culture. This means that the pages are still visible in the backend and it looks like they are translated to the default language.
On a database level whenever we are adding languages (up to 5) new columns for this language are created, but all of them share the same PageNode id. When we create a page in Spanish the Spanish columns are filled. When the Spanish language is deleted in the backend this does not mean that the pages that have Spanish translation are deleted. They still exist and are visible in the backend. Sitefinity tries to shows the page in one of the other translations that are still available in the backend.

Whenever there are pages translated in different languages then there is one PageNode (shared PageNode) and different PageData for each translation. When a translation, a language, is deleted we delete the corresponding PageData. It is equal to null and this page can not be accessed if there are no other translations (no other PageData).

This is problem leads to another: the page still exists, but when we try to index the pages the operation fails. The reason is that these pages can not be resolved in the PushData method in the PageInboundPipe. And documents are not passed to the UpdateIndex method.

How can this bug be handled?
There is a simple workaround for handling this issue. Here are the steps:

  1. Inherit the PageInboundPipe
  2. Override the PushData(IList items)
  3. Add the logic shown below to check whether the PageData is null.
  4. Register the Pipe in the Global.asax file → follow the second code snippet.
using System.Collections.Generic;
using System.Linq;
using Telerik.Sitefinity.Pages.Model;
using Telerik.Sitefinity.Publishing;
using Telerik.Sitefinity.Publishing.Pipes;

namespace Hawksearch.Publishing
{
    public class CustomPageInboundPipe : PageInboundPipe
    {
        public override void PushData(IList<PublishingSystemEventInfo> items)
        {
            var itemsToPass = new List<PublishingSystemEventInfo>();

            var pageNodes = items.Where(i => i.Item is PageNode).Select(i => i.Item as PageNode).ToList();
            var pageNodeIds = pageNodes.Select(pn => pn.Id).ToList();

            var pageManager = Telerik.Sitefinity.Modules.Pages.PageManager.GetManager();
            var pageData = pageManager.GetPageDataList().Where(pd => pageNodeIds.Contains(pd.NavigationNodeId)).ToList();

            foreach (var page in pageData)
            {
                if (page != null)
                {
                    var itemToAdd = items.Where(i => i.Item is PageNode).FirstOrDefault(i => ((PageNode)i.Item).Id == page.NavigationNodeId);
                    if (itemToAdd != null)
                    {
                        itemsToPass.Add(itemToAdd);
                    }
                }
            }
            base.PushData(itemsToPass);
        }
    }
}

Hawksearch widgets on Hybrid page template in mobile resolution

Goal
The aim of this article is to demonstrate how to enable mobile users to search while the Hawksearch widgets are placed on a Hybrid template.

Overview
Due to limitations in Sitefinity while using the Hawksearch widgets on a Hybrid page while on mobile searching doesn’t work correctly. It simply redirects you to the next input on the page. Tо overcome this problem we have developed a simple mechanism which provides the users with a button to use while searching. In order for this button to submit the search request a script is needed. Below you will find both the code for the button and the script sample as well as ready to use examples.

🚧

This fix applies to the Vue template only.

Script and Button
You need to place this button under the search-box component. For further clarity you can look at the examples at the end of this article.

<input type="button" value="Search" id="search-btn"/>

There are two versions of the script. One is for the Hawksearh widget the other for the HawksearchBox widget.

  1. Hawksearch widget
<script>
    window.addEventListener("load", function (event) {
        var keyword;
        var searchButton = document.getElementById('search-btn');
        var searchBox = document.getElementsByClassName('hawk__searchBox__searchInput')[0].children[0];

        searchButton.addEventListener('click', function () {
            if (keyword && keyword.length > 0) {
                var url = window.location.href.split('?')[0];
                var query = "?keyword=" + keyword;
                url += query;
                location.replace(url);
            }
        });

        searchBox.addEventListener('input', function () {
            keyword = searchBox.value;
        });
    });
</script>
  1. HawksearchBox widget
<script>
    window.addEventListener("load", function (event) {
    var keyword;
    var searchButton = document.getElementById('search-btn');
    var searchBox = document.getElementsByClassName('hawk__searchBox__searchInput')[0].children[0];
    var model = @Html.Raw(Json.Encode(Model));
    var resultsUrl = model.ResultsUrl;

        searchButton.addEventListener('click', function () {
            if (keyword && keyword.length > 0) {
                var url = window.location.href.split('?')[0];
                if (resultsUrl && resultsUrl.length > 0) {
                    url = 'http://' + window.location.host + resultsUrl;
                }
                var query = "?keyword=" + keyword;
                url += query;
                location.replace(url);
            }
        });

        searchBox.addEventListener('input', function () {
            keyword = searchBox.value;
        });
    });
</script>

🚧

Choose the script according to which search box you are using - the separate box widget or the build in search box in the results widget.

Ready to use examples

Hawksearch widget:

@model HawksearchWidgets.Mvc.ViewModels.Hawksearch.SearchViewModel
@using Telerik.Sitefinity.Frontend.Mvc.Helpers;
@using Telerik.Sitefinity.Services
@using Telerik.Sitefinity.Web
@using Newtonsoft.Json
@using QueryStringHelper = HawksearchWidgets.Mvc.Helpers.QueryStringHelper;

@if (Model.Version == "V2L" || Model.Version == "V3L" || Model.Version == "V4L")
{
    <div data-role="hawk-results" class="row hs-wrap" style="display: none;">
        <div class="col-md-3 hs-col-3">
            <div id="hawkbannerlefttop"></div>
            <div id="hawkfacets"></div>
            <div id="hawkbannerleftbottom"></div>
            <input type="hidden" name="search-term" value="@Model.Keyword" />
        </div>
        <div role="main" class="col-md-9 hs-col-9" id="main-content">
            <div id="hawktitle"></div>
            <div class="right-bg">
                <div id="hawkbannertop"></div>
                <div id="hawktoptext"></div>
                <div id="hawkrelated"></div>
                <div id="hawktoppager"></div>
                <div id="hawktoptext"></div>

                <div id="hawkitemlist" class="item-list horizontal resource-listing clearfix">
                </div>

                <div id="hawkbottompager"></div>
                <div class="clear">&nbsp;</div>
            </div>
        </div>
    </div>

    if (!SystemManager.IsDesignMode)
    {
        @Html.Script(Url.WidgetContent("Mvc/Scripts/polyfills.js"), "top", false)
        @Html.Script(Url.WidgetContent("Mvc/Scripts/hawksearch-init.js"), "head", false)
        @Html.Script(Url.WidgetContent("Mvc/Scripts/hawksearch.js"), "head", false)
    }
}
else if (!SystemManager.IsDesignMode)
{
    if (Model.Languages.Length > 1 && Model.IsMultilingualEnabled)
    {
        <div>
            <span>@Html.Resource("ChangeResultsLanguageLabel", "HawkWidgetsResources") </span>

            @{
                var queryParameters = HttpContext.Current.Request.QueryString;

                for (var i = 0; i < Model.Languages.Length; i++)
                {
                    var language = Model.Languages[i];
                    var languageQuery = QueryStringHelper.Get(queryParameters, language);

                    <a href="@languageQuery">@language.DisplayName</a>
                    if (i < Model.Languages.Length - 2)
                    {
                        <span>, </span>
                    }
                    else if (i == Model.Languages.Length - 2)
                    {
                        <span> @Html.Resource("OrLabel", "HawkWidgetsResources") </span>
                    }
                }
            }
        </div>
    }

    @Html.Script(Url.WidgetContent("assets/build/js/vendor.bundle.js"), "bottom", false)
    @Html.Script(Url.WidgetContent("assets/build/js/main.js"), "bottom", false)

    @Html.StyleSheet(Url.WidgetContent("assets/build/css/vendor.css"), "head")
    @Html.StyleSheet(Url.WidgetContent("assets/build/css/main.css"), "head")
    @Html.StyleSheet(Url.WidgetContent("assets/dist/vue-hawksearch.css"), "head")
    @Html.StyleSheet(Url.WidgetContent("assets/dist/vue-hawksearch-override.css"), "head")


    <div data-component="vue-app-spa"
         data-client-guid="@Model.ClientId"
         data-hawksearch-tracking-api="@Model.TrackingUrl"
         data-hawksearch-base-api="@Model.HawksearchBaseAPI"
         data-hawksearch-search-api="@Model.HawksearchSearchingAPI"
         data-hawksearch-autocomplete-api="@Model.AutocompleteUrl"
         data-tracking-events="@Model.TrackingEvents"
         data-index-name="@Model.HawksearchIndexName"
         data-json-params="@Model.Data"
         data-current-culture="@Model.CurrentCulture"
         data-hawksearch-recommendation-api=""
         data-widget-guid=""
         data-show-searchbox="@Model.ShowSearchBox.ToString()">

        <div class="hawk">
            @if (Model.ShowSearchBox)
            {
                <div class="hawk__header">
                    <div data-component="hawksearch-field">
                        <search-box></search-box>
                        <input type="button" value="Search" id="search-btn" style="display:flex"/>
                    </div>
                </div>
            }

            <div class="hawk__body">
                <div data-component="hawksearch-facets">
                    <facet-list></facet-list>
                </div>

                <div data-component="hawksearch-results">
                    <results></results>
                </div>
            </div>
        </div>
    </div>

    <script id="vue-hawksearch-result-item" type="x-template">
        <div class="media-body sf-media-body" v-on:click="onClick">
            <h3>
                <template v-if="link">
                    <a :href=link>{{ title }}</a>
                </template>
                <template v-else>
                    {{ title }}
                </template>
            </h3>
            <p>
                <strong class="sfHighlight">{{ title }}</strong>
                <span>{{ content }}</span>
            </p>
            <a :href="link">{{ link }}</a>
        </div>
    </script>

    <script>
        window.addEventListener("load", function (event) {
            var keyword;
            var searchButton = document.getElementById('search-btn');
            var searchBox = document.getElementsByClassName('hawk__searchBox__searchInput')[0].children[0];

            searchButton.addEventListener('click', function () {
                if (keyword && keyword.length > 0) {
                    var url = window.location.href.split('?')[0];
                    var query = "?keyword=" + keyword;
                    url += query;
                    location.replace(url);
                }
            });

            searchBox.addEventListener('input', function () {
                keyword = searchBox.value;
            });
        });
    </script>

    <script data-translations="vue-translations" type="application/json">
        {
        "Narrow Results": "@Html.HtmlSanitize(Html.Resource("NarrowResults", "HawkWidgetsResources"))",
        "Search Results": "@Html.HtmlSanitize(Html.Resource("SearchResults", "HawkWidgetsResources"))",
        "Search Results for": "@Html.HtmlSanitize(Html.Resource("SearchResultsFor", "HawkWidgetsResources"))",
        "Sort By": "@Html.HtmlSanitize(Html.Resource("SortBy", "HawkWidgetsResources"))",
        "Enter a search term": "@Html.HtmlSanitize(Html.Resource("EnterKeyword", "HawkWidgetsResources"))",
        "Quick Lookup": "@Html.HtmlSanitize(Html.Resource("QuickLookup", "HawkWidgetsResources"))",
        "Clear All": "@Html.HtmlSanitize(Html.Resource("ClearAll", "HawkWidgetsResources"))",
        "Clear": "@Html.HtmlSanitize(Html.Resource("Clear", "HawkWidgetsResources"))",
        "No Results": "@Html.HtmlSanitize(Html.Resource("NoResults", "HawkWidgetsResources"))",
        "Loading": "@Html.HtmlSanitize(Html.Resource("Loading", "HawkWidgetsResources"))",
        "You've Selected": "@Html.HtmlSanitize(Html.Resource("YouSelected", "HawkWidgetsResources"))",
        "response_error_generic": "An error occurred while searching for your results. Please contact the site administrator."
        }
    </script>

}

HawksearchBox widget:

@model HawksearchWidgets.Mvc.ViewModels.HawksearchBox.SearchBoxViewModel
@using Telerik.Sitefinity.Frontend.Mvc.Helpers;
@using Telerik.Sitefinity.Modules.Pages
@using Telerik.Sitefinity.Services
@using Newtonsoft.Json

@if (Model.Version == "V2L" || Model.Version == "V3L" || Model.Version == "V4L")
{
    <div class="site-search">
        <input type="hidden" value="@Model.Index">
        <input type="hidden" value="@Model.ResultsUrl" data-search-page="@Model.ResultsUrl">

        <div>
            <input class="site-search-input" placeholder="@Html.HtmlSanitize(Html.Resource("ImLookingFor", "HawkWidgetsResources"))" type="text" id="txtSiteSearch">
            <button class="site-search-btn" id="btnSiteSearch">
                <span class="visually-hidden">@Html.HtmlSanitize(Html.Resource("SubmitButtonText"))</span>
                <svg class="icon icon-search-01">
                    <use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#icon-search-01"></use>
                </svg>
            </button>
        </div>
    </div>

    //string hawkCssUrl = Hawksearch.Helpers.HawksearchApiHelper.GetHawksearchUrl().Replace("http://", "https://") + "/includes/hawksearch.css";
    @*@Html.StyleSheet(hawkCssUrl, "head", false)*@
    @Html.Script(ScriptRef.JQuery, "top", false)

    if (!SystemManager.IsDesignMode)
    {
        @Html.Script(Url.WidgetContent("Mvc/Scripts/polyfills.js"), "top", false)
        @Html.Script(Url.WidgetContent("Mvc/Scripts/hawksearch-init.js"), "head", false)
        @Html.Script(Url.WidgetContent("Mvc/Scripts/hawksearch.js"), "head", false)
        @Html.Script(Url.WidgetContent("Mvc/Scripts/hawksearch-autosuggest.js"), "bottom", false)
        @Html.Script(Url.WidgetContent("Mvc/Scripts/hawksearchbox.js"), "bottom", false)
    }
}
else
{

    @Html.Script(Url.WidgetContent("assets/build/js/vendor.bundle.js"), "bottom", false)
    @Html.Script(Url.WidgetContent("assets/build/js/main.js"), "bottom", false)

    @Html.StyleSheet(Url.WidgetContent("assets/build/css/vendor.css"), "head")
    @Html.StyleSheet(Url.WidgetContent("assets/build/css/main.css"), "head")
    @Html.StyleSheet(Url.WidgetContent("assets/dist/vue-hawksearch.css"), "head")
    @Html.StyleSheet(Url.WidgetContent("assets/dist/vue-hawksearch-override.css"), "head")

    <div data-component="vue-app-searchbox"
         data-client-guid="@Model.ClientId"
         data-hawksearch-tracking-api="@Model.TrackingUrl"
         data-hawksearch-base-api="@Model.HawksearchBaseAPI"
         data-hawksearch-search-api="@Model.HawksearchSearchingAPI"
         data-hawksearch-autocomplete-api="@Model.AutocompleteUrl"
         data-search-page="@Model.ResultsUrl"
         data-show-searchbox="True"
         data-json-params="@Model.Data"
         data-index-name="@Model.Index"
         data-current-culture="@Model.CurrentCulture">

        <div class="hawk">
            <div class="hawk__header">
                <div data-component="hawksearch-field" style="display:flex">
                    <search-box search-page="@Model.ResultsUrl"></search-box>
                    <input type="button" value="Search" id="search-btn"  style="display:flex"/>
                </div>
            </div>
        </div>
    </div>


    <script>
        window.addEventListener("load", function (event) {
        var keyword;
        var searchButton = document.getElementById('search-btn');
        var searchBox = document.getElementsByClassName('hawk__searchBox__searchInput')[0].children[0];
        var model = @Html.Raw(Json.Encode(Model));
        var resultsUrl = model.ResultsUrl;

            searchButton.addEventListener('click', function () {
                if (keyword && keyword.length > 0) {
                    var url = window.location.href.split('?')[0];
                    if (resultsUrl && resultsUrl.length > 0) {
                        url = 'http://' + window.location.host + resultsUrl;
                    }
                    var query = "?keyword=" + keyword;
                    url += query;
                    location.replace(url);
                }
            });

            searchBox.addEventListener('input', function () {
                keyword = searchBox.value;
            });
        });
    </script>

    <script data-translations="vue-translations" type="application/json">
            {
            "Narrow Results": "@Html.HtmlSanitize(Html.Resource("NarrowResults", "HawkWidgetsResources"))",
            "Search Results": "@Html.HtmlSanitize(Html.Resource("SearchResults", "HawkWidgetsResources"))",
            "Search Results for": "@Html.HtmlSanitize(Html.Resource("SearchResultsFor", "HawkWidgetsResources"))",
            "Sort By": "@Html.HtmlSanitize(Html.Resource("SortBy", "HawkWidgetsResources"))",
            "Enter a search term": "@Html.HtmlSanitize(Html.Resource("EnterKeyword", "HawkWidgetsResources"))",
            "Quick Lookup": "@Html.HtmlSanitize(Html.Resource("QuickLookup", "HawkWidgetsResources"))",
            "Clear All": "@Html.HtmlSanitize(Html.Resource("ClearAll", "HawkWidgetsResources"))",
            "Clear": "@Html.HtmlSanitize(Html.Resource("Clear", "HawkWidgetsResources"))",
            "No Results": "@Html.HtmlSanitize(Html.Resource("NoResults", "HawkWidgetsResources"))",
            "Loading": "@Html.HtmlSanitize(Html.Resource("Loading", "HawkWidgetsResources"))",
            "You've Selected": "@Html.HtmlSanitize(Html.Resource("YouSelected", "HawkWidgetsResources"))",
            "response_error_generic": "An error occurred while searching for your results. Please contact the site administrator."
            }
    </script>

}