<!DOCTYPE html>
<html>

  <head>
    <link data-require="bootstrap-css@3.0.2" data-semver="3.0.2" rel="stylesheet" href="//netdna.bootstrapcdn.com/bootstrap/3.0.2/css/bootstrap.min.css" />
    <link data-require="jqueryui@*" data-semver="1.10.0" rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.0/css/smoothness/jquery-ui-1.10.0.custom.min.css" />
    <link rel="stylesheet" href="footable.core.css" />
    <script data-require="jquery@*" data-semver="2.0.3" src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
    <script data-require="jqueryui@*" data-semver="1.10.0" src="//cdnjs.cloudflare.com/ajax/libs/jqueryui/1.10.0/jquery-ui.js"></script>
    <script data-require="bootstrap@*" data-semver="3.0.2" src="//netdna.bootstrapcdn.com/bootstrap/3.0.2/js/bootstrap.min.js"></script>
    <script src="footable.js"></script>
    <script src="footable.paginate.js"></script>
    <script src="footable.sort.js"></script>
    <script src="footable.striping.js"></script>
    <script data-require="knockout@*" data-semver="3.0.0" src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.0.0/knockout-debug.js"></script>
    <script src="knockout-delegatedEvents.js"></script>
    <script src="showModalBinding.js"></script>
    <script src="footableBinding.js"></script>
    <script src="tasks.js"></script>
  </head>

  <body>
    <div id="tasks" class="panel panel-default">
      <div class="panel-heading">
        <span class="panel-title">
          <i class="pad icon-gear"></i>

Tasks</span>
      </div>
      <div class="panel-body panel-trim"></div>
      <div class="panel-body no-padding">
        <div data-bind="template: { name: 'taskTable'}"></div>
        <div data-bind="template: { name: 'taskModal'}"></div>
      </div>
      <div class="panel-footer"></div>
    </div>
    <script type="text/html" id="taskTable">
      <table class="table table-striped footable toggle-circle" data-page-navigation=".paginate" data-page-size="3">
          <thead>
              <tr>
                  <th colspan="5">
                      <a data-bind="click: addItem" href="#">
                          <span class="btn btn-primary btn-xs pull-left">
                              <i class="icon-plus">Add Task</i>
                          </span>
                      </a>
                  </th>
              </tr>
              <tr>
                  <th>Desc</th>
                  <th data-hide="phone">Due On</th>
                  <th data-sort-ignore="true"></th>
              </tr>
          </thead>
          <tbody data-bind="footable: tasks, delegatedHandler: 'click'">
              <tr>
                  <td data-click="selectItem" data-bind="text: description"></td>
  
                  <td data-click="selectItem" data-bind="text: dueOn"></td>
                  
                  <td class="text-center">
                      <div>
                          <a href="#" data-click="removeItem">
                              <span class="btn btn-danger btn-xs pull-right">
                                  <i class="icon-remove">Remove Task</i>
                              </span>
                          </a>
                      </div>
                  </td>
              </tr>
          </tbody>
          <tfoot>
            <tr class="hide-if-no-paging">
                <td colspan="100" class="text-center">
                    <div class="paginate">
                        <ul class="pagination"></ul>
                    </div>
                </td>
            </tr>
        </tfoot>
      </table>
    </script>
    <script type="text/html" id="taskModal">
        <div class="modal fade" data-backdrop="static" data-bind="showModal: itemForEditing">
            <div class="modal-dialog" data-bind="with: itemForEditing">
                <div class="modal-content">
                    <div class="modal-header">
                        <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
                        <h3 data-bind=""></h3>
                    </div>
                    <div class="modal-body">
                        <p>
                            <label>Description: </label>
                            <input data-bind="value: description" />
                        </p>
                        <p>
                            <label>Due On: </label>
                            <input data-bind="value: dueOn" />
                        </p>
                        <p>
                            <div data-bind="visible: showTasks">
                                <label>Sub Tasks: </label>
                                <div data-bind="template: { name: 'taskTable', data: tasksObj}"></div>
                                <div data-bind="template: { name: 'taskModal', data: tasksObj}"></div>
                            </div>
                        </p>
                    </div>
                    <div class="modal-footer">
                        <a href="#" class="btn" data-bind="click: $parent.acceptEdit">Ok</a>
                        <a href="#" class="btn" data-bind="click: $parent.applyEdit">Apply</a>
                        <a href="#" class="btn" data-bind="click: $parent.cancelEdit">Cancel</a>
                    </div>
                </div>
            </div>
        </div>
    </script>
    <script>
      $(document).ready(function() {
        var allData = [{"id":"70000000-0000-0000-0000-00000000901","description":"1","tasks":[{"id":"70000000-0000-0000-0000-00000000802","description":"2","tasks":[{"id":"50000000-0000-0000-0000-000000001001","description":"3","tasks":[],"tasksObj":{"tasks":[],"parentId":"50000000-0000-0000-0000-000000001001"},"showTasks":true,"parentTaskId":"70000000-0000-0000-0000-00000000802"}],"tasksObj":{"tasks":[{"id":"50000000-0000-0000-0000-000000001001","description":"3","tasks":[],"tasksObj":{"tasks":[],"parentId":"50000000-0000-0000-0000-000000001001"},"showTasks":true,"parentTaskId":"70000000-0000-0000-0000-00000000802"}],"parentId":"70000000-0000-0000-0000-00000000802"},"showTasks":true,"parentTaskId":"70000000-0000-0000-0000-00000000901"}],"tasksObj":{"tasks":[{"id":"70000000-0000-0000-0000-00000000802","description":"2","tasks":[{"id":"50000000-0000-0000-0000-000000001001","description":"3","tasks":[],"tasksObj":{"tasks":[],"parentId":"50000000-0000-0000-0000-000000001001"},"showTasks":true,"parentTaskId":"70000000-0000-0000-0000-00000000802"}],"tasksObj":{"tasks":[{"id":"50000000-0000-0000-0000-000000001001","description":"3","tasks":[],"tasksObj":{"tasks":[],"parentId":"50000000-0000-0000-0000-000000001001"},"showTasks":true,"parentTaskId":"70000000-0000-0000-0000-00000000802"}],"parentId":"70000000-0000-0000-0000-00000000802"},"showTasks":true,"parentTaskId":"70000000-0000-0000-0000-00000000901"}],"parentId":"70000000-0000-0000-0000-00000000901"},"showTasks":true,"parentTaskId":""},{"id":"70000000-0000-0000-0000-00000000902","description":"2","tasks":[],"tasksObj":{"tasks":[],"parentId":"70000000-0000-0000-0000-00000000902"},"showTasks":true,"parentTaskId":""},{"id":"70000000-0000-0000-0000-00000000903","description":"3","tasks":[],"tasksObj":{"tasks":[],"parentId":"70000000-0000-0000-0000-00000000903"},"showTasks":true,"parentTaskId":""}];
        ko.applyBindings(new Tasks(allData),document.getElementById("tasks"));
      });
    </script>
  </body>

