KO Bootstrap bindings.

by gbos

HTML

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css">
<div class="container">

  <ul data-bind="tbsTabs: tabIdx">
    <li>aaaaaa</li>
    <li>bbbbb</li>
    <li>cccccccc</li>
    <li>ddddd</li>
    <li>dddddd</li>
    <li>eeeeee</li>
  </ul>
  <br>
  <input data-bind="value: tabIdx">
  <br>

<hr>

  <tbs-pager params="total: total, pageSize: pageSize, pageIndex: pageIndex"></tbs-pager>
  <br>
  <input data-bind="value: total">
  <input data-bind="value: pageSize">
  <input data-bind="value: pageIndex">

<hr>

<button class="btn" data-bind="click: show">Dialog</button>



</div>



<!-- Modal -->
<div class="modal fade"  tabindex="-1" data-bind="tbsModal: modalOpen">
  <div class="modal-dialog" role="document">
    <div class="modal-content">
      <div class="modal-header">
        <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
        <h4 class="modal-title" id="myModalLabel">Modal title</h4>
      </div>
      <div class="modal-body">
        ...
      </div>
      <div class="modal-footer">
        <button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
        <button type="button" class="btn btn-primary">Save changes</button>
      </div>
    </div>
  </div>
</div>

JavaScript

(function(ko) {
  //'use strict';
  
Element.prototype.on = Element.prototype.addEventListener;
NodeList.prototype.forEach = Array.prototype.forEach;

ko.bindingHandlers.tbsTabs = {
  init: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    var val = valueAccessor();
    var $ = element.querySelector.bind(element),
      $$ = element.querySelectorAll.bind(element);

    element.classList.add('nav', 'nav-tabs');
    $('li').classList.add('active');
    
    var eventHandlers = []; // array of objects: target, eventName, handler
    
    $$('li').forEach(function (li, i) {
      var a = document.createElement('a');
      a.setAttribute('href', '#');
      a.textContent = li.textContent;
			li.replaceChild(a, li.firstChild);
      
      a.on('click', function(evt) {
        eventHandlers.push({target: evt.target, eventName: evt.type, handler: arguments.callee});
        val(i);
        $('li.active').classList.remove('active');
        $$('li')[i].classList.add('active');
      });
    });

 		ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
      this.eventHandlers.forEach(function (eh) {  eh.target.removeEventListener(eh.eventName, eh.handler); });
      $$('li').forEach(function (li, i) {
      	li.replaceChild(li.firstChild.firstChild, li.firstChild);
      });
    });


  },
  update: function(element, valueAccessor, allBindings, viewModel, bindingContext) {
    var val = valueAccessor();
    var $ = element.querySelector.bind(element),
      $$ = element.querySelectorAll.bind(element);

    var idx = val() || 0;
    $('li.active').classList.remove('active');
    $$('li')[idx].classList.add('active');

  }

};

})(ko);

/**
 *  Twitter bootstrap pagination component.
 *       component name: 'tbs-pager'
 *       component template id: 'tbs-pager'
 *       params: total: nbr of elements, pageSize: nr of elements to show on 1 page, pageIndex: currently selected page (0 based) (observable).
 *
 */
(function(ko) {
 ...