knocout bootstrap popover with templating

by Keyur Patel

HTML

<script src="http://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/2.3.0/js/bootstrap.min.js"></script>
<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">
<script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.debug.js"></script>
<!doctype html>
<html>
    
    <body>
        <div data-bind="foreach: items">
            <span data-bind="text: label"></span>
            <input type="checkbox" data-bind="checked: required" />
            <button data-bind="popover: 'settingsPopover'">settings</button>
            <br />
        </div>
        <script type="text/html" id="settingsPopover">
            <h4> <span class="icon-cog"> &nbsp; </span> Attributes</h4> <label> Label </label>
            <input type="text" data-bind="value: label, valueUpdate:'afterkeydown'" /> <label class = "checkbox" > <input type="checkbox" data-bind="checked: required" />Required</label>
        </script>
    </body>
</html>

JavaScript

function s4() {
    return Math.floor((1 + Math.random()) * 0x10000)
        .toString(16)
        .substring(1);
};

function guid() {
    return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
}

ko.bindingHandlers.withProperties = {
    init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        // Make a modified binding context, with a extra properties, and apply it to descendant elements
        var newProperties = valueAccessor(),
            innerBindingContext = bindingContext.extend(newProperties);
        ko.applyBindingsToDescendants(innerBindingContext, element);
 
        // Also tell KO *not* to bind the descendants itself, otherwise they will be bound twice
        return { controlsDescendantBindings: true };
    }
};

// Bind Twitter Popover
ko.bindingHandlers.popover = {
    init: function (element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
        var tmplId = ko.utils.unwrapObservable(valueAccessor());
        var tmplHtml = $('#' + tmplId).html();
        var uuid = guid();
        var domId = "ko-bs-popover-" + uuid;
        var tmplDom = $('<div/>', {
            "class": "ko-popover",
            "id": domId,
            "data-bind": "withProperties: { label: '" + viewModel.label() + "', required: '" + viewModel.required() + "' }"
        }).html(tmplHtml);

        options = {
            content: tmplDom[0].outerHTML
        };

        // Need to copy this, otherwise all the popups end up with the value of the last item
        var popoverOptions = $.extend({}, ko.bindingHandlers.popover.options);
        popoverOptions.content = options.content;

        console.log($(element));
        console.log(element);

        $(element).bind('click', function () {
            $(this).popover(popoverOptions).popover('toggle');
            // If you apply this when the popup isn't visible, I think that it tries to bind to thewhole pageand throws an error
  ...