</html>
@font-face {
  font-family: 'footable';
  src: url('fonts/footable.eot');
  src: url('fonts/footable.eot?#iefix') format('embedded-opentype'), url('fonts/footable.woff') format('woff'), url('fonts/footable.ttf') format('truetype'), url('fonts/footable.svg#footable') format('svg');
  font-weight: normal;
  font-style: normal;
}
@media screen and (-webkit-min-device-pixel-ratio: 0) {
  @font-face {
    font-family: 'footable';
    src: url('fonts/footable.svg#footable') format('svg');
    font-weight: normal;
    font-style: normal;
  }
}
.footable {
  width: 100%;
  /** SORTING **/

  /** PAGINATION **/

}
.footable.breakpoint > tbody > tr.footable-detail-show > td {
  border-bottom: none;
}
.footable.breakpoint > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
  content: "\e001";
}
.footable.breakpoint > tbody > tr:hover:not(.footable-row-detail) {
  cursor: pointer;
}
.footable.breakpoint > tbody > tr > td.footable-cell-detail {
  background: #eee;
  border-top: none;
}
.footable.breakpoint > tbody > tr > td > span.footable-toggle {
  display: inline-block;
  font-family: 'footable';
  speak: none;
  font-style: normal;
  font-weight: normal;
  font-variant: normal;
  text-transform: none;
  -webkit-font-smoothing: antialiased;
  padding-right: 5px;
  font-size: 14px;
  color: #888888;
}
.footable.breakpoint > tbody > tr > td > span.footable-toggle:before {
  content: "\e000";
}
.footable.breakpoint.toggle-circle > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
  content: "\e005";
}
.footable.breakpoint.toggle-circle > tbody > tr > td > span.footable-toggle:before {
  content: "\e004";
}
.footable.breakpoint.toggle-circle-filled > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
  content: "\e003";
}
.footable.breakpoint.toggle-circle-filled > tbody > tr > td > span.footable-toggle:before {
  content: "\e002";
}
.footable.breakpoint.toggle-square > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
  content: "\e007";
}
.footable.breakpoint.toggle-square > tbody > tr > td > span.footable-toggle:before {
  content: "\e006";
}
.footable.breakpoint.toggle-square-filled > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
  content: "\e009";
}
.footable.breakpoint.toggle-square-filled > tbody > tr > td > span.footable-toggle:before {
  content: "\e008";
}
.footable.breakpoint.toggle-arrow > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
  content: "\e00f";
}
.footable.breakpoint.toggle-arrow > tbody > tr > td > span.footable-toggle:before {
  content: "\e011";
}
.footable.breakpoint.toggle-arrow-small > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
  content: "\e013";
}
.footable.breakpoint.toggle-arrow-small > tbody > tr > td > span.footable-toggle:before {
  content: "\e015";
}
.footable.breakpoint.toggle-arrow-circle > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
  content: "\e01b";
}
.footable.breakpoint.toggle-arrow-circle > tbody > tr > td > span.footable-toggle:before {
  content: "\e01d";
}
.footable.breakpoint.toggle-arrow-circle-filled > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
  content: "\e00b";
}
.footable.breakpoint.toggle-arrow-circle-filled > tbody > tr > td > span.footable-toggle:before {
  content: "\e00d";
}
.footable.breakpoint.toggle-arrow-tiny > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
  content: "\e01f";
}
.footable.breakpoint.toggle-arrow-tiny > tbody > tr > td > span.footable-toggle:before {
  content: "\e021";
}
.footable.breakpoint.toggle-arrow-alt > tbody > tr.footable-detail-show > td > span.footable-toggle:before {
  content: "\e017";
}
.footable.breakpoint.toggle-arrow-alt > tbody > tr > td > span.footable-toggle:before {
  content: "\e019";
}
.footable.breakpoint.toggle-medium > tbody > tr > td > span.footable-toggle {
  font-size: 18px;
}
.footable.breakpoint.toggle-large > tbody > tr > td > span.footable-toggle {
  font-size: 24px;
}
.footable > thead > tr > th {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: -moz-none;
  -ms-user-select: none;
  user-select: none;
}
.footable > thead > tr > th.footable-sortable:hover {
  cursor: pointer;
}
.footable > thead > tr > th.footable-sorted > span.footable-sort-indicator:before {
  content: "\e013";
}
.footable > thead > tr > th.footable-sorted-desc > span.footable-sort-indicator:before {
  content: "\e012";
}
.footable > thead > tr > th > span.footable-sort-indicator {
  display: inline-block;
  font-family: 'footable';
  speak: none;
  font-style: normal;
  font-weight: normal;
  font-variant: normal;
  text-transform: none;
  -webkit-font-smoothing: antialiased;
  padding-left: 5px;
}
.footable > thead > tr > th > span.footable-sort-indicator:before {
  content: "\e022";
}
.footable > tfoot .pagination {
  margin: 0;
}
.footable.no-paging .hide-if-no-paging {
  display: none;
}
.footable-row-detail-inner {
  display: table;
}
.footable-row-detail-row {
  display: table-row;
  line-height: 1.5em;
}
.footable-row-detail-group {
  display: block;
  line-height: 2em;
  font-size: 1.2em;
  font-weight: bold;
}
.footable-row-detail-name {
  display: table-cell;
  font-weight: bold;
  padding-right: 0.5em;
}
.footable-row-detail-value {
  display: table-cell;
}
.footable-odd {
  background-color: #f7f7f7;
}
/*!
 * FooTable - Awesome Responsive Tables
 * Version : 2.0.1.2
 * http://fooplugins.com/plugins/footable-jquery/
 *
 * Requires jQuery - http://jquery.com/
 *
 * Copyright 2013 Steven Usher & Brad Vincent
 * Released under the MIT license
 * You are free to use FooTable in commercial projects as long as this copyright header is left intact.
 *
 * Date: 21 Sep 2013
 */
