<!DOCTYPE html>
<html>

<head>
    <script type='text/javascript' src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
    <script type='text/javascript' src="http://cdn.jsdelivr.net/momentjs/latest/moment-with-locales.min.js"></script>
    <script type='text/javascript' src="http://cdn.jsdelivr.net/angular.moment/latest/angular-moment.min.js"></script>

    <script type='text/javascript' src="http://rawgit.com/JimLiu/angular-ui-tree/master/dist/angular-ui-tree.js"></script>
    <link rel="stylesheet" type="text/css" href="http://rawgit.com/angular-ui-tree/angular-ui-tree/master/dist/angular-ui-tree.min.css">
    

    <link rel="stylesheet" type="text/css" href="http://maxcdn.bootstrapcdn.com/font-awesome/4.3.2/css/font-awesome.min.css">
    <link rel="stylesheet" type="text/css" href="http://cdn.jsdelivr.net/bootstrap/latest/css/bootstrap.css">
    <link rel="stylesheet" type="text/css" href="style.css">

    <script type='text/javascript' src="http://rawgit.com/angular-gantt/angular-gantt/master/assets/angular-gantt.js"></script>
    <script type='text/javascript' src="http://rawgit.com/angular-gantt/angular-gantt/master/assets/angular-gantt-plugins.js"></script>
    <link rel="stylesheet" type="text/css" href="http://rawgit.com/angular-gantt/angular-gantt/master/assets/angular-gantt.css">
    <link rel="stylesheet" type="text/css" href="http://rawgit.com/angular-gantt/angular-gantt/master/assets/angular-gantt-plugins.css">

    <script type='text/javascript' src="script.js"></script>
</head>

