Project

General

Profile

1
/*
2
*  Altair Admin
3
*  Gantt Chart
4
*/
5

    
6
/*! jquery.kinetic - v2.1.0 - 2015-06-23 http://the-taylors.org/jquery.kinetic
7
 * Copyright (c) 2015 Dave Taylor; Licensed MIT */
8
!function(a){"use strict";var b="kinetic-active";window.requestAnimationFrame||(window.requestAnimationFrame=function(){return window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame||window.oRequestAnimationFrame||window.msRequestAnimationFrame||function(a){window.setTimeout(a,1e3/60)}}()),a.support=a.support||{},a.extend(a.support,{touch:"ontouchend"in document});var c=function(){return!1},d=function(b,c){return this.settings=c,this.el=b,this.$el=a(b),this._initElements(),this};d.DATA_KEY="kinetic",d.DEFAULTS={cursor:"move",decelerate:!0,triggerHardware:!1,threshold:0,y:!0,x:!0,slowdown:.9,maxvelocity:40,throttleFPS:60,movingClass:{up:"kinetic-moving-up",down:"kinetic-moving-down",left:"kinetic-moving-left",right:"kinetic-moving-right"},deceleratingClass:{up:"kinetic-decelerating-up",down:"kinetic-decelerating-down",left:"kinetic-decelerating-left",right:"kinetic-decelerating-right"}},d.prototype.start=function(b){this.settings=a.extend(this.settings,b),this.velocity=b.velocity||this.velocity,this.velocityY=b.velocityY||this.velocityY,this.settings.decelerate=!1,this._move()},d.prototype.end=function(){this.settings.decelerate=!0},d.prototype.stop=function(){this.velocity=0,this.velocityY=0,this.settings.decelerate=!0,a.isFunction(this.settings.stopped)&&this.settings.stopped.call(this)},d.prototype.detach=function(){this._detachListeners(),this.$el.removeClass(b).css("cursor","")},d.prototype.attach=function(){this.$el.hasClass(b)||(this._attachListeners(this.$el),this.$el.addClass(b).css("cursor",this.settings.cursor))},d.prototype._initElements=function(){this.$el.addClass(b),a.extend(this,{xpos:null,prevXPos:!1,ypos:null,prevYPos:!1,mouseDown:!1,throttleTimeout:1e3/this.settings.throttleFPS,lastMove:null,elementFocused:null}),this.velocity=0,this.velocityY=0,a(document).mouseup(a.proxy(this._resetMouse,this)).click(a.proxy(this._resetMouse,this)),this._initEvents(),this.$el.css("cursor",this.settings.cursor),this.settings.triggerHardware&&this.$el.css({"-webkit-transform":"translate3d(0,0,0)","-webkit-perspective":"1000","-webkit-backface-visibility":"hidden"})},d.prototype._initEvents=function(){var b=this;this.settings.events={touchStart:function(a){var c;b._useTarget(a.target,a)&&(c=a.originalEvent.touches[0],b.threshold=b._threshold(a.target,a),b._start(c.clientX,c.clientY),a.stopPropagation())},touchMove:function(a){var c;b.mouseDown&&(c=a.originalEvent.touches[0],b._inputmove(c.clientX,c.clientY),a.preventDefault&&a.preventDefault())},inputDown:function(a){b._useTarget(a.target,a)&&(b.threshold=b._threshold(a.target,a),b._start(a.clientX,a.clientY),b.elementFocused=a.target,"IMG"===a.target.nodeName&&a.preventDefault(),a.stopPropagation())},inputEnd:function(a){b._useTarget(a.target,a)&&(b._end(),b.elementFocused=null,a.preventDefault&&a.preventDefault())},inputMove:function(a){b.mouseDown&&(b._inputmove(a.clientX,a.clientY),a.preventDefault&&a.preventDefault())},scroll:function(c){a.isFunction(b.settings.moved)&&b.settings.moved.call(b,b.settings),c.preventDefault&&c.preventDefault()},inputClick:function(a){return Math.abs(b.velocity)>0?(a.preventDefault(),!1):void 0},dragStart:function(a){return b._useTarget(a.target,a)&&b.elementFocused?!1:void 0}},this._attachListeners(this.$el,this.settings)},d.prototype._inputmove=function(b,c){{var d=this.$el;this.el}if((!this.lastMove||new Date>new Date(this.lastMove.getTime()+this.throttleTimeout))&&(this.lastMove=new Date,this.mouseDown&&(this.xpos||this.ypos))){var e=b-this.xpos,f=c-this.ypos;if(this.threshold>0){var g=Math.sqrt(e*e+f*f);if(this.threshold>g)return;this.threshold=0}this.elementFocused&&(a(this.elementFocused).blur(),this.elementFocused=null,d.focus()),this.settings.decelerate=!1,this.velocity=this.velocityY=0;var h=this.scrollLeft(),i=this.scrollTop();this.scrollLeft(this.settings.x?h-e:h),this.scrollTop(this.settings.y?i-f:i),this.prevXPos=this.xpos,this.prevYPos=this.ypos,this.xpos=b,this.ypos=c,this._calculateVelocities(),this._setMoveClasses(this.settings.movingClass),a.isFunction(this.settings.moved)&&this.settings.moved.call(this,this.settings)}},d.prototype._calculateVelocities=function(){this.velocity=this._capVelocity(this.prevXPos-this.xpos,this.settings.maxvelocity),this.velocityY=this._capVelocity(this.prevYPos-this.ypos,this.settings.maxvelocity)},d.prototype._end=function(){this.xpos&&this.prevXPos&&this.settings.decelerate===!1&&(this.settings.decelerate=!0,this._calculateVelocities(),this.xpos=this.prevXPos=this.mouseDown=!1,this._move())},d.prototype._useTarget=function(b,c){return a.isFunction(this.settings.filterTarget)?this.settings.filterTarget.call(this,b,c)!==!1:!0},d.prototype._threshold=function(b,c){return a.isFunction(this.settings.threshold)?this.settings.threshold.call(this,b,c):this.settings.threshold},d.prototype._start=function(a,b){this.mouseDown=!0,this.velocity=this.prevXPos=0,this.velocityY=this.prevYPos=0,this.xpos=a,this.ypos=b},d.prototype._resetMouse=function(){this.xpos=!1,this.ypos=!1,this.mouseDown=!1},d.prototype._decelerateVelocity=function(a,b){return 0===Math.floor(Math.abs(a))?0:a*b},d.prototype._capVelocity=function(a,b){var c=a;return a>0?a>b&&(c=b):0-b>a&&(c=0-b),c},d.prototype._setMoveClasses=function(a){var b=this.settings,c=this.$el;c.removeClass(b.movingClass.up).removeClass(b.movingClass.down).removeClass(b.movingClass.left).removeClass(b.movingClass.right).removeClass(b.deceleratingClass.up).removeClass(b.deceleratingClass.down).removeClass(b.deceleratingClass.left).removeClass(b.deceleratingClass.right),this.velocity>0&&c.addClass(a.right),this.velocity<0&&c.addClass(a.left),this.velocityY>0&&c.addClass(a.down),this.velocityY<0&&c.addClass(a.up)},d.prototype._move=function(){var b=(this.$el,this.el),c=this,d=c.settings;d.x&&b.scrollWidth>0?(this.scrollLeft(this.scrollLeft()+this.velocity),Math.abs(this.velocity)>0&&(this.velocity=d.decelerate?c._decelerateVelocity(this.velocity,d.slowdown):this.velocity)):this.velocity=0,d.y&&b.scrollHeight>0?(this.scrollTop(this.scrollTop()+this.velocityY),Math.abs(this.velocityY)>0&&(this.velocityY=d.decelerate?c._decelerateVelocity(this.velocityY,d.slowdown):this.velocityY)):this.velocityY=0,c._setMoveClasses(d.deceleratingClass),a.isFunction(d.moved)&&d.moved.call(this,d),Math.abs(this.velocity)>0||Math.abs(this.velocityY)>0?this.moving||(this.moving=!0,window.requestAnimationFrame(function(){c.moving=!1,c._move()})):c.stop()},d.prototype._getScroller=function(){var b=this.$el;return(this.$el.is("body")||this.$el.is("html"))&&(b=a(window)),b},d.prototype.scrollLeft=function(a){var b=this._getScroller();return"number"!=typeof a?b.scrollLeft():(b.scrollLeft(a),void(this.settings.scrollLeft=a))},d.prototype.scrollTop=function(a){var b=this._getScroller();return"number"!=typeof a?b.scrollTop():(b.scrollTop(a),void(this.settings.scrollTop=a))},d.prototype._attachListeners=function(){var b=this.$el,d=this.settings;a.support.touch&&b.bind("touchstart",d.events.touchStart).bind("touchend",d.events.inputEnd).bind("touchmove",d.events.touchMove),b.mousedown(d.events.inputDown).mouseup(d.events.inputEnd).mousemove(d.events.inputMove),b.click(d.events.inputClick).scroll(d.events.scroll).bind("selectstart",c).bind("dragstart",d.events.dragStart)},d.prototype._detachListeners=function(){var b=this.$el,d=this.settings;a.support.touch&&b.unbind("touchstart",d.events.touchStart).unbind("touchend",d.events.inputEnd).unbind("touchmove",d.events.touchMove),b.unbind("mousedown",d.events.inputDown).unbind("mouseup",d.events.inputEnd).unbind("mousemove",d.events.inputMove),b.unbind("click",d.events.inputClick).unbind("scroll",d.events.scroll).unbind("selectstart",c).unbind("dragstart",d.events.dragStart)},a.Kinetic=d,a.fn.kinetic=function(b,c){return this.each(function(){var e=a(this),f=e.data(d.DATA_KEY),g=a.extend({},d.DEFAULTS,e.data(),"object"==typeof b&&b);f||e.data(d.DATA_KEY,f=new d(this,g)),"string"==typeof b&&f[b](c)})}}(window.jQuery||window.Zepto);
9

    
10
/*! Moment Duration Format v1.3.0
11
 *  https://github.com/jsmreese/moment-duration-format
12
 *  Date: 2014-07-15
13
 *
14
 *  Duration format plugin function for the Moment.js library
15
 *  http://momentjs.com/
16
 *
17
 *  Copyright 2014 John Madhavan-Reese
18
 *  Released under the MIT license
19
*/
20
(function(n,B){function r(a){var b="";a=parseInt(a,10);if(!a||1>a)return b;for(;a;)b+="0",--a;return b}function l(a,b,c){null==a&&(a="");a=""+a;return(c?a:"")+r(b-a.length)+(c?"":a)}function x(a,b){for(var c=a.length;--c;)if(b(a[c]))return a[c]}function t(a,b){var c=0,e=a.length,f;"function"!==typeof b&&(f=b,b=function(a){return a===f});for(;c<e;){if(b(a[c]))return a[c];c+=1}}function h(a,b){var c=0,e=a.length;if(a&&e)for(;c<e&&!1!==b(a[c],c);)c+=1}function k(a,b){var c=0,e=a.length,f=[];if(!a||!e)return f; for(;c<e;)f[c]=b(a[c],c),c+=1;return f}function u(a,b){return k(a,function(a){return a[b]})}function y(a){var b=[];h(a,function(a){a&&b.push(a)});return b}function v(a){var b=[];h(a,function(a){t(b,a)||b.push(a)});return b}function z(a,b){var c=[];h(a,function(a){h(b,function(b){a===b&&c.push(a)})});return v(c)}function w(a,b){var c=[];h(a,function(e,f){if(!b(e))return c=a.slice(f),!1});return c}function A(a,b){var c=a.slice().reverse();return w(c,b).reverse()}function q(a,b){for(var c in b)b.hasOwnProperty(c)&& (a[c]=b[c]);return a}var g;if("function"===typeof require)try{g=require("moment")}catch(a){}!g&&n.moment&&(g=n.moment);if(!g)throw"Moment Duration Format cannot find Moment.js";g.duration.fn.format=function(){var a,b,c,e,f,p;a=[].slice.call(arguments);var d=q({},this.format.defaults),n=g.duration(this);d.duration=this;h(a,function(a){"string"===typeof a||"function"===typeof a?d.template=a:"number"===typeof a?d.precision=a:"[object Object]"===Object.prototype.toString.call(a)&&q(d,a)});c=d.types="[object Array]"=== Object.prototype.toString.call(d.types)?d.types:d.types.split(" ");"function"===typeof d.template&&(d.template=d.template.apply(d));a=new RegExp(k(c,function(a){return d[a].source}).join("|"),"g");e=function(a){return t(c,function(b){return d[b].test(a)})};b=k(d.template.match(a),function(a,b){var c=e(a);return{index:b,length:a.length,token:"escape"===c?a.replace(d.escape,"$1"):a,type:"escape"===c||"general"===c?null:c}},this);f=z(c,v(y(u(b,"type"))));if(!f.length)return u(b,"token").join("");h(f, function(a,c){var m,e,g,l,k;m=n.as(a);e=0<m?Math.floor(m):Math.ceil(m);g=m-e;l=c+1===f.length;k=!c;h(b,function(b){b.type===a&&(q(b,{value:m,wholeValue:e,decimalValue:g,isLeast:l,isMost:k}),k&&null==d.forceLength&&1<b.length&&(d.forceLength=!0))});n.subtract(e,a)});d.trim&&(b=("left"===d.trim?w:A)(b,function(a){return!(a.isLeast||null!=a.type&&a.wholeValue)}));p=!1;"right"===d.trim&&b.reverse();b=k(b,function(a){var b,c;if(!a.type)return a.token;b=a.isLeast&&0>d.precision?(Math.floor(a.wholeValue* Math.pow(10,d.precision))*Math.pow(10,-d.precision)).toString():a.wholeValue.toString();b=b.replace(/^\-/,"");1<a.length&&(p||a.isMost||d.forceLength)&&(b=l(b,a.length));if(a.isLeast&&0<d.precision)switch(c=a.decimalValue.toString().replace(/^\-/,"").split(/\.|e\-/),c.length){case 1:b+="."+l(c[0],d.precision,!0).slice(0,d.precision);break;case 2:b+="."+l(c[1],d.precision,!0).slice(0,d.precision);break;case 3:b+="."+l(r(+c[2]-1)+(c[0]||"0")+c[1],d.precision,!0).slice(0,d.precision);break;default:throw"Moment Duration Format: unable to parse token decimal value."; }a.isMost&&0>a.value&&(b="-"+b);p=!0;return b});"right"===d.trim&&b.reverse();return b.join("")};g.duration.fn.format.defaults={escape:/\[(.+?)\]/,years:/[Yy]+/,months:/M+/,weeks:/[Ww]+/,days:/[Dd]+/,hours:/[Hh]+/,minutes:/m+/,seconds:/s+/,milliseconds:/S+/,general:/.+?/,types:"escape years months weeks days hours minutes seconds milliseconds general",trim:"left",precision:0,forceLength:null,template:function(){var a=this.duration;switch(x(this.types,function(b){return a._data[b]})){case "seconds":return"h:mm:ss"; case "minutes":return"d[d] h:mm";case "hours":return"d[d] h[h]";case "days":return"M[m] d[d]";case "weeks":return"y[y] w[w]";case "months":return"y[y] M[m]";case "years":return"y[y]";default:return"y[y] M[m] d[d] h:mm:ss"}}}})(this);
21

    
22
/*
23
 jQuery.ganttView v.0.8.8
24
 Copyright (c) 2010 JC Grubbs - jc.grubbs@devmynd.com
25
 MIT License Applies
26
 */