(function ($, w, undefined) {
    w.footable = {
        options: {
            delay: 100, // The number of millseconds to wait before triggering the react event
            breakpoints: { // The different screen resolution breakpoints
                phone: 480,
                tablet: 1024
            },
            parsers: {  // The default parser to parse the value out of a cell (values are used in building up row detail)
                alpha: function (cell) {
                    return $(cell).data('value') || $.trim($(cell).text());
                },
                numeric: function (cell) {
                    var val = $(cell).data('value') || $(cell).text().replace(/[^0-9.\-]/g, '');
                    val = parseFloat(val);
                    if (isNaN(val)) val = 0;
                    return val;
                }
            },
            addRowToggle: true,
            calculateWidthOverride: null,
            toggleSelector: ' > tbody > tr:not(.footable-row-detail)', //the selector to show/hide the detail row
            columnDataSelector: '> thead > tr:last-child > th, > thead > tr:last-child > td', //the selector used to find the column data in the thead
            detailSeparator: ':', //the separator character used when building up the detail row
            toggleHTMLElement: '<span />', // override this if you want to insert a click target rather than use a background image.
            createGroupedDetail: function (data) {
                var groups = { '_none': { 'name': null, 'data': [] } };
                for (var i = 0; i < data.length; i++) {
                    var groupid = data[i].group;
                    if (groupid !== null) {
                        if (!(groupid in groups))
                            groups[groupid] = { 'name': data[i].groupName || data[i].group, 'data': [] };

                        groups[groupid].data.push(data[i]);
                    } else {
                        groups._none.data.push(data[i]);
                    }
                }
                return groups;
            },
            createDetail: function (element, data, createGroupedDetail, separatorChar, classes) {
                /// <summary>This function is used by FooTable to generate the detail view seen when expanding a collapsed row.</summary>
                /// <param name="element">This is the div that contains all the detail row information, anything could be added to it.</param>
                /// <param name="data">
                ///  This is an array of objects containing the cell information for the current row.
                ///  These objects look like the below:
                ///    obj = {
                ///      'name': String, // The name of the column
                ///      'value': Object, // The value parsed from the cell using the parsers. This could be a string, a number or whatever the parser outputs.
                ///      'display': String, // This is the actual HTML from the cell, so if you have images etc you want moved this is the one to use and is the default value used.
                ///      'group': String, // This is the identifier used in the data-group attribute of the column.
                ///      'groupName': String // This is the actual name of the group the column belongs to.
                ///    }
                /// </param>
                /// <param name="createGroupedDetail">The grouping function to group the data</param>
                /// <param name="separatorChar">The separator charactor used</param>
                /// <param name="classes">The array of class names used to build up the detail row</param>

                var groups = createGroupedDetail(data);
                for (var group in groups) {
                    if (groups[group].data.length === 0) continue;
                    if (group !== '_none') element.append('<div class="' + classes.detailInnerGroup + '">' + groups[group].name + '</div>');

                    for (var j = 0; j < groups[group].data.length; j++) {
                        var separator = (groups[group].data[j].name) ? separatorChar : '';
                        element.append('<div class="' + classes.detailInnerRow + '"><div class="' + classes.detailInnerName + '">' + groups[group].data[j].name + separator + '</div><div class="' + classes.detailInnerValue + '">' + groups[group].data[j].display + '</div></div>');
                    }
                }
            },
            classes: {
                main: 'footable',
                loading: 'footable-loading',
                loaded: 'footable-loaded',
                toggle: 'footable-toggle',
                disabled: 'footable-disabled',
                detail: 'footable-row-detail',
                detailCell: 'footable-row-detail-cell',
                detailInner: 'footable-row-detail-inner',
                detailInnerRow: 'footable-row-detail-row',
                detailInnerGroup: 'footable-row-detail-group',
                detailInnerName: 'footable-row-detail-name',
                detailInnerValue: 'footable-row-detail-value',
                detailShow: 'footable-detail-show'
            },
            triggers: {
                initialize: 'footable_initialize',                      //trigger this event to force FooTable to reinitialize
                resize: 'footable_resize',                              //trigger this event to force FooTable to resize
                redraw: 'footable_redraw',								//trigger this event to force FooTable to redraw
                toggleRow: 'footable_toggle_row',                       //trigger this event to force FooTable to toggle a row
                expandFirstRow: 'footable_expand_first_row',            //trigger this event to force FooTable to expand the first row
                expandAll: 'footable_expand_all',                       //trigger this event to force FooTable to expand all rows
                collapseAll: 'footable_collapse_all'                    //trigger this event to force FooTable to collapse all rows
            },
            events: {
                alreadyInitialized: 'footable_already_initialized',     //fires when the FooTable has already been initialized
                initializing: 'footable_initializing',                  //fires before FooTable starts initializing
                initialized: 'footable_initialized',                    //fires after FooTable has finished initializing
                resizing: 'footable_resizing',                          //fires before FooTable resizes
                resized: 'footable_resized',                            //fires after FooTable has resized
                redrawn: 'footable_redrawn',                            //fires after FooTable has redrawn
                breakpoint: 'footable_breakpoint',                      //fires inside the resize function, when a breakpoint is hit
                columnData: 'footable_column_data',                     //fires when setting up column data. Plugins should use this event to capture their own info about a column
                rowDetailUpdating: 'footable_row_detail_updating',      //fires before a detail row is updated
                rowDetailUpdated: 'footable_row_detail_updated',        //fires when a detail row is being updated
                rowCollapsed: 'footable_row_collapsed',                 //fires when a row is collapsed
                rowExpanded: 'footable_row_expanded',                   //fires when a row is expanded
                rowRemoved: 'footable_row_removed',                     //fires when a row is removed
                reset: 'footable_reset'                                 //fires when FooTable is reset
            },
            debug: false, // Whether or not to log information to the console.
            log: null
        },

        version: {
            major: 0, minor: 5,
            toString: function () {
                return w.footable.version.major + '.' + w.footable.version.minor;
            },
            parse: function (str) {
                version = /(\d+)\.?(\d+)?\.?(\d+)?/.exec(str);
                return {
                    major: parseInt(version[1], 10) || 0,
                    minor: parseInt(version[2], 10) || 0,
                    patch: parseInt(version[3], 10) || 0
                };
            }
        },

        plugins: {
            _validate: function (plugin) {
                ///<summary>Simple validation of the <paramref name="plugin"/> to make sure any members called by FooTable actually exist.</summary>
                ///<param name="plugin">The object defining the plugin, this should implement a string property called "name" and a function called "init".</param>

                if (!$.isFunction(plugin)) {
                  if (w.footable.options.debug === true) console.error('Validation failed, expected type "function", received type "{0}".', typeof plugin);
                  return false;
                }
                var p = new plugin();
                if (typeof p['name'] !== 'string') {
                    if (w.footable.options.debug === true) console.error('Validation failed, plugin does not implement a string property called "name".', p);
                    return false;
                }
                if (!$.isFunction(p['init'])) {
                    if (w.footable.options.debug === true) console.error('Validation failed, plugin "' + p['name'] + '" does not implement a function called "init".', p);
                    return false;
                }
                if (w.footable.options.debug === true) console.log('Validation succeeded for plugin "' + p['name'] + '".', p);
                return true;
            },
            registered: [], // An array containing all registered plugins.
            register: function (plugin, options) {
                ///<summary>Registers a <paramref name="plugin"/> and its default <paramref name="options"/> with FooTable.</summary>
                ///<param name="plugin">The plugin that should implement a string property called "name" and a function called "init".</param>
                ///<param name="options">The default options to merge with the FooTable's base options.</param>

                if (w.footable.plugins._validate(plugin)) {
                    w.footable.plugins.registered.push(plugin);
                    if (typeof options === 'object') $.extend(true, w.footable.options, options);
                }
            },
            load: function(instance){
              var loaded = [], registered, i;
              for(i = 0; i < w.footable.plugins.registered.length; i++){
                try {
                  registered = w.footable.plugins.registered[i];
                  loaded.push(new registered(instance));
                } catch (err) {
                  if (w.footable.options.debug === true) console.error(err);
                }
              }
              return loaded;
            },
            init: function (instance) {
                ///<summary>Loops through all registered plugins and calls the "init" method supplying the current <paramref name="instance"/> of the FooTable as the first parameter.</summary>
                ///<param name="instance">The current instance of the FooTable that the plugin is being initialized for.</param>

                for (var i = 0; i < instance.plugins.length; i++) {
                    try {
                      instance.plugins[i]['init'](instance);
                    } catch (err) {
                        if (w.footable.options.debug === true) console.error(err);
                    }
                }
            }
        }
    };

    var instanceCount = 0;

    $.fn.footable = function (options) {
        ///<summary>The main constructor call to initialize the plugin using the supplied <paramref name="options"/>.</summary>
        ///<param name="options">
        ///<para>A JSON object containing user defined options for the plugin to use. Any options not supplied will have a default value assigned.</para>
        ///<para>Check the documentation or the default options object above for more information on available options.</para>
        ///</param>

        options = options || {};
        var o = $.extend(true, {}, w.footable.options, options); //merge user and default options
        return this.each(function () {
            instanceCount++;
            var footable = new Footable(this, o, instanceCount);
			$(this).data('footable', footable);
        });
    };

    //helper for using timeouts
    function Timer() {
        ///<summary>Simple timer object created around a timeout.</summary>
        var t = this;
        t.id = null;
        t.busy = false;
        t.start = function (code, milliseconds) {
            ///<summary>Starts the timer and waits the specified amount of <paramref name="milliseconds"/> before executing the supplied <paramref name="code"/>.</summary>
            ///<param name="code">The code to execute once the timer runs out.</param>
            ///<param name="milliseconds">The time in milliseconds to wait before executing the supplied <paramref name="code"/>.</param>

            if (t.busy) {
                return;
            }
            t.stop();
            t.id = setTimeout(function () {
                code();
                t.id = null;
                t.busy = false;
            }, milliseconds);
            t.busy = true;
        };
        t.stop = function () {
            ///<summary>Stops the timer if its runnning and resets it back to its starting state.</summary>

            if (t.id !== null) {
                clearTimeout(t.id);
                t.id = null;
                t.busy = false;
            }
        };
    }

    function Footable(t, o, id) {
        ///<summary>Inits a new instance of the plugin.</summary>
        ///<param name="t">The main table element to apply this plugin to.</param>
        ///<param name="o">The options supplied to the plugin. Check the defaults object to see all available options.</param>
        ///<param name="id">The id to assign to this instance of the plugin.</param>

        var ft = this;
        ft.id = id;
        ft.table = t;
        ft.options = o;
        ft.breakpoints = [];
        ft.breakpointNames = '';
        ft.columns = {};
        ft.plugins = w.footable.plugins.load(ft);

        var opt = ft.options,
            cls = opt.classes,
            evt = opt.events,
            trg = opt.triggers,
            indexOffset = 0;

        // This object simply houses all the timers used in the FooTable.
        ft.timers = {
            resize: new Timer(),
            register: function (name) {
                ft.timers[name] = new Timer();
                return ft.timers[name];
            }
        };

        ft.init = function () {
            var $window = $(w), $table = $(ft.table);

            w.footable.plugins.init(ft);

            if ($table.hasClass(cls.loaded)) {
                //already loaded FooTable for the table, so don't init again
                ft.raise(evt.alreadyInitialized);
                return;
            }

            //raise the initializing event
            ft.raise(evt.initializing);

            $table.addClass(cls.loading);

            // Get the column data once for the life time of the plugin
            $table.find(opt.columnDataSelector).each(function () {
                var data = ft.getColumnData(this);
                ft.columns[data.index] = data;
            });

            // Create a nice friendly array to work with out of the breakpoints object.
            for (var name in opt.breakpoints) {
                ft.breakpoints.push({ 'name': name, 'width': opt.breakpoints[name] });
                ft.breakpointNames += (name + ' ');
            }

            // Sort the breakpoints so the smallest is checked first
            ft.breakpoints.sort(function (a, b) {
                return a['width'] - b['width'];
            });

            $table
                .unbind(trg.initialize)
                //bind to FooTable initialize trigger
                .bind(trg.initialize, function () {
                    //remove previous "state" (to "force" a resize)
                    $table.removeData('footable_info');
                    $table.data('breakpoint', '');

                    //trigger the FooTable resize
                    $table.trigger(trg.resize);

                    //remove the loading class
                    $table.removeClass(cls.loading);

                    //add the FooTable and loaded class
                    $table.addClass(cls.loaded).addClass(cls.main);

                    //raise the initialized event
                    ft.raise(evt.initialized);
                })
                .unbind(trg.redraw)
                //bind to FooTable redraw trigger
                .bind(trg.redraw, function () {
                    ft.redraw();
                })
                .unbind(trg.resize)
                //bind to FooTable resize trigger
                .bind(trg.resize, function () {
                    ft.resize();
                })
                .unbind(trg.expandFirstRow)
                //bind to FooTable expandFirstRow trigger
                .bind(trg.expandFirstRow, function () {
                    $table.find(opt.toggleSelector).first().not('.' + cls.detailShow).trigger(trg.toggleRow);
                })
                .unbind(trg.expandAll)
                //bind to FooTable expandFirstRow trigger
                .bind(trg.expandAll, function () {
                    $table.find(opt.toggleSelector).not('.' + cls.detailShow).trigger(trg.toggleRow);
                })
                .unbind(trg.collapseAll)
                //bind to FooTable expandFirstRow trigger
                .bind(trg.collapseAll, function () {
                    $table.find('.' + cls.detailShow).trigger(trg.toggleRow);
                });

            //trigger a FooTable initialize
            $table.trigger(trg.initialize);

            //bind to window resize
            $window
                .bind('resize.footable', function () {
                    ft.timers.resize.stop();
                    ft.timers.resize.start(function () {
                        ft.raise(trg.resize);
                    }, opt.delay);
                });
        };

        ft.addRowToggle = function () {
            if (!opt.addRowToggle) return;

            var $table = $(ft.table),
                hasToggleColumn = false;

            //first remove all toggle spans
            $table.find('span.' + cls.toggle).remove();

            for (var c in ft.columns) {
                var col = ft.columns[c];
                if (col.toggle) {
                    hasToggleColumn = true;
                    var selector = '> tbody > tr:not(.' + cls.detail + ',.' + cls.disabled + ') > td:nth-child(' + (parseInt(col.index, 10) + 1) + ')';
                    $table.find(selector).not('.' + cls.detailCell).prepend($(opt.toggleHTMLElement).addClass(cls.toggle));
                    return;
                }
            }
            //check if we have an toggle column. If not then add it to the first column just to be safe
            if (!hasToggleColumn) {
                $table
                    .find('> tbody > tr:not(.' + cls.detail + ',.' + cls.disabled + ') > td:first-child')
                    .not('.' + cls.detailCell)
                    .prepend($(opt.toggleHTMLElement).addClass(cls.toggle));
            }
        };

        ft.setColumnClasses = function () {
            $table = $(ft.table);
            for (var c in ft.columns) {
                var col = ft.columns[c];
                if (col.className !== null) {
                    var selector = '', first = true;
                    $.each(col.matches, function (m, match) { //support for colspans
                        if (!first) selector += ', ';
                        selector += '> tbody > tr:not(.' + cls.detail + ') > td:nth-child(' + (parseInt(match, 10) + 1) + ')';
                        first = false;
                    });
                    //add the className to the cells specified by data-class="blah"
                    $table.find(selector).not('.' + cls.detailCell).addClass(col.className);
                }
            }
        };

        //moved this out into it's own function so that it can be called from other add-ons
        ft.bindToggleSelectors = function () {
            var $table = $(ft.table);

            if (!ft.hasAnyBreakpointColumn()) return;

            $table.find(opt.toggleSelector).unbind(trg.toggleRow).bind(trg.toggleRow, function (e) {
                var $row = $(this).is('tr') ? $(this) : $(this).parents('tr:first');
                ft.toggleDetail($row);
            });

            $table.find(opt.toggleSelector).unbind('click.footable').bind('click.footable', function (e) {
                if ($table.is('.breakpoint') && $(e.target).is('td,.'+ cls.toggle)) {
                    $(this).trigger(trg.toggleRow);
                }
            });
        };

        ft.parse = function (cell, column) {
            var parser = opt.parsers[column.type] || opt.parsers.alpha;
            return parser(cell);
        };

        ft.getColumnData = function (th) {
            var $th = $(th), hide = $th.data('hide'), index = $th.index();
            hide = hide || '';
            hide = jQuery.map(hide.split(','), function (a) {
                return jQuery.trim(a);
            });
            var data = {
                'index': index,
                'hide': { },
                'type': $th.data('type') || 'alpha',
                'name': $th.data('name') || $.trim($th.text()),
                'ignore': $th.data('ignore') || false,
                'toggle': $th.data('toggle') || false,
                'className': $th.data('class') || null,
                'matches': [],
                'names': { },
                'group': $th.data('group') || null,
                'groupName': null
            };

            if (data.group !== null) {
                var $group = $(ft.table).find('> thead > tr.footable-group-row > th[data-group="' + data.group + '"], > thead > tr.footable-group-row > td[data-group="' + data.group + '"]').first();
                data.groupName = ft.parse($group, { 'type': 'alpha' });
            }

            var pcolspan = parseInt($th.prev().attr('colspan') || 0, 10);
            indexOffset += pcolspan > 1 ? pcolspan - 1 : 0;
            var colspan = parseInt($th.attr('colspan') || 0, 10), curindex = data.index + indexOffset;
            if (colspan > 1) {
                var names = $th.data('names');
                names = names || '';
                names = names.split(',');
                for (var i = 0; i < colspan; i++) {
                    data.matches.push(i + curindex);
                    if (i < names.length) data.names[i + curindex] = names[i];
                }
            } else {
                data.matches.push(curindex);
            }

            data.hide['default'] = ($th.data('hide') === "all") || ($.inArray('default', hide) >= 0);

            var hasBreakpoint = false;
            for (var name in opt.breakpoints) {
                data.hide[name] = ($th.data('hide') === "all") || ($.inArray(name, hide) >= 0);
                hasBreakpoint = hasBreakpoint || data.hide[name];
            }
            data.hasBreakpoint = hasBreakpoint;
            var e = ft.raise(evt.columnData, { 'column': { 'data': data, 'th': th } });
            return e.column.data;
        };

        ft.getViewportWidth = function () {
            return window.innerWidth || (document.body ? document.body.offsetWidth : 0);
        };

        ft.calculateWidth = function ($table, info) {
            if (jQuery.isFunction(opt.calculateWidthOverride)) {
                return opt.calculateWidthOverride($table, info);
            }
            if (info.viewportWidth < info.width) info.width = info.viewportWidth;
            if (info.parentWidth < info.width) info.width = info.parentWidth;
            return info;
        };

        ft.hasBreakpointColumn = function (breakpoint) {
            for (var c in ft.columns) {
                if (ft.columns[c].hide[breakpoint]) {
                    if (ft.columns[c].ignore) {
                        continue;
                    }
                    return true;
                }
            }
            return false;
        };

        ft.hasAnyBreakpointColumn = function () {
            for (var c in ft.columns) {
                if (ft.columns[c].hasBreakpoint) {
                    return true;
                }
            }
            return false;
        };

        ft.resize = function () {
            var $table = $(ft.table);

            if (!$table.is(':visible')) {
                return;
            } //we only care about FooTables that are visible

            if (!ft.hasAnyBreakpointColumn()) {
                return;
            } //we only care about FooTables that have breakpoints

            var info = {
                'width': $table.width(),                  //the table width
                'viewportWidth': ft.getViewportWidth(),   //the width of the viewport
                'parentWidth': $table.parent().width()    //the width of the parent
            };

            info = ft.calculateWidth($table, info);

            var pinfo = $table.data('footable_info');
            $table.data('footable_info', info);
            ft.raise(evt.resizing, { 'old': pinfo, 'info': info });

            // This (if) statement is here purely to make sure events aren't raised twice as mobile safari seems to do
            if (!pinfo || (pinfo && pinfo.width && pinfo.width !== info.width)) {

                var current = null, breakpoint;
                for (var i = 0; i < ft.breakpoints.length; i++) {
                    breakpoint = ft.breakpoints[i];
                    if (breakpoint && breakpoint.width && info.width <= breakpoint.width) {
                        current = breakpoint;
                        break;
                    }
                }

                var breakpointName = (current === null ? 'default' : current['name']),
                    hasBreakpointFired = ft.hasBreakpointColumn(breakpointName),
                    previousBreakpoint = $table.data('breakpoint');

                $table
                    .data('breakpoint', breakpointName)
                    .removeClass('default breakpoint').removeClass(ft.breakpointNames)
                    .addClass(breakpointName + (hasBreakpointFired ? ' breakpoint' : ''));

                //only do something if the breakpoint has changed
                if (breakpointName !== previousBreakpoint) {
                    //trigger a redraw
                    $table.trigger(trg.redraw);
                    //raise a breakpoint event
                    ft.raise(evt.breakpoint, { 'breakpoint': breakpointName, 'info': info });
                }
            }

            ft.raise(evt.resized, { 'old': pinfo, 'info': info });
        };

        ft.redraw = function () {
            //add the toggler to each row
            ft.addRowToggle();

            //bind the toggle selector click events
            ft.bindToggleSelectors();

            //set any cell classes defined for the columns
            ft.setColumnClasses();

            var $table = $(ft.table),
                breakpointName = $table.data('breakpoint'),
                hasBreakpointFired = ft.hasBreakpointColumn(breakpointName);

            $table
                .find('> tbody > tr:not(.' + cls.detail + ')').data('detail_created', false).end()
                .find('> thead > tr:last-child > th')
                .each(function () {
                    var data = ft.columns[$(this).index()], selector = '', first = true;
                    $.each(data.matches, function (m, match) {
                        if (!first) {
                            selector += ', ';
                        }
                        var count = match + 1;
                        selector += '> tbody > tr:not(.' + cls.detail + ') > td:nth-child(' + count + ')';
                        selector += ', > tfoot > tr:not(.' + cls.detail + ') > td:nth-child(' + count + ')';
                        selector += ', > colgroup > col:nth-child(' + count + ')';
                        first = false;
                    });

                    selector += ', > thead > tr[data-group-row="true"] > th[data-group="' + data.group + '"]';
                    var $column = $table.find(selector).add(this);
                    if (data.hide[breakpointName] === false) $column.show();
                    else $column.hide();

                    if ($table.find('> thead > tr.footable-group-row').length === 1) {
                        var $groupcols = $table.find('> thead > tr:last-child > th[data-group="' + data.group + '"]:visible, > thead > tr:last-child > th[data-group="' + data.group + '"]:visible'),
                            $group = $table.find('> thead > tr.footable-group-row > th[data-group="' + data.group + '"], > thead > tr.footable-group-row > td[data-group="' + data.group + '"]'),
                            groupspan = 0;

                        $.each($groupcols, function () {
                            groupspan += parseInt($(this).attr('colspan') || 1, 10);
                        });

                        if (groupspan > 0) $group.attr('colspan', groupspan).show();
                        else $group.hide();
                    }
                })
                .end()
                .find('> tbody > tr.' + cls.detailShow).each(function () {
                    ft.createOrUpdateDetailRow(this);
                });

            $table.find('> tbody > tr.' + cls.detailShow + ':visible').each(function () {
                var $next = $(this).next();
                if ($next.hasClass(cls.detail)) {
                    if (!hasBreakpointFired) $next.hide();
                    else $next.show();
                }
            });

            // adding .footable-first-column and .footable-last-column to the first and last th and td of each row in order to allow
            // for styling if the first or last column is hidden (which won't work using :first-child or :last-child)
            $table.find('> thead > tr > th.footable-last-column, > tbody > tr > td.footable-last-column').removeClass('footable-last-column');
            $table.find('> thead > tr > th.footable-first-column, > tbody > tr > td.footable-first-column').removeClass('footable-first-column');
            $table.find('> thead > tr, > tbody > tr')
                .find('> th:visible:last, > td:visible:last')
                .addClass('footable-last-column')
                .end()
                .find('> th:visible:first, > td:visible:first')
                .addClass('footable-first-column');

            ft.raise(evt.redrawn);
        };

        ft.toggleDetail = function (row) {
            var $row = (row.jquery) ? row : $(row),
                $next = $row.next();

            //check if the row is already expanded
            if ($row.hasClass(cls.detailShow)) {
                $row.removeClass(cls.detailShow);

                //only hide the next row if it's a detail row
                if ($next.hasClass(cls.detail)) $next.hide();

                ft.raise(evt.rowCollapsed, { 'row': $row[0] });

            } else {
                ft.createOrUpdateDetailRow($row[0]);
                $row.addClass(cls.detailShow)
					.next().show();

                ft.raise(evt.rowExpanded, { 'row': $row[0] });
            }
        };

        ft.removeRow = function (row) {
            var $row = (row.jquery) ? row : $(row);
            if ($row.hasClass(cls.detail)) {
                $row = $row.prev();
            }
            var $next = $row.next();
            if ($row.data('detail_created') === true) {
                //remove the detail row
                $next.remove();
            }
            $row.remove();

            //raise event
            ft.raise(evt.rowRemoved);
        };

        ft.appendRow = function (row) {
            var $row = (row.jquery) ? row : $(row);
            $(ft.table).find('tbody').append($row);

            //redraw the table
            ft.redraw();
        };

        ft.getColumnFromTdIndex = function (index) {
            /// <summary>Returns the correct column data for the supplied index taking into account colspans.</summary>
            /// <param name="index">The index to retrieve the column data for.</param>
            /// <returns type="json">A JSON object containing the column data for the supplied index.</returns>
            var result = null;
            for (var column in ft.columns) {
                if ($.inArray(index, ft.columns[column].matches) >= 0) {
                    result = ft.columns[column];
                    break;
                }
            }
            return result;
        };

        ft.createOrUpdateDetailRow = function (actualRow) {
            var $row = $(actualRow), $next = $row.next(), $detail, values = [];
            if ($row.data('detail_created') === true) return true;

            if ($row.is(':hidden')) return false; //if the row is hidden for some reason (perhaps filtered) then get out of here
            ft.raise(evt.rowDetailUpdating, { 'row': $row, 'detail': $next });
            $row.find('> td:hidden').each(function () {
                var index = $(this).index(), column = ft.getColumnFromTdIndex(index), name = column.name;
                if (column.ignore === true) return true;

                if (index in column.names) name = column.names[index];
                values.push({ 'name': name, 'value': ft.parse(this, column), 'display': $.trim($(this).html()), 'group': column.group, 'groupName': column.groupName });
                return true;
            });
            if (values.length === 0) return false; //return if we don't have any data to show
            var colspan = $row.find('> td:visible').length;
            var exists = $next.hasClass(cls.detail);
            if (!exists) { // Create
                $next = $('<tr class="' + cls.detail + '"><td class="' + cls.detailCell + '"><div class="' + cls.detailInner + '"></div></td></tr>');
                $row.after($next);
            }
            $next.find('> td:first').attr('colspan', colspan);
            $detail = $next.find('.' + cls.detailInner).empty();
            opt.createDetail($detail, values, opt.createGroupedDetail, opt.detailSeparator, cls);
            $row.data('detail_created', true);
            ft.raise(evt.rowDetailUpdated, { 'row': $row, 'detail': $next });
            return !exists;
        };

        ft.raise = function (eventName, args) {

            if (ft.options.debug === true && $.isFunction(ft.options.log)) ft.options.log(eventName, 'event');

            args = args || { };
            var def = { 'ft': ft };
            $.extend(true, def, args);
            var e = $.Event(eventName, def);
            if (!e.ft) {
                $.extend(true, e, def);
            } //pre jQuery 1.6 which did not allow data to be passed to event object constructor
            $(ft.table).trigger(e);
            return e;
        };

        //reset the state of FooTable
        ft.reset = function() {
            var $table = $(ft.table);
            $table.removeData('footable_info')
                .data('breakpoint', '')
                .removeClass(cls.loading)
                .removeClass(cls.loaded);

            $table.find(opt.toggleSelector).unbind(trg.toggleRow).unbind('click.footable');

            $table.find('> tbody > tr').removeClass(cls.detailShow);

            $table.find('> tbody > tr.' + cls.detail).remove();

            ft.raise(evt.reset);
        };

        ft.init();
        return ft;
    }
})(jQuery, window);
(function ($, w, undefined) {
    if (w.footable === undefined || w.footable === null)
        throw new Error('Please check and make sure footable.js is included in the page and is loaded prior to this script.');

    var defaults = {
        paginate: true,
        pageSize: 10,
        pageNavigation: '.pagination',
        firstText: '&laquo;',
        previousText: '&lsaquo;',
        nextText: '&rsaquo;',
        lastText: '&raquo;'
    };

    function pageInfo(ft) {
        var $table = $(ft.table), $tbody = $table.find('> tbody');
        this.pageNavigation = $table.data('page-navigation') || ft.options.pageNavigation;
        this.pageSize = $table.data('page-size') || ft.options.pageSize;
        this.firstText = $table.data('page-first-text') || ft.options.firstText;
        this.previousText = $table.data('page-previous-text') || ft.options.previousText;
        this.nextText = $table.data('page-next-text') || ft.options.nextText;
        this.lastText = $table.data('page-last-text') || ft.options.lastText;
        this.currentPage = 0;
        this.pages = [];
        this.control = false;
    }

    function Paginate() {
        var p = this;
        p.name = 'Footable Paginate';

        p.init = function (ft) {
            if (ft.options.paginate === true) {
                if ($(ft.table).data('page') === false) return;
                p.footable = ft;
                $(ft.table)
                    .unbind('.paging')
                    .bind({
                        'footable_initialized.paging footable_row_removed.paging footable_redrawn.paging footable_sorted.paging footable_filtered.paging': function () {
                            p.setupPaging();
                        }
                    })
                    //save the filter object onto the table so we can access it later
                    .data('footable-paging', p);
            }
        };

        p.setupPaging = function () {
            var ft = p.footable,
                $tbody = $(ft.table).find('> tbody');

            if (!ft.pageInfo) {
                ft.pageInfo = new pageInfo(ft);
            }

            p.createPages(ft, $tbody);
            p.createNavigation(ft, $tbody);
            p.fillPage(ft, $tbody, ft.pageInfo.currentPage);
        };

        p.createPages = function (ft, tbody) {
            var pages = 1;
            var info = ft.pageInfo;
            var pageCount = pages * info.pageSize;
            var page = [];
            var lastPage = [];
            info.pages = [];
            var rows = tbody.find('> tr:not(.footable-filtered,.footable-row-detail)');
            rows.each(function (i, row) {
                page.push(row);
                if (i === pageCount - 1) {
                    info.pages.push(page);
                    pages++;
                    pageCount = pages * info.pageSize;
                    page = [];
                } else if (i >= rows.length - (rows.length % info.pageSize)) {
                    lastPage.push(row);
                }
            });
            if (lastPage.length > 0) info.pages.push(lastPage);
            if (info.currentPage >= info.pages.length) info.currentPage = info.pages.length - 1;
            if (info.currentPage < 0) info.currentPage = 0;
            if (info.pages.length === 1) {
                //we only have a single page
                $(ft.table).addClass('no-paging');
            } else {
                $(ft.table).removeClass('no-paging');
            }
        };

        p.createNavigation = function (ft, tbody) {
            var $nav = $(ft.table).find(ft.pageInfo.pageNavigation);
            //if we cannot find the navigation control within the table, then try find it outside
            if ($nav.length === 0) {
                $nav = $(ft.pageInfo.pageNavigation);
                //if the navigation control is inside another table, then get out
                if ($nav.parents('table:first') !== $(ft.table)) return;
                //if we found more than one navigation control, write error to console
                if ($nav.length > 1 && ft.options.debug === true) console.error('More than one pagination control was found!');
            }
            //if we still cannot find the control, then don't do anything
            if ($nav.length === 0) return;
            //if the nav is not a UL, then find or create a UL
            if (!$nav.is('ul')) {
                if ($nav.find('ul:first').length === 0) {
                    $nav.append('<ul />');
                }
                $nav = $nav.find('ul');
            }
            $nav.find('li').remove();
            var info = ft.pageInfo;
            info.control = $nav;
            if (info.pages.length > 0) {
                $nav.append('<li class="footable-page-arrow"><a data-page="first" href="#first">' + ft.pageInfo.firstText + '</a>');
                $nav.append('<li class="footable-page-arrow"><a data-page="prev" href="#prev">' + ft.pageInfo.previousText + '</a></li>');
                $.each(info.pages, function (i, page) {
                    if (page.length > 0) {
                        $nav.append('<li class="footable-page"><a data-page="' + i + '" href="#">' + (i + 1) + '</a></li>');
                    }
                });
                $nav.append('<li class="footable-page-arrow"><a data-page="next" href="#next">' + ft.pageInfo.nextText + '</a></li>');
                $nav.append('<li class="footable-page-arrow"><a data-page="last" href="#last">' + ft.pageInfo.lastText + '</a></li>');
            }
            $nav.find('a').click(function (e) {
                e.preventDefault();
                var page = $(this).data('page');
                var newPage = info.currentPage;
                if (page === 'first') {
                    newPage = 0;
                } else if (page === 'prev') {
                    if (newPage > 0) newPage--;
                } else if (page === 'next') {
                    if (newPage < info.pages.length - 1) newPage++;
                } else if (page === 'last') {
                    newPage = info.pages.length - 1;
                } else {
                    newPage = page;
                }
                p.paginate(ft, newPage);
            });
            p.setPagingClasses($nav, info.currentPage, info.pages.length);
        };

        p.paginate = function (ft, newPage) {
            var info = ft.pageInfo;
            if (info.currentPage !== newPage) {
                var $tbody = $(ft.table).find('> tbody');

                //raise a pre-pagin event so that we can cancel the paging if needed
                var event = ft.raise('footable_paging', { page: newPage, size: info.pageSize });
                if (event && event.result === false) return;

                p.fillPage(ft, $tbody, newPage);
                info.control.find('li').removeClass('active disabled');
                p.setPagingClasses(info.control, info.currentPage, info.pages.length);
            }
        };

        p.setPagingClasses = function (nav, currentPage, pageCount) {
            nav.find('li.footable-page > a[data-page=' + currentPage + ']').parent().addClass('active');
            if (currentPage >= pageCount - 1) {
                nav.find('li.footable-page-arrow > a[data-page="next"]').parent().addClass('disabled');
                nav.find('li.footable-page-arrow > a[data-page="last"]').parent().addClass('disabled');
            }
            if (currentPage < 1) {
                nav.find('li.footable-page-arrow > a[data-page="first"]').parent().addClass('disabled');
                nav.find('li.footable-page-arrow > a[data-page="prev"]').parent().addClass('disabled');
            }
        };

        p.fillPage = function (ft, tbody, pageNumber) {
            ft.pageInfo.currentPage = pageNumber;
            tbody.find('> tr').hide();
            $(ft.pageInfo.pages[pageNumber]).each(function () {
                p.showRow(this, ft);
            });
        };

        p.showRow = function (row, ft) {
            var $row = $(row), $next = $row.next(), $table = $(ft.table);
            if ($table.hasClass('breakpoint') && $row.hasClass('footable-detail-show') && $next.hasClass('footable-row-detail')) {
                $row.add($next).show();
                ft.createOrUpdateDetailRow(row);
            }
            else $row.show();
        };
    }

    w.footable.plugins.register(Paginate, defaults);

})(jQuery, window);
(function ($, w, undefined) {
    if (w.footable === undefined || w.footable === null)
        throw new Error('Please check and make sure footable.js is included in the page and is loaded prior to this script.');

    var defaults = {
        sort: true,
        sorters: {
            alpha: function (a, b) {
                if (a === b) return 0;
                if (a < b) return -1;
                return 1;
            },
            numeric: function (a, b) {
                return a - b;
            }
        },
        classes: {
            sort: {
                sortable: 'footable-sortable',
                sorted: 'footable-sorted',
                descending: 'footable-sorted-desc',
                indicator: 'footable-sort-indicator'
            }
        },
        events: {
            sort: {
                sorting: 'footable_sorting',
                sorted: 'footable_sorted'
            }
        }
    };

    function Sort() {
        var p = this;
        p.name = 'Footable Sortable';
        p.init = function (ft) {
            p.footable = ft;
            if (ft.options.sort === true) {
                $(ft.table)
                    .unbind('.sorting')
                    .bind({
                        'footable_initialized.sorting': function (e) {
                            var $table = $(ft.table),
                                $tbody = $table.find('> tbody'),
                                cls = ft.options.classes.sort,
                                column, $th;

                            if ($table.data('sort') === false) return;

                            $table.find('> thead > tr:last-child > th, > thead > tr:last-child > td').each(function (ec) {
                                $th = $(this), column = ft.columns[$th.index()];
                                if (column.sort.ignore !== true && !$th.hasClass(cls.sortable)) {
                                    $th.addClass(cls.sortable);
                                    $('<span />').addClass(cls.indicator).appendTo($th);
                                }
                            });

                            $table.find('> thead > tr:last-child > th.' + cls.sortable + ', > thead > tr:last-child > td.' + cls.sortable).unbind('click.footable').bind('click.footable', function (ec) {
                                ec.preventDefault();
                                $th = $(this);
                                var ascending = !$th.hasClass(cls.sorted);
                                p.doSort($th.index(), ascending);
                                return false;
                            });

                            var didSomeSorting = false;
                            for (var c in ft.columns) {
                                column = ft.columns[c];
                                if (column.sort.initial) {
                                    var ascending = (column.sort.initial !== 'descending');
                                    p.doSort(column.index, ascending);
                                    break;
                                }
                            }
                            if (didSomeSorting) {
                                ft.bindToggleSelectors();
                            }
                        },
                        'footable_redrawn.sorting': function(e) {
                            var $table = $(ft.table),
                                cls = ft.options.classes.sort;
                            if ($table.data('sorted') >= 0) {
                                $table.find('> thead > tr:last-child > th').each(function(i){
                                    var $th = $(this);
                                    if ($th.hasClass(cls.sorted) || $th.hasClass(cls.descending)) {
                                        p.doSort(i);
                                        return;
                                    }
                                });
                            }
                        },
                        'footable_column_data.sorting': function (e) {
                            var $th = $(e.column.th);
                            e.column.data.sort = e.column.data.sort || {};
                            e.column.data.sort.initial = $th.data('sort-initial') || false;
                            e.column.data.sort.ignore = $th.data('sort-ignore') || false;
                            e.column.data.sort.selector = $th.data('sort-selector') || null;

                            var match = $th.data('sort-match') || 0;
                            if (match >= e.column.data.matches.length) match = 0;
                            e.column.data.sort.match = e.column.data.matches[match];
                        }
                    })
                //save the sort object onto the table so we can access it later
                .data('footable-sort', p);
            }
        };

        p.doSort = function(columnIndex, ascending) {
            var ft = p.footable;
            if ($(ft.table).data('sort') === false) return;

            var $table = $(ft.table),
                $tbody = $table.find('> tbody'),
                column = ft.columns[columnIndex],
                $th = $table.find('> thead > tr:last-child > th:eq(' + columnIndex + ')'),
                cls = ft.options.classes.sort,
                evt = ft.options.events.sort;

            ascending = (ascending === undefined) ? $th.hasClass(cls.sorted) :
                (ascending === 'toggle') ? !$th.hasClass(cls.sorted) : ascending;

            if (column.sort.ignore === true) return true;

            //raise a pre-sorting event so that we can cancel the sorting if needed
            var event = ft.raise(evt.sorting, { column: column, direction: ascending ? 'ASC' : 'DESC' });
            if (event && event.result === false) return;

            $table.data('sorted', column.index);

            $table.find('> thead > tr:last-child > th, > thead > tr:last-child > td').not($th).removeClass(cls.sorted + ' ' + cls.descending);

            if (ascending === undefined) {
                ascending = $th.hasClass(cls.sorted);
            }

            if (ascending) {
                $th.removeClass(cls.descending).addClass(cls.sorted);
            } else {
                $th.removeClass(cls.sorted).addClass(cls.descending);
            }

            p.sort(ft, $tbody, column, ascending);

            ft.bindToggleSelectors();
            ft.raise(evt.sorted, { column: column, direction: ascending ? 'ASC' : 'DESC' });
        };

        p.rows = function (ft, tbody, column) {
            var rows = [];
            tbody.find('> tr').each(function () {
                var $row = $(this), $next = null;
                if ($row.hasClass(ft.options.classes.detail)) return true;
                if ($row.next().hasClass(ft.options.classes.detail)) {
                    $next = $row.next().get(0);
                }
                var row = { 'row': $row, 'detail': $next };
                if (column !== undefined) {
                    row.value = ft.parse(this.cells[column.sort.match], column);
                }
                rows.push(row);
                return true;
            }).detach();
            return rows;
        };

        p.sort = function (ft, tbody, column, ascending) {
            var rows = p.rows(ft, tbody, column);
            var sorter = ft.options.sorters[column.type] || ft.options.sorters.alpha;
            rows.sort(function (a, b) {
                if (ascending) {
                    return sorter(a.value, b.value);
                } else {
                    return sorter(b.value, a.value);
                }
            });
            for (var j = 0; j < rows.length; j++) {
                tbody.append(rows[j].row);
                if (rows[j].detail !== null) {
                    tbody.append(rows[j].detail);
                }
            }
        };
    }

    w.footable.plugins.register(Sort, defaults);

})(jQuery, window);
(function ($, w, undefined) {
  if (w.footable === undefined || w.foobox === null)
    throw new Error('Please check and make sure footable.js is included in the page and is loaded prior to this script.');

  var defaults = {
    striping: {
      enabled: true
    },
    classes: {
      striping: {
        odd: 'footable-odd',
        even: 'footable-even'
      }
    }
  };

  function Striping() {
    var p = this;
    p.name = 'Footable Striping';
    p.init = function (ft) {
      p.footable = ft;
      $(ft.table)
        .unbind('striping')
        .bind({
          'footable_initialized.striping footable_row_removed.striping footable_redrawn.striping footable_sorted.striping footable_filtered.striping': function () {
            
            if ($table.data('striping') === false) return;

            p.setupStriping(ft);
          }
        });
    };

    p.setupStriping = function (ft) {

      var rowIndex = 0;
      $(ft.table).find('> tbody > tr:visible:not(.footable-row-detail)').each(function () {

        var $row = $(this);

        //Clean off old classes
        $row.removeClass(ft.options.classes.striping.even).removeClass(ft.options.classes.striping.odd);

        if (rowIndex % 2 === 0) {
          $row.addClass(ft.options.classes.striping.even);
        } else {
          $row.addClass(ft.options.classes.striping.odd);
        }

        rowIndex++;
      });
    };
  }

  w.footable.plugins.register(Striping, defaults);

})(jQuery, window);
// knockout-delegatedEvents 0.1.2 | (c) 2013 Ryan Niemeyer |  http://www.opensource.org/licenses/mit-license
;(function(factory) {
    //CommonJS
    if (typeof require === "function" && typeof exports === "object" && typeof module === "object") {
        factory(require("knockout"), exports);
        //AMD
    } else if (typeof define === "function" && define.amd) {
        define(["knockout", "exports"], factory);
        //normal script tag
    } else {
        factory(ko, ko.actions = {});
    }
}(function(ko, actions) {
    var prefix = "ko_delegated_";
    var createDelegatedHandler = function(eventName, root) {
        return function(event) {
            var data, method, action, owner, matchingParent, command, result,
                el = event.target || event.srcElement,
                context = ko.contextFor(el),
                attr = "data-" + eventName,
                key = prefix + eventName;

            if (context) {
                //loop until we either find an action, run out of elements, or hit the root element that has our delegated handler
                while (!method && el) {
                    method = el.getAttribute(attr) || ko.utils.domData.get(el, key);
                    if (!method) {
                        el = el !== root ? el.parentNode : null;
                    }
                }

                if (method) {
                    //get context of the element that actually held the action
                    context = ko.contextFor(el);

                    if (context) {
                        data = context.$data;

                        if (typeof method === "string") {
                            //check defined actions
                            if (method in actions) {
                                command = actions[method];
                                if (command) {
                                    action = typeof command === "function" ? command : command.action;
                                    owner = command.owner || data;
                                }
                            }
                            //search for the action
                            else if (data && data[method] && typeof data[method] === "function") {
                                action = data[method];
                                owner = data;
                            }

                            //search parents for the action
                            if (!action) {
                                matchingParent = ko.utils.arrayFirst(context.$parents, function(parent) {
                                    return parent[method] && typeof parent[method] === "function";
                                });

                                action = matchingParent && matchingParent[method];
                                owner = matchingParent;
                            }
                        }
                        //a binding handler was used to associate the element with a function
                        else if (typeof method === "function") {
                            action = method;
                            owner = data;
                        }
                    }

                    //execute the action as KO normally would
                    if (action) {
                        result = action.call(owner, data, event);

                        //prevent default action, if handler returns true
                        if (result !== true) {
                            if (event.preventDefault) {
                                event.preventDefault();
                            }
                            else {
                                event.returnValue = false;
                            }
                        }
                    }
                }
            }
        };
    };

    //create a binding for an event to associate a function with the element
    var createDelegatedBinding = function(event) {
        var bindingName;
        if (event) {
            //capitalize first letter
            bindingName = "delegated" + event.substr(0, 1).toUpperCase() + event.slice(1);
        }

        //create the binding, if it does not exist
        if (!ko.bindingHandlers[bindingName]) {
            ko.bindingHandlers[bindingName] = {
                init: function(element, valueAccessor) {
                    var action = valueAccessor();
                    ko.utils.domData.set(element, prefix + event, action);
                }
            };
        }
    };

    //add a handler on a parent element that responds to events from the children
    ko.bindingHandlers.delegatedHandler = {
        init: function(element, valueAccessor) {
            var events = ko.utils.unwrapObservable(valueAccessor()) || [];

            if (typeof events === "string") {
                events = [events];
            }

            ko.utils.arrayForEach(events, function(event) {
                createDelegatedBinding(event);
                ko.utils.registerEventHandler(element, event, createDelegatedHandler(event, element));
            });
        }
    };
}));
ko.bindingHandlers.showModal = {
	init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
		$(element).draggable({ cancel: ".modal-body,.modal-footer" });

		var modalsDiv = $("body").find("#modalsDiv");

		if (modalsDiv.length === 0) {
			modalsDiv = $("<div id='modalsDiv'></div>");
			$("body").append(modalsDiv);
		}

		$(modalsDiv).append(element);
	},
	update: function (element, valueAccessor) {
		var value = valueAccessor();
		if (ko.utils.unwrapObservable(value)) {
			$(element).modal('show');
		}
		else {
			$(element).modal('hide');
		}
	}
};
ko.bindingHandlers.footable = {
	init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
		$(element).closest("table").footable();
	},
	update: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
		//this is called when the observableArray changes
		//and after the foreach has rendered the contents
		var value = ko.unwrap(valueAccessor()); //needed so that update is called
		var footable = $(element).closest("table").data('footable');
		//footable.reset();
		//footable.init();
		footable.redraw();
	}
};

