diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 79c1167..4b47047 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -12,6 +12,6 @@ Just a few guidelines before submitting issues and pull requests: - Ensure the code passes [JSHint](http://jshint.com) completely - Always strive to write code that meets [best practices](http://taitems.github.com/Front-End-Development-Guidelines/) - If you're attempting to solve a very unique bug, a test case is preferred -- Please target all pull requests to the [develop](/../../tree/develop) branch +- Please target all pull requests to the [master](/../../tree/master) branch And thanks once again! diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d8c2dc9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Marek Bielańczuk, Tait Brown, Leo Pfeifenberger, Grzegorz Russek and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 6052e12..d7e52a9 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,13 @@ -[Demo and Documentation](http://taitems.github.com/jQuery.Gantt/) -============== +![npm](https://img.shields.io/npm/v/@taitems/jquery-gantt) +## [Demo and Documentation](http://taitems.github.com/jQuery.Gantt/) + +## Installation + +- Git clone +- `yarn add @taitems/jquery-gantt`, or +- `npm install @taitems/jquery-gantt` + +## About jQuery Gantt Chart is a simple chart that implements gantt functionality as a jQuery component. diff --git a/css/style.css b/css/style.css index 4980b9d..1d6d46c 100644 --- a/css/style.css +++ b/css/style.css @@ -1,14 +1,14 @@ -.gantt, .gantt2 { +.gantt { width: 100%; margin: 20px auto; border: 14px solid #ddd; position: relative; - -webkit-border-radius: 6px; - -moz-border-radius: 6px; +-webkit-border-radius: 6px; + -moz-border-radius: 6px; border-radius: 6px; - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; +-webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; } .gantt:after { @@ -23,13 +23,26 @@ width: 100%; } +.fn-gantt *, +.fn-gantt *:after, +.fn-gantt *:before { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} + .fn-gantt .fn-content { overflow: hidden; position: relative; width: 100%; } - +.fn-gantt .row { + float: left; + height: 24px; + line-height: 24px; + margin: 0; +} /* === LEFT PANEL === */ @@ -43,13 +56,6 @@ z-index: 20; } -.fn-gantt .row { - float: left; - height: 24px; - line-height: 24px; - margin-left: -1px; -} - .fn-gantt .leftPanel .fn-label { display: inline-block; margin: 0 0 0 5px; @@ -60,34 +66,27 @@ overflow: hidden; } -.fn-gantt .leftPanel .row0 { - border-top: 1px solid #DDD; +.fn-gantt .leftPanel .row { + border-bottom: 1px solid #DDD; } .fn-gantt .leftPanel .name, .fn-gantt .leftPanel .desc { float: left; - height: 23px; - margin: 0; - border-bottom: 1px solid #DDD; + height: 24px; + width: 50%; background-color: #f6f6f6; } .fn-gantt .leftPanel .name { - width: 110px; font-weight: bold; } -.fn-gantt .leftPanel .desc { - width: 115px; -} - .fn-gantt .leftPanel .fn-wide, .fn-gantt .leftPanel .fn-wide .fn-label { - width: 225px; + width: 100%; } -.fn-gantt .spacer { - margin: -2px 0 1px 0; - border-bottom: none; +.fn-gantt .leftPanel .spacer { background-color: #f6f6f6; + width: 100%; } @@ -100,44 +99,37 @@ } .fn-gantt .dataPanel { - margin-left: 0px; - border-right: 1px solid #DDD; + margin-left: 0; + outline: 1px solid #DDD; + /* TODO: Replace image with gradient? + background-size: 24px 24px; + background-image: linear-gradient(to left, rgba(221, 221, 221, 0.7) 1px, transparent 1px), linear-gradient(to top, rgba(221, 221, 221, 0.7) 1px, transparent 1px); + */ background-image: url(../img/grid.png); background-repeat: repeat; - background-position: 24px 24px; position: relative; } + +.fn-gantt .row.header { + margin-right: -1px; + width: 100%; +} + .fn-gantt .day, .fn-gantt .date { overflow: visible; width: 24px; line-height: 24px; text-align: center; - border-left: 1px solid #DDD; + border-right: 1px solid #DDD; border-bottom: 1px solid #DDD; - margin: -1px 0 0 -1px; font-size: 11px; color: #484a4d; text-shadow: 0 1px 0 rgba(255,255,255,0.75); text-align: center; } -.fn-gantt .holiday { - background-color: #ffd263; - height: 23px; - margin: 0 0 -1px -1px; -} - -.fn-gantt .today { - background-color: #fff8da; - height: 23px; - margin: 0 0 -1px -1px; - font-weight: bold; - text-align: center; -} - .fn-gantt .sa, .fn-gantt .sn, .fn-gantt .wd { - height: 23px; - margin: 0 0 0 -1px; + height: 24px; text-align: center; } @@ -152,13 +144,24 @@ text-align: center; } +.fn-gantt .holiday { + background-color: #ffd263; + height: 24px; +} + +.fn-gantt .today { + background-color: #fff8da; + height: 24px; + font-weight: bold; + text-align: center; +} + .fn-gantt .rightPanel .month, .fn-gantt .rightPanel .year { float: left; overflow: hidden; - border-left: 1px solid #DDD; + border-right: 1px solid #DDD; border-bottom: 1px solid #DDD; - height: 23px; - margin: 0 0 0 -1px; + height: 24px; background-color: #f6f6f6; font-weight: bold; font-size: 11px; @@ -174,24 +177,24 @@ position: absolute; display: none; z-index: 11; - -webkit-border-radius: 4px; - -moz-border-radius: 4px; +-webkit-border-radius: 4px; + -moz-border-radius: 4px; border-radius: 4px; } .fn-gantt .bar { background-color: #D0E4FD; height: 18px; - margin: 0px 3px 3px 0px; + margin: 0 3px 3px 0; position: absolute; z-index: 10; text-align: center; - -webkit-box-shadow: 0 0 1px rgba(0,0,0,0.25) inset; - -moz-box-shadow: 0 0 1px rgba(0,0,0,0.25) inset; +-webkit-box-shadow: 0 0 1px rgba(0,0,0,0.25) inset; + -moz-box-shadow: 0 0 1px rgba(0,0,0,0.25) inset; box-shadow: 0 0 1px rgba(0,0,0,0.25) inset; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; +-webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; } .fn-gantt .bar .fn-label { @@ -278,20 +281,20 @@ height: 6px; background-color: #838688; margin: 8px 0 0 0; - -webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.6) inset; - -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.6) inset; +-webkit-box-shadow: 0 1px 3px rgba(0,0,0,0.6) inset; + -moz-box-shadow: 0 1px 3px rgba(0,0,0,0.6) inset; box-shadow: 0 1px 3px rgba(0,0,0,0.6) inset; - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; +-webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; } .fn-gantt .navigate .nav-slider-button { width: 17px; height: 60px; background: url(../img/slider_handle.png) center center no-repeat; - left: 0px; - top: 0px; + left: 0; + top: 0; margin: -26px 0 0 0; cursor: pointer; } @@ -319,25 +322,25 @@ display: inline-block; width: 20px; height: 20px; - font-size: 0px; + font-size: 0; background: #595959 url(../img/icon_sprite.png) !important; border: 1px solid #454546; cursor: pointer; vertical-align: top; - -webkit-border-radius: 2px; - -moz-border-radius: 2px; +-webkit-border-radius: 2px; + -moz-border-radius: 2px; border-radius: 2px; - -webkit-box-shadow: 0 1px 0 rgba(255,255,255,0.1) inset, 0 1px 1px rgba(0,0,0,0.2); - -moz-box-shadow: 0 1px 0 rgba(255,255,255,0.1) inset, 0 1px 1px rgba(0,0,0,0.2); - box-shadow: 0 1px 0 rgba(255,255,255,0.1) inset, 0 1px 1px rgba(0,0,0,0.2); - -webkit-box-sizing: border-box; - -moz-box-sizing: border-box; - box-sizing: border-box; +-webkit-box-shadow: 0 1px 0 rgba(255,255,255,0.1) inset, 0 1px 1px rgba(0,0,0,0.2); + -moz-box-shadow: 0 1px 0 rgba(255,255,255,0.1) inset, 0 1px 1px rgba(0,0,0,0.2); + box-shadow: 0 1px 0 rgba(255,255,255,0.1) inset, 0 1px 1px rgba(0,0,0,0.2); +-webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; } .fn-gantt .nav-link:active { - -webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.25) inset, 0 1px 0 #FFF; - -moz-box-shadow: 0 1px 1px rgba(0,0,0,0.25) inset, 0 1px 0 #FFF; - box-shadow: 0 1px 1px rgba(0,0,0,0.25) inset, 0 1px 0 #FFF; +-webkit-box-shadow: 0 1px 1px rgba(0,0,0,0.25) inset, 0 1px 0 #FFF; + -moz-box-shadow: 0 1px 1px rgba(0,0,0,0.25) inset, 0 1px 0 #FFF; + box-shadow: 0 1px 1px rgba(0,0,0,0.25) inset, 0 1px 0 #FFF; } .fn-gantt .navigate .nav-page-back { diff --git a/index.html b/index.html index f8434c6..8ba3d3d 100644 --- a/index.html +++ b/index.html @@ -2,558 +2,626 @@ jQuery.Gantt - - + + + + - - - + + -
+
-

- jQuery.Gantt - — Draw Gantt charts with the famous jQuery ease of development -

+

+ jQuery.Gantt + — Draw Gantt charts with the famous jQuery ease of development +

-

Contributors

- +

Contributors

+ -

- Example -

+

+ Example +

-
+
-

- Gantt Configuration -

+

+ Gantt Configuration +

-
+
 $(".selector").gantt({
-	source: "ajax/data.json",
-	scale: "weeks",
-	minScale: "weeks",
-	maxScale: "months",
-	onItemClick: function(data) {
-		alert("Item clicked - show some details");
-	},
-	onAddClick: function(dt, rowId) {
-		alert("Empty space clicked - add an item!");
-	},
-	onRender: function() {
-		console.log("chart rendered");
-	}
+    source: "ajax/data.json",
+    scale: "weeks",
+    minScale: "weeks",
+    maxScale: "months",
+    onItemClick: function(data) {
+        alert("Item clicked - show some details");
+    },
+    onAddClick: function(dt, rowId) {
+        alert("Empty space clicked - add an item!");
+    },
+    onRender: function() {
+        console.log("chart rendered");
+    }
 });
 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +
- Parameter - - Default - - Accepts Type -
- source - - [] - - Array, String (url) -
- itemsPerPage - - 7 - - Number -
- months - - ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] - - Array -
- dow - - ["S", "M", "T", "W", "T", "F", "S"] - - Array -
- navigate - - "buttons" - - String ("buttons","scroll") -
- scale - - "days" - - String -
- maxScale - - "months" - - String -
- minScale - - "hours" - - String -
- waitText - - "Please Wait..." - - String -
- onItemClick - - function (data) { return; } - a JS Function that gets called when clicking on a Gantt-Item.
The parameter passed to the function is the dataObj of the item
- onAddClick - function (dt, rowId) { return; } - a JS Function that gets called when clicking on a Gantt-Item.
The parameter passed to the function is the DateTime in ms for the clicked Cell, and the ID if the source object (row)
- onRender - function () { return; } - a JS Function called whenever the chart is (re)rendered
+ - - - - - - - - - - -
- useCookie - false - indicates if cookies should be used to track the chart's state (scale, scrollposition) between postpacks
- jquery.cookie.js needs to be referenced for this to work
- scrollToToday - true - Boolean
+ + Name + + + Default + + + Type + + + + + + + source + + + [] + + + Array, string (url) + + + + + itemsPerPage + + + 7 + + + number + + + + + months + + + ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"] + + + Array (12 strings representing the months of the year) + + + + + dow + + + ["S", "M", "T", "W", "T", "F", "S"] + + + Array (7 strings representing the days of the week) + + + + + holidays + + + undefined + + + Array of numbers (ms), date strings (see formats), or Date objects + + + + + navigate + + + "buttons" + + + string ("buttons", "scroll") + + + + + scale + + + "days" + + + string ("months", "weeks", "days", "hours") + + + + + maxScale + + + "months" + + + string ("months", "weeks", "days", "hours") + + + + + minScale + + + "hours" + + + string ("months", "weeks", "days", "hours") + + + + + waitText + + + "Please wait..." + + + string + + + + + onItemClick + + + function (data) { return; } + + Function called when clicking on a Gantt item.
The parameter passed to the function is the dataObj of the source item, if one was provided. + + + + onAddClick + + function (dt, rowId) { return; } + + Function called when clicking on empty space inside the Gantt data panel.
The parameter passed to the function is the date/time in milliseconds for the clicked cell, and the ID of the source object (row), if one was provided. + + + + onRender + + $.noop + + Function called whenever the chart is (re)rendered + + + + useCookie + + false + + indicates whether or not cookies should be used to save and restore the chart's view state (scale, scroll position) between page loads; + jquery.cookie needs to be referenced for this to work + + + + cookieKey + + "jquery.fn.gantt" + + The prefix used when storing cookies (depends on useCookie being set to true) + + + + scrollToToday + + true + + Boolean + + + -

