Lookup binding

by whelkaholism

HTML

<script src="http://cdnjs.cloudflare.com/ajax/libs/knockout/3.5.1/knockout-latest.js"></script>
<script src="https://cdn.cfront-cloud.co.uk/scripts/koa1.2/cf.linq.js"></script>
<script src="https://cdn.cfront-cloud.co.uk/scripts/koa1.2/cf.DOM.js"></script>
<input type="text" data-bind="lookup: { boundField: Category, source: AvailableCategories, selected: categorySelected, allowDropDown: true }" />

CSS

.koa-lookup {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 1px;
}

.koa-lookup-items {
    display: none;
    position: absolute;
    background: #ffffff;
    z-index: 1;
    min-width: 100%;
    box-shadow: 5px 5px 15px -3px rgba(0, 0, 0, 0.25);
    border-radius: var(--control-radius);
    top: 100%;
}

.koa-lookup-items.show {
  display: block;
}

.koa-lookup-items > li {
    padding: 0.1em 1em;
}

.koa-lookup-items > li:hover,
.koa-lookup-items > li.active {
    background: #00000040;
}

.koa-lookup-dropdown:before {
    font-family: 'Font Awesome 6 Pro';
    content: '\f078';
}

JavaScript

(function()
{
    var _getConfig = function(el, f_valueaccessor, allbindings, viewmodel, bindingcontext)
    {
        var o = ko.unwrap(f_valueaccessor());

        var res = {
            obs: o.boundField,
            idobs: o.boundIDField,
            idField: 'ID',
            valueField: 'Name',
            source: o.source,
            itemTemplate: o.itemTemplate || '{{Name}}',
            valueTemplate: o.itemTemplate || '{{Name}}',
            rateLimit: typeof (o.rateLimit) != 'undefined' ? o.rateLimit : 300,
            renderItem: o.renderItem,
            selected: o.selected,
            allowDropDown: !!o.allowDropDown,
            closeOnBodyClick: o.closeOnBodyClick !== true,
            _input: el
        };

        return res;
    };

    var _populateTemplate = function(template, item, cfg)
    {
        var res = (template || '').replace(/\{\{([^\}]+)\}\}/, function(m, g1)
        {
            var repres = item && ko.unwrap(item[g1]);
            return repres || '';
        });

        return res || '';
    };

    var _callSource = function(term, cfg)
    {
        return new Promise(function(resolve)
        {
            var res = null;
            var a = null;

            if (ko.isObservableArray(cfg.source))
                a = cfg.source();
            else if (typeof (cfg.source) == 'function')
                cfg.source(term).then(resolve);
            else
                a = cfg.source;

            if (a != null)
            {
                res = a.where(cfg.predicate || function(x)
                {
                    return new RegExp(RegExp.escape(term), 'i').test(typeof (x) == 'string' ? x : (x && x[cfg.valueField]));
                });
            }

            if (res != null)
                resolve(res);
        });
    };

    var _renderItem = function(item, cfg)
    {
        if (cfg.renderItem)
            return cfg.renderItem(item, cfg);
  ...