/** * 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 var total = 0 // 计划年份 var planYear // 记录原始top值 var curItemId = null var historyTop = null var hiddenBar = [] var isHidden = true // 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 openProjectUrl(url) { if (url) { window.oepn(url) } else { alert(123) } } 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 ? function(elt, i, match) { return findDay(elt, match[3]) } : $.expr.createPseudo(function(text) { return function(elt) { return findDay(elt, text) } }) // 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) } // 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) } } $.fn.gantt = function(options) { planYear = options.planYear total = options.source.length var scales = ['hours', 'days', 'weeks', 'months'] //Default settings var settings = { source: [], holidays: [], // paging itemsPerPage: 7, // localisation dow: ['7', '1', '2', '3', '4', '5', '6'], months: [ '一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月', ], waitText: '加载中...', // navigation navigate: 'buttons', scrollToToday: true, // cookie options useCookie: false, cookieKey: 'jquery.fn.gantt', // scale parameters scale: 'days', maxScale: 'months', minScale: 'hours', // callbacks onItemClick: function(data) { return }, onAddClick: function(dt, rowId) { return }, onRender: $.noop, } // read options $.extend(settings, options) // can't use cookie if don't have `$.cookie` settings.useCookie = settings.useCookie && $.isFunction($.cookie) // 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 (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') { return function(x, y) { x -= window.pageXOffset y -= window.pageYOffset return document.elementFromPoint(x, y) } } // version for older browsers return function(x, y) { x -= $(document).scrollLeft() y -= $(document).scrollTop() return document.elementFromPoint(x, y) } })(), // **Create the chart** create: function(element) { // Initialize data with a json object or fetch via an xhr // request depending on `settings.source` if (typeof settings.source !== 'string') { element.data = settings.source core.init(element) } else { $.getJSON(settings.source, function(jsData) { element.data = jsData core.init(element) }) } }, // **Setup the initial view** // Here we calculate the number of rows, pages and visible start // and end dates once the data are ready init: function(element) { element.rowsNum = element.data.length element.pageCount = Math.ceil(element.rowsNum / settings.itemsPerPage) element.rowsOnLastPage = element.rowsNum - Math.floor(element.rowsNum / settings.itemsPerPage) * settings.itemsPerPage // element.dateStart = tools.getMinDate(element) // element.dateEnd = tools.getMaxDate(element) element.dateStart = new Date(planYear - 1 + '/10/1 00:00:00') element.dateEnd = new Date(planYear + 1 + '/3/31 23:59:59') /* core.render(element); */ core.waitToggle(element, function() { core.render(element) }) }, // **Render the grid** render: function(element) { var content = $('
') var maindiv = $('
') var $leftPanel = core.leftPanel(element) maindiv.append($leftPanel) var $rightPanel = core.rightPanel(element, $leftPanel) var pLeft, hPos maindiv.append($rightPanel) content.append(core.navigation(element)) var $dataPanel = $rightPanel.find('.dataPanel') element.gantt = $('
') .append(maindiv) .append(content) $(element) .empty() .append(element.gantt) element.scrollNavigation.panelMargin = parseInt( $dataPanel.css('left').replace('px', ''), 10 ) element.scrollNavigation.panelMaxPos = $dataPanel.width() - $rightPanel.width() element.scrollNavigation.canScroll = $dataPanel.width() > $rightPanel.width() core.markNow(element) core.fillData(element, $dataPanel, $leftPanel) // Set a cookie to record current position in the view if (settings.useCookie) { var sc = $.cookie(settings.cookieKey + 'ScrollPos') if (sc) { element.hPosition = sc } } // Scroll the grid to today's date if (settings.scrollToToday) { core.navigateTo(element, 'now') core.scrollPanel(element, 0) // or, scroll the grid to the left most date in the panel } else { if (element.hPosition !== 0) { if (element.scaleOldWidth) { pLeft = $dataPanel.width() - $rightPanel.width() hPos = (pLeft * element.hPosition) / element.scaleOldWidth element.hPosition = hPos > 0 ? 0 : hPos element.scaleOldWidth = null } $dataPanel.css({ left: element.hPosition }) element.scrollNavigation.panelMargin = element.hPosition } core.repositionLabel(element) } $dataPanel.css({ height: $leftPanel.height() }) core.waitToggle(element) settings.onRender() }, // Create and return the left panel with labels leftPanel: function(element) { /* Left panel */ var ganttLeftPanel = $('
').append( $( '
序号
项目名称
项目状态
' ).css('height', tools.getCellSize() * element.headerRows) ) var entries = [] var k = 1 $.each(element.data, function(i, entry) { if ( i >= element.pageNum * settings.itemsPerPage && i < element.pageNum * settings.itemsPerPage + settings.itemsPerPage ) { var dataId = 'id' in entry ? '" data-id="' + entry.id : '' entries.push( '
' + '' + (i + 1) + '' + '' + (entry.name || '') + '' + '' + '
' ) k++ if (entry.desc) { entries.push( '
' + '' + entry.desc + '' + '
' ) } } }) return ganttLeftPanel.append(entries.join('')) }, // Create and return the data panel element dataPanel: function(element, width) { var dataPanel = $('
') // Handle mousewheel events for scrolling the data panel 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 dataPanel.click(function(e) { e.stopPropagation() var corrX /* <- never used? */, corrY var leftpanel = $(element).find('.fn-gantt .leftPanel') var datapanel = $(element).find('.fn-gantt .dataPanel') switch (settings.scale) { 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 corrY -= Math.floor(tools.getCellSize() / 2); */ // Find column where click occurred var col = core.elementFromPoint(e.pageX, datapanel.offset().top + corrY) // Was the label clicked directly? if (col.className === 'fn-label') { col = $(col.parentNode) } else { col = $(col) } var dt = col.data('repdate') // Find row where click occurred var row = core.elementFromPoint(leftpanel.offset().left + leftpanel.width() - 10, e.pageY) // Was the label clicked directly? if (row.className.indexOf('fn-label') === 0) { row = $(row.parentNode) } else { row = $(row) } var rowId = row.data('id') // Dispatch user registered function with the DateTime in ms // and the id if the clicked object is a row settings.onAddClick(dt, rowId) }) return dataPanel }, // 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'] //unused: was someone planning to allow styles to stretch to the bottom of the chart? //var gridDowClass = [" sn", "", "", "", "", "", " sa"]; var yearArr = [] var scaleUnitsThisYear = 0 var monthArr = [] var scaleUnitsThisMonth = 0 var dayArr = [] var hoursInDay = 0 var dowArr = [] var horArr = [] var today = new Date() 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) 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++ // Fill months var rm = rday.getMonth() if (rm !== month) { monthArr.push( '
' + settings.months[month] + '
' ) month = rm scaleUnitsThisMonth = 0 } 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 month monthArr.push( '
' + settings.months[month] + '
' ) dayClass = dowClass[day.getDay()] if (tools.isHoliday(day)) { dayClass = 'holiday' } 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 // **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 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() * 3 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( '
' + (1 + rday.getMonth()) + '
' ) } // Last year yearArr.push( '
' + year + '
' ) dataPanel = core.dataPanel(element, dataPanelWidth) // Append panel elements dataPanel.append( $row.clone().html(yearArr.join('')), $row.clone().html(monthArr.join('')) ) break // **Days (default)** default: range = tools.parseDateRange(element.dateStart, element.dateEnd) dataPanelWidth = range.length * tools.getCellSize() var dateBefore = ktkGetNextDate(range[0], -1) year = dateBefore.getFullYear() month = dateBefore.getMonth() //day = dateBefore; // <- never used? 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++ day = rday.getDay() dayClass = dowClass[day] if (tools.isHoliday(rday)) { dayClass = 'holiday' } 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) }, // **Navigation** navigation: function(element) { var ganttNavigate = null // Scrolling navigation is provided by setting // `settings.navigate='scroll'` if (settings.navigate === 'scroll') { ganttNavigate = $('