ko.bindingHandlers.footable.preprocess = function(value, name, addBindingCallback) {
	//add foreach binding
	addBindingCallback('foreach', '{ data: ' + value + '}');
	return value;
};
var Task = (function (data) {
	var id = ko.observable("00000000-0000-0000-0000-000000000000");
	var description = ko.observable();
	var dueOn = ko.observable();
	var tasks = new Tasks();
	var showTasks = ko.computed(function () {
		var result = id() != "00000000-0000-0000-0000-000000000000";
		return result;
	});
	var parentTaskId = "";

	var initOrUpdate = function (task) {
		id(task.id || "00000000-0000-0000-0000-000000000000");
		description(task.description);
		dueOn(task.dueOn);
		tasks.initOrUpdate(task.tasks);
		tasks.parentId = task.id;
		parentTaskId = task.parentTaskId || "";
	};

	if (data) {
		initOrUpdate(data);
	}

	return {
		id: id,
		description: description,
		dueOn: dueOn,
		tasks: tasks.tasks,
		tasksObj: tasks,
		showTasks: showTasks,
		get parentTaskId() { return parentTaskId; },
		set parentTaskId(parentId) { parentTaskId = parentId; },
		initOrUpdate: initOrUpdate
	};
});

var Tasks = (function (data) {
	var parentId = "";
	var tasks = ko.observableArray([]);
	var selectedItem = ko.observable();
	var itemForEditing = ko.observable();

	var addItem = function (tasksData) {
		var newItem = new Task();
		newItem.parentTaskId = tasksData.parentId;
		itemForEditing(newItem);
	};

	var selectItem = function (task) {
		var newItem = new Task(ko.toJS(task));
		itemForEditing(newItem);
		selectedItem(task);
	};

	var removeItem = function (task) {
		tasks.remove(task);
	};

	function acceptOrApply(apply) {
		if (itemForEditing().id() === null || itemForEditing().id() === "00000000-0000-0000-0000-000000000000") {
      var random1 = Math.floor((Math.random()*10)+1);
      var random2 = Math.floor((Math.random()*10)+1);
      var random3 = Math.floor((Math.random()*10)+1);
      var guid = random1 + "0000000-0000-0000-0000-00000000" + random2 + "0" + random3;
      
      itemForEditing().id(guid);
      itemForEditing().initOrUpdate(ko.toJS(itemForEditing()));
      tasks.push(itemForEditing());
      
			if (apply) {
				selectedItem(itemForEditing());
				var newItem = new Task(ko.toJS(itemForEditing()));
				itemForEditing(newItem);
			} else {
				itemForEditing(null);
			}
		} else {
			selectedItem().initOrUpdate(ko.toJS(itemForEditing()));
			var jsonData = ko.toJSON(selectedItem());
			if (apply === undefined || apply === false) {
				itemForEditing(null);
				selectedItem(null);
			}
		}
	}

	var acceptEdit = function () {
		acceptOrApply();
	};

	var applyEdit = function () {
		acceptOrApply(true);
	};

	var cancelEdit = function () {
		selectedItem(null);
		itemForEditing(null);
	};

	var initOrUpdate = function (tasksData) {
		tasksData.forEach(function (item) {
			var tempItem = findItemById(item.id);
			if (tempItem) {
				tempItem.initOrUpdate(item);
			} else {
				tasks.push(new Task(item));
			}
		});
	};

	var findItemById = function (id) {
		var result;
		tasks().forEach(function (item) {
			if (item.id() == id) {
				result = item;
				return;
			}
		});
		return result;
	};

	if (data) {
		initOrUpdate(data);
	}

	return {
		tasks: tasks,
		get parentId() { return parentId; },
		set parentId(forId) { parentId = forId; },
		addItem: addItem,
		removeItem: removeItem,
		selectItem: selectItem,
		selectedItem: selectedItem,
		itemForEditing: itemForEditing,
		acceptEdit: acceptEdit,
		applyEdit: applyEdit,
		cancelEdit: cancelEdit,
		initOrUpdate: initOrUpdate
	};
});
allData = {"id":"70000000-0000-0000-0000-00000000901","description":"1","tasks":[{"id":"70000000-0000-0000-0000-00000000802","description":"2","tasks":[{"id":"50000000-0000-0000-0000-000000001001","description":"3","tasks":[],"tasksObj":{"tasks":[],"parentId":"50000000-0000-0000-0000-000000001001"},"showTasks":true,"parentTaskId":"70000000-0000-0000-0000-00000000802"}],"tasksObj":{"tasks":[{"id":"50000000-0000-0000-0000-000000001001","description":"3","tasks":[],"tasksObj":{"tasks":[],"parentId":"50000000-0000-0000-0000-000000001001"},"showTasks":true,"parentTaskId":"70000000-0000-0000-0000-00000000802"}],"parentId":"70000000-0000-0000-0000-00000000802"},"showTasks":true,"parentTaskId":"70000000-0000-0000-0000-00000000901"}],"tasksObj":{"tasks":[{"id":"70000000-0000-0000-0000-00000000802","description":"2","tasks":[{"id":"50000000-0000-0000-0000-000000001001","description":"3","tasks":[],"tasksObj":{"tasks":[],"parentId":"50000000-0000-0000-0000-000000001001"},"showTasks":true,"parentTaskId":"70000000-0000-0000-0000-00000000802"}],"tasksObj":{"tasks":[{"id":"50000000-0000-0000-0000-000000001001","description":"3","tasks":[],"tasksObj":{"tasks":[],"parentId":"50000000-0000-0000-0000-000000001001"},"showTasks":true,"parentTaskId":"70000000-0000-0000-0000-00000000802"}],"parentId":"70000000-0000-0000-0000-00000000802"},"showTasks":true,"parentTaskId":"70000000-0000-0000-0000-00000000901"}],"parentId":"70000000-0000-0000-0000-00000000901"},"showTasks":true,"parentTaskId":""};