- Source Configuration -

+

+ Source Configuration +

-
-source: [{
-	name: "Example",
-	desc: "Lorem ipsum dolor sit amet.",
-	values: [ ... ]
-}]
+
+source: [
+    {
+        name: "Example",
+        desc: "Lorem ipsum dolor sit amet.",
+        values: [ ... ]
+        id: 1,
+        cssClass: "redLabel"
+    },
+    ... // more rows
+]
 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Parameter - - Default - - Accepts Type - - Meaning -
- name - - null - - String - - Bold value in the left-most column of the gantt row. -
- desc - - null - - String - - Secondary value in the gantt row. -
- values - - null - - Array - - Collection of date ranges for gantt items. See next table. -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Name + + Type + + Description +
+ name + + string + + Optional primary label for this row of values; appears in the leftmost column of the row. +
+ desc + + string + + Optional secondary label for this row of the Gantt. +
+ values + + Array + + Sequence of date ranges for each row of the Gantt. See table below. +
+ id + + string or number + + Optional value to be passed as second parameter to onAddClick() callback when triggered. +
+ cssClass + + string + + Optional space-separated class names to be applied to this row's labels. +
-

- Value Configuration -

+

+ Value Configuration +

-
-values: [{
-	to: "/Date(1328832000000)/",
-	from: "/Date(1333411200000)/",
-	desc: "Something",
-	label: "Example Value",
-	customClass: "ganttRed",
-	dataObj: foo.bar[i]
-}]
+
+values: [
+    {
+        from: '2012-02-10',
+        to: '2012-04-03',
+        label: "Example Value",
+        desc: "Something",
+        customClass: "ganttRed",
+        dataObj: foo.bar[i]
+    },
+    ... // more items for the row (though Gantt charts traditionally have only one item per row)
+]
 
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- Parameter - - Accepts Type - - Meaning -
- to - - String (Date) - - - -
- from - - String (Date) - - - -
- desc - - String - - Text that appears on hover, I think? -
- label - - String - - Appears on the gantt item. -
- customClass - - String - - Custom class to be applied to the gantt item. -
- dataObj - - All - - A data object that is applied directly to the gantt item. -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Name + + Type + + Description +
+ from + + number (ms), string (see formats) + + Start date/time of the Gantt item. +
+ to + + number (ms), string (see formats) + + End date/time of the Gantt item. +
+ label + + string + + Optional label/name of the Gantt item. +
+ desc + + string + + Optional description of the Gantt item, used as HTML content of hover "hint"). +
+ customClass + + string + + Optional space-separated class names to be applied to the Gantt item. +
+ dataObj + + Any + + Optional data object that is stored directly on the Gantt item. +
-
+
+ + + + + + + + + - - - - + }); + diff --git a/js/jquery.cookie.js b/js/jquery.cookie.js deleted file mode 100644 index f8f852c..0000000 --- a/js/jquery.cookie.js +++ /dev/null @@ -1,96 +0,0 @@ -/*! - * jQuery Cookie Plugin v1.3.1 - * https://github.com/carhartl/jquery-cookie - * - * Copyright 2013 Klaus Hartl - * Released under the MIT license - */ -(function (factory) { - if (typeof define === 'function' && define.amd) { - // AMD. Register as anonymous module. - define(['jquery'], factory); - } else { - // Browser globals. - factory(jQuery); - } -}(function ($) { - - var pluses = /\+/g; - - function decode(s) { - if (config.raw) { - return s; - } - return decodeURIComponent(s.replace(pluses, ' ')); - } - - function decodeAndParse(s) { - if (s.indexOf('"') === 0) { - // This is a quoted cookie as according to RFC2068, unescape... - s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); - } - - s = decode(s); - - try { - return config.json ? JSON.parse(s) : s; - } catch(e) {} - } - - var config = $.cookie = function (key, value, options) { - - // Write - if (value !== undefined) { - options = $.extend({}, config.defaults, options); - - if (typeof options.expires === 'number') { - var days = options.expires, t = options.expires = new Date(); - t.setDate(t.getDate() + days); - } - - value = config.json ? JSON.stringify(value) : String(value); - - return (document.cookie = [ - config.raw ? key : encodeURIComponent(key), - '=', - config.raw ? value : encodeURIComponent(value), - options.expires ? '; expires=' + options.expires.toUTCString() : '', // use expires attribute, max-age is not supported by IE - options.path ? '; path=' + options.path : '', - options.domain ? '; domain=' + options.domain : '', - options.secure ? '; secure' : '' - ].join('')); - } - - // Read - var cookies = document.cookie.split('; '); - var result = key ? undefined : {}; - for (var i = 0, l = cookies.length; i < l; i++) { - var parts = cookies[i].split('='); - var name = decode(parts.shift()); - var cookie = parts.join('='); - - if (key && key === name) { - result = decodeAndParse(cookie); - break; - } - - if (!key) { - result[name] = decodeAndParse(cookie); - } - } - - return result; - }; - - config.defaults = {}; - - $.removeCookie = function (key, options) { - if ($.cookie(key) !== undefined) { - // Must not alter options, thus extending a fresh object... - $.cookie(key, '', $.extend({}, options, { expires: -1 })); - return true; - } - return false; - }; - -})); diff --git a/js/jquery.fn.gantt.js b/js/jquery.fn.gantt.js index 4629ffc..fcb582f 100644 --- a/js/jquery.fn.gantt.js +++ b/js/jquery.fn.gantt.js @@ -1,210 +1,200 @@ -// jQuery Gantt Chart -// ================== - -// Basic usage: - -// $(".selector").gantt({ -// source: "ajax/data.json", -// scale: "weeks", -// minScale: "weeks", -// maxScale: "months", -// onItemClick: function(data) { -// alert("Item clicked - show some details"); -// }, -// onAddClick: function(dt, rowId) { -// alert("Empty space clicked - add an item!"); -// }, -// onRender: function() { -// console.log("chart rendered"); -// } -// }); - -// -/*jshint shadow:true, laxbreak:true, jquery:true, strict:true, trailing:true */ +/** + * jQuery Gantt Chart + * + * @see http://taitems.github.io/jQuery.Gantt/ + * @license MIT + */ +/*jshint camelcase:true, freeze:true, jquery:true */ (function ($, undefined) { - "use strict"; + var UTC_DAY_IN_MS = 24 * 60 * 60 * 1000; + + // custom selector `:findday` used to match on specified day in ms. + // + // The selector is passed a date in ms and elements are added to the + // selection filter if the element date matches, as determined by the + // id attribute containing a parsable date in ms. + function findDay(elt, text) { + var cd = new Date(parseInt(text, 10)); + cd.setHours(0, 0, 0, 0); + var id = $(elt).attr("id") || ""; + var si = id.indexOf("-") + 1; + var ed = new Date(parseInt(id.substring(si, id.length), 10)); + ed.setHours(0, 0, 0, 0); + return cd.getTime() === ed.getTime(); + } + $.expr.pseudos.findday = $.expr.createPseudo ? + $.expr.createPseudo(function(text) { + return function(elt) { + return findDay(elt, text); + }; + }) : + function(elt, i, match) { + return findDay(elt, match[3]); + }; + + // custom selector `:findweek` used to match on specified week in ms. + function findWeek(elt, text) { + var cd = new Date(parseInt(text, 10)); + var y = cd.getFullYear(); + var w = cd.getWeekOfYear(); + var m = cd.getMonth(); + if (m === 11 && w === 1) { + y++; + } else if (!m && w > 51) { + y--; + } + cd = y + "-" + w; + var id = $(elt).attr("id") || ""; + var si = id.indexOf("-") + 1; + var ed = id.substring(si, id.length); + return cd === ed; + } + $.expr.pseudos.findweek = $.expr.createPseudo ? + $.expr.createPseudo(function(text) { + return function(elt) { + return findWeek(elt, text); + }; + }) : + function(elt, i, match) { + return findWeek(elt, match[3]); + }; + + // custom selector `:findmonth` used to match on specified month in ms. + function findMonth(elt, text) { + var cd = new Date(parseInt(text, 10)); + cd = cd.getFullYear() + "-" + cd.getMonth(); + var id = $(elt).attr("id") || ""; + var si = id.indexOf("-") + 1; + var ed = id.substring(si, id.length); + return cd === ed; + } + $.expr[':'].findmonth = $.expr.createPseudo ? + $.expr.createPseudo(function(text) { + return function(elt) { + return findMonth(elt, text); + }; + }) : + function(elt, i, match) { + return findMonth(elt, match[3]); + }; + + // Date prototype helpers + // ====================== + + // `getWeekId` returns a string in the form of 'dh-YYYY-WW', where WW is + // the week # for the year. + // It is used to add an id to the week divs + Date.prototype.getWeekId = function () { + var y = this.getFullYear(); + var w = this.getWeekOfYear(); + var m = this.getMonth(); + if (m === 11 && w === 1) { + y++; + } else if (!m && w > 51) { + y--; + } + return 'dh-' + y + "-" + w; + }; + + // `getRepDate` returns the milliseconds since the epoch for a given date + // depending on the active scale + Date.prototype.getRepDate = function (scale) { + switch (scale) { + case "hours": + return this.getTime(); + case "weeks": + return this.getDayForWeek().getTime(); + case "months": + return new Date(this.getFullYear(), this.getMonth(), 1).getTime(); + case "days": + /* falls through */ + default: + return this.getTime(); + } + }; + + // `getDayOfYear` returns the day number for the year + Date.prototype.getDayOfYear = function () { + var year = this.getFullYear(); + return (Date.UTC(year, this.getMonth(), this.getDate()) - + Date.UTC(year, 0, 0)) / UTC_DAY_IN_MS; + }; + + // Use ISO week by default + //TODO: make these options. + var firstDay = 1; // ISO week starts with Monday (1); use Sunday (0) for, e.g., North America + var weekOneDate = 4; // ISO week one always contains 4 Jan; use 1 Jan for, e.g., North America + + // `getWeekOfYear` returns the week number for the year + //TODO: fix bug when firstDay=6/weekOneDate=1 : https://github.com/moment/moment/issues/2115 + Date.prototype.getWeekOfYear = function () { + var year = this.getFullYear(), + month = this.getMonth(), + date = this.getDate(), + day = this.getDay(); + //var diff = weekOneDate - day + 7 * (day < firstDay ? -1 : 1); + var diff = weekOneDate - day; + if (day < firstDay) { + diff -= 7; + } + if (diff + 7 < weekOneDate - firstDay) { + diff += 7; + } + return Math.ceil(new Date(year, month, date + diff).getDayOfYear() / 7); + }; + + // `getDayForWeek` returns the first day of this Date's week + Date.prototype.getDayForWeek = function () { + var day = this.getDay(); + var diff = (day < firstDay ? -7 : 0) + firstDay - day; + return new Date( this.getFullYear(), this.getMonth(), this.getDate() + diff ); + }; + $.fn.gantt = function (options) { - var cookieKey = "jquery.fn.gantt"; var scales = ["hours", "days", "weeks", "months"]; //Default settings var settings = { source: [], + holidays: [], + // paging itemsPerPage: 7, - months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], + // localisation dow: ["S", "M", "T", "W", "T", "F", "S"], + months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], + waitText: "Please wait...", + // navigation navigate: "buttons", - scale: "days", + scrollToToday: true, + // cookie options useCookie: false, + cookieKey: "jquery.fn.gantt", + // scale parameters + scale: "days", maxScale: "months", minScale: "hours", - waitText: "Please wait...", + // callbacks onItemClick: function (data) { return; }, - onAddClick: function (data) { return; }, - onRender: function() { return; }, - scrollToToday: true + onAddClick: function (dt, rowId) { return; }, + onRender: $.noop }; - /** - * Extend options with default values - */ - if (options) { - $.extend(settings, options); - } + // read options + $.extend(settings, options); // can't use cookie if don't have `$.cookie` settings.useCookie = settings.useCookie && $.isFunction($.cookie); - // custom selector `:findday` used to match on specified day in ms. - // - // The selector is passed a date in ms and elements are added to the - // selection filter if the element date matches, as determined by the - // id attribute containing a parsable date in ms. - $.extend($.expr[":"], { - findday: function (a, i, m) { - var cd = new Date(parseInt(m[3], 10)); - var id = $(a).attr("id"); - id = id ? id : ""; - var si = id.indexOf("-") + 1; - var ed = new Date(parseInt(id.substring(si, id.length), 10)); - cd = new Date(cd.getFullYear(), cd.getMonth(), cd.getDate()); - ed = new Date(ed.getFullYear(), ed.getMonth(), ed.getDate()); - return cd.getTime() === ed.getTime(); - } - }); - // custom selector `:findweek` used to match on specified week in ms. - $.extend($.expr[":"], { - findweek: function (a, i, m) { - var cd = new Date(parseInt(m[3], 10)); - var id = $(a).attr("id"); - id = id ? id : ""; - var si = id.indexOf("-") + 1; - cd = cd.getFullYear() + "-" + cd.getDayForWeek().getWeekOfYear(); - var ed = id.substring(si, id.length); - return cd === ed; - } - }); - // custom selector `:findmonth` used to match on specified month in ms. - $.extend($.expr[":"], { - findmonth: function (a, i, m) { - var cd = new Date(parseInt(m[3], 10)); - cd = cd.getFullYear() + "-" + cd.getMonth(); - var id = $(a).attr("id"); - id = id ? id : ""; - var si = id.indexOf("-") + 1; - var ed = id.substring(si, id.length); - return cd === ed; - } - }); - - // Date prototype helpers - // ====================== - - // `getWeekId` returns a string in the form of 'dh-YYYY-WW', where WW is - // the week # for the year. - // It is used to add an id to the week divs - Date.prototype.getWeekId = function () { - var y = this.getFullYear(); - var w = this.getDayForWeek().getWeekOfYear(); - var m = this.getMonth(); - if (m === 11 && w === 1) { - y++; - } - return 'dh-' + y + "-" + w; - }; - - // `getRepDate` returns the seconds since the epoch for a given date - // depending on the active scale - Date.prototype.getRepDate = function () { - switch (settings.scale) { - case "hours": - return this.getTime(); - case "weeks": - return this.getDayForWeek().getTime(); - case "months": - return new Date(this.getFullYear(), this.getMonth(), 1).getTime(); - default: - return this.getTime(); - } - }; - - // `getDayOfYear` returns the day number for the year - Date.prototype.getDayOfYear = function () { - var fd = new Date(this.getFullYear(), 0, 0); - var sd = new Date(this.getFullYear(), this.getMonth(), this.getDate()); - return Math.ceil((sd - fd) / 86400000); - }; - - // `getWeekOfYear` returns the week number for the year - Date.prototype.getWeekOfYear = function () { - var ys = new Date(this.getFullYear(), 0, 1); - var sd = new Date(this.getFullYear(), this.getMonth(), this.getDate()); - if (ys.getDay() > 3) { - ys = new Date(sd.getFullYear(), 0, (7 - ys.getDay())); - } - var daysCount = sd.getDayOfYear() - ys.getDayOfYear(); - return Math.ceil(daysCount / 7); - - }; - - // `getDaysInMonth` returns the number of days in a month - Date.prototype.getDaysInMonth = function () { - return 32 - new Date(this.getFullYear(), this.getMonth(), 32).getDate(); - }; - - // `hasWeek` returns `true` if the date resides on a week boundary - // **????????????????? Don't know if this is true** - Date.prototype.hasWeek = function () { - var df = new Date(this.valueOf()); - df.setDate(df.getDate() - df.getDay()); - var dt = new Date(this.valueOf()); - dt.setDate(dt.getDate() + (6 - dt.getDay())); - - if (df.getMonth() === dt.getMonth()) { - return true; - } else { - return (df.getMonth() === this.getMonth() && dt.getDate() < 4) || (df.getMonth() !== this.getMonth() && dt.getDate() >= 4); - } - }; - - // `getDayForWeek` returns the Date object for the starting date of - // the week # for the year - Date.prototype.getDayForWeek = function () { - var df = new Date(this.valueOf()); - df.setDate(df.getDate() - df.getDay()); - var dt = new Date(this.valueOf()); - dt.setDate(dt.getDate() + (6 - dt.getDay())); - if ((df.getMonth() === dt.getMonth()) || (df.getMonth() !== dt.getMonth() && dt.getDate() >= 4)) { - return new Date(dt.setDate(dt.getDate() - 3)); - } else { - return new Date(df.setDate(df.getDate() + 3)); - } - }; - - // fixes https://github.com/taitems/jQuery.Gantt/issues/62 - function ktkGetNextDate(currentDate, scaleStep) { - for(var minIncrements = 1;; minIncrements++) { - var nextDate = new Date(currentDate); - nextDate.setHours(currentDate.getHours() + scaleStep * minIncrements); - - if (nextDate.getTime() !== currentDate.getTime()) { - return nextDate; - } - - // If code reaches here, it's because current didn't really increment (invalid local time) because of daylight-saving adjustments - // => retry adding 2, 3, 4 hours, and so on (until nextDate > current) - } - } - // Grid management // =============== // Core object is responsible for navigation and rendering var core = { // Return the element whose topmost point lies under the given point - // Normalizes for old browsers + // Normalizes for old browsers (NOTE: doesn't work when element is outside viewport) + //TODO: https://github.com/taitems/jQuery.Gantt/issues/137 elementFromPoint: (function(){ // IIFE // version for normal browsers if (document.compatMode === "CSS1Compat") { @@ -240,7 +230,7 @@ // **Setup the initial view** // Here we calculate the number of rows, pages and visible start - // and end dates once the data is ready + // and end dates once the data are ready init: function (element) { element.rowsNum = element.data.length; element.pageCount = Math.ceil(element.rowsNum / settings.itemsPerPage); @@ -251,7 +241,7 @@ /* core.render(element); */ - core.waitToggle(element, true, function () { core.render(element); }); + core.waitToggle(element, function () { core.render(element); }); }, // **Render the grid** @@ -260,7 +250,7 @@ var $leftPanel = core.leftPanel(element); content.append($leftPanel); var $rightPanel = core.rightPanel(element, $leftPanel); - var mLeft, hPos; + var pLeft, hPos; content.append($rightPanel); content.append(core.navigation(element)); @@ -271,7 +261,7 @@ $(element).empty().append(element.gantt); - element.scrollNavigation.panelMargin = parseInt($dataPanel.css("margin-left").replace("px", ""), 10); + element.scrollNavigation.panelMargin = parseInt($dataPanel.css("left").replace("px", ""), 10); element.scrollNavigation.panelMaxPos = ($dataPanel.width() - $rightPanel.width()); element.scrollNavigation.canScroll = ($dataPanel.width() > $rightPanel.width()); @@ -281,7 +271,7 @@ // Set a cookie to record current position in the view if (settings.useCookie) { - var sc = $.cookie(this.cookieKey + "ScrollPos"); + var sc = $.cookie(settings.cookieKey + "ScrollPos"); if (sc) { element.hPosition = sc; } @@ -295,23 +285,19 @@ } else { if (element.hPosition !== 0) { if (element.scaleOldWidth) { - mLeft = ($dataPanel.width() - $rightPanel.width()); - hPos = mLeft * element.hPosition / element.scaleOldWidth; - hPos = hPos > 0 ? 0 : hPos; - $dataPanel.css({ "margin-left": hPos + "px" }); - element.scrollNavigation.panelMargin = hPos; - element.hPosition = hPos; + pLeft = ($dataPanel.width() - $rightPanel.width()); + hPos = pLeft * element.hPosition / element.scaleOldWidth; + element.hPosition = hPos > 0 ? 0 : hPos; element.scaleOldWidth = null; - } else { - $dataPanel.css({ "margin-left": element.hPosition + "px" }); - element.scrollNavigation.panelMargin = element.hPosition; } + $dataPanel.css({ "left": element.hPosition }); + element.scrollNavigation.panelMargin = element.hPosition; } core.repositionLabel(element); } $dataPanel.css({ height: $leftPanel.height() }); - core.waitToggle(element, false); + core.waitToggle(element); settings.onRender(); }, @@ -320,26 +306,38 @@ /* Left panel */ var ganttLeftPanel = $('
') .append($('
') - .css("height", tools.getCellSize() * element.headerRows + "px") - .css("width", "100%")); + .css("height", tools.getCellSize() * element.headerRows)); var entries = []; $.each(element.data, function (i, entry) { - if (i >= element.pageNum * settings.itemsPerPage && i < (element.pageNum * settings.itemsPerPage + settings.itemsPerPage)) { - entries.push('
'); - entries.push('' + (entry.name || '') + ''); - entries.push('
'); + if (i >= element.pageNum * settings.itemsPerPage && + i < (element.pageNum * settings.itemsPerPage + settings.itemsPerPage)) { + var dataId = ('id' in entry) ? '" data-id="' + entry.id : ''; + entries.push( + '
' + + '' + + (entry.name || '') + + '' + + '
'); if (entry.desc) { - entries.push('
'); - entries.push('' + entry.desc + ''); - entries.push('
'); + entries.push( + '
' + + '' + + entry.desc + + '' + + '
'); } } }); - ganttLeftPanel.append(entries.join("")); - return ganttLeftPanel; + return ganttLeftPanel.append(entries.join("")); }, // Create and return the data panel element @@ -347,11 +345,12 @@ var dataPanel = $('
'); // Handle mousewheel events for scrolling the data panel - var wheel = 'onwheel' in element ? 'wheel' : document.onmousewheel !== undefined ? 'mousewheel' : 'DOMMouseScroll'; + var wheel = 'onwheel' in element ? + 'wheel' : document.onmousewheel !== undefined ? + 'mousewheel' : 'DOMMouseScroll'; $(element).on(wheel, function (e) { core.wheelScroll(element, e); }); - // Handle click events and dispatch to registered `onAddClick` - // function + // Handle click events and dispatch to registered `onAddClick` function dataPanel.click(function (e) { e.stopPropagation(); @@ -359,21 +358,19 @@ var leftpanel = $(element).find(".fn-gantt .leftPanel"); var datapanel = $(element).find(".fn-gantt .dataPanel"); switch (settings.scale) { - case "weeks": - corrY = tools.getCellSize() * 2; - break; - case "months": - corrY = tools.getCellSize(); - break; - case "hours": - corrY = tools.getCellSize() * 4; - break; - case "days": - corrY = tools.getCellSize() * 3; - break; - default: - corrY = tools.getCellSize() * 2; - break; + case "months": + corrY = tools.getCellSize(); + break; + case "hours": + corrY = tools.getCellSize() * 4; + break; + case "days": + corrY = tools.getCellSize() * 3; + break; + case "weeks": + /* falls through */ + default: + corrY = tools.getCellSize() * 2; } /* Adjust, so get middle of elm @@ -389,16 +386,16 @@ col = $(col); } - var dt = col.attr("repdate"); + var dt = col.data("repdate"); // Find row where click occurred var row = core.elementFromPoint(leftpanel.offset().left + leftpanel.width() - 10, e.pageY); - // Was the lable clicked directly? + // Was the label clicked directly? if (row.className.indexOf("fn-label") === 0) { row = $(row.parentNode); } else { row = $(row); } - var rowId = row.data().id; + var rowId = row.data('id'); // Dispatch user registered function with the DateTime in ms // and the id if the clicked object is a row @@ -407,346 +404,368 @@ return dataPanel; }, - // Creates and return the right panel containing the year/week/day - // header + // Creates and return the right panel containing the year/week/day header rightPanel: function (element, leftPanel /* <- never used? */) { - var range = null; // Days of the week have a class of one of // `sn` (Sunday), `sa` (Saturday), or `wd` (Weekday) var dowClass = ["sn", "wd", "wd", "wd", "wd", "wd", "sa"]; - //TODO: was someone planning to allow styles to stretch to the bottom of the chart? + //unused: was someone planning to allow styles to stretch to the bottom of the chart? //var gridDowClass = [" sn", "", "", "", "", "", " sa"]; - var yearArr = ['
']; - var daysInYear = 0; + var yearArr = []; + var scaleUnitsThisYear = 0; - var monthArr = ['
']; - var daysInMonth = 0; + var monthArr = []; + var scaleUnitsThisMonth = 0; var dayArr = []; - var hoursInDay = 0; var dowArr = []; - var horArr = []; - var today = new Date(); - today = new Date(today.getFullYear(), today.getMonth(), today.getDate()); + today.setHours(0, 0, 0, 0); + + // reused variables + var $row = $('
'); + var i, len; + var year, month, week, day; + var rday, dayClass; + var dataPanel, dataPanelWidth; // Setup the headings based on the chosen `settings.scale` switch (settings.scale) { - // **Hours** - case "hours": - - range = tools.parseTimeRange(element.dateStart, element.dateEnd, element.scaleStep); - - var year = range[0].getFullYear(); - var month = range[0].getMonth(); - var day = range[0]; - - for (var i = 0, len = range.length; i < len; i++) { - var rday = range[i]; - - // Fill years - var rfy = rday.getFullYear(); - if (rfy !== year) { - yearArr.push( - ('
' - + year - + '
')); - - year = rfy; - daysInYear = 0; - } - daysInYear++; - - - // Fill months - var rm = rday.getMonth(); - if (rm !== month) { - monthArr.push( - ('
' - + settings.months[month] - + '
')); - - month = rm; - daysInMonth = 0; - } - daysInMonth++; - - - // Fill days & hours - - var rgetDay = rday.getDay(); - var getDay = day.getDay(); - var day_class = dowClass[rgetDay]; - if (tools.isHoliday(rday)) { - day_class = "holiday"; - } - if (rgetDay !== getDay) { - var day_class2 = (today - day === 0) ? "today" : tools.isHoliday( day.getTime() ) ? "holiday" : dowClass[getDay]; - - dayArr.push('
' - + '
' + day.getDate() + '
'); - dowArr.push('
' - + '
' + settings.dow[getDay] + '
'); - - day = rday; - hoursInDay = 0; - } - hoursInDay++; - - horArr.push('
' - + rday.getHours() - + '
'); + // **Hours** + case "hours": + range = tools.parseTimeRange(element.dateStart, element.dateEnd, element.scaleStep); + dataPanelWidth = range.length * tools.getCellSize(); + + year = range[0].getFullYear(); + month = range[0].getMonth(); + day = range[0]; + + for (i = 0, len = range.length; i < len; i++) { + rday = range[i]; + + // Fill years + var rfy = rday.getFullYear(); + if (rfy !== year) { + yearArr.push( + '
' + + year + + '
'); + + year = rfy; + scaleUnitsThisYear = 0; } + scaleUnitsThisYear++; - // Last year - yearArr.push( - '
' - + year - + '
'); - - // Last month - monthArr.push( - '
' - + settings.months[month] - + '
'); - - var day_class = dowClass[day.getDay()]; + // Fill months + var rm = rday.getMonth(); + if (rm !== month) { + monthArr.push( + '
' + + settings.months[month] + + '
'); - if ( tools.isHoliday(day) ) { - day_class = "holiday"; + month = rm; + scaleUnitsThisMonth = 0; } - - dayArr.push('
' - + '
' + day.getDate() + '
'); - - dowArr.push('
' - + '
' + settings.dow[day.getDay()] + '
'); - - var dataPanel = core.dataPanel(element, range.length * tools.getCellSize()); - - - // Append panel elements - dataPanel.append(yearArr.join("")); - dataPanel.append(monthArr.join("")); - dataPanel.append($('
').html(dayArr.join(""))); - dataPanel.append($('
').html(dowArr.join(""))); - dataPanel.append($('
').html(horArr.join(""))); - - break; - - // **Weeks** - case "weeks": - range = tools.parseWeeksRange(element.dateStart, element.dateEnd); - yearArr = ['
']; - monthArr = ['
']; - var year = range[0].getFullYear(); - var month = range[0].getMonth(); - var day = range[0]; - - for (var i = 0, len = range.length; i < len; i++) { - var rday = range[i]; - - // Fill years - if (rday.getFullYear() !== year) { - yearArr.push( - ('
' - + year - + '
')); - year = rday.getFullYear(); - daysInYear = 0; - } - daysInYear++; - - // Fill months - if (rday.getMonth() !== month) { - monthArr.push( - ('
' - + settings.months[month] - + '
')); - month = rday.getMonth(); - daysInMonth = 0; - } - daysInMonth++; - - // Fill weeks - dayArr.push('
' - + '
' + rday.getWeekOfYear() + '
'); + scaleUnitsThisMonth++; + + // Fill days & hours + var rgetDay = rday.getDay(); + var getDay = day.getDay(); + if (rgetDay !== getDay) { + dayClass = (today - day === 0) ? + "today" : tools.isHoliday( day.getTime() ) ? + "holiday" : dowClass[getDay]; + + dayArr.push( + '
' + + '
' + day.getDate() + '
'); + dowArr.push( + '
' + + '
' + settings.dow[getDay] + '
'); + + day = rday; + hoursInDay = 0; } + hoursInDay++; + dayClass = dowClass[rgetDay]; + if (tools.isHoliday(rday)) { + dayClass = "holiday"; + } + horArr.push( + '
' + + rday.getHours() + + '
'); + } - // Last year - yearArr.push( - '
' - + year - + '
'); + // Last year + yearArr.push( + '
' + + year + + '
'); - // Last month - monthArr.push( - '
' - + settings.months[month] - + '
'); + // Last month + monthArr.push( + '
' + + settings.months[month] + + '
'); - var dataPanel = core.dataPanel(element, range.length * tools.getCellSize()); + dayClass = dowClass[day.getDay()]; - dataPanel.append(yearArr.join("") + monthArr.join("") + dayArr.join("") + (dowArr.join(""))); + if ( tools.isHoliday(day) ) { + dayClass = "holiday"; + } - break; + dayArr.push( + '
' + + '
' + day.getDate() + '
'); + + dowArr.push( + '
' + + '
' + settings.dow[day.getDay()] + '
'); + + dataPanel = core.dataPanel(element, dataPanelWidth); + + // Append panel elements + dataPanel.append( + $row.clone().html(yearArr.join("")), + $row.clone().html(monthArr.join("")), + $row.clone().html(dayArr.join("")), + $row.clone().html(dowArr.join("")), + $row.clone().html(horArr.join("")) + ); + break; - // **Months** - case 'months': - range = tools.parseMonthsRange(element.dateStart, element.dateEnd); - - var year = range[0].getFullYear(); - var month = range[0].getMonth(); - var day = range[0]; - - for (var i = 0, len = range.length; i < len; i++) { - var rday = range[i]; - - // Fill years - if (rday.getFullYear() !== year) { - yearArr.push( - ('
' - + year - + '
')); - year = rday.getFullYear(); - daysInYear = 0; - } - daysInYear++; - monthArr.push('
' + (1 + rday.getMonth()) + '
'); + // **Weeks** + case "weeks": + range = tools.parseWeeksRange(element.dateStart, element.dateEnd); + dataPanelWidth = range.length * tools.getCellSize(); + + year = range[0].getFullYear(); + month = range[0].getMonth(); + week = range[0].getWeekOfYear(); + var diff; + + for (i = 0, len = range.length; i < len; i++) { + rday = range[i]; + + // Fill years + if (week > (week = rday.getWeekOfYear())) { + // partial weeks to subtract from year header + diff = rday.getDate() - 1; + // offset one month (December) if week starts in last year + diff -= !rday.getMonth() ? 0 : 31; + diff /= 7; + yearArr.push( + '
' + + year + + '
'); + year++; + scaleUnitsThisYear = diff; } + scaleUnitsThisYear++; + + // Fill months + if (rday.getMonth() !== month) { + // partial weeks to subtract from month header + diff = rday.getDate() - 1; + // offset one week if week starts in last month + //diff -= (diff <= 6) ? 0 : 7; + diff /= 7; + monthArr.push( + '
' + + settings.months[month] + + '
'); + month = rday.getMonth(); + scaleUnitsThisMonth = diff; + } + scaleUnitsThisMonth++; + + // Fill weeks + dayArr.push( + '
' + + '
' + week + '
'); + } - - // Last year - yearArr.push( - '
' - + year - + '
'); - - // Last month + // Last year + yearArr.push( + '
' + + year + + '
'); + + // Last month + monthArr.push( + '
' + + settings.months[month] + + '
'); + + dataPanel = core.dataPanel(element, dataPanelWidth); + + // Append panel elements + dataPanel.append( + $row.clone().html(yearArr.join("")), + $row.clone().html(monthArr.join("")), + $row.clone().html(dayArr.join("")) + ); + break; + + // **Months** + case 'months': + range = tools.parseMonthsRange(element.dateStart, element.dateEnd); + dataPanelWidth = range.length * tools.getCellSize(); + + year = range[0].getFullYear(); + month = range[0].getMonth(); + + for (i = 0, len = range.length; i < len; i++) { + rday = range[i]; + + // Fill years + if (rday.getFullYear() !== year) { + yearArr.push( + '
' + + year + + '
'); + year = rday.getFullYear(); + scaleUnitsThisYear = 0; + } + scaleUnitsThisYear++; monthArr.push( - '
' - + settings.months[month] - + '
'); - - var dataPanel = core.dataPanel(element, range.length * tools.getCellSize()); - - // Append panel elements - dataPanel.append(yearArr.join("")); - dataPanel.append(monthArr.join("")); - dataPanel.append($('
').html(dayArr.join(""))); - dataPanel.append($('
').html(dowArr.join(""))); - - break; - - // **Days (default)** - default: - range = tools.parseDateRange(element.dateStart, element.dateEnd); - - var dateBefore = ktkGetNextDate(range[0], -1); - var year = dateBefore.getFullYear(); - var month = dateBefore.getMonth(); - var day = dateBefore; // <- never used? - - for (var i = 0, len = range.length; i < len; i++) { - var rday = range[i]; - - // Fill years - if (rday.getFullYear() !== year) { - yearArr.push( - ('
' - + year - + '
')); - year = rday.getFullYear(); - daysInYear = 0; - } - daysInYear++; - - // Fill months - if (rday.getMonth() !== month) { - monthArr.push( - ('
' - + settings.months[month] - + '
')); - month = rday.getMonth(); - daysInMonth = 0; - } - daysInMonth++; + '
' + + (1 + rday.getMonth()) + '
'); + } - var getDay = rday.getDay(); - var day_class = dowClass[getDay]; - if ( tools.isHoliday(rday) ) { - day_class = "holiday"; - } + // Last year + yearArr.push( + '
' + + year + + '
'); - dayArr.push('
' - + '
' + rday.getDate() + '
'); - dowArr.push('
' - + '
' + settings.dow[getDay] + '
'); - } //for - - // Last year - yearArr.push( - '
' - + year - + '
'); - - // Last month - monthArr.push( - '
' - + settings.months[month] - + '
'); - - var dataPanel = core.dataPanel(element, range.length * tools.getCellSize()); + dataPanel = core.dataPanel(element, dataPanelWidth); + // Append panel elements + dataPanel.append( + $row.clone().html(yearArr.join("")), + $row.clone().html(monthArr.join("")) + ); + break; - // Append panel elements + // **Days (default)** + default: + range = tools.parseDateRange(element.dateStart, element.dateEnd); + dataPanelWidth = range.length * tools.getCellSize(); + + year = range[0].getFullYear(); + month = range[0].getMonth(); + + for (i = 0, len = range.length; i < len; i++) { + rday = range[i]; + + // Fill years + if (rday.getFullYear() !== year) { + yearArr.push( + '
' + + year + + '
'); + year = rday.getFullYear(); + scaleUnitsThisYear = 0; + } + scaleUnitsThisYear++; + + // Fill months + if (rday.getMonth() !== month) { + monthArr.push( + '
' + + settings.months[month] + + '
'); + month = rday.getMonth(); + scaleUnitsThisMonth = 0; + } + scaleUnitsThisMonth++; - dataPanel.append(yearArr.join("")); - dataPanel.append(monthArr.join("")); - dataPanel.append($('
').html(dayArr.join(""))); - dataPanel.append($('
').html(dowArr.join(""))); + day = rday.getDay(); + dayClass = dowClass[day]; + if ( tools.isHoliday(rday) ) { + dayClass = "holiday"; + } - break; + dayArr.push( + '
' + + '
' + rday.getDate() + '
'); + dowArr.push( + '
' + + '
' + settings.dow[day] + '
'); + } //for + + // Last year + yearArr.push( + '
' + + year + + '
'); + + // Last month + monthArr.push( + '
' + + settings.months[month] + + '
'); + + dataPanel = core.dataPanel(element, dataPanelWidth); + + // Append panel elements + dataPanel.append( + $row.clone().html(yearArr.join("")), + $row.clone().html(monthArr.join("")), + $row.clone().html(dayArr.join("")), + $row.clone().html(dowArr.join("")) + ); } return $('
').append(dataPanel); @@ -762,7 +781,7 @@ .append($('