<body>
<div ng-app="plnkrGanttMaster" ng-controller="Ctrl">
     <div class="container-content" ng-cloak="true">
        <div class="container-fluid">
            <div class="row">
                <div class="col-md-12">
                    <div class="panel-group" bs-collapse>
                        <div class="panel panel-default">
                            <div class="panel-heading">
                                <h4 class="panel-title">
                                    <a href="" bs-collapse-toggle>Options</a>
                                </h4>
                            </div>
                            <div class="panel-collapse" bs-collapse-target>
                                <div class="panel-body">
                                    <div class="container-fluid">
                                        <div class="row">
                                            <div class="form-inline">
                                                <div class="form-group text-center">
                                                    <label class="control-label"><i class="fa fa-search"></i> Scale</label><br>
                                                    <button type="button" style="width: 5em; text-align: left" class="btn btn-default" ng-model="options.scale" ng-options="s for s in ['minute', '5 minutes', 'hour', '3 hours', 'day', 'week', '2 weeks', 'month', 'quarter', '6 months', 'year']" bs-select></button>
                                                </div>
                                                <div class="form-group text-center">
                                                    <label class="control-label"><i class="fa fa-sort"></i> Sort</label><br>
                                                    <button type="button" style="width: 6em; text-align: left" class="btn btn-default" ng-model="options.sortMode" ng-options="m.value as m.label for m in [{label: 'disabled', value: undefined}, {label: 'name', value: 'model.name'}, {label: 'from', value: 'from'}, {label: 'to', value: 'to'}]" bs-select></button>
                                                </div>
                                                <div class="form-group input-append text-center">
                                                    <label class="control-label"><i class="fa fa-filter"></i> Filter Tasks</label><br>
                                                    <input type="text" class="form-control" style="width: 8em; text-align: left" ng-model="options.filterTask">
                                                </div>
                                                <div class="form-group input-append text-center">
                                                    <label class="control-label"><i class="fa fa-filter"></i> Filter Rows</label><br>
                                                    <input type="text" class="form-control" style="width: 8em; text-align: left" ng-model="options.filterRow">
                                                </div>

                                                <div class="form-group text-center">
                                                    <label class="control-label"><i class="fa fa-clock-o"></i> Today</label><br>
                                                    <button type="button" style="width: 6em; text-align: left" class="btn btn-default" ng-model="options.currentDate" ng-options="d for d in ['none', 'line', 'column']" bs-select></button>
                                                </div>

                                                <div class="form-group text-center">
                                                    <label class="control-label"><i class="fa fa-expand"></i> Expand</label><br>
                                                    <button type="button" style="width: 6em; text-align: left" class="btn btn-default" ng-model="options.autoExpand" ng-options="e for e in ['none', 'both', 'left', 'right']" bs-select></button>
                                                </div>

                                                <div class="form-group text-center">
                                                    <label class="control-label"><i class="fa fa-scissors"></i> Out of range</label><br>
                                                    <button type="button" style="width: 8em; text-align: left" class="btn btn-default" ng-model="options.taskOutOfRange" ng-options="e for e in ['expand', 'truncate']" bs-select></button>
                                                </div>

                                                <div class="form-group text-center">
                                                    <label class="control-label"><i class="fa fa-pencil-square-o"></i> Edit</label><br>
                                                    <button type="button" class="btn btn-default" ng-model="options.draw" bs-checkbox>Draw</button>
                                                    <button type="button" class="btn btn-default" ng-model="options.readOnly" bs-checkbox>Read Only</button>
                                                </div>

                                                <div class="form-group text-center">
                                                    <label class="control-label"><i class="fa fa-magnet"></i> Magnet</label><br>
                                                    <button type="button" style="width: 8em; text-align: left" class="btn btn-default" ng-model="options.columnMagnet" ng-options="m for m in ['column', '1 second', '1 minute', '5 minutes', '15 minutes', '1 hour', '1 day', '5 days']" bs-select></button>
                                                    <button type="button" class="btn btn-default" ng-model="options.daily" bs-checkbox>Daily</button>
                                                    <button type="button" class="btn btn-default" ng-model="options.timeFramesMagnet" bs-checkbox>TimeFrames</button>
                                                </div>

                                                <div class="form-group text-center">
                                                    <label class="control-label"><i class="fa fa-bars"></i> Side</label><br>
                                                    <div class="btn-group" bs-checkbox-group>
                                                        <button type="button" style="width: 8em; text-align: left" class="btn btn-default" ng-model="options.sideMode" ng-options="s for s in ['Tree', 'Table', 'TreeTable', 'Disabled']" bs-select></button>
                                                    </div>
                                                </div>

                                                <div class="form-group text-center">
                                                    <label class="control-label"><i class="fa fa-gear"></i> Groups</label><br>
                                                    <div class="btn-group" bs-checkbox-group>
                                                        <button type="button" style="width: 8em; text-align: left" class="btn btn-default" ng-model="options.groupDisplayMode" ng-options="s for s in ['group', 'overview', 'promote', 'Disabled']" bs-select></button>
                                                    </div>
                                                </div>

                                                <div class="form-group text-center">
                                                    <label class="control-label"><i class="fa fa-crop"></i> Layout</label><br>
                                                    <div class="btn-group" bs-checkbox-group>
                                                        <button type="button" class="btn btn-default" ng-model="options.maxHeight" bs-checkbox>Height</button>
                                                        <button ng-disabled="!canAutoWidth(options.scale)" type="button" class="btn btn-default" ng-model="options.width" bs-checkbox>Width</button>
                                                    </div>
                                                </div>

                                                <div class="form-group text-center">
                                                    <label class="control-label"><i class="fa fa-search"></i> Zoom</label><br>
                                                    <input ng-disabled="!options.width" type="number" ng-model="options.zoom" step="0.1" min="0.1" max="5" class="form-control" />
                                                </div>


                                                <div class="form-group text-center">
                                                    <label class="control-label"><i class="fa fa-text-width"></i> Labels</label><br>
                                                    <div class="btn-group" bs-checkbox-group>
                                                        <button type="button" class="btn btn-default" ng-model="options.labelsEnabled" bs-checkbox>Show</button>
                                                        <button type="button" class="btn btn-default" ng-model="options.allowSideResizing" bs-checkbox>Resizable</button>
                                                    </div>
                                                </div>

                                                <div class="form-group text-center">
                                                    <label class="control-label"><i class="fa fa-code"></i> Content</label><br>
                                                    <div class="btn-group" bs-checkbox-group>
                                                        <button type="button" class="btn btn-default" ng-model="options.rowContentEnabled" bs-checkbox>Rows</button>
                                                        <button type="button" class="btn btn-default" ng-model="options.taskContentEnabled" bs-checkbox>Tasks</button>
                                                    </div>
                                                </div>

                                                <div class="form-group text-center">
                                                    <label class="control-label"><i class="fa fa-calendar"></i> <i class="fa fa-arrows-h"></i> <i class="fa fa-calendar"></i> Date range</label><br>
                                                    <div class="form-group">
                                                        <input type="text" class="form-control" ng-model="options.fromDate" max-date="{{options.toDate}}" start-date="{{options.currentDateValue.toString()}}" start-week="1" placeholder="From" bs-datepicker>
                                                    </div>
                                                    <div class="form-group">
                                                        <input type="text" class="form-control" ng-model="options.toDate" min-date="{{options.fromDate}}" start-date="{{options.currentDateValue.toString()}}" start-week="1" placeholder="To" bs-datepicker>
                                                    </div>
                                                </div>

                                                <div class="form-group text-center">
                                                    <label class="control-label"><i class="fa fa-sign-out"></i> Non-working</label><br>
                                                    <button type="button" style="width: 8em; text-align: left" class="btn btn-default" ng-model="options.timeFramesNonWorkingMode" ng-options="e for e in ['visible', 'hidden', 'cropped']" bs-select></button>
                                                </div>

                                                <div class="form-group text-center">
                                                    <label class="control-label"><i class="fa fa-database"></i> Data actions</label><br>
                                                    <div class="btn-group">
                                                        <button class="btn btn-default" ng-click="reload()">Reload</button>
                                                        <button class="btn btn-default" ng-click="remove()">Remove</button>
                                                        <button class="btn btn-default" ng-click="clear()">Clear</button>
                                                    </div>
                                                </div>

                                                <div ng-if="options.sideMode === 'Tree' || options.sideMode === 'TreeTable'" class="form-group text-center">
                                                    <label class="control-label"><i class="fa fa fa-chevron-circle-right"></i> Tree actions</label><br>
                                                    <div class="btn-group">
                                                        <button class="btn btn-default" ng-click="expandAll()">Expand all</button>
                                                        <button class="btn btn-default" ng-click="collapseAll()">Collapse all</button>
                                                    </div>
                                                </div>
                                            </div>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
            <div class="row top-buffer">
                <div class="col-md-12">
                    <div class="panel-group" bs-collapse>
                        <div class="panel panel-default">
                            <div class="panel-heading">
                                <h4 class="panel-title">
                                    <a href="" bs-collapse-toggle>Gantt</a>
                                </h4>
                            </div>
                            <div class="panel-collapse" bs-collapse-target>
                                <div class="panel-body">
                                    <div gantt
                                         data="data"
                                         timespans="timespans"
                                         show-side="options.labelsEnabled"
                                         daily="options.daily"
                                         filter-task="{'name': options.filterTask}"
                                         filter-row="{'name': options.filterRow}"
                                         sort-mode="options.sortMode"
                                         view-scale="options.scale"
                                         column-width="getColumnWidth(options.width, options.scale, options.zoom)"
                                         auto-expand="options.autoExpand"
                                         task-out-of-range="options.taskOutOfRange"
                                         from-date = "options.fromDate"
                                         to-date = "options.toDate"
                                         allow-side-resizing = "options.allowSideResizing"
                                         task-content = "options.taskContentEnabled ? options.taskContent : undefined"
                                         row-content = "options.rowContentEnabled ? options.rowContent : undefined"
                                         current-date="options.currentDate"
                                         current-date-value="options.currentDateValue"
                                         headers="options.width && options.shortHeaders || options.longHeaders"
                                         max-height="options.maxHeight && 300 || 0"
                                         time-frames="options.timeFrames"
                                         date-frames="options.dateFrames"
                                         time-frames-non-working-mode="options.timeFramesNonWorkingMode"
                                         time-frames-magnet="options.timeFramesMagnet"
                                         api="options.api"
                                         column-magnet="options.columnMagnet">
                                        <gantt-tree enabled="options.sideMode === 'Tree' || options.sideMode === 'TreeTable'"
                                                    header-content="options.treeHeaderContent"
                                                    keep-ancestor-on-filter-row="true">
                                        </gantt-tree>
                                        <gantt-table enabled="options.sideMode === 'Table' || options.sideMode === 'TreeTable'"
                                                     columns="options.sideMode === 'TreeTable' ? options.treeTableColumns : options.columns"
                                                     headers="options.columnsHeaders"
                                                     classes="options.columnsClasses"
                                                     formatters="options.columnsFormatters"
                                                     contents="options.columnsContents"
                                                     header-contents="options.columnsHeaderContents">
                                        </gantt-table>
                                        <gantt-groups enabled="options.groupDisplayMode === 'group' || options.groupDisplayMode === 'overview' || options.groupDisplayMode === 'promote'" display="options.groupDisplayMode"></gantt-groups>
                                        <gantt-tooltips></gantt-tooltips>
                                        <gantt-bounds></gantt-bounds>
                                        <gantt-progress></gantt-progress>
                                        <gantt-sortable></gantt-sortable>
                                        <gantt-movable enabled="!options.readOnly"></gantt-movable>
                                        <gantt-draw-task
                                            enabled="options.canDraw"
                                            move-threshold="2"
                                            task-factory="options.drawTaskFactory">
                                        </gantt-draw-task>
                                        <gantt-overlap></gantt-overlap>
                                        <gantt-resize-sensor></gantt-resize-sensor>
                                    </div>
                                </div>
                                <div class="panel-body">
                                    <div class="live-table">
                                        <div class="live-cell" ng-show="live.row.tasks.length > 0">
                                            <h4><button type="button" style="text-align: left" class="btn btn-default" ng-model="live.task" ng-options="t as t.name for t in live.row.tasks" bs-select></button> (Task object)</h4>
                                            <textarea class="live-task" ng-model="live.taskJson"></textarea>
                                        </div>
                                        <div class="live-cell" ng-show="data.length > 0">
                                            <h4><button type="button" style="text-align: left" class="btn btn-default" ng-model="live.row" ng-options="r as r.name for r in data" bs-select></button> (Row object)</h4>
                                            <textarea class="live-row" ng-model="live.rowJson"></textarea>
                                        </div>
                                    </div>
                                </div>
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
</body>