27

    
28

    
29
(function (jQuery) {
30

    
31
    jQuery.fn.ganttView = function () {
32

    
33
        var args = Array.prototype.slice.call(arguments);
34

    
35
        if (args.length == 1 && typeof(args[0]) == "object") {
36
            build.call(this, args[0]);
37
        }
38

    
39
        if (args.length == 2 && typeof(args[0]) == "string") {
40
            handleMethod.call(this, args[0], args[1]);
41
        }
42
    };
43

    
44
    function build(options) {
45

    
46
        var els = this;
47
        var defaults = {
48
            showWeekends: true,
49
            showToday: true,
50
            toggleProjects: true,
51
            cellWidth: 20,
52
            slideWidth: 400,
53
            kineticScroll: true,
54
            startDate: false,
55
            endDate: false,
56
            startToday: false,
57
            behavior: {
58
                clickable: false,
59
                draggable: true,
60
                resizable: true
61
            }
62
        };
63

    
64
        var opts = jQuery.extend(true, defaults, options);
65

    
66
        if (opts.data) {
67
            build();
68
        } else if (opts.dataUrl) {
69
            jQuery.getJSON(opts.dataUrl, function (data) { opts.data = data; build(); });
70
        }
71

    
72
        function build() {
73

    
74
            var minDays = Math.floor((opts.slideWidth / opts.cellWidth)  + 5);
75
            var startEnd = DateUtils.getBoundaryDatesFromData(opts.data, minDays);
76
            opts.start = opts.startDate ? moment(opts.startDate,"MM/DD/YYYY")._d : startEnd[0];
77
            opts.end = opts.endDate ? moment(opts.endDate,"MM/DD/YYYY")._d : startEnd[1];
78

    
79
            els.each(function () {
80

    
81
                var container = jQuery(this);
82
                var div = jQuery("<div>", { "class": "ganttview" });
83
                new Chart(div, opts).render();
84
                container.append(div);
85

    
86
                var w = jQuery("div.ganttview-vtheader", container).outerWidth() +
87
                    jQuery("div.ganttview-slide-container", container).outerWidth();
88

    
89
                new Behavior(container, opts).apply();
90
            });
91
        }
92
    }
93

    
94
    function handleMethod(method, value) {
95

    
96
        if (method == "setSlideWidth") {
97
            var div = $("div.ganttview", this);
98
            div.each(function () {
99
                var vtWidth = $("div.ganttview-vtheader", div).outerWidth();
100
                $(div).width(vtWidth + value + 1);
101
                $("div.ganttview-slide-container", this).width(value);
102
            });
103
        }
104
    }
105

    
106
    var Chart = function(div, opts) {
107

    
108
        function render() {
109
            addVtHeader(div, opts.data);
110

    
111
            var slideDiv = jQuery("<div>", {
112
                "class": "ganttview-slide-container"
113
            });
114

    
115
            dates = getDates(opts.start, opts.end);
116
            addHzHeader(slideDiv, dates, opts.cellWidth, opts.showWeekends, opts.showToday, opts.startToday);
117
            addGrid(slideDiv, opts.data, dates, opts.cellWidth, opts.showWeekends, opts.showToday);
118
            addBlockContainers(slideDiv, opts.data);
119
            addBlocks(slideDiv, opts.data, opts.cellWidth, opts.start);
120
            div.append(slideDiv);
121
            jQuery(div).find('.ganttview-blocks').width(slideDiv.find('.ganttview-grid').width());
122
            applyLastClass(div.parent());
123
            if(opts.toggleProjects) {
124
                toggleProjects(div);
125
            }
126
            if(opts.kineticScroll && !Modernizr.touch) {
127
                kineticScroll(div);
128
            }
129
            if(opts.startToday) {
130
                setTimeout(function () {
131
                    var days = DateUtils.daysBetween(opts.start, moment()._d);
132
                    slideDiv.scrollLeft( days * opts.cellWidth );
133
                },100)
134
            }
135
        }
136

    
137
        // Creates a 3 dimensional array [year][month][day] of every day
138
        // between the given start and end dates
139
        function getDates(start, end) {
140
            var dates = [];
141
            dates[start.getFullYear()] = [];
142
            dates[start.getFullYear()][start.getMonth()] = [start];
143
            var last = start;
144
            while (moment(end).isAfter(last)) {
145
                var next = moment(last).add(1,'days')._d;
146
                if (!dates[next.getFullYear()]) { dates[next.getFullYear()] = []; }
147
                if (!dates[next.getFullYear()][next.getMonth()]) {
148
                    dates[next.getFullYear()][next.getMonth()] = [];
149
                }
150
                dates[next.getFullYear()][next.getMonth()].push(next);
151
                last = next;
152
            }
153
            return dates;
154
        }
155

    
156
        function addVtHeader(div, data) {
157
            var headerDiv = jQuery("<div>", { "class": "ganttview-vtheader" });
158
            for (var i = 0; i < data.length; i++) {
159
                var itemDiv = jQuery("<div>", { "class": "ganttview-vtheader-group" });
160
                itemDiv.append(jQuery("<div>", {
161
                    "class": "ganttview-vtheader-group-name",
162
                    "id": "groupId_" + i
163
                }).append(data[i].name).append('<span/>'));
164

    
165
                var seriesDiv = jQuery("<div>", { "class": "ganttview-vtheader-series" });
166
                for (var j = 0; j < data[i].series.length; j++) {
167
                    var seriesRow = jQuery("<div>", { "class": "ganttview-vtheader-series-row" });
168
                    if((data[i].series[j].sub_series)) {
169
                        var series_content = jQuery("<div>", { "class": "series-content" }),
170
                            series_dates = jQuery("<div>", { "class": "series-dates" });
171
                            series_users = jQuery("<div>", { "class": "series-users" });
172

    
173
                        series_content.append( '<div class="series-name">' +
174
                            data[i].series[j].name +
175
                        '</div>');
176

    
177
                        for (var k = 0; k < data[i].series[j].sub_series.length; k++) {
178
                            var moreItems = k > 0 ? '<span class="date-sep">|</span>' : '',
179
                                sub_serie = data[i].series[j].sub_series[k];
180

    
181
                            if(sub_serie.user_avatar) {
182
                                var series_user = jQuery("<div>", { "class": "series-user" }),
183
                                    user_name = sub_serie.user_name;
184
                                    user_avatar = sub_serie.user_avatar;
185
                                series_user.append('<span><img src="'+ user_avatar +'"/></span>');
186
                                series_users.append(series_user);
187
                                seriesRow.append(series_users);
188
                            }
189

    
190
                            series_dates.append( function() {
191
                                return moreItems +
192
                                "<span data-event-id='"+ data[i].series[j].sub_series[k].id +"'>" +
193
                                    moment(sub_serie.start,"MM/DD/YYYY").format('D MMM') +
194
                                    " - " +
195
                                    moment(sub_serie.end,"MM/DD/YYYY").format('D MMM') +
196
                                "</span>"
197
                            });
198

    
199
                            series_content.append(series_dates);
200
                            seriesRow.append(series_content);
201
                        }
202
                    } else {
203
                        seriesRow.append(
204
                            function() {
205
                                var avatar =
206
                                    (data[i].series[j].user_avatar)
207
                                        ?   "<div class='series-user'><span><img src='"+ data[i].series[j].user_avatar +"' /></span></div>"
208
                                        :   "";
209

    
210
                                return avatar +
211
                                    "<div class='series-content'>" +
212
                                        '<div class="series-name">' +
213
                                            data[i].series[j].name +
214
                                        '</div>' +
215
                                        "<div class='series-dates'>" +
216
                                            "<span data-event-id='"+ data[i].series[j].id +"'>" +
217
                                                moment(data[i].series[j].start,"MM/DD/YYYY").format('D MMM') +
218
                                                " - " +
219
                                                moment(data[i].series[j].end,"MM/DD/YYYY").format('D MMM') +
220
                                            "</span>" +
221
                                        "</div>" +
222
                                    "</div>"
223
                            }
224
                        );
225
                    }
226
                    seriesDiv.append(seriesRow);
227
                }
228
                itemDiv.append(seriesDiv);
229
                headerDiv.append(itemDiv);
230
            }
231
            div.append(headerDiv);
232
        }
233

    
234
        function addHzHeader(div, dates, cellWidth, showWeekends, showToday, startToday) {
235
            var headerDiv = jQuery("<div>", { "class": "ganttview-hzheader" });
236
            var monthsDiv = jQuery("<div>", { "class": "ganttview-hzheader-months" });
237
            var daysDiv = jQuery("<div>", { "class": "ganttview-hzheader-days" });
238
            var totalW = 0;
239
            for (var y in dates) {
240
                for (var m in dates[y]) {
241
                    var w = dates[y][m].length * cellWidth;
242
                    totalW = totalW + w;
243
                    monthsDiv.append(jQuery("<div>", {
244
                        "class": "ganttview-hzheader-month",
245
                        "css": { "width": (w - 1) + "px" }
246
                    }).append(moment(parseInt(m)+1,"M").format('MMMM') + " " + y));
247
                    for (var d in dates[y][m]) {
248
                        var headerDay = jQuery("<div>", { "class": "ganttview-hzheader-day" });
249
                        if (DateUtils.isWeekend(dates[y][m][d]) && showWeekends) {
250
                            headerDay.addClass("ganttview-weekend");
251
                        }
252
                        if (moment(dates[y][m][d]).isSame(Date.now(), 'day') && showToday) {
253
                            headerDay.addClass("ganttview-today");
254
                        }
255
                        if (moment(dates[y][m][d]).isSame(Date.now(), 'day') && startToday) {
256
                            headerDay.addClass("ganttview-startToday");
257
                        }
258
                        daysDiv.append( headerDay.append(dates[y][m][d].getDate()) );
259
                    }
260
                }
261
            }
262
            monthsDiv.css("width", totalW + "px");
263
            daysDiv.css("width", totalW + "px");
264
            headerDiv.append(monthsDiv).append(daysDiv);
265
            div.append(headerDiv);
266
        }
267

    
268
        function addGrid(div, data, dates, cellWidth, showWeekends, showToday) {
269
            var gridDiv = jQuery("<div>", { "class": "ganttview-grid" });
270
            var rowDiv = jQuery("<div>", { "class": "ganttview-grid-row" });
271
            for (var y in dates) {
272
                for (var m in dates[y]) {
273
                    for (var d in dates[y][m]) {
274
                        var cellDiv = jQuery("<div>", { "class": "ganttview-grid-row-cell" });
275
                        if (DateUtils.isWeekend(dates[y][m][d]) && showWeekends) {
276
                            cellDiv.addClass("ganttview-weekend");
277
                        }
278
                        if (moment(dates[y][m][d]).isSame(Date.now(), 'day') && showToday) {
279
                            cellDiv.addClass("ganttview-today");
280
                        }
281
                        rowDiv.append(cellDiv);
282
                    }
283
                }
284
            }
285
            var w = jQuery("div.ganttview-grid-row-cell", rowDiv).length * cellWidth;
286
            rowDiv.css("width", w + "px");
287
            gridDiv.css("width", w + "px");
288
            for (var i = 0; i < data.length; i++) {
289
                gridDiv.append(jQuery("<div>", {
290
                    "class": "ganttview-grid-spacer",
291
                    "data-click-target": "groupId_" + i
292
                }));
293
                for (var j = 0; j < data[i].series.length; j++) {
294
                    gridDiv.append(rowDiv.clone().addClass('groupId_' + i));
295
                }
296
            }
297
            div.append(gridDiv);
298
        }
299

    
300
        function addBlockContainers(div, data) {
301
            var blocksDiv = jQuery("<div>", {
302
                "class": "ganttview-blocks"
303
            });
304
            for (var i = 0; i < data.length; i++) {
305
                blocksDiv.append(jQuery("<div>", { "class": "ganttview-block-spacer" }));
306
                for (var j = 0; j < data[i].series.length; j++) {
307
                    blocksDiv.append(jQuery("<div>", {
308
                        "class": "ganttview-block-container groupId_" + i
309
                    }));
310
                }
311
            }
312
            div.append(blocksDiv);
313
        }
314

    
315
        function addBlocks(div, data, cellWidth, start) {
316
            var rows = jQuery("div.ganttview-blocks div.ganttview-block-container", div),
317
                rowIdx = 0;
318

    
319
            for (var i = 0; i < data.length; i++) {
320
                for (var j = 0; j < data[i].series.length; j++) {
321

    
322
                    if(data[i].series[j].sub_series) {
323
                        var sub_series = data[i].series[j].sub_series,
324
                            $sub_series_name = (data[i].series[j].name).replace(/(<([^>]+)>)/ig," ");
325
                        $.each(sub_series, function($key,$value) {
326
                            var size = DateUtils.daysBetween($value.start, $value.end) + 1,
327
                                offset = DateUtils.daysBetween(start, $value.start),
328
                                user_name = $value.user_name ? ' (' + $value.user_name + ')' : '',
329
                                block = jQuery("<div>", {
330
                                    "class": "ganttview-block",
331
                                    "title": $sub_series_name + user_name,
332
                                    "data-id": $value.id,
333
                                    "css": {
334
                                        "width": ((size * cellWidth) - 7) + "px",
335
                                        "left": ((offset * cellWidth) + 3) + "px"
336
                                    }
337
                                });
338

    
339
                            addBlockData(block, data[i], sub_series[$key]);
340
                            if ($value.color) {
341
                                block.css("background-color", $value.color);
342
                            }
343
                            var title = $value.title ? $value.link ? '<a href="'+$value.link+'" title="'+$value.link+'">' + $value.title + '</a>': $value.title : moment.duration(size,'days').format();
344
                            if($value.link) {
345
                                block.append(jQuery("<div>", { "class": "ganttview-block-text" }).html( title ));
346
                            } else {
347
                                block.append(jQuery("<div>", { "class": "ganttview-block-text" }).text( title ));
348
                            }
349
                            jQuery(rows[rowIdx]).append(block);
350
                        });
351
                    } else {
352
                        var series = data[i].series[j],
353
                            $series_name = (series.name).replace(/(<([^>]+)>)/ig," "),
354
                            size = DateUtils.daysBetween(series.start, series.end) + 1,
355
                            offset = DateUtils.daysBetween(start, series.start),
356
                            block = jQuery("<div>", {
357
                                "class": "ganttview-block",
358
                                "title": $series_name,
359
                                "css": {
360
                                    "width": ((size * cellWidth) - 7) + "px",
361
                                    "left": ((offset * cellWidth) + 3) + "px"
362
                                }
363
                            });
364
                        addBlockData(block, data[i], series);
365
                        if (data[i].series[j].color) {
366
                            block.css("background-color", data[i].series[j].color);
367
                        }
368
                        var title = series.title ? series.link ? '<a href="'+series.link+'" title="'+series.link+'">' + series.title + '</a>': series.title : moment.duration(size,'days').format();
369
                        if(series.link) {
370
                            block.append(jQuery("<div>", { "class": "ganttview-block-text" }).html( title ));
371
                        } else {
372
                            block.append(jQuery("<div>", { "class": "ganttview-block-text" }).text( title ));
373
                        }
374

    
375
                        jQuery(rows[rowIdx]).append(block);
376
                    }
377
                    rowIdx++;
378
                }
379
            }
380

    
381
            jQuery(".ganttview-blocks").css({
382
                width: $(div).width()
383
            });
384

    
385
        }
386

    
387
        function addBlockData(block, data, series) {
388
            // This allows custom attributes to be added to the series data objects
389
            // and makes them available to the 'data' argument of click, resize, and drag handlers
390
            var blockData = { name: data.name };
391
            jQuery.extend(blockData, series);
392
            block.data("block-data", blockData);
393
        }
394

    
395
        function applyLastClass(div) {
396
            jQuery("div.ganttview-grid-row div.ganttview-grid-row-cell:last-child", div).addClass("last");
397
            jQuery("div.ganttview-hzheader-days div.ganttview-hzheader-day:last-child", div).addClass("last");
398
            jQuery("div.ganttview-hzheader-months div.ganttview-hzheader-month:last-child", div).addClass("last");
399
        }
400

    
401
        function toggleProjects(div) {
402

    
403
            $('div.ganttview-vtheader-group-name',div).addClass('toggle_enabled').on('click', function() {
404
                $thisId = $(this).attr('id');
405
                if(!$(this).hasClass('projectHidden')) {
406
                    $(this)
407
                        .addClass('projectHidden')
408
                        .next('div')
409
                        .children()
410
                        .velocity("slideUp",{
411
                            duration: 180,
412
                            easing: easing_swiftOut
413
                        });
414

    
415
                    $('.ganttview-block-container.'+$thisId).hide();
416

    
417
                    $('.ganttview-grid-row.'+$thisId)
418
                        .velocity("slideUp",{
419
                            duration: 180,
420
                            easing: easing_swiftOut
421
                        });
422

    
423
                } else {
424
                    $(this)
425
                        .removeClass('projectHidden')
426
                        .next('div')
427
                        .children()
428
                        .velocity("slideDown",{
429
                            duration: 180,
430
                            easing: easing_swiftOut
431
                        });
432

    
433
                    $('.ganttview-block-container.'+$thisId).show();
434

    
435
                    $('.ganttview-grid-row.'+$thisId)
436
                        .velocity("slideDown",{
437
                            duration: 180,
438
                            easing: easing_swiftOut
439
                        });
440

    
441
                }
442
            });
443

    
444
            $('div.ganttview-grid-spacer',div).on('click', function() {
445
                $('#'+$(this).attr('data-click-target')).click()
446
            });
447

    
448
        }
449

    
450
        function kineticScroll(div) {
451
            var container = jQuery("div.ganttview-slide-container", div);
452
            $(container).kinetic({
453
                y: false,
454
                filterTarget: function(target, e){
455
                    return !($(target).closest(".ganttview-block").length || $(target).closest(".ganttview-grid-spacer").length);
456
                }
457
            });
458
        }
459

    
460
        return {
461
            render: render
462
        };
463
    };
464

    
465
    var Behavior = function (div, opts) {
466

    
467
        function apply() {
468

    
469
            if (opts.behavior.clickable) {
470
                bindBlockClick(div, opts.behavior.onClick);
471
            }
472

    
473
            if (opts.behavior.resizable) {
474
                bindBlockResize(div, opts.cellWidth, opts.start, opts.behavior.onResize);
475
            }
476

    
477
            if (opts.behavior.draggable) {
478
                bindBlockDrag(div, opts.cellWidth, opts.start, opts.behavior.onDrag);
479
            }
480
        }
481

    
482
        function bindBlockClick(div, callback) {
483
            jQuery("div.ganttview-block", div).on("click", function () {
484
                if (callback) { callback(jQuery(this).data("block-data")); }
485
            });
486
        }
487

    
488
        function bindBlockResize(div, cellWidth, startDate, callback) {
489
            jQuery("div.ganttview-block", div).resizable({
490
                grid: cellWidth,
491
                handles: "e,w",
492
                stop: function () {
493
                    var block = jQuery(this);
494
                    updateDataAndPosition(div, block, cellWidth, startDate);
495

    
496
                    var blockData = block.data("block-data");
497
                    div.find('[data-event-id='+blockData.id+']').text(moment(blockData.start).format('D MMM') + ' - ' + moment(blockData.end).format('D MMM'));
498

    
499
                    if (callback) { callback(block.data("block-data")); }
500
                }
501
            });
502
        }
503

    
504
        function bindBlockDrag(div, cellWidth, startDate, callback) {
505
            jQuery("div.ganttview-block", div).draggable({
506
                axis: "x",
507
                grid: [cellWidth, cellWidth],
508
                containment: "parent",
509
                stop: function () {
510
                    var block = jQuery(this);
511
                    updateDataAndPosition(div, block, cellWidth, startDate);
512

    
513
                    var blockData = block.data("block-data");
514
                    div.find('[data-event-id='+blockData.id+']').text(moment(blockData.start).format('D MMM') + ' - ' + moment(blockData.end).format('D MMM'));
515

    
516
                    if (callback) { callback(block.data("block-data")); }
517
                }
518
            });
519
        }
520

    
521
        function updateDataAndPosition(div, block, cellWidth, startDate) {
522
            var container = jQuery("div.ganttview-slide-container", div);
523
            var scroll = container.scrollLeft();
524
            var offset = block.offset().left - container.offset().left - 1 + scroll;
525

    
526
            // Set new start date
527
            var daysFromStart = Math.round(offset / cellWidth);
528
            var newStart = moment(startDate).add(daysFromStart,'days');
529
            block.data("block-data").start = newStart;
530

    
531
            // Set new end date
532
            var width = block.outerWidth();
533
            var numberOfDays = Math.round(width / cellWidth) - 1;
534
            block.data("block-data").end = moment(newStart).add(numberOfDays,'days');
535
            if(!block.data("block-data").title) {
536
                jQuery("div.ganttview-block-text", block).text( moment.duration(numberOfDays+1,'days').format());
537
            }
538
        }
539

    
540
        return {
541
            apply: apply
542
        };
543
    };
544

    
545
    var DateUtils = {
546
        daysBetween: function (start, end) {
547
            if (!start || !end) { return 0; }
548
            start = Date.parse(start); end = Date.parse(end);
549
            if (moment(start)._d.getYear() == 1901 || moment(end)._d.getYear() == 8099) { return 0; }
550
            return moment(end).diff(moment(start),'days');
551
        },
552
        isWeekend: function (date) {
553
            return date.getDay() % 6 == 0;
554
        },
555
        getBoundaryDatesFromData: function (data, minDays) {
556
            var minStart = new Date(),
557
                maxEnd = new Date(),
558
                entries = [];
559

    
560
            data.forEach(function(group) {
561
                group.series.forEach(function (entry) {
562
                    if (entry.sub_series) {
563
                        entry.sub_series.forEach(function (subEntry) {
564
                            entries.push(subEntry);
565
                        })
566
                    } else {
567
                        entries.push(entry);
568
                    }
569
                });
570
            });
571
            entries.forEach(function(entry,index) {
572
                var start = Date.parse(entry.start),
573
                    end = Date.parse(entry.end);
574

    
575
                if (index == 0) {
576
                    minStart = start;
577
                    maxEnd = end;
578
                }
579

    
580
                if (moment(minStart).isAfter(start)) { minStart = start; }
581
                if (moment(maxEnd).isBefore(end)) { maxEnd = end; }
582
            });
583

    
584
            // Insure that the width of the chart is at least the slide width to avoid empty
585
            // whitespace to the right of the grid
586
            if (moment(maxEnd).diff(minStart,'days') < minDays) {
587
                maxEnd = moment(minStart).add(minDays, 'days');
588
            }
589

    
590
            return [moment(minStart)._d, moment(maxEnd)._d];
591
        }
592
    };
593

    
594
})(jQuery);
(9-9/27)