learn.knockoutjs.com - Creating custom bindings

http://learn.knockoutjs.com/#/?tutorial=custombindings

by olli

HTML

<script src="http://learn.knockoutjs.com/Scripts/Lib/jquery.tmpl.js"></script>
<script src="http://learn.knockoutjs.com/Scripts/Lib/jquery.address.js"></script>
<script src="http://learn.knockoutjs.com/Scripts/Lib/knockout-1.3.0.latest.js"></script>
<link rel="stylesheet" href="http://learn.knockoutjs.com/Content/App/coderunner.css">
<link rel="stylesheet" href="http://learn.knockoutjs.com/Content/TutorialSpecific/custombindings.css">
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.8.14/themes/start/jquery-ui.css">
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.0rc2/jquery.mobile-1.0rc2.min.css">
<script src="http://code.jquery.com/mobile/1.0rc2/jquery.mobile-1.0rc2.min.js"></script>
<body>
    <div data-role="page" id="mypage">
        <div data-role="header">
            <h1>
                Tutorial 4
            </h1>
        </div>
        <div data-role="content">
            <h3 data-bind="text: question"></h3>
            <p class="content-textbox">Please distribute <span data-bind="text: pointsBudget"></span> points between the following options.</p>
            
            
            <div data-bind="template: { name: 'answerTemplate', foreach: answers }"></div>
            <script type="text/html" id="answerTemplate">
                <div class="content-colored-textbox">
                    ${ text }<br/>
                    <span data-bind="starRating: points"></span>
                </div>
            </script>
            
            
            <h3 data-bind="fadeVisible: pointsUsed() > pointsBudget">You've used too many points! Please remove some.</h3>
            <p class="content-textbox">You've got <b data-bind="text: pointsBudget - pointsUsed()"></b> points left to use.</p>
            <button data-bind="jqmButton: { enable: pointsUsed() <= pointsBudget }, click: save">Finished</button>             
            
        </div>
    </div>
</body>

CSS

.content-textbox {
    background: none repeat scroll 0 0 #F9F9F9;
    box-shadow: 0 0 3px #CCCCCC;
    line-height: 1.2em;
    margin-bottom: 15px;
    margin-top: 10px;
    padding: 8px;
}


.content-colored-textbox {
    background: none repeat scroll 0 0 #FFFFCC;
    box-shadow: 0 0 3px #CCCCCC;
    line-height: 1.2em;
    margin-bottom: 15px;
    margin-top: 10px;
    padding: 8px;

}

JavaScript

(function() { // Wrap in function to prevent accidental globals
    $('#mypage').live('pageinit', function() {

        // ----------------------------------------------------------------------------
        // Reusable bindings - ideally kept in a separate file
        ko.bindingHandlers.fadeVisible = {
            init: function(element, valueAccessor) {
                // Start visible/invisible according to initial value
                var shouldDisplay = valueAccessor();
                $(element).toggle(shouldDisplay);
            },
            update: function(element, valueAccessor) {
                // On update, fade in/out
                var shouldDisplay = valueAccessor();
                shouldDisplay ? $(element).fadeIn() : $(element).fadeOut();
            }
        };

        ko.bindingHandlers.jqmButton = {
/*
        init: function(element) {
            $(element).button(); 
        },*/
            update: function(element, valueAccessor) {
                var currentValue = valueAccessor();
                $(element).button((currentValue.enable === true) ? "enable" : "disable");
            }
        };

        ko.bindingHandlers.starRating = {
            init: function(element, valueAccessor) {
                $(element).addClass("starRating");
                for (var i = 0; i < 5; i++)
                $("<span>").appendTo(element);

                // Handle mouse events on the stars
                $("span", element).each(function(index) {
                    $(this).hover(

                    function() {
                        $(this).prevAll().add(this).addClass("hoverChosen")
                    }, function() {
                        $(this).prevAll().add(this).removeClass("hoverChosen")
                    }).click(function() {
                        var observable = valueAccessor(); // Get the associated observable
                        observable(index + 1); // Write the new rating to it
                    });
        ...