</html>
angular.module('plnkrGanttMaster', [
            'gantt', // angular-gantt.
            'gantt.sortable',
            'gantt.movable',
            'gantt.drawtask',
            'gantt.tooltips',
            'gantt.bounds',
            'gantt.progress',
            'gantt.table',
            'gantt.tree',
            'gantt.groups',
            'gantt.overlap',
            'gantt.resizeSensor'
        ]).config(['$compileProvider', function ($compileProvider) {
            $compileProvider.debugInfoEnabled(true); // Remove debug info (angularJS >= 1.3)
        }]).controller('Ctrl', ['$scope', '$timeout', '$log', 'ganttUtils', 'GanttObjectModel', 'Sample', 'ganttMouseOffset', 'ganttDebounce', 'moment', function ($scope, $timeout, $log, utils, ObjectModel, Sample, mouseOffset, debounce, moment) {
                var objectModel;
                var dataToRemove;

                $scope.options = {
                    mode: 'custom',
                    scale: 'day',
                    sortMode: undefined,
                    sideMode: 'TreeTable',
                    daily: false,
                    maxHeight: false,
                    width: false,
                    zoom: 1,
                    columns: ['model.name', 'from', 'to'],
                    treeTableColumns: ['from', 'to'],
                    columnsHeaders: { 'model.name': 'Name', 'from': 'From', 'to': 'To' },
                    columnsClasses: { 'model.name': 'gantt-column-name', 'from': 'gantt-column-from', 'to': 'gantt-column-to' },
                    columnsFormatters: {
                        'from': function (from) {
                            return from !== undefined ? from.format('lll') : undefined;
                        },
                        'to': function (to) {
                            return to !== undefined ? to.format('lll') : undefined;
                        }
                    },
                    treeHeaderContent: '<i class="fa fa-align-justify"></i> {{getHeader()}}',
                    columnsHeaderContents: {
                        'model.name': '<i class="fa fa-align-justify"></i> {{getHeader()}}',
                        'from': '<i class="fa fa-calendar"></i> {{getHeader()}}',
                        'to': '<i class="fa fa-calendar"></i> {{getHeader()}}'
                    },
                    autoExpand: 'none',
                    taskOutOfRange: 'truncate',
                    fromDate: moment(null),
                    toDate: undefined,
                    rowContent: '<i class="fa fa-align-justify"></i> {{row.model.name}}',
                    taskContent: '<i class="fa fa-tasks"></i> {{task.model.name}}',
                    allowSideResizing: true,
                    labelsEnabled: true,
                    currentDate: 'line',
                    currentDateValue: new Date(2013, 9, 23, 11, 20, 0),
                    draw: false,
                    readOnly: false,
                    groupDisplayMode: 'group',
                    filterTask: '',
                    filterRow: '',
                    timeFrames: {
                        'day': {
                            start: moment('8:00', 'HH:mm'),
                            end: moment('20:00', 'HH:mm'),
                            working: true,
                            default: true
                        },
                        'noon': {
                            start: moment('12:00', 'HH:mm'),
                            end: moment('13:30', 'HH:mm'),
                            working: false,
                            default: true
                        },
                        'weekend': {
                            working: false
                        },
                        'holiday': {
                            working: false,
                            color: 'red',
                            classes: ['gantt-timeframe-holiday']
                        }
                    },
                    dateFrames: {
                        'weekend': {
                            evaluator: function (date) {
                                return date.isoWeekday() === 6 || date.isoWeekday() === 7;
                            },
                            targets: ['weekend']
                        },
                        '11-november': {
                            evaluator: function (date) {
                                return date.month() === 10 && date.date() === 11;
                            },
                            targets: ['holiday']
                        }
                    },
                    timeFramesNonWorkingMode: 'visible',
                    columnMagnet: '15 minutes',
                    timeFramesMagnet: true,
                    canDraw: function (event) {
                        var isLeftMouseButton = event.button === 0 || event.button === 1;
                        return $scope.options.draw && !$scope.options.readOnly && isLeftMouseButton;
                    },
                    drawTaskFactory: function () {
                        return {
                            id: utils.randomUuid(),  // Unique id of the task.
                            name: 'Drawn task', // Name shown on top of each task.
                            color: '#AA8833' // Color of the task in HEX format (Optional).
                        };
                    },
                    api: function (api) {
                        // API Object is used to control methods and events from angular-gantt.
                        $scope.api = api;

                        api.core.on.ready($scope, function () {
                            // Log various events to console
                            api.scroll.on.scroll($scope, logScrollEvent);
                            api.core.on.ready($scope, logReadyEvent);

                            api.data.on.remove($scope, addEventName('data.on.remove', logDataEvent));
                            api.data.on.load($scope, addEventName('data.on.load', logDataEvent));
                            api.data.on.clear($scope, addEventName('data.on.clear', logDataEvent));

                            api.tasks.on.add($scope, addEventName('tasks.on.add', logTaskEvent));
                            api.tasks.on.change($scope, addEventName('tasks.on.change', logTaskEvent));
                            api.tasks.on.rowChange($scope, addEventName('tasks.on.rowChange', logTaskEvent));
                            api.tasks.on.remove($scope, addEventName('tasks.on.remove', logTaskEvent));

                            if (api.tasks.on.moveBegin) {
                                api.tasks.on.moveBegin($scope, addEventName('tasks.on.moveBegin', logTaskEvent));
                                //api.tasks.on.move($scope, addEventName('tasks.on.move', logTaskEvent));
                                api.tasks.on.moveEnd($scope, addEventName('tasks.on.moveEnd', logTaskEvent));

                                api.tasks.on.resizeBegin($scope, addEventName('tasks.on.resizeBegin', logTaskEvent));
                                //api.tasks.on.resize($scope, addEventName('tasks.on.resize', logTaskEvent));
                                api.tasks.on.resizeEnd($scope, addEventName('tasks.on.resizeEnd', logTaskEvent));
                            }

                            api.rows.on.add($scope, addEventName('rows.on.add', logRowEvent));
                            api.rows.on.change($scope, addEventName('rows.on.change', logRowEvent));
                            api.rows.on.move($scope, addEventName('rows.on.move', logRowEvent));
                            api.rows.on.remove($scope, addEventName('rows.on.remove', logRowEvent));

                            api.side.on.resizeBegin($scope, addEventName('labels.on.resizeBegin', logLabelsEvent));
                            //api.side.on.resize($scope, addEventName('labels.on.resize', logLabelsEvent));
                            api.side.on.resizeEnd($scope, addEventName('labels.on.resizeEnd', logLabelsEvent));

                            api.timespans.on.add($scope, addEventName('timespans.on.add', logTimespanEvent));
                            api.columns.on.generate($scope, logColumnsGenerateEvent);

                            api.rows.on.filter($scope, logRowsFilterEvent);
                            api.tasks.on.filter($scope, logTasksFilterEvent);

                            // When gantt is ready, load data.
                            // `data` attribute could have been used too.
                            $scope.load();

                            // Add some DOM events
                            api.directives.on.new($scope, function (directiveName, directiveScope, element) {
                                if (directiveName === 'ganttTask') {
                                    element.bind('click', function (event) {
                                        event.stopPropagation();
                                        logTaskEvent('task-click', directiveScope.task);
                                    });
                                    element.bind('mousedown touchstart', function (event) {
                                        event.stopPropagation();
                                        $scope.live.row = directiveScope.task.row.model;
                                        if (directiveScope.task.originalModel !== undefined) {
                                            $scope.live.task = directiveScope.task.originalModel;
                                        } else {
                                            $scope.live.task = directiveScope.task.model;
                                        }
                                        $scope.$digest();
                                    });
                                } else if (directiveName === 'ganttRow') {
                                    element.bind('click', function (event) {
                                        event.stopPropagation();
                                        logRowEvent('row-click', directiveScope.row);
                                    });
                                    element.bind('mousedown touchstart', function (event) {
                                        event.stopPropagation();
                                        $scope.live.row = directiveScope.row.model;
                                        $scope.$digest();
                                    });
                                } else if (directiveName === 'ganttRowLabel') {
                                    element.bind('click', function () {
                                        logRowEvent('row-label-click', directiveScope.row);
                                    });
                                    element.bind('mousedown touchstart', function () {
                                        $scope.live.row = directiveScope.row.model;
                                        $scope.$digest();
                                    });
                                }
                            });

                            api.tasks.on.rowChange($scope, function (task) {
                                $scope.live.row = task.row.model;
                            });

                            objectModel = new ObjectModel(api);
                        });
                    }
                };

                $scope.handleTaskIconClick = function (taskModel) {
                    alert('Icon from ' + taskModel.name + ' task has been clicked.');
                };

                $scope.handleRowIconClick = function (rowModel) {
                    alert('Icon from ' + rowModel.name + ' row has been clicked.');
                };

                $scope.expandAll = function () {
                    $scope.api.tree.expandAll();
                };

                $scope.collapseAll = function () {
                    $scope.api.tree.collapseAll();
                };

                $scope.$watch('options.sideMode', function (newValue, oldValue) {
                    if (newValue !== oldValue) {
                        $scope.api.side.setWidth(undefined);
                        $timeout(function () {
                            $scope.api.columns.refresh();
                        });
                    }
                });

                $scope.canAutoWidth = function (scale) {
                    if (scale.match(/.*?hour.*?/) || scale.match(/.*?minute.*?/)) {
                        return false;
                    }
                    return true;
                };

                $scope.getColumnWidth = function (widthEnabled, scale, zoom) {
                    if (!widthEnabled && $scope.canAutoWidth(scale)) {
                        return undefined;
                    }

                    if (scale.match(/.*?week.*?/)) {
                        return 150 * zoom;
                    }

                    if (scale.match(/.*?month.*?/)) {
                        return 300 * zoom;
                    }

                    if (scale.match(/.*?quarter.*?/)) {
                        return 500 * zoom;
                    }

                    if (scale.match(/.*?year.*?/)) {
                        return 800 * zoom;
                    }

                    return 40 * zoom;
                };

                // Reload data action
                $scope.load = function () {
                    $scope.data = Sample.getSampleData();
                    dataToRemove = undefined;

                    $scope.timespans = Sample.getSampleTimespans();
                };

                $scope.reload = function () {
                    $scope.load();
                };

                // Remove data action
                $scope.remove = function () {
                    $scope.api.data.remove(dataToRemove);
                };

                // Clear data action
                $scope.clear = function () {
                    $scope.data = [];
                };


                // Visual two way binding.
                $scope.live = {};

                var debounceValue = 1000;

                var listenTaskJson = debounce(function (taskJson) {
                    if (taskJson !== undefined) {
                        var task = angular.fromJson(taskJson);
                        objectModel.cleanTask(task);
                        var model = $scope.live.task;
                        angular.extend(model, task);
                    }
                }, debounceValue);
                $scope.$watch('live.taskJson', listenTaskJson);

                var listenRowJson = debounce(function (rowJson) {
                    if (rowJson !== undefined) {
                        var row = angular.fromJson(rowJson);
                        objectModel.cleanRow(row);
                        var tasks = row.tasks;

                        delete row.tasks;
                        var rowModel = $scope.live.row;

                        angular.extend(rowModel, row);

                        var newTasks = {};
                        var i, l;

                        if (tasks !== undefined) {
                            for (i = 0, l = tasks.length; i < l; i++) {
                                objectModel.cleanTask(tasks[i]);
                            }

                            for (i = 0, l = tasks.length; i < l; i++) {
                                newTasks[tasks[i].id] = tasks[i];
                            }

                            if (rowModel.tasks === undefined) {
                                rowModel.tasks = [];
                            }
                            for (i = rowModel.tasks.length - 1; i >= 0; i--) {
                                var existingTask = rowModel.tasks[i];
                                var newTask = newTasks[existingTask.id];
                                if (newTask === undefined) {
                                    rowModel.tasks.splice(i, 1);
                                } else {
                                    objectModel.cleanTask(newTask);
                                    angular.extend(existingTask, newTask);
                                    delete newTasks[existingTask.id];
                                }
                            }
                        } else {
                            delete rowModel.tasks;
                        }

                        angular.forEach(newTasks, function (newTask) {
                            rowModel.tasks.push(newTask);
                        });
                    }
                }, debounceValue);
                $scope.$watch('live.rowJson', listenRowJson);

                $scope.$watchCollection('live.task', function (task) {
                    $scope.live.taskJson = angular.toJson(task, true);
                    $scope.live.rowJson = angular.toJson($scope.live.row, true);
                });

                $scope.$watchCollection('live.row', function (row) {
                    $scope.live.rowJson = angular.toJson(row, true);
                    if (row !== undefined && row.tasks !== undefined && row.tasks.indexOf($scope.live.task) < 0) {
                        $scope.live.task = row.tasks[0];
                    }
                });

                $scope.$watchCollection('live.row.tasks', function () {
                    $scope.live.rowJson = angular.toJson($scope.live.row, true);
                });

                // Event handler
                var logScrollEvent = function (left, date, direction) {
                    if (date !== undefined) {
                        $log.info('[Event] api.on.scroll: ' + left + ', ' + (date === undefined ? 'undefined' : date.format()) + ', ' + direction);
                    }
                };

                // Event handler
                var logDataEvent = function (eventName) {
                    $log.info('[Event] ' + eventName);
                };

                // Event handler
                var logTaskEvent = function (eventName, task) {
                    $log.info('[Event] ' + eventName + ': ' + task.model.name);
                };

                // Event handler
                var logRowEvent = function (eventName, row) {
                    $log.info('[Event] ' + eventName + ': ' + row.model.name);
                };

                // Event handler
                var logTimespanEvent = function (eventName, timespan) {
                    $log.info('[Event] ' + eventName + ': ' + timespan.model.name);
                };

                // Event handler
                var logLabelsEvent = function (eventName, width) {
                    $log.info('[Event] ' + eventName + ': ' + width);
                };

                // Event handler
                var logColumnsGenerateEvent = function (columns, headers) {
                    $log.info('[Event] ' + 'columns.on.generate' + ': ' + columns.length + ' column(s), ' + headers.length + ' header(s)');
                };

                // Event handler
                var logRowsFilterEvent = function (rows, filteredRows) {
                    $log.info('[Event] rows.on.filter: ' + filteredRows.length + '/' + rows.length + ' rows displayed.');
                };

                // Event handler
                var logTasksFilterEvent = function (tasks, filteredTasks) {
                    $log.info('[Event] tasks.on.filter: ' + filteredTasks.length + '/' + tasks.length + ' tasks displayed.');
                };

                // Event handler
                var logReadyEvent = function () {
                    $log.info('[Event] core.on.ready');
                };

                // Event utility function
                var addEventName = function (eventName, func) {
                    return function (data) {
                        return func(eventName, data);
                    };
                };

            }])
            .service('Sample', function Sample() {
                return {
                    getSampleData: function () {
                        return [
                                // Order is optional. If not specified it will be assigned automatically
                                {
                                    name: 'Milestones', height: '3em', sortable: false, classes: 'gantt-row-milestone', color: '#45607D', tasks: [
                                       // Dates can be specified as string, timestamp or javascript date object. The data attribute can be used to attach a custom object
                                       { name: 'Kickoff', color: '#93C47D', from: '2013-10-07T09:00:00', to: '2013-10-07T10:00:00', data: 'Can contain any custom data or object' },
                                       { name: 'Concept approval', color: '#93C47D', from: new Date(2013, 9, 18, 18, 0, 0), to: new Date(2013, 9, 18, 18, 0, 0), est: new Date(2013, 9, 16, 7, 0, 0), lct: new Date(2013, 9, 19, 0, 0, 0) },
                                       { name: 'Development finished', color: '#93C47D', from: new Date(2013, 10, 15, 18, 0, 0), to: new Date(2013, 10, 15, 18, 0, 0) },
                                       { name: 'Shop is running', color: '#93C47D', from: new Date(2013, 10, 22, 12, 0, 0), to: new Date(2013, 10, 22, 12, 0, 0) },
                                       { name: 'Go-live', color: '#93C47D', from: new Date(2013, 10, 29, 16, 0, 0), to: new Date(2013, 10, 29, 16, 0, 0) }
                                    ], data: 'Can contain any custom data or object'
                                },
                                {
                                    name: 'Status meetings', tasks: [
                                       { name: 'Demo #1', color: '#9FC5F8', from: new Date(2013, 9, 25, 15, 0, 0), to: new Date(2013, 9, 25, 18, 30, 0) },
                                       { name: 'Demo #2', color: '#9FC5F8', from: new Date(2013, 10, 1, 15, 0, 0), to: new Date(2013, 10, 1, 18, 0, 0) },
                                       { name: 'Demo #3', color: '#9FC5F8', from: new Date(2013, 10, 8, 15, 0, 0), to: new Date(2013, 10, 8, 18, 0, 0) },
                                       { name: 'Demo #4', color: '#9FC5F8', from: new Date(2013, 10, 15, 15, 0, 0), to: new Date(2013, 10, 15, 18, 0, 0) },
                                       { name: 'Demo #5', color: '#9FC5F8', from: new Date(2013, 10, 24, 9, 0, 0), to: new Date(2013, 10, 24, 10, 0, 0) }
                                    ]
                                },
                                {
                                    name: 'Kickoff', movable: { allowResizing: false }, tasks: [
                                       {
                                           name: 'Day 1', color: '#9FC5F8', from: new Date(2013, 9, 7, 9, 0, 0), to: new Date(2013, 9, 7, 17, 0, 0),
                                           progress: { percent: 100, color: '#3C8CF8' }, movable: false
                                       },
                                       {
                                           name: 'Day 2', color: '#9FC5F8', from: new Date(2013, 9, 8, 9, 0, 0), to: new Date(2013, 9, 8, 17, 0, 0),
                                           progress: { percent: 100, color: '#3C8CF8' }
                                       },
                                       {
                                           name: 'Day 3', color: '#9FC5F8', from: new Date(2013, 9, 9, 8, 30, 0), to: new Date(2013, 9, 9, 12, 0, 0),
                                           progress: { percent: 100, color: '#3C8CF8' }
                                       }
                                    ]
                                },
                                {
                                    name: 'Create concept', tasks: [
                                       {
                                           name: 'Create concept', content: '<i class="fa fa-cog" ng-click="scope.handleTaskIconClick(task.model)"></i> {{task.model.name}}', color: '#F1C232', from: new Date(2013, 9, 10, 8, 0, 0), to: new Date(2013, 9, 16, 18, 0, 0), est: new Date(2013, 9, 8, 8, 0, 0), lct: new Date(2013, 9, 18, 20, 0, 0),
                                           progress: 100
                                       }
                                    ]
                                },
                                {
                                    name: 'Finalize concept', tasks: [
                                       {
                                           name: 'Finalize concept', color: '#F1C232', from: new Date(2013, 9, 17, 8, 0, 0), to: new Date(2013, 9, 18, 18, 0, 0),
                                           progress: 100
                                       }
                                    ]
                                },
                                { name: 'Development', children: ['Sprint 1', 'Sprint 2', 'Sprint 3', 'Sprint 4'], content: '<i class="fa fa-file-code-o" ng-click="scope.handleRowIconClick(row.model)"></i> {{row.model.name}}' },
                                {
                                    name: 'Sprint 1', tooltips: false, tasks: [
                                       {
                                           name: 'Product list view', color: '#F1C232', from: new Date(2013, 9, 21, 8, 0, 0), to: new Date(2013, 9, 25, 15, 0, 0),
                                           progress: 25
                                       }
                                    ]
                                },
                                {
                                    name: 'Sprint 2', tasks: [
                                       { name: 'Order basket', color: '#F1C232', from: new Date(2013, 9, 28, 8, 0, 0), to: new Date(2013, 10, 1, 15, 0, 0) }
                                    ]
                                },
                                {
                                    name: 'Sprint 3', tasks: [
                                       { name: 'Checkout', color: '#F1C232', from: new Date(2013, 10, 4, 8, 0, 0), to: new Date(2013, 10, 8, 15, 0, 0) }
                                    ]
                                },
                                {
                                    name: 'Sprint 4', tasks: [
                                       { name: 'Login & Signup & Admin Views', color: '#F1C232', from: new Date(2013, 10, 11, 8, 0, 0), to: new Date(2013, 10, 15, 15, 0, 0) }
                                    ]
                                },
                                { name: 'Hosting' },
                                {
                                    name: 'Setup', tasks: [
                                       { name: 'HW', color: '#F1C232', from: new Date(2013, 10, 18, 8, 0, 0), to: new Date(2013, 10, 18, 12, 0, 0) }
                                    ]
                                },
                                {
                                    name: 'Config', tasks: [
                                       { name: 'SW / DNS/ Backups', color: '#F1C232', from: new Date(2013, 10, 18, 12, 0, 0), to: new Date(2013, 10, 21, 18, 0, 0) }
                                    ]
                                },
                                { name: 'Server', parent: 'Hosting', children: ['Setup', 'Config'] },
                                {
                                    name: 'Deployment', parent: 'Hosting', tasks: [
                                       { name: 'Depl. & Final testing', color: '#F1C232', from: new Date(2013, 10, 21, 8, 0, 0), to: new Date(2013, 10, 22, 12, 0, 0), 'classes': 'gantt-task-deployment' }
                                    ]
                                },
                                {
                                    name: 'Workshop', tasks: [
                                       { name: 'On-side education', color: '#F1C232', from: new Date(2013, 10, 24, 9, 0, 0), to: new Date(2013, 10, 25, 15, 0, 0) }
                                    ]
                                },
                                {
                                    name: 'Content', tasks: [
                                       { name: 'Supervise content creation', color: '#F1C232', from: new Date(2013, 10, 26, 9, 0, 0), to: new Date(2013, 10, 29, 16, 0, 0) }
                                    ]
                                },
                                {
                                    name: 'Documentation', tasks: [
                                       { name: 'Technical/User documentation', color: '#F1C232', from: new Date(2013, 10, 26, 8, 0, 0), to: new Date(2013, 10, 28, 18, 0, 0) }
                                    ]
                                }
                        ];
                    },
                    getSampleTimespans: function () {
                        return [
                                {
                                    from: new Date(2013, 9, 21, 8, 0, 0),
                                    to: new Date(2013, 9, 25, 15, 0, 0),
                                    name: 'Sprint 1 Timespan'
                                    //priority: undefined,
                                    //classes: [],
                                    //data: undefined
                                }
                        ];
                    }
                };
            })
        ;
.gantt-row-milestone {
    font-weight: bold;
    vertical-align: middle;
    color: white;
}
[angular-gantt](http://www.angular-gantt.com) master branch