r344 - in trunk/chorem-webmotion/src/main/webapp/js: . jQueryGantt jQueryGantt/libs jQueryGantt/libs/JST jQueryGantt/libs/dateField jQueryGantt/libs/dateField/images
Author: meynier Date: 2013-06-20 16:35:02 +0200 (Thu, 20 Jun 2013) New Revision: 344 Url: http://chorem.org/projects/chorem/repository/revisions/344 Log: Added jQueryGantt library Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/add.gif trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/alert.gif trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/closeBig.png trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/del.gif trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/edit.gif trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/gantt.css trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttDrawer.js trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttGridEditor.js trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttMaster.js trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttTask.js trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttUtilities.js trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/gantt_compact.css trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/hasExternalDeps.png trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/JST/ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/JST/jquery.JST.js trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/date.js trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/dateField/ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/dateField/images/ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/dateField/images/next.png trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/dateField/images/prev.png trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/dateField/jquery.dateField.css trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/dateField/jquery.dateField.js trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/i18nJs.js trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/jquery.livequery.min.js trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/jquery.timers.js trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/platform.js trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/linkArrow.png trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/milestone.png trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/platform.css trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamwork-regular-webfont.eot trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamwork-regular-webfont.otf trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamwork-regular-webfont.svg trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamwork-regular-webfont.ttf trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamwork-regular-webfont.woff trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamworkFont.css trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/twGanttSmall.png Modified: trunk/chorem-webmotion/src/main/webapp/js/chorem.js Modified: trunk/chorem-webmotion/src/main/webapp/js/chorem.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/chorem.js 2013-06-20 10:37:21 UTC (rev 343) +++ trunk/chorem-webmotion/src/main/webapp/js/chorem.js 2013-06-20 14:35:02 UTC (rev 344) @@ -500,34 +500,33 @@ function displayFields() { var id = $(this).attr('wikittyId'); - $("#upgradeFields-" + id ).text("Loading fields for " + this.item(this.selectedIndex).label + "..."); - var extensionName = this.item(this.selectedIndex).label; - $.get(createUrl("/project/json/getExtension/", extensionName, "/" + $(this).attr('wikittyId')), - function(data){ - //success - var el = document.createElement( 'div' ); - el.innerHTML = data; + if(this.selectedIndex == 0) { + $("#upgradeFields-" + id).text(""); + } + else + { - var view = el.getElementsByClassName("well")[0]; - var dateFields = view.getElementsByClassName("datepicker"); - var d = new Date(); - for(var i = 0; i < dateFields.length; i++) { - dateFields[i].value = d.getDate() + "/" + (d.getMonth() +1) + "/" + d.getFullYear(); - //alert(dateFields[i].value); - } - - $("#upgradeFields-" + id).html(view.innerHTML); - initFields(); - - + $("#upgradeFields-" + id ).text("Loading fields for " + this.item(this.selectedIndex).label + "..."); + var extensionName = this.item(this.selectedIndex).label; + $.get(createUrl("/project/json/getExtension/", extensionName, "/" + $(this).attr('wikittyId')), + function(data){ + //success + var $jData = $(data); + - }); + $("#upgradeFields-" + id).html($jData.html());//.innerHTML); + initFields(); + + + + }); + } } - $("a.upgrade").click(upgradeQuotation); - $("select.extBox").change(displayFields); - $("#project_hidden").change(function() { - $("#projectSearch").submit(); - }); +$("a.upgrade").click(upgradeQuotation); +$("select.extBox").change(displayFields); +$("#project_hidden").change(function() { + $("#projectSearch").submit(); +}); }); \ No newline at end of file Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/add.gif =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/add.gif ___________________________________________________________________ Added: svn:mime-type + image/gif Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/alert.gif =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/alert.gif ___________________________________________________________________ Added: svn:mime-type + image/gif Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/closeBig.png =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/closeBig.png ___________________________________________________________________ Added: svn:mime-type + image/png Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/del.gif =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/del.gif ___________________________________________________________________ Added: svn:mime-type + image/gif Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/edit.gif =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/edit.gif ___________________________________________________________________ Added: svn:mime-type + image/gif Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/gantt.css =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/gantt.css (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/gantt.css 2013-06-20 14:35:02 UTC (rev 344) @@ -0,0 +1,323 @@ +.gdfTable { + table-layout: fixed; + border-collapse: separate; + border-spacing: 0; +} + +.gdfTable td, .gdfTable th { + vertical-align: middle; + overflow: hidden; + text-overflow: clip; + white-space: nowrap; + font-size: 14px +} + +.gdfCell { + overflow: hidden; + padding:4px 2px +} + +.gdfColHeader { + min-width: 5px; + height: 30px; +} + +.gdfCell, .gdfColHeader { + border-bottom: 1px solid #eee; + border-right: 1px solid #eee; +} + + +.ganttLines{ + position:absolute; + width:100%; + height:1px; + border-top:1px solid #eee; + z-index:1; +} + +.gdfCellInput { + border: 0 none; + font-size: 12px; + height: 20px; + margin: 0; + padding: 0; + width: 100%; + background-color: #d4fbe8; +} + +.gdfCellWrap { + border: 0 none; + font-size: 12px; + height: 17px; + margin: 0; + padding: 0; + width: 100%; + overflow: hidden; + + background-color: #ffcccc; +} + +.gdfColHeaderOver { + opacity: .7; +} + +.gdfHResizing { + cursor: w-resize; +} + +.splitterContainer { + width: 100%; + height: 100%; +} + +.splitBox1, .splitBox2 { + overflow-x: scroll; + overflow-y: hidden; + + /*background-color: yellow;*/ +} + +.splitBox2 { + /*background-color: orange;*/ +} + +.unselectable { + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -o-user-select: none; + user-select: none; +} + +.splitElement { + outline-style: none; + position: absolute; + height: 100%; +} + +.vSplitBar { + width: 5px; + background-color: #aaa; + cursor: w-resize; + text-align: center; + color: white; +} + +.end{ + border-right:1px dotted #666 +} + +.holyH{ + background-color: #9CB7AA; +} +.holy{ + background-color: #FFF5E6; +} + + +.expcoll{ + width:6px; + height:6px; + margin:1px; + padding:1px; + background-color:yellow; + display:inline-block; + border:1px solid gray; +} +.expcoll.exp{ + display:none; +} + + +.ganttTable{ + table-layout:fixed; +} + +.ganttTable td,.ganttTable th{ + overflow: hidden; + text-overflow: clip; + white-space: nowrap; +} + +.ganttHead1,.ganttHead2{ + height:20px; +} + +.ganttHead1 th,.ganttHead2 th{ + border-left:1px solid white; +} + +.ganttToday{ + position:absolute; + top:0; + width:1px; + height:100%; + border-left:2px dotted #13AFA5; +} + +.ganttHighLight{ + position:absolute; + width:100%; + height:28px; + background-color:yellow; + opacity:.4; +} + +.ganttButtonBar{ + position:relative; + padding:5px; +} + +.ganttButtonBar .buttons { + float:left; margin:45px 0 0 40px +} + + +.ganttButtonBar .button span.teamworkIcon{ + font-size: 150% +} + +.ganttButtonSeparator{ + border-left:1px solid gray; + padding-right:10px; + margin-left:10px; + font-size: 130% +} + +.ganttLinks{ + z-index:10; +} + +.taskBox{ + position:absolute; + height:25px; + margin-top:3px; + z-index:100; +} + +.taskBox .layout { + height:100%; + color:red; + border-radius:2px; + background: #eee; /* Old browsers */ + border:1px solid #bbb; +} + +.taskBox .taskStatus { + left:5px; + top:10px; + position:absolute; + width:10px; + height:10px; +} + +.taskBox .layout .milestone{ + top:0px; + position:absolute; + width:18px; + background: url(milestone.png) no-repeat; + height:18px; + display:none; +} +.taskBox .layout .milestone.end{ + right:0; +} +.taskBox .layout .milestone.active{ + display:block; +} + +.taskBox.hasChild .layout{ + border-top:2px solid black; +} + +.taskBox .taskProgress{ + height:5px; + position:absolute; +} + +.taskBox .layout.extDep{ + background-image:url(hasExternalDeps.png); +} + + +.taskLabel{ + position:absolute; + height:28px; + color:black; + text-align:right; + padding-right:5px; + overflow:hidden; + left:-200px; + width:195px; + white-space:nowrap; +} + + +.taskDepLine { + border: 1px solid #9999ff; + overflow: hidden; + position: absolute; +} + + +.taskEditRow,.emptyRow { + height:30px; +} + +.taskEditRow input{ + border: 0 none; + font-size: 14px; + height: 20px; + margin: 0; + padding: 0; + width: 100%; + font-family: Arial, sans-serif +} + +.taskEditRow.rowSelected td,.taskEditRow.rowSelected input{ + background-color:#FFFF99; +} + +.taskStatusBox{ + position:absolute; + width:100px; + height:24px; + border:1px solid #a0a0a0; + background-color:#fff; + margin-top:2px; + margin-left:-1px; + padding: 2px +} +.taskStatus{ + width:15px; + height:15px; + display:inline-block; +} +.taskStatus[status=STATUS_ACTIVE]{ + background-color: #66FF99; +} +.taskStatus[status=STATUS_DONE]{ + background-color: #0099FF; +} +.taskStatus[status=STATUS_FAILED]{ + background-color: #660066; +} +.taskStatus[status=STATUS_SUSPENDED]{ + background-color: #fbb11e; +} +.taskStatus[status=STATUS_UNDEFINED]{ + background-color: #ffffff; +} +.taskStatus.selected{ + border:#666 2px solid; +} + + +.ui-resizable-helper { border: 1px dotted #00F; } +.ui-resizable-e, .ui-resizable-w {width: 5px;} +.ui-draggable{ + cursor:move; +} + + Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttDrawer.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttDrawer.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttDrawer.js 2013-06-20 14:35:02 UTC (rev 344) @@ -0,0 +1,752 @@ +/* + Copyright (c) 2012-2013 Open Lab + Written by Roberto Bicchierai and Silvia Chelazzi http://roberto.open-lab.com + 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. +*/ +function Ganttalendar(zoom, startmillis, endMillis, master, minGanttSize) { + this.master = master; // is the a GantEditor instance + this.element; // is the jquery element containing gantt + this.highlightBar; + this.zoom = zoom; + this.minGanttSize = minGanttSize; + this.includeToday=true; //when true today is always visible. If false boundaries comes from tasks periods + + //this.zoomLevels = ["d","w","m","q","s","y"]; + this.zoomLevels = ["w","m","q","s","y"]; + + this.element = this.create(zoom, startmillis, endMillis); + +} + +Ganttalendar.prototype.zoomGantt = function(isPlus) { + var curLevel = this.zoom; + var pos = this.zoomLevels.indexOf(curLevel + ""); + + var newPos = pos; + if (isPlus) { + newPos = pos <= 0 ? 0 : pos - 1; + } else { + newPos = pos >= this.zoomLevels.length - 1 ? this.zoomLevels.length - 1 : pos + 1; + } + if (newPos != pos) { + curLevel = this.zoomLevels[newPos]; + this.zoom = curLevel; + this.refreshGantt(); + } +}; + + +Ganttalendar.prototype.create = function(zoom, originalStartmillis, originalEndMillis) { + //console.debug("Gantt.create " + new Date(originalStartmillis) + " - " + new Date(originalEndMillis)); + + var self = this; + + function getPeriod(zoomLevel, stMil, endMillis) { + var start = new Date(stMil); + var end = new Date(endMillis); + + + //reset hours + if (zoomLevel == "d") { + start.setHours(0, 0, 0, 0); + end.setHours(23, 59, 59, 999); + + //reset day of week + } else if (zoomLevel == "w") { + start.setHours(0, 0, 0, 0); + end.setHours(23, 59, 59, 999); + + start.setFirstDayOfThisWeek(); + end.setFirstDayOfThisWeek(); + end.setDate(end.getDate() + 6); + + //reset day of month + } else if (zoomLevel == "m") { + start.setHours(0, 0, 0, 0); + end.setHours(23, 59, 59, 999); + + start.setDate(1); + end.setDate(1); + end.setMonth(end.getMonth() + 1); + end.setDate(end.getDate() - 1); + + //reset to quarter + } else if (zoomLevel == "q") { + start.setHours(0, 0, 0, 0); + end.setHours(23, 59, 59, 999); + start.setDate(1); + start.setMonth(Math.floor(start.getMonth() / 3) * 3); + end.setDate(1); + end.setMonth(Math.floor(end.getMonth() / 3) * 3 + 3); + end.setDate(end.getDate() - 1); + + //reset to semester + } else if (zoomLevel == "s") { + start.setHours(0, 0, 0, 0); + end.setHours(23, 59, 59, 999); + start.setDate(1); + + start.setMonth(Math.floor(start.getMonth() / 6) * 6); + end.setDate(1); + end.setMonth(Math.floor(end.getMonth() / 6) * 6 + 6); + end.setDate(end.getDate() - 1); + + //reset to year - > gen + } else if (zoomLevel == "y") { + start.setHours(0, 0, 0, 0); + end.setHours(23, 59, 59, 999); + + start.setDate(1); + start.setMonth(0); + + end.setDate(1); + end.setMonth(12); + end.setDate(end.getDate() - 1); + } + return {start:start.getTime(),end:end.getTime()}; + } + + function createHeadCell(lbl, span, additionalClass) { + var th = $("<th>").html(lbl).attr("colSpan", span); + if (additionalClass) + th.addClass(additionalClass); + return th; + } + + function createBodyCell(span, isEnd, additionalClass) { + var ret = $("<td>").html(" ").attr("colSpan", span).addClass("ganttBodyCell"); + if (isEnd) + ret.addClass("end"); + if (additionalClass) + ret.addClass(additionalClass); + return ret; + } + + function createGantt(zoom, startPeriod, endPeriod) { + var tr1 = $("<tr>").addClass("ganttHead1"); + var tr2 = $("<tr>").addClass("ganttHead2"); + var trBody = $("<tr>").addClass("ganttBody"); + + function iterate(renderFunction1, renderFunction2) { + var start = new Date(startPeriod); + //loop for header1 + while (start.getTime() <= endPeriod) { + renderFunction1(start); + } + + //loop for header2 + start = new Date(startPeriod); + while (start.getTime() <= endPeriod) { + renderFunction2(start); + } + } + + //this is computed by hand in order to optimize cell size + var computedTableWidth; + + // year + if (zoom == "y") { + computedTableWidth = Math.floor(((endPeriod - startPeriod) / (3600000 * 24 * 180)) * 100); //180gg = 1 sem = 100px + iterate(function(date) { + tr1.append(createHeadCell(date.format("yyyy"), 2)); + date.setFullYear(date.getFullYear() + 1); + }, function(date) { + var sem = (Math.floor(date.getMonth() / 6) + 1); + tr2.append(createHeadCell(GanttMaster.messages["GANT_SEMESTER_SHORT"] + sem, 1)); + trBody.append(createBodyCell(1, sem == 2)); + date.setMonth(date.getMonth() + 6); + }); + + //semester + } else if (zoom == "s") { + computedTableWidth = Math.floor(((endPeriod - startPeriod) / (3600000 * 24 * 90)) * 100); //90gg = 1 quarter = 100px + iterate(function(date) { + var end = new Date(date.getTime()); + end.setMonth(end.getMonth() + 6); + end.setDate(end.getDate() - 1); + tr1.append(createHeadCell(date.format("MMM") + " - " + end.format("MMM yyyy"), 2)); + date.setMonth(date.getMonth() + 6); + }, function(date) { + var quarter = ( Math.floor(date.getMonth() / 3) + 1); + tr2.append(createHeadCell(GanttMaster.messages["GANT_QUARTER_SHORT"] + quarter, 1)); + trBody.append(createBodyCell(1, quarter % 2 == 0)); + date.setMonth(date.getMonth() + 3); + }); + + //quarter + } else if (zoom == "q") { + computedTableWidth = Math.floor(((endPeriod - startPeriod) / (3600000 * 24 * 30)) * 300); //1 month= 300px + iterate(function(date) { + var end = new Date(date.getTime()); + end.setMonth(end.getMonth() + 3); + end.setDate(end.getDate() - 1); + tr1.append(createHeadCell(date.format("MMM") + " - " + end.format("MMM yyyy"), 3)); + date.setMonth(date.getMonth() + 3); + }, function(date) { + var lbl = date.format("MMM"); + tr2.append(createHeadCell(lbl, 1)); + trBody.append(createBodyCell(1, date.getMonth() % 3 == 2)); + date.setMonth(date.getMonth() + 1); + }); + + //month + } else if (zoom == "m") { + computedTableWidth = Math.floor(((endPeriod - startPeriod) / (3600000 * 24 * 1)) * 20); //1 day= 20px + iterate(function(date) { + var sm = date.getTime(); + date.setMonth(date.getMonth() + 1); + var daysInMonth = parseInt((date.getTime() - sm) / (3600000 * 24)); + tr1.append(createHeadCell(new Date(sm).format("MMMM yyyy"), daysInMonth)); //spans mumber of dayn in the month + }, function(date) { + tr2.append(createHeadCell(date.format("d"), 1, isHoliday(date) ? "holyH" : null)); + var nd = new Date(date.getTime()); + nd.setDate(date.getDate() + 1); + trBody.append(createBodyCell(1, nd.getDate() == 1, isHoliday(date) ? "holy" : null)); + date.setDate(date.getDate() + 1); + }); + + //week + } else if (zoom == "w") { + computedTableWidth = Math.floor(((endPeriod - startPeriod) / (3600000 * 24)) * 30); //1 day= 30px + iterate(function(date) { + var end = new Date(date.getTime()); + end.setDate(end.getDate() + 6); + tr1.append(createHeadCell(date.format("MMM d") + " - " + end.format("MMM d'yy"), 7)); + date.setDate(date.getDate() + 7); + }, function(date) { + tr2.append(createHeadCell(date.format("EEEE").substr(0, 1), 1, isHoliday(date) ? "holyH" : null)); + trBody.append(createBodyCell(1, date.getDay() % 7 == (self.master.firstDayOfWeek + 6) % 7, isHoliday(date) ? "holy" : null)); + date.setDate(date.getDate() + 1); + }); + + //days + } else if (zoom == "d") { + computedTableWidth = Math.floor(((endPeriod - startPeriod) / (3600000 * 24)) * 200); //1 day= 200px + iterate(function(date) { + tr1.append(createHeadCell(date.format("EEEE d MMMM yyyy"), 4, isHoliday(date) ? "holyH" : null)); + date.setDate(date.getDate() + 1); + }, function(date) { + tr2.append(createHeadCell(date.format("HH"), 1, isHoliday(date) ? "holyH" : null)); + trBody.append(createBodyCell(1, date.getHours() > 17, isHoliday(date) ? "holy" : null)); + date.setHours(date.getHours() + 6); + }); + + } else { + console.error("Wrong level " + zoom); + } + + //set a minimal width + computedTableWidth = Math.max(computedTableWidth, self.minGanttSize); + + var table = $("<table cellspacing=0 cellpadding=0>"); + table.append(tr1).append(tr2).append(trBody).addClass("ganttTable").css({width:computedTableWidth}); + table.height(self.master.editor.element.height()); + + var box = $("<div>"); + box.addClass("gantt unselectable").attr("unselectable","true").css({position:"relative",width:computedTableWidth}); + box.append(table); + + //highlightBar + var hlb = $("<div>").addClass("ganttHighLight"); + box.append(hlb); + self.highlightBar = hlb; + + //create link container + var links = $("<div>"); + links.addClass("ganttLinks").css({position:"absolute",top:0,width:computedTableWidth,height:"100%"}); + box.append(links); + + + //compute scalefactor fx + self.fx = computedTableWidth / (endPeriod - startPeriod); + + // drawTodayLine + if (new Date().getTime() > self.startMillis && new Date().getTime() < self.endMillis) { + var x = Math.round(((new Date().getTime()) - self.startMillis) * self.fx); + var today = $("<div>").addClass("ganttToday").css("left", x); + box.append(today); + } + + return box; + } + + //if include today synch extremes + if (this.includeToday){ + var today=new Date().getTime(); + originalStartmillis=originalStartmillis>today ? today:originalStartmillis; + originalEndMillis=originalEndMillis<today ? today:originalEndMillis; + } + + + //get best dimension fo gantt + var period = getPeriod(zoom, originalStartmillis, originalEndMillis); //this is enlarged to match complete periods basing on zoom level + + //console.debug(new Date(period.start) + " " + new Date(period.end)); + self.startMillis = period.start; //real dimension of gantt + self.endMillis = period.end; + self.originalStartMillis = originalStartmillis; //minimal dimension required by user or by task duration + self.originalEndMillis = originalEndMillis; + + var table = createGantt(zoom, period.start, period.end); + + return table; +}; + + +//<%-------------------------------------- GANT TASK GRAPHIC ELEMENT --------------------------------------%> +Ganttalendar.prototype.drawTask = function (task) { + //console.debug("drawTask", task.name,new Date(task.start)); + var self = this; + //var prof = new Profiler("ganttDrawTask"); + //var editorRow = self.master.editor.element.find("tr[taskId=" + task.id + "]"); + editorRow = task.rowElement; + var top = editorRow.position().top+self.master.editor.element.parent().scrollTop(); + var x = Math.round((task.start - self.startMillis) * self.fx); + var taskBox = $.JST.createFromTemplate(task, "TASKBAR"); + + + + //save row element on task + task.ganttElement = taskBox; + + //if I'm parent + if (task.isParent()) + taskBox.addClass("hasChild"); + + + taskBox.css({top:top,left:x,width:Math.round((task.end - task.start) * self.fx)}); + + if (this.master.canWrite) { + taskBox.resizable({ + handles: 'e' + ( task.depends ? "" : ",w"), //if depends cannot move start + //helper: "ui-resizable-helper", + //grid:[oneDaySize,oneDaySize], + + resize:function(event, ui) { + //console.debug(ui) + $(".taskLabel[taskId=" + ui.helper.attr("taskId") + "]").css("width", ui.position.left); + event.stopImmediatePropagation(); + event.stopPropagation(); + }, + stop:function(event, ui) { + //console.debug(ui) + var task = self.master.getTask(ui.element.attr("taskId")); + var s = Math.round((ui.position.left / self.fx) + self.startMillis); + var e = Math.round(((ui.position.left + ui.size.width) / self.fx) + self.startMillis); + + self.master.beginTransaction(); + self.master.changeTaskDates(task, new Date(s), new Date(e)); + self.master.endTransaction(); + } + + }); + + } + + taskBox.dblclick(function() { + self.master.showTaskEditor($(this).closest("[taskId]").attr("taskId")); + + }).mousedown(function() { + var task = self.master.getTask($(this).attr("taskId")); + task.rowElement.click(); + }); + + //panning only in no depends + if (!task.depends && this.master.canWrite) { + + taskBox.css("position", "absolute").draggable({ + axis:'x', + drag:function (event, ui) { + $(".taskLabel[taskId=" + $(this).attr("taskId") + "]").css("width", ui.position.left); + }, + stop:function(event, ui) { + //console.debug(ui,$(this)) + var task = self.master.getTask($(this).attr("taskId")); + var s = Math.round((ui.position.left / self.fx) + self.startMillis); + + self.master.beginTransaction(); + self.master.moveTask(task, new Date(s)); + self.master.endTransaction(); + }/*, + start:function(event, ui) { + var task = self.master.getTask($(this).attr("taskId")); + var s = Math.round((ui.position.left / self.fx) + self.startMillis); + console.debug("start",new Date(s)); + }*/ + }); + } + + + var taskBoxSeparator=$("<div class='ganttLines'></div>"); + taskBoxSeparator.css({top:top+taskBoxSeparator.height()}); +// taskBoxSeparator.css({top:top+18}); + + + self.element.append(taskBox); + self.element.append(taskBoxSeparator); + + //ask for redraw link + self.redrawLinks(); + + //prof.stop(); +}; + + +Ganttalendar.prototype.addTask = function (task) { + //set new boundaries for gantt + this.originalEndMillis = this.originalEndMillis > task.end ? this.originalEndMillis : task.end; + this.originalStartMillis = this.originalStartMillis < task.start ? this.originalStartMillis : task.start; +}; + + +//<%-------------------------------------- GANT DRAW LINK ELEMENT --------------------------------------%> +//'from' and 'to' are tasks already drawn +Ganttalendar.prototype.drawLink = function (from, to, type) { + var peduncolusSize = 10; + var lineSize = 2; + + /** + * A representation of a Horizontal line + */ + HLine = function(width, top, left) { + var hl = $("<div>").addClass("taskDepLine"); + hl.css({ + height: lineSize, + left: left, + width: width, + top: top - lineSize / 2 + }); + return hl; + }; + + /** + * A representation of a Vertical line + */ + VLine = function(height, top, left) { + var vl = $("<div>").addClass("taskDepLine"); + vl.css({ + height: height, + left:left - lineSize / 2, + width: lineSize, + top: top + }); + return vl; + }; + + /** + * Given an item, extract its rendered position + * width and height into a structure. + */ + function buildRect(item) { + var rect = item.ganttElement.position(); + rect.width = item.ganttElement.width(); + rect.height = item.ganttElement.height(); + + return rect; + } + + /** + * The default rendering method, which paints a start to end dependency. + * + * @see buildRect + */ + function drawStartToEnd(rectFrom, rectTo, peduncolusSize) { + var left, top; + + var ndo = $("<div>").attr({ + from: from.id, + to: to.id + }); + + var currentX = rectFrom.left + rectFrom.width; + var currentY = rectFrom.height / 2 + rectFrom.top; + + var useThreeLine = (currentX + 2 * peduncolusSize) < rectTo.left; + + if (!useThreeLine) { + // L1 + if (peduncolusSize > 0) { + var l1 = new HLine(peduncolusSize, currentY, currentX); + currentX = currentX + peduncolusSize; + ndo.append(l1); + } + + // L2 + var l2_4size = ((rectTo.top + rectTo.height / 2) - (rectFrom.top + rectFrom.height / 2)) / 2; + var l2; + if (l2_4size < 0) { + l2 = new VLine(-l2_4size, currentY + l2_4size, currentX); + } else { + l2 = new VLine(l2_4size, currentY, currentX); + } + currentY = currentY + l2_4size; + + ndo.append(l2); + + // L3 + var l3size = rectFrom.left + rectFrom.width + peduncolusSize - (rectTo.left - peduncolusSize); + currentX = currentX - l3size; + var l3 = new HLine(l3size, currentY, currentX); + ndo.append(l3); + + // L4 + var l4; + if (l2_4size < 0) { + l4 = new VLine(-l2_4size, currentY + l2_4size, currentX); + } else { + l4 = new VLine(l2_4size, currentY, currentX); + } + ndo.append(l4); + + currentY = currentY + l2_4size; + + // L5 + if (peduncolusSize > 0) { + var l5 = new HLine(peduncolusSize, currentY, currentX); + currentX = currentX + peduncolusSize; + ndo.append(l5); + + } + } else { + //L1 + var l1_3Size = (rectTo.left - currentX) / 2; + var l1 = new HLine(l1_3Size, currentY, currentX); + currentX = currentX + l1_3Size; + ndo.append(l1); + + //L2 + var l2Size = ((rectTo.top + rectTo.height / 2) - (rectFrom.top + rectFrom.height / 2)); + var l2; + if (l2Size < 0) { + l2 = new VLine(-l2Size, currentY + l2Size, currentX); + } else { + l2 = new VLine(l2Size, currentY, currentX); + } + ndo.append(l2); + + currentY = currentY + l2Size; + + //L3 + var l3 = new HLine(l1_3Size, currentY, currentX); + currentX = currentX + l1_3Size; + ndo.append(l3); + } + + //arrow + var arr = $("<img src='linkArrow.png'>").css({ + position: 'absolute', + top: rectTo.top + rectTo.height / 2 - 5, + left: rectTo.left - 5 + }); + + ndo.append(arr); + + return ndo; + } + + /** + * A rendering method which paints a start to start dependency. + * + * @see buildRect + */ + function drawStartToStart(rectFrom, rectTo, peduncolusSize) { + var left, top; + + var ndo = $("<div>").attr({ + from: from.id, + to: to.id + }); + + var currentX = rectFrom.left; + var currentY = rectFrom.height / 2 + rectFrom.top; + + var useThreeLine = (currentX + 2 * peduncolusSize) < rectTo.left; + + if (!useThreeLine) { + // L1 + if (peduncolusSize > 0) { + var l1 = new HLine(peduncolusSize, currentY, currentX - peduncolusSize); + currentX = currentX - peduncolusSize; + ndo.append(l1); + } + + // L2 + var l2_4size = ((rectTo.top + rectTo.height / 2) - (rectFrom.top + rectFrom.height / 2)) / 2; + var l2; + if (l2_4size < 0) { + l2 = new VLine(-l2_4size, currentY + l2_4size, currentX); + } else { + l2 = new VLine(l2_4size, currentY, currentX); + } + currentY = currentY + l2_4size; + + ndo.append(l2); + + // L3 + var l3size = (rectFrom.left - peduncolusSize) - (rectTo.left - peduncolusSize); + currentX = currentX - l3size; + var l3 = new HLine(l3size, currentY, currentX); + ndo.append(l3); + + // L4 + var l4; + if (l2_4size < 0) { + l4 = new VLine(-l2_4size, currentY + l2_4size, currentX); + } else { + l4 = new VLine(l2_4size, currentY, currentX); + } + ndo.append(l4); + + currentY = currentY + l2_4size; + + // L5 + if (peduncolusSize > 0) { + var l5 = new HLine(peduncolusSize, currentY, currentX); + currentX = currentX + peduncolusSize; + ndo.append(l5); + } + } else { + //L1 + + var l1 = new HLine(peduncolusSize, currentY, currentX - peduncolusSize); + currentX = currentX - peduncolusSize; + ndo.append(l1); + + //L2 + var l2Size = ((rectTo.top + rectTo.height / 2) - (rectFrom.top + rectFrom.height / 2)); + var l2; + if (l2Size < 0) { + l2 = new VLine(-l2Size, currentY + l2Size, currentX); + } else { + l2 = new VLine(l2Size, currentY, currentX); + } + ndo.append(l2); + + currentY = currentY + l2Size; + + //L3 + + var l3 = new HLine(peduncolusSize + (rectTo.left - rectFrom.left), currentY, currentX); + currentX = currentX + peduncolusSize + (rectTo.left - rectFrom.left); + ndo.append(l3); + } + + //arrow + var arr = $("<img src='linkArrow.png'>").css({ + position: 'absolute', + top: rectTo.top + rectTo.height / 2 - 5, + left: rectTo.left - 5 + }); + + ndo.append(arr); + + return ndo; + } + + var rectFrom = buildRect(from); + var rectTo = buildRect(to); + + // Dispatch to the correct renderer + if (type == 'start-to-start') { + this.element.find(".ganttLinks").append( + drawStartToStart(rectFrom, rectTo, peduncolusSize) + ); + } else { + this.element.find(".ganttLinks").append( + drawStartToEnd(rectFrom, rectTo, peduncolusSize) + ); + } +}; + + +Ganttalendar.prototype.redrawLinks = function() { + //console.debug("redrawLinks "); + var self = this; + this.element.stopTime("ganttlnksredr"); + this.element.oneTime(60, "ganttlnksredr", function() { + //var prof=new Profiler("gd_drawLink_real"); + self.element.find(".ganttLinks").empty(); + for (var i=0;i<self.master.links.length;i++) { + var link = self.master.links[i]; + self.drawLink(link.from, link.to); + } + //prof.stop(); + }); +}; + + +Ganttalendar.prototype.reset = function() { + this.element.find(".ganttLinks").empty(); + this.element.find("[taskId]").remove(); +}; + + +Ganttalendar.prototype.redrawTasks = function() { + for (var i=0;i<this.master.tasks.length;i++) { + var task = this.master.tasks[i]; + this.drawTask(task); + } +}; + + +Ganttalendar.prototype.refreshGantt = function() { + //console.debug("refreshGantt") + var par = this.element.parent(); + + //try to maintain last scroll + var scrollY=par.scrollTop(); + var scrollX=par.scrollLeft(); + + this.element.remove(); + //guess the zoom level in base of period + if (!this.zoom ){ + var days = (this.originalEndMillis - this.originalStartMillis) / (3600000 * 24); + this.zoom = this.zoomLevels[days < 2 ? 0 : (days < 15 ? 1 : (days < 60 ? 2 : (days < 150 ? 3 : (days < 400 ? 4 : 5 ) ) ) )]; + } + var domEl = this.create(this.zoom, this.originalStartMillis, this.originalEndMillis); + this.element = domEl; + par.append(domEl); + this.redrawTasks(); + + //set old scroll + //console.debug("old scroll:",scrollX,scrollY) + par.scrollTop(scrollY); + par.scrollLeft(scrollX); + + //set current task + if (this.master.currentTask) { + this.highlightBar.css("top", this.master.currentTask.ganttElement.position().top); + } +}; + + +Ganttalendar.prototype.fitGantt = function() { + delete this.zoom; + this.refreshGantt(); +}; + +Ganttalendar.prototype.centerOnToday = function() { + //console.debug("centerOnToday"); + var x = Math.round(((new Date().getTime()) - this.startMillis) * this.fx); + this.element.parent().scrollLeft(x); +}; + + + Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttGridEditor.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttGridEditor.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttGridEditor.js 2013-06-20 14:35:02 UTC (rev 344) @@ -0,0 +1,518 @@ +/* + Copyright (c) 2012-2013 Open Lab + Written by Roberto Bicchierai and Silvia Chelazzi http://roberto.open-lab.com + 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. +*/ +function GridEditor(master) { + this.master = master; // is the a GantEditor instance + var gridEditor = $.JST.createFromTemplate({}, "TASKSEDITHEAD"); + gridEditor.gridify(); + this.element = gridEditor; +} + + +GridEditor.prototype.fillEmptyLines = function() { + var factory = new TaskFactory(); + + //console.debug("GridEditor.fillEmptyLines"); + var rowsToAdd = 30 - this.element.find(".taskEditRow").size(); + + //fill with empty lines + for (var i = 0; i < rowsToAdd; i++) { + var emptyRow = $.JST.createFromTemplate({}, "TASKEMPTYROW"); + //click on empty row create a task and fill above + var master = this.master; + emptyRow.click(function(ev) { + master.beginTransaction(); + var emptyRow = $(this); + var lastTask; + var start = new Date().getTime(); + var level = 0; + if (master.tasks[0]) { + start = master.tasks[0].start; + level = master.tasks[0].level + 1; + } + + //fill all empty previouses + emptyRow.prevAll(".emptyRow").andSelf().each(function() { + var ch = factory.build("tmp_fk" + new Date().getTime(), "", "", level, start, 1); + var task = master.addTask(ch); + lastTask = ch; + }); + master.endTransaction(); + lastTask.rowElement.click(); + lastTask.rowElement.find("[name=name]").focus()//focus to "name" input + .blur(function() { //if name not inserted -> undo -> remove just added lines + var imp = $(this); + if (!imp.isValueChanged()) + master.undo(); + }); + }); + this.element.append(emptyRow); + } +}; + + +GridEditor.prototype.addTask = function(task, row) { + //console.debug("GridEditor.addTask",task,row); + //var prof = new Profiler("editorAddTaskHtml"); + + //remove extisting row + this.element.find("[taskId=" + task.id + "]").remove(); + + var taskRow = $.JST.createFromTemplate(task, "TASKROW"); + //save row element on task + task.rowElement = taskRow; + + this.bindRowEvents(task, taskRow); + + if (typeof(row) != "number") { + var emptyRow = this.element.find(".emptyRow:first"); //tries to fill an empty row + if (emptyRow.size() > 0) + emptyRow.replaceWith(taskRow); + else + this.element.append(taskRow); + } else { + var tr = this.element.find("tr.taskEditRow").eq(row); + if (tr.size() > 0) { + tr.before(taskRow); + } else { + this.element.append(taskRow); + } + + } + this.element.find(".taskRowIndex").each(function(i, el) { + $(el).html(i + 1); + }); + //prof.stop(); + + return taskRow; +}; + + +GridEditor.prototype.refreshTaskRow = function(task) { + //console.debug("refreshTaskRow") + //var profiler = new Profiler("editorRefreshTaskRow"); + var row = task.rowElement; + + row.find(".taskRowIndex").html(task.getRow() + 1); + row.find(".indentCell").css("padding-left", task.level * 10); + row.find("[name=name]").val(task.name); + row.find("[name=code]").val(task.code); + row.find("[status]").attr("status", task.status); + + row.find("[name=duration]").val(task.duration); + row.find("[name=start]").val(new Date(task.start).format()).updateOldValue(); // called on dates only because for other field is called on focus event + row.find("[name=end]").val(new Date(task.end).format()).updateOldValue(); + row.find("[name=depends]").val(task.depends); + row.find(".taskAssigs").html(task.getAssigsString()); + + //profiler.stop(); +}; + +GridEditor.prototype.redraw = function() { + for (var i = 0; i < this.master.tasks.length; i++) { + this.refreshTaskRow(this.master.tasks[i]); + } +}; + +GridEditor.prototype.reset = function() { + this.element.find("[taskId]").remove(); +}; + + +GridEditor.prototype.bindRowEvents = function (task, taskRow) { + var self = this; + //console.debug("bindRowEvents",this,this.master,this.master.canWrite); + if (this.master.canWrite) { + self.bindRowInputEvents(task,taskRow); + + } else { //cannot write: disable input + taskRow.find("input").attr("readonly", true); + } + + taskRow.find(".edit").click(function() {self.openFullEditor(task,taskRow)}); + +}; + + +GridEditor.prototype.bindRowInputEvents = function (task, taskRow) { + var self = this; + + //bind dateField on dates + taskRow.find(".date").each(function () { + var el = $(this); + + el.click(function () { + var inp = $(this); + inp.dateField({ + inputField:el + }); + }); + + el.blur(function (date) { + var inp = $(this); + if (inp.isValueChanged()) { + if (!Date.isValid(inp.val())) { + alert(GanttMaster.messages["INVALID_DATE_FORMAT"]); + inp.val(inp.getOldValue()); + + } else { + var date = Date.parseString(inp.val()); + var row = inp.closest("tr"); + var taskId = row.attr("taskId"); + var task = self.master.getTask(taskId); + var lstart = task.start; + var lend = task.end; + + if (inp.attr("name") == "start") { + lstart = date.getTime(); + if (lstart >= lend) { + var end_as_date = new Date(lstart); + lend = end_as_date.add('d', task.duration).getTime(); + } + + //update task from editor + self.master.beginTransaction(); + self.master.moveTask(task, lstart); + self.master.endTransaction(); + + } else { + var end_as_date = new Date(date.getTime()); + lend = end_as_date.getTime(); + if (lstart >= lend) { + end_as_date.add('d', -1 * task.duration); + lstart = end_as_date.getTime(); + } + + //update task from editor + self.master.beginTransaction(); + self.master.changeTaskDates(task, lstart, lend); + self.master.endTransaction(); + } + + + inp.updateOldValue(); //in order to avoid multiple call if nothing changed + } + } + }); + }); + + + //binding on blur for task update (date exluded as click on calendar blur and then focus, so will always return false, its called refreshing the task row) + taskRow.find("input:not(.date)").focus(function () { + $(this).updateOldValue(); + + }).blur(function () { + var el = $(this); + if (el.isValueChanged()) { + var row = el.closest("tr"); + var taskId = row.attr("taskId"); + + var task = self.master.getTask(taskId); + + //update task from editor + var field = el.attr("name"); + + self.master.beginTransaction(); + + if (field == "depends") { + + var oldDeps = task.depends; + task.depends = el.val(); + // update links + var linkOK = self.master.updateLinks(task); + if (linkOK) { + //synchronize status fro superiors states + var sups = task.getSuperiors(); + for (var i = 0; i < sups.length; i++) { + if (!sups[i].from.synchronizeStatus()) + break; + } + + self.master.changeTaskDates(task, task.start, task.end); + } + + } else if (field == "duration") { + var dur = task.duration; + dur = parseInt(el.val()) || 1; + el.val(dur); + var newEnd = computeEndByDuration(task.start, dur); + self.master.changeTaskDates(task, task.start, newEnd); + + } else { + task[field] = el.val(); + } + self.master.endTransaction(); + } + }); + + + //change status + taskRow.find(".taskStatus").click(function () { + var el = $(this); + var tr = el.closest("[taskId]"); + var taskId = tr.attr("taskId"); + var task = self.master.getTask(taskId); + + var changer = $.JST.createFromTemplate({}, "CHANGE_STATUS"); + changer.css("top", tr.position().top + self.element.parent().scrollTop()); + changer.find("[status=" + task.status + "]").addClass("selected"); + changer.find(".taskStatus").click(function () { + self.master.beginTransaction(); + task.changeStatus($(this).attr("status")); + self.master.endTransaction(); + el.attr("status", task.status); + changer.remove(); + el.show(); + + }); + el.hide().oneTime(3000, "hideChanger", function () { + changer.remove(); + $(this).show(); + }); + el.after(changer); + }); + + + /*//expand collapse todo to be completed + taskRow.find(".expcoll").click(function(){ + //expand? + var el=$(this); + var taskId=el.closest("[taskId]").attr("taskId"); + var task=self.master.getTask(taskId); + var descs=task.getDescendant(); + if (el.is(".exp")){ + for (var i=0;i<descs.length;i++) + descs[i].rowElement.show(); + } else { + for (var i=0;i<descs.length;i++) + descs[i].rowElement.hide(); + } + + });*/ + + //bind row selection + taskRow.click(function () { + var row = $(this); + //var isSel = row.hasClass("rowSelected"); + row.closest("table").find(".rowSelected").removeClass("rowSelected"); + row.addClass("rowSelected"); + + //set current task + self.master.currentTask = self.master.getTask(row.attr("taskId")); + + //move highlighter + if (self.master.currentTask.ganttElement) + self.master.gantt.highlightBar.css("top", self.master.currentTask.ganttElement.position().top); + + //if offscreen scroll to element + var top = row.position().top; + if (row.position().top > self.element.parent().height()) { + self.master.gantt.element.parent().scrollTop(row.position().top - self.element.parent().height() + 100); + } + }); + +}; + + + +GridEditor.prototype.openFullEditor = function (task, taskRow) { + + var self=this; + + //task editor in popup + var taskId = taskRow.attr("taskId"); + //console.debug(task); + + //make task editor + var taskEditor = $.JST.createFromTemplate({}, "TASK_EDITOR"); + + taskEditor.find("#name").val(task.name); + taskEditor.find("#description").val(task.description); + taskEditor.find("#code").val(task.code); + taskEditor.find("#progress").val(task.progress ? parseFloat(task.progress) : 0); + taskEditor.find("#status").attr("status", task.status); + + if (task.startIsMilestone) + taskEditor.find("#startIsMilestone").attr("checked", true); + if (task.endIsMilestone) + taskEditor.find("#endIsMilestone").attr("checked", true); + + taskEditor.find("#duration").val(task.duration); + taskEditor.find("#start").val(new Date(task.start).format()); + taskEditor.find("#end").val(new Date(task.end).format()); + + //taskEditor.find("[name=depends]").val(task.depends); + + //make assignments table + var assigsTable = taskEditor.find("#assigsTable"); + assigsTable.find("[assigId]").remove(); + // loop on already assigned resources + for (var i = 0; i < task.assigs.length; i++) { + var assig = task.assigs[i]; + var assigRow = $.JST.createFromTemplate({task:task, assig:assig}, "ASSIGNMENT_ROW"); + assigsTable.append(assigRow); + } + + if (!self.master.canWrite) { + taskEditor.find("input,textarea").attr("readOnly", true); + taskEditor.find("input:checkbox,select").attr("disabled", true); + } else { + + //bind dateField on dates + taskEditor.find("#start").click(function () { + $(this).dateField({ + inputField:$(this), + callback: function (date) { + var dur = parseInt(taskEditor.find("#duration").val()); + date.clearTime(); + taskEditor.find("#end").val(new Date(computeEndByDuration(date.getTime(), dur)).format()); + } + }); + }); + + //bind dateField on dates + taskEditor.find("#end").click(function () { + $(this).dateField({ + inputField:$(this), + callback: function (end) { + var start = Date.parseString(taskEditor.find("#start").val()); + end.setHours(23, 59, 59, 999); + + if (end.getTime() < start.getTime()) { + var dur = parseInt(taskEditor.find("#duration").val()); + start = incrementDateByWorkingDays(end.getTime(), -dur); + taskEditor.find("#start").val(new Date(computeStart(start)).format()); + } else { + taskEditor.find("#duration").val(recomputeDuration(start.getTime(), end.getTime())); + } + } + }); + }); + + //bind blur on duration + taskEditor.find("#duration").change(function () { + var start = Date.parseString(taskEditor.find("#start").val()); + var el = $(this); + var dur = parseInt(el.val()); + dur = dur <= 0 ? 1 : dur; + el.val(dur); + taskEditor.find("#end").val(new Date(computeEndByDuration(start.getTime(), dur)).format()); + }); + + //bind add assignment + taskEditor.find("#addAssig").click(function () { + var assigsTable = taskEditor.find("#assigsTable"); + var assigRow = $.JST.createFromTemplate({task:task, assig:{id:"tmp_" + new Date().getTime()}}, "ASSIGNMENT_ROW"); + assigsTable.append(assigRow); + }); + + taskEditor.find("#status").click(function () { + var tskStatusChooser = $(this); + var changer = $.JST.createFromTemplate({}, "CHANGE_STATUS"); + changer.css("top", tskStatusChooser.position().top); + changer.find("[status=" + task.status + "]").addClass("selected"); + changer.find(".taskStatus").click(function () { + tskStatusChooser.attr("status", $(this).attr("status")); + changer.remove(); + tskStatusChooser.show(); + }); + tskStatusChooser.hide().oneTime(3000, "hideChanger", function () { + changer.remove(); + $(this).show(); + }); + tskStatusChooser.after(changer); + }); + + //save task + taskEditor.find("#saveButton").click(function () { + var task = self.master.getTask(taskId); // get task again because in case of rollback old task is lost + + self.master.beginTransaction(); + task.name = taskEditor.find("#name").val(); + task.description = taskEditor.find("#description").val(); + task.code = taskEditor.find("#code").val(); + task.progress = parseFloat(taskEditor.find("#progress").val()); + task.duration = parseInt(taskEditor.find("#duration").val()); + task.startIsMilestone = taskEditor.find("#startIsMilestone").is(":checked"); + task.endIsMilestone = taskEditor.find("#endIsMilestone").is(":checked"); + + //set assignments + taskEditor.find("tr[assigId]").each(function () { + var trAss = $(this); + var assId = trAss.attr("assigId"); + var resId = trAss.find("[name=resourceId]").val(); + var roleId = trAss.find("[name=roleId]").val(); + var effort = millisFromString(trAss.find("[name=effort]").val()); + + + //check if an existing assig has been deleted and re-created with the same values + var found = false; + for (var i = 0; i < task.assigs.length; i++) { + var ass = task.assigs[i]; + + if (assId == ass.id) { + ass.effort = effort; + ass.roleId = roleId; + ass.resourceId = resId; + ass.touched = true; + found = true; + break; + + } else if (roleId == ass.roleId && resId == ass.resourceId) { + ass.effort = effort; + ass.touched = true; + found = true; + break; + + } + } + + if (!found) { //insert + var ass = task.createAssignment("tmp_" + new Date().getTime(), resId, roleId, effort); + ass.touched = true; + } + + }); + + //remove untouched assigs + task.assigs = task.assigs.filter(function (ass) { + var ret = ass.touched; + delete ass.touched; + return ret; + }); + + //change dates + task.setPeriod(Date.parseString(taskEditor.find("#start").val()).getTime(), Date.parseString(taskEditor.find("#end").val()).getTime() + (3600000 * 24)); + + //change status + task.changeStatus(taskEditor.find("#status").attr("status")); + + if (self.master.endTransaction()) { + $("#__blackpopup__").trigger("close"); + } + + }); + } + + var ndo = createBlackPage(800, 500).append(taskEditor); + +}; Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttMaster.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttMaster.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttMaster.js 2013-06-20 14:35:02 UTC (rev 344) @@ -0,0 +1,702 @@ +/* + Copyright (c) 2012-2013 Open Lab + Written by Roberto Bicchierai and Silvia Chelazzi http://roberto.open-lab.com + 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. +*/ +function GanttMaster() { + this.tasks = []; + this.deletedTaskIds=[]; + this.links = []; + + this.editor; //element for editor + this.gantt; //element for gantt + + this.element; + + + this.resources; //list of resources + this.roles; //list of roles + + this.minEditableDate = 0; + this.maxEditableDate = Infinity; + + this.canWriteOnParent=true; + this.canWrite=true; + + this.firstDayOfWeek = Date.firstDayOfWeek; + + this.currentTask; // task currently selected; + + this.__currentTransaction; // a transaction object holds previous state during changes + this.__undoStack = []; + this.__redoStack = []; + + var self = this; +} + +GanttMaster.prototype.init = function(place) { + this.element = place; + + var self=this; + + //load templates + $("#gantEditorTemplates").loadTemplates().remove(); // TODO: Remove inline jquery, this should be injected + + //create editor + this.editor = new GridEditor(this); + this.editor.element.width(place.width() * .9 - 10); + place.append(this.editor.element); + + //create gantt + this.gantt = new Ganttalendar("m", new Date().getTime() - 3600000 * 24 * 2, new Date().getTime() + 3600000 * 24 * 15, this, place.width() * .6); + + //setup splitter + var splitter = $.splittify.init(place, this.editor.element, this.gantt.element, 70); + splitter.secondBox.css("overflow-y", "auto").scroll(function() { + splitter.firstBox.scrollTop(splitter.secondBox.scrollTop()); + }); + + + //prepend buttons + place.before($.JST.createFromTemplate({}, "GANTBUTTONS")); + + + //bindings + place.bind("refreshTasks.gantt", function() { + self.redrawTasks(); + }).bind("refreshTask.gantt", function(e, task) { + self.drawTask(task); + + }).bind("deleteCurrentTask.gantt", function(e) { + var row = self.currentTask.getRow(); + if (self.currentTask && (row>0 || self.currentTask.isNew())) { + self.beginTransaction(); + + self.currentTask.deleteTask(); + + self.currentTask = undefined; + //recompute depends string + self.updateDependsStrings(); + + //redraw + self.redraw(); + + //focus next row + row = row > self.tasks.length - 1 ? self.tasks.length - 1 : row; + if (row >= 0) { + self.currentTask = self.tasks[row]; + self.currentTask.rowElement.click(); + self.currentTask.rowElement.find("[name=name]").focus(); + } + self.endTransaction(); + } + + + }).bind("addAboveCurrentTask.gantt", function() { + var factory = new TaskFactory(); + + var ch; + var row = 0; + if (self.currentTask) { + //cannot add brothers to root + if (self.currentTask.level<=0) + return; + + ch = factory.build("tmp_" + new Date().getTime(), "", "", self.currentTask.level, self.currentTask.start, 1); + row = self.currentTask.getRow(); + } else { + ch = factory.build("tmp_" + new Date().getTime(), "", "", 0, new Date().getTime(), 1); + } + self.beginTransaction(); + var task = self.addTask(ch, row); + if (task) { + task.rowElement.click(); + task.rowElement.find("[name=name]").focus(); + } + self.endTransaction(); + + }).bind("addBelowCurrentTask.gantt", function() { + var factory = new TaskFactory(); + self.beginTransaction(); + var ch; + var row = 0; + if (self.currentTask) { + ch = factory.build("tmp_" + new Date().getTime(), "", "", self.currentTask.level + 1, self.currentTask.start, 1); + row = self.currentTask.getRow() + 1; + } else { + ch = factory.build("tmp_" + new Date().getTime(), "", "", 0, new Date().getTime(), 1); + } + var task = self.addTask(ch, row); + if (task) { + task.rowElement.click(); + task.rowElement.find("[name=name]").focus(); + } + self.endTransaction(); + + + }).bind("indentCurrentTask.gantt", function() { + if (self.currentTask) { + self.beginTransaction(); + self.currentTask.indent(); + self.endTransaction(); + } + + }).bind("outdentCurrentTask.gantt", function() { + if (self.currentTask) { + self.beginTransaction(); + self.currentTask.outdent(); + self.endTransaction(); + } + + }).bind("moveUpCurrentTask.gantt", function() { + if (self.currentTask) { + self.beginTransaction(); + self.currentTask.moveUp(); + self.endTransaction(); + } + }).bind("moveDownCurrentTask.gantt", function() { + if (self.currentTask) { + self.beginTransaction(); + self.currentTask.moveDown(); + self.endTransaction(); + } + + }).bind("zoomPlus.gantt", function() { + self.gantt.zoomGantt(true); + }).bind("zoomMinus.gantt", function() { + self.gantt.zoomGantt(false); + + }).bind("undo.gantt", function() { + self.undo(); + }).bind("redo.gantt", function() { + self.redo(); + }); +}; + +GanttMaster.messages = { + "CHANGE_OUT_OF_SCOPE": "NO_RIGHTS_FOR_UPDATE_PARENTS_OUT_OF_EDITOR_SCOPE", + "START_IS_MILESTONE": "START_IS_MILESTONE", + "END_IS_MILESTONE": "END_IS_MILESTONE", + "TASK_HAS_CONSTRAINTS": "TASK_HAS_CONSTRAINTS", + "GANTT_ERROR_DEPENDS_ON_OPEN_TASK": "GANTT_ERROR_DEPENDS_ON_OPEN_TASK", + "GANTT_ERROR_DESCENDANT_OF_CLOSED_TASK":"GANTT_ERROR_DESCENDANT_OF_CLOSED_TASK", + "TASK_HAS_EXTERNAL_DEPS": "TASK_HAS_EXTERNAL_DEPS", + "GANTT_ERROR_LOADING_DATA_TASK_REMOVED":"GANTT_ERROR_LOADING_DATA_TASK_REMOVED", + "CIRCULAR_REFERENCE": "CIRCULAR_REFERENCE", + "ERROR_SETTING_DATES": "ERROR_SETTING_DATES", + "CANNOT_DEPENDS_ON_ANCESTORS": "CANNOT_DEPENDS_ON_ANCESTORS", + "CANNOT_DEPENDS_ON_DESCENDANTS": "CANNOT_DEPENDS_ON_DESCENDANTS", + "INVALID_DATE_FORMAT": "INVALID_DATE_FORMAT", + "GANTT_QUARTER_SHORT": "GANTT_QUARTER_SHORT", + "GANTT_SEMESTER_SHORT":"GANTT_SEMESTER_SHORT" +}; + + +GanttMaster.prototype.createTask = function (id, name, code, level, start, duration) { + var factory = new TaskFactory(); + + return factory.build(id, name, code, level, start, duration); +}; + + +GanttMaster.prototype.createResource = function (id, name) { + var res = new Resource(id, name); + return res; +}; + + +//update depends strings +GanttMaster.prototype.updateDependsStrings = function() { + //remove all deps + for (var i=0;i<this.tasks.length;i++) { + this.tasks[i].depends = ""; + } + + for (var i=0;i<this.links.length;i++) { + var link = this.links[i]; + var dep = link.to.depends; + link.to.depends = link.to.depends + (link.to.depends == "" ? "" : ",") + (link.from.getRow() + 1) + (link.lag ? ":" + link.lag : ""); + } + +}; + +//------------------------------------ ADD TASK -------------------------------------------- +GanttMaster.prototype.addTask = function(task, row) { + //console.debug("master.addTask",task,row,this); + task.master = this; // in order to access controller from task + + //replace if already exists + var pos = -1; + for (var i=0;i<this.tasks.length;i++) { + if (task.id == this.tasks[i].id) { + pos = i; + break; + } + } + + if (pos >= 0) { + this.tasks.splice(pos, 1); + row = parseInt(pos); + } + + //add task in collection + if (typeof(row) != "number") { + this.tasks.push(task); + } else { + this.tasks.splice(row, 0, task); + + //recompute depends string + this.updateDependsStrings(); + } + + //add Link collection in memory + var linkLoops = !this.updateLinks(task); + + //set the status according to parent + if (task.getParent()) + task.status=task.getParent().status; + else + task.status="STATUS_ACTIVE"; + + + var ret = task; + if (linkLoops || !task.setPeriod(task.start, task.end)) { + //remove task from in-memory collection + //console.debug("removing task from memory",task); + this.tasks.splice(task.getRow(), 1); + ret = undefined; + } else { + //append task to editor + this.editor.addTask(task, row); + //append task to gantt + this.gantt.addTask(task); + } + return ret; +}; + + +/** + * a project contais tasks, resources, roles, and info about permisions + * @param project + */ +GanttMaster.prototype.loadProject = function(project) { + this.beginTransaction(); + this.resources = project.resources; + this.roles = project.roles; + this.canWrite = project.canWrite; + this.canWriteOnParent = project.canWriteOnParent; + + if (project.minEditableDate) + this.minEditableDate = computeStart(project.minEditableDate); + else + this.minEditableDate =-Infinity; + + if (project.maxEditableDate) + this.maxEditableDate =computeEnd(project.maxEditableDate); + else + this.maxEditableDate =Infinity; + + this.loadTasks(project.tasks, project.selectedRow); + this.deletedTaskIds=[]; + this.endTransaction(); + var self=this; + this.gantt.element.oneTime(200,function(){self.gantt.centerOnToday()}); +}; + + +GanttMaster.prototype.loadTasks = function(tasks, selectedRow) { + var factory = new TaskFactory(); + //reset + this.reset(); + + for (var i=0;i<tasks.length;i++){ + var task = tasks[i]; + if (!(task instanceof Task)) { + var t = factory.build(task.id, task.name, task.code, task.level, task.start, task.duration); + for (var key in task) { + if (key!="end" && key!="start") + t[key] = task[key]; //copy all properties + } + task = t; + } + task.master = this; // in order to access controller from task + + /*//replace if already exists + var pos = -1; + for (var i=0;i<this.tasks.length;i++) { + if (task.id == this.tasks[i].id) { + pos = i; + break; + } + }*/ + + this.tasks.push(task); //append task at the end + } + + //var prof=new Profiler("gm_loadTasks_addTaskLoop"); + for (var i=0;i<this.tasks.length;i++) { + var task = this.tasks[i]; + + //add Link collection in memory + var linkLoops = !this.updateLinks(task); + + if (linkLoops || !task.setPeriod(task.start, task.end)) { + alert(GanttMaster.messages.GANNT_ERROR_LOADING_DATA_TASK_REMOVED+"\n" + task.name+"\n"+ + (linkLoops?GanttMaster.messages.CIRCULAR_REFERENCE:GanttMaster.messages.ERROR_SETTING_DATES)); + + //remove task from in-memory collection + this.tasks.splice(task.getRow(), 1); + } else { + //append task to editor + this.editor.addTask(task); + //append task to gantt + this.gantt.addTask(task); + } + } + + this.editor.fillEmptyLines(); + //prof.stop(); + + // re-select old row if tasks is not empty + if (this.tasks && this.tasks.length>0){ + selectedRow = selectedRow ? selectedRow : 0; + this.tasks[selectedRow].rowElement.click(); + } + +}; + + +GanttMaster.prototype.getTask = function(taskId) { + var ret; + for (var i=0;i<this.tasks.length;i++) { + var tsk = this.tasks[i]; + if (tsk.id == taskId) { + ret = tsk; + break; + } + } + return ret; +}; + + +GanttMaster.prototype.getResource = function(resId) { + var ret; + for (var i=0;i<this.resources.length;i++) { + var res = this.resources[i]; + if (res.id == resId) { + ret = res; + break; + } + } + return ret; +}; + + +GanttMaster.prototype.changeTaskDates = function(task, start, end) { + return task.setPeriod(start, end); +}; + + +GanttMaster.prototype.moveTask = function(task, newStart) { + return task.moveTo(newStart, true); +}; + + +GanttMaster.prototype.taskIsChanged = function() { + //console.debug("taskIsChanged"); + var master=this; + + //refresh is executed only once every 50ms + this.element.stopTime("gnnttaskIsChanged"); + //var profilerext = new Profiler("gm_taskIsChangedRequest"); + this.element.oneTime(50, "gnnttaskIsChanged", function() { + //console.debug("task Is Changed real call to redraw"); + //var profiler = new Profiler("gm_taskIsChangedReal"); + master.editor.redraw(); + master.gantt.refreshGantt(); + //profiler.stop(); + }); + //profilerext.stop(); +}; + + +GanttMaster.prototype.redraw = function() { + this.editor.redraw(); + this.gantt.refreshGantt(); +}; + +GanttMaster.prototype.reset = function() { + this.tasks = []; + this.links = []; + this.deletedTaskIds=[]; + this.__undoStack = []; + this.__redoStack = []; + delete this.currentTask; + + this.editor.reset(); + this.gantt.reset(); +}; + + +GanttMaster.prototype.showTaskEditor = function(taskId) { + var task = this.getTask(taskId); + task.rowElement.find(".edit").click(); +}; + +GanttMaster.prototype.saveProject = function() { + return this.saveGantt(false); +}; + +GanttMaster.prototype.saveGantt = function(forTransaction) { + //var prof = new Profiler("gm_saveGantt"); + var saved = []; + for (var i=0;i<this.tasks.length;i++) { + var task = this.tasks[i]; + var cloned = task.clone(); + delete cloned.master; + delete cloned.rowElement; + delete cloned.ganttElement; + + saved.push(cloned); + } + + var ret = {tasks:saved}; + if (this.currentTask) { + ret.selectedRow = this.currentTask.getRow(); + } + + ret.deletedTaskIds=this.deletedTaskIds; //this must be consistent with transactions and undo + + if (!forTransaction) { + ret.resources = this.resources; + ret.roles = this.roles; + ret.canWrite = this.canWrite; + ret.canWriteOnParent = this.canWriteOnParent; + } + + //prof.stop(); + return ret; +}; + + +GanttMaster.prototype.updateLinks = function(task) { + //console.debug("updateLinks"); + //var prof= new Profiler("gm_updateLinks"); + + // defines isLoop function + function isLoop(task, target, visited) { + if (target == task) { + return true; + } + + var sups = task.getSuperiors(); + var loop = false; + for (var i=0;i<sups.length;i++) { + var supLink = sups[i]; + if (supLink.from == target) { + loop = true; + break; + } else { + if (visited.indexOf(supLink.from) <= 0) { + visited.push(supLink.from); + if (isLoop(supLink.from, target, visited)) { + loop = true; + break; + } + } + } + } + return loop; + } + + //remove my depends + this.links = this.links.filter(function(link) { + return link.to != task; + }); + + var todoOk = true; + if (task.depends) { + + //cannot depend from an ancestor + var parents = task.getParents(); + //cannot depend from descendants + var descendants=task.getDescendant(); + + var deps = task.depends.split(","); + var newDepsString = ""; + + var visited = []; + for (var j=0;j<deps.length;j++) { + var dep = deps[j]; // in the form of row(lag) e.g. 2:3,3:4,5 + var par = dep.split(":"); + var lag = 0; + + if (par.length > 1) { + lag = parseInt(par[1]); + } + + var sup = this.tasks[parseInt(par[0] - 1)]; + + if (sup) { + if (parents && parents.indexOf(sup) >= 0) { + this.setErrorOnTransaction(task.name + "\n"+GanttMaster.messages.CANNOT_DEPENDS_ON_ANCESTORS+"\n" + sup.name); + todoOk = false; + + } else if (descendants && descendants.indexOf(sup) >= 0) { + this.setErrorOnTransaction(task.name + "\n"+GanttMaster.messages.CANNOT_DEPENDS_ON_DESCENDANTS+"\n" + sup.name); + todoOk = false; + + } else if (isLoop(sup, task, visited)) { + todoOk = false; + this.setErrorOnTransaction(GanttMaster.messages.CIRCULAR_REFERENCE+"\n" + task.name + " -> " + sup.name); + } else { + this.links.push(new Link(sup, task, lag)); + newDepsString = newDepsString + (newDepsString.length > 0 ? "," : "") + dep; + } + } + } + + if (todoOk) { + task.depends = newDepsString; + } + + } + + //prof.stop(); + + return todoOk; +}; + + +//<%----------------------------- TRANSACTION MANAGEMENT ---------------------------------%> +GanttMaster.prototype.beginTransaction = function() { + if (!this.__currentTransaction) { + this.__currentTransaction = { + snapshot:JSON.stringify(this.saveGantt(true)), + errors:[] + }; + } else { + console.error("Cannot open twice a transaction"); + } + return this.__currentTransaction; +}; + + +GanttMaster.prototype.endTransaction = function() { + if (!this.__currentTransaction) { + console.error("Transaction never started."); + return true; + } + + var ret = true; + + //no error -> commit + if (this.__currentTransaction.errors.length <= 0) { + //console.debug("committing transaction"); + + //put snapshot in undo + this.__undoStack.push(this.__currentTransaction.snapshot); + //clear redo stack + this.__redoStack = []; + + //shrink gantt bundaries + this.gantt.originalStartMillis = Infinity; + this.gantt.originalEndMillis = -Infinity; + for (var i=0;i<this.tasks.length;i++) { + var task = this.tasks[i]; + if (this.gantt.originalStartMillis > task.start) + this.gantt.originalStartMillis = task.start; + if (this.gantt.originalEndMillis < task.end) + this.gantt.originalEndMillis = task.end; + + } + this.taskIsChanged(); //enqueue for gantt refresh + + + //error -> rollback + } else { + ret = false; + //console.debug("rolling-back transaction"); + //try to restore changed tasks + var oldTasks = JSON.parse(this.__currentTransaction.snapshot); + this.deletedTaskIds=oldTasks.deletedTaskIds; + this.loadTasks(oldTasks.tasks, oldTasks.selectedRow); + this.redraw(); + + //compose error message + var msg = ""; + for (var i=0;i<this.__currentTransaction.errors.length;i++) { + var err = this.__currentTransaction.errors[i]; + msg = msg + err.msg + "\n\n"; + } + alert(msg); + } + //reset transaction + this.__currentTransaction = undefined; + + return ret; +}; + +//this function notify an error to a transaction -> transaction will rollback +GanttMaster.prototype.setErrorOnTransaction = function(errorMessage, task) { + if (this.__currentTransaction) { + this.__currentTransaction.errors.push({msg:errorMessage,task:task}); + } else { + console.error(errorMessage); + } +}; + +// inhibit undo-redo +GanttMaster.prototype.checkpoint= function() { + this.__undoStack = []; + this.__redoStack = []; +}; + +//----------------------------- UNDO/REDO MANAGEMENT ---------------------------------%> + +GanttMaster.prototype.undo = function() { + //console.debug("undo before:",undoStack,redoStack); + if (this.__undoStack.length > 0) { + var his = this.__undoStack.pop(); + this.__redoStack.push(JSON.stringify(this.saveGantt())); + + var oldTasks = JSON.parse(his); + this.deletedTaskIds=oldTasks.deletedTaskIds; + this.loadTasks(oldTasks.tasks, oldTasks.selectedRow); + //console.debug(oldTasks,oldTasks.deletedTaskIds) + this.redraw(); + //console.debug("undo after:",undoStack,redoStack); + } +}; + +GanttMaster.prototype.redo = function() { + //console.debug("redo before:",undoStack,redoStack); + if (this.__redoStack.length > 0) { + var his = this.__redoStack.pop(); + this.__undoStack.push(JSON.stringify(this.saveGantt())); + + var oldTasks = JSON.parse(his); + this.deletedTaskIds=oldTasks.deletedTaskIds; + this.loadTasks(oldTasks.tasks, oldTasks.selectedRow); + this.redraw(); + //console.debug("redo after:",undoStack,redoStack); + } +}; + Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttTask.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttTask.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttTask.js 2013-06-20 14:35:02 UTC (rev 344) @@ -0,0 +1,947 @@ +/* + Copyright (c) 2012-2013 Open Lab + Written by Roberto Bicchierai and Silvia Chelazzi http://roberto.open-lab.com + 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. +*/ + +/** + * A method to instantiate valid task models from + * raw data. + */ +function TaskFactory() { + + /** + * Build a new Task + */ + this.build = function(id, name, code, level, start, duration) { + // Set at beginning of day + var adjusted_start = computeStart(start); + var calculated_end = computeEndByDuration(adjusted_start, duration); + + return new Task(id, name, code, level, adjusted_start, calculated_end, duration); + }; + +} + +function Task(id, name, code, level, start, end, duration) { + this.id = id; + this.name = name; + this.code = code; + this.level = level; + this.status = "STATUS_UNDEFINED"; + + this.start = start + this.duration = duration; + this.end = end; + + this.startIsMilestone = false; + this.endIsMilestone = false; + + this.collapsed = false; + + this.rowElement; //row editor html element + this.ganttElement; //gantt html element + this.master; + + this.assigs = []; +} + +Task.prototype.clone = function () { + var ret = {}; + for (var key in this) { + if (typeof(this[key]) != "function") { + ret[key] = this[key]; + } + } + return ret; +}; + +Task.prototype.getAssigsString = function () { + var ret = ""; + for (var i=0;i<this.assigs.length;i++) { + var ass = this.assigs[i]; + var res = this.master.getResource(ass.resourceId); + if (res) { + ret = ret + (ret == "" ? "" : ", ") + res.name; + } + } + return ret; +}; + +Task.prototype.createAssignment = function (id, resourceId, roleId, effort) { + var assig = new Assignment(id, resourceId, roleId, effort); + this.assigs.push(assig); + return assig; +}; + + +//<%---------- SET PERIOD ---------------------- --%> +Task.prototype.setPeriod = function (start, end) { + //console.debug("setPeriod ",this.name,new Date(start),new Date(end)); + //var profilerSetPer = new Profiler("gt_setPeriodJS"); + + if (start instanceof Date) { + start = start.getTime(); + } + + if (end instanceof Date) { + end = end.getTime(); + } + + var originalPeriod = { + start: this.start, + end: this.end, + duration: this.duration + }; + + //console.debug("setStart",date,date instanceof Date); + var wantedStartMillis = start; + + //cannot start after end + if (start > end) { + start = end; + } + + //set a legal start + start = computeStart(start); + + //if depends -> start is set to max end + lag of superior + var sups = this.getSuperiors(); + if (sups && sups.length > 0) { + + var supEnd = 0; + for (var i=0;i<sups.length;i++) { + var link = sups[i]; + supEnd = Math.max(supEnd, incrementDateByWorkingDays(link.from.end, link.lag)); + } + //if changed by depends move it + if (computeStart(supEnd) != start) { + return this.moveTo(supEnd + 1, false); + } + } + + var somethingChanged = false; + + //move date to closest day + var date = new Date(start); + + if (this.start != start || this.start != wantedStartMillis) { + this.start = start; + somethingChanged = true; + } + + //set end + var wantedEndMillis = end; + + end = computeEnd(end); + + if (this.end != end || this.end != wantedEndMillis) { + this.end = end; + somethingChanged = true; + } + + this.duration = recomputeDuration(this.start, this.end); + + //profilerSetPer.stop(); + + //nothing changed exit + if (!somethingChanged) + return true; + + //external dependencies: exit with error + if (this.hasExternalDep) { + this.master.setErrorOnTransaction(GanttMaster.messages["TASK_HAS_EXTERNAL_DEPS"] + "\n" + this.name, this); + return false; + } + + var todoOk = true; + + //I'm restricting + var deltaPeriod = originalPeriod.duration - this.duration; + var restricting = deltaPeriod > 0; + var restrictingStart = restricting && (originalPeriod.start < this.start); + var restrictingEnd = restricting && (originalPeriod.end > this.end); + + //console.debug( " originalPeriod.duration "+ originalPeriod.duration +" deltaPeriod "+deltaPeriod+" "+"restricting "+restricting); + + if (restricting) { + //loops children to get boundaries + var children = this.getChildren(); + var bs = Infinity; + var be = 0; + for (var i=0;i<children.length;i++) { + + ch = children[i]; + //console.debug("restricting: test child "+ch.name+" "+ch.end) + if (restrictingEnd) { + be = Math.max(be, ch.end); + } else { + bs = Math.min(bs, ch.start); + } + } + + if (restrictingEnd) { + //console.debug("restricting end ",be, this.end); + this.end = Math.max(be, this.end); + } else { + //console.debug("restricting start"); + this.start = Math.min(bs, this.start); + } + + this.duration = recomputeDuration(this.start, this.end); + } else { + + //check global boundaries + if (this.start < this.master.minEditableDate || this.end > this.master.maxEditableDate) { + this.master.setErrorOnTransaction(GanttMaster.messages["CHANGE_OUT_OF_SCOPE"], this); + todoOk = false; + } + + //console.debug("set period: somethingChanged",this); + if (todoOk && !updateTree(this)) { + todoOk = false; + } + } + + if (todoOk) { + //and now propagate to inferiors + var infs = this.getInferiors(); + if (infs && infs.length > 0) { + for (var i=0;i<infs.length;i++) { + var link = infs[i]; + todoOk = link.to.moveTo(end, false); //this is not the right date but moveTo checks start + if (!todoOk) + break; + } + } + } + + return todoOk; +}; + + +//<%---------- MOVE TO ---------------------- --%> +Task.prototype.moveTo = function (start, ignoreMilestones) { + //console.debug("moveTo ",this,start,ignoreMilestones); + //var profiler = new Profiler("gt_task_moveTo"); + + if (start instanceof Date) { + start = start.getTime(); + } + + var originalPeriod = { + start:this.start, + end:this.end + }; + + var wantedStartMillis = start; + + //set a legal start + start = computeStart(start); + + //if start is milestone cannot be move + if (!ignoreMilestones && this.startIsMilestone && start != this.start) { + //notify error + this.master.setErrorOnTransaction(GanttMaster.messages["START_IS_MILESTONE"], this); + return false; + } else if (this.hasExternalDep) { + //notify error + this.master.setErrorOnTransaction(GanttMaster.messages["TASK_HAS_EXTERNAL_DEPS"], this); + return false; + } + + //if depends start is set to max end + lag of superior + var sups = this.getSuperiors(); + if (sups && sups.length > 0) { + var supEnd = 0; + for (var i=0;i<sups.length;i++) { + var link = sups[i]; + supEnd = Math.max(supEnd, incrementDateByWorkingDays(link.from.end, link.lag)); + } + start = supEnd + 1; + } + //set a legal start + start = computeStart(start); + + var end = computeEndByDuration(start, this.duration); + + if (this.start != start || this.start != wantedStartMillis) { + //in case of end is milestone it never changes, but recompute duration + if (!ignoreMilestones && this.endIsMilestone) { + end = this.end; + this.duration = recomputeDuration(start, end); + } + this.start = start; + this.end = end; + //profiler.stop(); + + //check global boundaries + if (this.start < this.master.minEditableDate || this.end > this.master.maxEditableDate) { + this.master.setErrorOnTransaction(GanttMaster.messages["CHANGE_OUT_OF_SCOPE"], this); + return false; + } + + + var panDelta = originalPeriod.start - this.start; + //console.debug("panDelta",panDelta); + //loops children to shift them + var children = this.getChildren(); + for (var i=0;i<children.length;i++) { + ch = children[i]; + if (!ch.moveTo(ch.start - panDelta, false)) { + return false; + } + } + + + //console.debug("set period: somethingChanged",this); + if (!updateTree(this)) { + return false; + } + + + //and now propagate to inferiors + var infs = this.getInferiors(); + if (infs && infs.length > 0) { + for (var i=0;i<infs.length;i++) { + var link = infs[i]; + + //this is not the right date but moveTo checks start + if (!link.to.moveTo(end, false)) { + return false; + } + } + } + + } + + return true; +}; + + +function updateTree(task) { + //console.debug("updateTree ",task); + var error; + + //try to enlarge parent + var p = task.getParent(); + + //no parent:exit + if (!p) + return true; + + + var newStart = p.start; + var newEnd = p.end; + + if (p.start > task.start) { + if (p.startIsMilestone) { + task.master.setErrorOnTransaction(GanttMaster.messages["START_IS_MILESTONE"] + "\n" + p.name, task); + return false; + } else if (p.depends) { + task.master.setErrorOnTransaction(GanttMaster.messages["TASK_HAS_CONSTRAINTS"] + "\n" + p.name, task); + return false; + } + + newStart = task.start; + } + + if (p.end < task.end) { + if (p.endIsMilestone) { + task.master.setErrorOnTransaction(GanttMaster.messages["END_IS_MILESTONE"] + "\n" + p.name, task); + return false; + } + + newEnd = task.end; + } + + //propagate updates if needed + if (newStart != p.start || newEnd != p.end) { + //has external deps ? + if (p.hasExternalDep) { + task.master.setErrorOnTransaction(GanttMaster.messages["TASK_HAS_EXTERNAL_DEPS"] + "\n" + p.name, task); + return false; + } + + return p.setPeriod(newStart, newEnd); + } + + + return true; +} + +//<%---------- CHANGE STATUS ---------------------- --%> +Task.prototype.changeStatus = function(newStatus) { + //console.debug("changeStatus: "+this.name+" from "+this.status+" -> "+newStatus); + //compute descendant for identify a cone where status changes propagate + var cone = this.getDescendant(); + + function propagateStatus(task, newStatus, manuallyChanged, propagateFromParent, propagateFromChildren) { + var oldStatus = task.status; + + //no changes exit + if(newStatus == oldStatus){ + return true; + } + //console.debug("propagateStatus: "+task.name + " from " + task.status + " to " + newStatus + " " + (manuallyChanged?" a manella":"")+(propagateFromParent?" da parent":"")+(propagateFromChildren?" da children":"")); + + var todoOk = true; + task.status = newStatus; + + //xxxx -> STATUS_DONE may activate dependent tasks, both suspended and undefined. Will set to done all descendants. + //STATUS_FAILED -> STATUS_DONE do nothing if not forced by hand + if (newStatus == "STATUS_DONE") { + + if ((manuallyChanged || oldStatus != "STATUS_FAILED")) { //cannot change for cascade when failed + + //can be closed only if superiors are already done + var sups = task.getSuperiors(); + for (var i=0;i<sups.length;i++) { + if (cone.indexOf(sups[i].from) < 0) { + if (sups[i].from.status != "STATUS_DONE") { + if (manuallyChanged || propagateFromParent) + task.master.setErrorOnTransaction(GanttMaster.messages["GANTT_ERROR_DEPENDS_ON_OPEN_TASK"] + "\n" + sups[i].from.name + " -> " + task.name); + todoOk = false; + break; + } + } + } + + if (todoOk) { + //todo set progress to 100% if set on config + + var chds = task.getChildren(); + //set children as done + for (var i=0;i<chds.length;i++) + propagateStatus(chds[i], "STATUS_DONE", false,true,false); + + //set inferiors as active if outside the cone + propagateToInferiors(cone, task.getInferiors(), "STATUS_ACTIVE"); + } + } else { + todoOk = false; + } + + + // STATUS_UNDEFINED -> STATUS_ACTIVE all children become active, if they have no dependencies. + // STATUS_SUSPENDED -> STATUS_ACTIVE sets to active all children and their descendants that have no inhibiting dependencies. + // STATUS_DONE -> STATUS_ACTIVE all those that have dependencies must be set to suspended. + // STATUS_FAILED -> STATUS_ACTIVE nothing happens: child statuses must be reset by hand. + } else if (newStatus == "STATUS_ACTIVE") { + + if ((manuallyChanged || oldStatus != "STATUS_FAILED")) { //cannot change for cascade when failed + + //activate parent if closed + var par=task.getParent(); + if (par && par.status != "STATUS_ACTIVE") { + todoOk=propagateStatus(par,"STATUS_ACTIVE",false,false,true); + } + + if(todoOk){ + //can be active only if superiors are already done + var sups = task.getSuperiors(); + for (var i=0;i<sups.length;i++) { + if (sups[i].from.status != "STATUS_DONE") { + if (manuallyChanged || propagateFromChildren) + task.master.setErrorOnTransaction(GanttMaster.messages["GANTT_ERROR_DEPENDS_ON_OPEN_TASK"] + "\n" + sups[i].from.name + " -> " + task.name); + todoOk = false; + break; + } + } + } + + if (todoOk) { + var chds = task.getChildren(); + if (oldStatus == "STATUS_UNDEFINED" || oldStatus == "STATUS_SUSPENDED") { + //set children as active + for (var i=0;i<chds.length;i++) + if (chds[i].status != "STATUS_DONE" ) + propagateStatus(chds[i], "STATUS_ACTIVE", false,true,false); + } + + //set inferiors as suspended + var infs = task.getInferiors(); + for (var i=0;i<infs.length;i++) + propagateStatus(infs[i].to, "STATUS_SUSPENDED", false,false,false); + } + } else { + todoOk = false; + } + + // xxxx -> STATUS_SUSPENDED all active children and their active descendants become suspended. when not failed or forced + // xxxx -> STATUS_UNDEFINED all active children and their active descendants become suspended. when not failed or forced + } else if (newStatus == "STATUS_SUSPENDED" || newStatus == "STATUS_UNDEFINED") { + if (manuallyChanged || oldStatus != "STATUS_FAILED") { //cannot change for cascade when failed + + //suspend parent if not active + var par=task.getParent(); + if (par && par.status != "STATUS_ACTIVE") { + todoOk=propagateStatus(par,newStatus,false,false,true); + } + + + var chds = task.getChildren(); + //set children as active + for (var i=0;i<chds.length;i++){ + if (chds[i].status != "STATUS_DONE") + propagateStatus(chds[i], newStatus, false,true,false); + } + + //set inferiors as STATUS_SUSPENDED or STATUS_UNDEFINED + propagateToInferiors(cone, task.getInferiors(), newStatus); + } else { + todoOk = false; + } + + // xxxx -> STATUS_FAILED children and dependent failed + } else if (newStatus == "STATUS_FAILED") { + var chds = task.getChildren(); + //set children as failed + for (var i=0;i<chds.length;i++) + propagateStatus(chds[i], "STATUS_FAILED", false,true,false); + + //set inferiors as active + //set children as done + propagateToInferiors(cone, task.getInferiors(), "STATUS_FAILED"); + } + if (!todoOk){ + task.status = oldStatus; + //console.debug("status rolled back: "+task.name + " to " + oldStatus); + } + + return todoOk; + } + + /** + * A helper method to traverse an array of 'inferior' tasks + * and signal a status change. + */ + function propagateToInferiors(cone, infs, status) { + for (var i=0;i<infs.length;i++) { + if (cone.indexOf(infs[i].to) < 0) { + propagateStatus(infs[i].to, status, false, false, false); + } + } + } + + var todoOk = true; + var oldStatus = this.status; + + todoOk = propagateStatus(this, newStatus, true,false,false); + + if (!todoOk) + this.status = oldStatus; + + return todoOk; +}; + +Task.prototype.synchronizeStatus=function(){ + var oldS=this.status; + this.status=""; + return this.changeStatus(oldS); +}; + +Task.prototype.isLocallyBlockedByDependencies=function(){ + var sups = this.getSuperiors(); + var blocked=false; + for (var i=0;i<sups.length;i++) { + if (sups[i].from.status != "STATUS_DONE") { + blocked=true; + break; + } + } + return blocked; +}; + +//<%---------- TASK STRUCTURE ---------------------- --%> +Task.prototype.getRow = function() { + ret = -1; + if (this.master) + ret = this.master.tasks.indexOf(this); + return ret; +}; + + +Task.prototype.getParents = function() { + var ret; + if (this.master) { + var topLevel = this.level; + var pos = this.getRow(); + ret = []; + for (var i = pos; i >= 0; i--) { + var par = this.master.tasks[i]; + if (topLevel > par.level) { + topLevel = par.level; + ret.push(par); + } + } + } + return ret; +}; + + +Task.prototype.getParent = function() { + var ret; + if (this.master) { + for (var i = this.getRow(); i >= 0; i--) { + var par = this.master.tasks[i]; + if (this.level > par.level) { + ret = par; + break; + } + } + } + return ret; +}; + + +Task.prototype.isParent = function() { + var ret = false; + if (this.master) { + var pos = this.getRow(); + if (pos < this.master.tasks.length - 1) + ret = this.master.tasks[pos + 1].level > this.level; + } + return ret; +}; + + +Task.prototype.getChildren = function() { + var ret = []; + if (this.master) { + var pos = this.getRow(); + for (var i = pos + 1; i < this.master.tasks.length; i++) { + var ch = this.master.tasks[i]; + if (ch.level == this.level + 1) + ret.push(ch); + else if (ch.level <= this.level) // exit loop if parent or brother + break; + } + } + return ret; +}; + + +Task.prototype.getDescendant = function() { + var ret = []; + if (this.master) { + var pos = this.getRow(); + for (var i = pos + 1; i < this.master.tasks.length; i++) { + var ch = this.master.tasks[i]; + if (ch.level > this.level) + ret.push(ch); + else + break; + } + } + return ret; +}; + + +Task.prototype.getSuperiors = function() { + var ret = []; + var task = this; + if (this.master) { + ret = this.master.links.filter(function(link) { + return link.to == task; + }); + } + return ret; +}; + + +Task.prototype.getInferiors = function() { + var ret = []; + var task = this; + if (this.master) { + ret = this.master.links.filter(function(link) { + return link.from == task; + }); + } + return ret; +}; + + +Task.prototype.deleteTask = function() { + //delete both dom elements + this.rowElement.remove(); + this.ganttElement.remove(); + + //remove children + var chd = this.getChildren(); + for (var i=0;i<chd.length;i++) { + //add removed child in list + if(!chd[i].isNew()) + this.master.deletedTaskIds.push(chd[i].id); + chd[i].deleteTask(); + } + + if(!this.isNew()) + this.master.deletedTaskIds.push(this.id); + + + //remove from in-memory collection + this.master.tasks.splice(this.getRow(), 1); + + //remove from links + var task = this; + this.master.links = this.master.links.filter(function(link) { + return link.from != task && link.to != task; + }); +}; + + +Task.prototype.isNew=function(){ + return (this.id+"").indexOf("tmp_")==0; +}; + +//<%------------------------------------------ INDENT/OUTDENT --------------------------------%> +Task.prototype.indent = function() { + //console.debug("indent", this); + //a row above must exist + var row = this.getRow(); + + //no row no party + if (row <=0) + return false; + + var ret = false; + var taskAbove = this.master.tasks[row - 1]; + var newLev = this.level + 1; + if (newLev <= taskAbove.level + 1) { + ret = true; + //trick to get parents after indent + this.level++; + var futureParents = this.getParents(); + this.level--; + var oldLevel = this.level; + for (var i = row; i < this.master.tasks.length; i++) { + var desc = this.master.tasks[i]; + if (desc.level > oldLevel || desc == this) { + desc.level++; + //remove links from descendant to my parents + this.master.links = this.master.links.filter(function(link) { + var linkToParent = false; + if (link.to == desc) + linkToParent = futureParents.indexOf(link.from) >= 0; + else if (link.from == desc) + linkToParent = futureParents.indexOf(link.to) >= 0; + return !linkToParent; + }); + } else + break; + } + //recompute depends string + this.master.updateDependsStrings(); + //enlarge parent using a fake set period + this.setPeriod(this.start + 1, this.end + 1); + + //force status check + this.synchronizeStatus(); + } + return ret; +}; + + +Task.prototype.outdent = function() { + //console.debug("outdent", this); + + //a level must be >1 -> cannot escape from root + if (this.level <= 1) + return false; + + var ret = false; + var oldLevel = this.level; + + ret = true; + var row = this.getRow(); + for (var i = row; i < this.master.tasks.length; i++) { + var desc = this.master.tasks[i]; + if (desc.level > oldLevel || desc == this) { + desc.level--; + } else + break; + } + + var task = this; + var chds = this.getChildren(); + //remove links from me to my new children + this.master.links = this.master.links.filter(function(link) { + var linkExist = (link.to == task && chds.indexOf(link.from) >= 0 || link.from == task && chds.indexOf(link.to) >= 0); + return !linkExist; + }); + + + //enlarge me if inherited children are larger + for (var i=0;i<chds.length;i++) { + //remove links from me to my new children + chds[i].setPeriod(chds[i].start + 1, chds[i].end + 1); + } + + //enlarge parent using a fake set period + this.setPeriod(this.start + 1, this.end + 1); + + //force status check + this.synchronizeStatus(); + return ret; +}; + + +//<%------------------------------------------ MOVE UP / MOVE DOWN --------------------------------%> +Task.prototype.moveUp = function() { + //console.debug("moveUp", this); + var ret = false; + + //a row above must exist + var row = this.getRow(); + + //no row no party + if (row <=0) + return false; + + //find new row + var newRow; + for (newRow = row - 1; newRow >= 0; newRow--) { + if (this.master.tasks[newRow].level <= this.level) + break; + } + + //is a parent or a brother + if (this.master.tasks[newRow].level == this.level) { + ret = true; + //compute descendant + var descNumber = 0; + for (var i = row + 1; i < this.master.tasks.length; i++) { + var desc = this.master.tasks[i]; + if (desc.level > this.level) { + descNumber++; + } else { + break; + } + } + //move in memory + var blockToMove = this.master.tasks.splice(row, descNumber + 1); + var top = this.master.tasks.splice(0, newRow); + this.master.tasks = [].concat(top, blockToMove, this.master.tasks); + //move on dom + var rows = this.master.editor.element.find("tr[taskId]"); + var domBlockToMove = rows.slice(row, row + descNumber + 1); + rows.eq(newRow).before(domBlockToMove); + + //recompute depends string + this.master.updateDependsStrings(); + } else { + this.master.setErrorOnTransaction(GanttMaster.messages["TASK_MOVE_INCONSISTENT_LEVEL"], this); + ret = false; + } + return ret; +}; + + +Task.prototype.moveDown = function() { + //console.debug("moveDown", this); + + //a row below must exist, and cannot move root task + var row = this.getRow(); + if (row >= this.master.tasks.length - 1 || row==0) + return false; + + var ret = false; + + //find nearest brother + var newRow; + for (newRow = row + 1; newRow < this.master.tasks.length; newRow++) { + if (this.master.tasks[newRow].level <= this.level) + break; + } + + //is brother + if (this.master.tasks[newRow].level == this.level) { + ret = true; + //find last desc + for (newRow = newRow + 1; newRow < this.master.tasks.length; newRow++) { + if (this.master.tasks[newRow].level <= this.level) + break; + } + + //compute descendant + var descNumber = 0; + for (var i = row + 1; i < this.master.tasks.length; i++) { + var desc = this.master.tasks[i]; + if (desc.level > this.level) { + descNumber++; + } else { + break; + } + } + + //move in memory + var blockToMove = this.master.tasks.splice(row, descNumber + 1); + var top = this.master.tasks.splice(0, newRow - descNumber - 1); + this.master.tasks = [].concat(top, blockToMove, this.master.tasks); + + + //move on dom + var rows = this.master.editor.element.find("tr[taskId]"); + var aft = rows.eq(newRow - 1); + var domBlockToMove = rows.slice(row, row + descNumber + 1); + aft.after(domBlockToMove); + + //recompute depends string + this.master.updateDependsStrings(); + } + + return ret; +}; + + +//<%------------------------------------------------------------------------ LINKS OBJECT ---------------------------------------------------------------%> +function Link(taskFrom, taskTo, lagInWorkingDays) { + this.from = taskFrom; + this.to = taskTo; + this.lag = lagInWorkingDays; +} + + +//<%------------------------------------------------------------------------ ASSIGNMENT ---------------------------------------------------------------%> +function Assignment(id, resourceId, roleId, effort) { + this.id = id; + this.resourceId = resourceId; + this.roleId = roleId; + this.effort = effort; +} + + +//<%------------------------------------------------------------------------ RESOURCE ---------------------------------------------------------------%> +function Resource(id, name) { + this.id = id; + this.name = name; +} + + +//<%------------------------------------------------------------------------ ROLE ---------------------------------------------------------------%> +function Role(id, name) { + this.id = id; + this.name = name; +} + + + + Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttUtilities.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttUtilities.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/ganttUtilities.js 2013-06-20 14:35:02 UTC (rev 344) @@ -0,0 +1,237 @@ +/* + Copyright (c) 2012-2013 Open Lab + Written by Roberto Bicchierai and Silvia Chelazzi http://roberto.open-lab.com + 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. +*/ + +$.fn.gridify = function(options) { + this.options = { + colResizeZoneWidth:10 + }; + + $.extend(this.options, options); + $.gridify.init($(this), this.options); + return this; +}; + +$.gridify = { + init: function(elems, opt) { + elems.each(function() { + var table = $(this); + + //---------------------- header management start + table.find("th.gdfColHeader.gdfResizable:not(.gdfied)").mouseover(function() { + $(this).addClass("gdfColHeaderOver"); + + }).bind("mouseout.gdf", function() { + $(this).removeClass("gdfColHeaderOver"); + if (!$.gridify.columInResize) { + $("body").removeClass("gdfHResizing"); + } + + }).bind("mousemove.gdf", function(e) { + if (!$.gridify.columInResize) { + var colHeader = $(this); + var mousePos = e.pageX - colHeader.offset().left; + + if (colHeader.width() - mousePos < opt.colResizeZoneWidth) { + $("body").addClass("gdfHResizing"); + } else { + $("body").removeClass("gdfHResizing"); + } + } + + }).bind("mousedown.gdf", function(e) { + var colHeader = $(this); + var mousePos = e.pageX - colHeader.offset().left; + if (colHeader.width() - mousePos < opt.colResizeZoneWidth) { + $.gridify.columInResize = $(this); + //bind event for start resizing + //console.debug("start resizing"); + $(document).bind("mousemove.gdf", function(e) { + //manage resizing + //console.debug(e.pageX - $.gridify.columInResize.offset().left) + $.gridify.columInResize.width(e.pageX - $.gridify.columInResize.offset().left); + + + //bind mouse up on body to stop resizing + }).bind("mouseup.gdf", function() { + //console.debug("stop resizing"); + $(this).unbind("mousemove.gdf").unbind("mouseup.gdf"); + $("body").removeClass("gdfHResizing"); + delete $.gridify.columInResize; + }); + } + }).addClass("gdfied unselectable").attr("unselectable","true"); + + + //---------------------- cell management start wrapping + table.find("td.gdfCell:not(.gdfied)").each(function() { + var cell = $(this); + if (cell.is(".gdfEditable")) { + var inp = $("<input type='text'>").addClass("gdfCellInput"); + inp.val(cell.text()); + cell.empty().append(inp); + } else { + var wrp = $("<div>").addClass("gdfCellWrap"); + wrp.html(cell.html()); + cell.empty().append(wrp); + } + }).addClass("gdfied"); + ; + + }); + } +}; + +$.splittify = { + init: function(where, first, second,perc) { + + perc=perc || 50; + + var splitter = $("<div>").addClass("splitterContainer"); + + var firstBox = $("<div>").addClass("splitElement splitBox1"); + var splitterBar = $("<div>").addClass("splitElement vSplitBar").attr("unselectable", "on").html("|").css("padding-top",where.height()/2+"px"); + var secondBox = $("<div>").addClass("splitElement splitBox2"); + + firstBox.append(first); + secondBox.append(second); + + splitter.append(firstBox); + splitter.append(secondBox); + splitter.append(splitterBar); + + + where.append(splitter); + + var w = where.innerWidth(); + firstBox.width(w *perc/ 100 - splitterBar.width()).css({left:0}); + splitterBar.css({left:firstBox.width()}); + secondBox.width(w -firstBox.width()-splitterBar.width() ).css({left:firstBox.width() + splitterBar.width()}); + + + + splitterBar.bind("mousedown.gdf", function(e) { + $.splittify.splitterBar = $(this); + //bind event for start resizing + //console.debug("start splitting"); + $("body").unselectable().bind("mousemove.gdf", function(e) { + //manage resizing + //console.debug(e.pageX - $.gridify.columInResize.offset().left) + var sb = $.splittify.splitterBar; + var pos = e.pageX - sb.parent().offset().left; + var w = sb.parent().width(); + if (pos > 10 && pos < w - 20) { + sb.css({left:pos}); + firstBox.width(pos); + secondBox.css({left:pos + sb.width(),width:w - pos - sb.width()}); + } + + //bind mouse up on body to stop resizing + }).bind("mouseup.gdf", function() { + //console.debug("stop splitting"); + $(this).unbind("mousemove.gdf").unbind("mouseup.gdf").clearUnselectable(); + delete $.splittify.splitterBar; + + }); + }); + + return {firstBox:firstBox,secondBox:secondBox,splitterBar:splitterBar}; + } +}; + + + + +//<%------------------------------------------------------------------------ UTILITIES ---------------------------------------------------------------%> + function computeStart(start) { + var d = new Date(start+3600000*12); + d.setHours(0, 0, 0, 0); + //move to next working day + while (isHoliday(d)) { + d.setDate(d.getDate() + 1); + } + d.setHours(0, 0, 0, 0); + return d.getTime(); + } + + function computeEnd(end) { + var d = new Date(end-3600000*12); + d.setHours(23, 59, 59, 999); + //move to next working day + while (isHoliday(d)) { + d.setDate(d.getDate() + 1); + } + d.setHours(23, 59, 59, 999); + return d.getTime(); + } + + function computeEndByDuration(start, duration) { + var d = new Date(start); + //console.debug("computeEndByDuration start ",d,duration) + var q = duration - 1; + while (q > 0) { + d.setDate(d.getDate() + 1); + if (!isHoliday(d)) + q--; + } + d.setHours(23, 59, 59, 999); + return d.getTime(); + } + + function incrementDateByWorkingDays(date, days) { + var d = new Date(date); + d.incrementDateByWorkingDays(days); + return d.getTime(); + } + +function recomputeDuration(start, end) { + //console.debug("recomputeDuration"); + return new Date(start).distanceInWorkingDays(new Date(end)); + } + + + +//This prototype is provided by the Mozilla foundation and +//is distributed under the MIT license. +//http://www.ibiblio.org/pub/Linux/LICENSES/mit.license + +if (!Array.prototype.filter){ + Array.prototype.filter = function(fun ) + { + var len = this.length; + if (typeof fun != "function") + throw new TypeError(); + + var res = new Array(); + var thisp = arguments[1]; + for (var i = 0; i < len; i++) + { + if (i in this) + { + var val = this[i]; // in case fun mutates this + if (fun.call(thisp, val, i, this)) + res.push(val); + } + } + return res; + }; +} \ No newline at end of file Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/gantt_compact.css =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/gantt_compact.css (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/gantt_compact.css 2013-06-20 14:35:02 UTC (rev 344) @@ -0,0 +1,323 @@ +.gdfTable { + table-layout: fixed; + border-collapse: separate; + border-spacing: 0; +} + +.gdfTable td, .gdfTable th { + vertical-align: middle; + overflow: hidden; + text-overflow: clip; + white-space: nowrap; + font-size: 10px +} + +.gdfCell { + overflow: hidden; + padding:1px +} + +.gdfColHeader { + min-width: 5px; + height: 30px; +} + +.gdfCell, .gdfColHeader { + border-bottom: 1px solid #eee; + border-right: 1px solid #eee; +} + + +.ganttLines{ + position:absolute; + width:100%; + height:1px; + border-top:1px solid #eee; + z-index:1; +} + +.gdfCellInput { + border: 0 none; + font-size: 12px; + height: 14px; + margin: 0; + padding: 0; + width: 100%; + background-color: #d4fbe8; +} + +.gdfCellWrap { + border: 0 none; + font-size: 12px; + height: 14px; + margin: 0; + padding: 0; + width: 100%; + overflow: hidden; + + background-color: #ffcccc; +} + +.gdfColHeaderOver { + opacity: .7; +} + +.gdfHResizing { + cursor: w-resize; +} + +.splitterContainer { + width: 100%; + height: 100%; +} + +.splitBox1, .splitBox2 { + overflow-x: scroll; + overflow-y: hidden; + + /*background-color: yellow;*/ +} + +.splitBox2 { + /*background-color: orange;*/ +} + +.unselectable { + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -o-user-select: none; + user-select: none; +} + +.splitElement { + outline-style: none; + position: absolute; + height: 100%; +} + +.vSplitBar { + width: 5px; + background-color: #aaa; + cursor: w-resize; + text-align: center; + color: white; +} + +.end{ + border-right:1px dotted #666 +} + +.holyH{ + background-color: #9CB7AA; +} +.holy{ + background-color: #FFF5E6; +} + + +.expcoll{ + width:6px; + height:6px; + margin:1px; + padding:1px; + background-color:yellow; + display:inline-block; + border:1px solid gray; +} +.expcoll.exp{ + display:none; +} + + +.ganttTable{ + table-layout:fixed; +} + +.ganttTable td,.ganttTable th{ + overflow: hidden; + text-overflow: clip; + white-space: nowrap; +} + +.ganttHead1,.ganttHead2{ + height:20px; +} + +.ganttHead1 th,.ganttHead2 th{ + border-left:1px solid white; +} + +.ganttToday{ + position:absolute; + top:0; + width:1px; + height:100%; + border-left:2px dotted #13AFA5; +} + +.ganttHighLight{ + position:absolute; + width:100%; + height:18px; + background-color:yellow; + opacity:.4; +} + +.ganttButtonBar{ + position:relative; + padding:5px; +} + +.ganttButtonBar .buttons { + float:left; margin:45px 0 0 40px +} + + +.ganttButtonBar .button span.teamworkIcon{ + font-size: 150% +} + +.ganttButtonSeparator{ + border-left:1px solid gray; + padding-right:10px; + margin-left:10px; + font-size: 130% +} + +.ganttLinks{ + z-index:10; +} + +.taskBox{ + position:absolute; + height:18px; + margin-top:1px; + z-index:100; +} + +.taskBox .layout { + height:100%; + color:red; + border-radius:2px; + background: #eee; /* Old browsers */ + border:1px solid #bbb; +} + +.taskBox .taskStatus { + left:5px; + top:4px; + position:absolute; + width:10px; + height:10px; +} + +.taskBox .layout .milestone{ + top:0px; + position:absolute; + width:16px; + background: url(milestone.png) no-repeat; + height:16px; + display:none; +} +.taskBox .layout .milestone.end{ + right:0; +} +.taskBox .layout .milestone.active{ + display:block; +} + +.taskBox.hasChild .layout{ + border-top:2px solid black; +} + +.taskBox .taskProgress{ + height:5px; + position:absolute; +} + +.taskBox .layout.extDep{ + background-image:url(hasExternalDeps.png); +} + + +.taskLabel{ + position:absolute; + height:18px; + color:black; + text-align:right; + padding-right:5px; + overflow:hidden; + left:-200px; + width:195px; + white-space:nowrap; +} + + +.taskDepLine { + border: 1px solid #9999ff; + overflow: hidden; + position: absolute; +} + + +.taskEditRow,.emptyRow { + height:18px; +} + +.taskEditRow input{ + border: 0 none; + font-size: 10px; + height: 14px; + margin: 0; + padding: 0; + width: 100%; + font-family: Arial, sans-serif +} + +.taskEditRow.rowSelected td,.taskEditRow.rowSelected input{ + background-color:#FFFF99; +} + +.taskStatusBox{ + position:absolute; + width:100px; + height:18px; + border:1px solid #a0a0a0; + background-color:#fff; + margin-top:2px; + margin-left:-1px; + padding: 2px +} +.taskStatus{ + width:12px; + height:12px; + display:inline-block; +} +.taskStatus[status=STATUS_ACTIVE]{ + background-color: #66FF99; +} +.taskStatus[status=STATUS_DONE]{ + background-color: #0099FF; +} +.taskStatus[status=STATUS_FAILED]{ + background-color: #660066; +} +.taskStatus[status=STATUS_SUSPENDED]{ + background-color: #fbb11e; +} +.taskStatus[status=STATUS_UNDEFINED]{ + background-color: #ffffff; +} +.taskStatus.selected{ + border:#666 2px solid; +} + + +.ui-resizable-helper { border: 1px dotted #00F; } +.ui-resizable-e, .ui-resizable-w {width: 5px;} +.ui-draggable{ + cursor:move; +} + + Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/hasExternalDeps.png =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/hasExternalDeps.png ___________________________________________________________________ Added: svn:mime-type + image/png Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/JST/jquery.JST.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/JST/jquery.JST.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/JST/jquery.JST.js 2013-06-20 14:35:02 UTC (rev 344) @@ -0,0 +1,167 @@ +$.fn.loadTemplates = function() { + $.JST.loadTemplates($(this)); + return this; +}; + +$.JST = { + _templates: new Object(), + _decorators:new Object(), + + loadTemplates: function(elems) { + elems.each(function() { + $(this).find(".__template__").each(function() { + var tmpl = $(this); + var type = tmpl.attr("type"); + + //template may be inside <!-- ... --> or not in case of ajax loaded templates + if (tmpl.get(0).firstChild.nodeType == 8) // 8==comment + var templateBody = tmpl.get(0).firstChild.nodeValue; // this is inside the comment + else + var templateBody = tmpl.html(); // this is the whole template + + if (!templateBody.match(/##\w+##/)) { // is Resig' style? e.g. (#=id#) or (# ...some javascript code 'obj' is the alias for the object #) + var strFunc = + "var p=[],print=function(){p.push.apply(p,arguments);};" + + "with(obj){p.push('" + + templateBody.replace(/[\r\t\n]/g, " ") + .replace(/'(?=[^#]*#\))/g, "\t") + .split("'").join("\\'") + .split("\t").join("'") + .replace(/\(#=(.+?)#\)/g, "',$1,'") + .split("(#").join("');") + .split("#)").join("p.push('") + + "');}return p.join('');"; + + try { + $.JST._templates[type] = new Function("obj", strFunc); + } catch (e) { + console.error("JST error: "+type, e,strFunc); + } + + } else { //plain template e.g. ##id## + try { + $.JST._templates[type] = templateBody; + } catch (e) { + console.error("JST error: "+type, e,templateBody); + } + } + + tmpl.remove(); + + }); + }); + }, + + createFromTemplate: function(jsonData, template, transformToPrintable) { + var templates = $.JST._templates; + + var jsData=new Object(); + if (transformToPrintable){ + for (var prop in jsonData){ + var value = jsonData[prop]; + if (typeof(value) == "string") + value = (value + "").replace(/\n/g, "<br>"); + jsData[prop]=value; + } + } else { + jsData=jsonData; + } + + + function fillStripData(strip, data) { + for (var prop in data) { + var value = data[prop]; + + strip = strip.replace(new RegExp("##" + prop + "##", "gi"), value); + } + // then clean the remaining ##xxx## + strip = strip.replace(new RegExp("##\\w+##", "gi"), ""); + return strip; + } + + var stripString = ""; + if (typeof(template) == "undefined") { + alert("Template is required"); + stripString = "<div>Template is required</div>"; + + } else if (typeof(templates[template]) == "function") { // resig template + try { + stripString = templates[template](jsData);// create a jquery object in memory + } catch (e) { + console.error("JST error: "+template,e.message); + stripString = "<div> ERROR: "+template+"<br>" + e.message + "</div>"; + } + + } else { + stripString = templates[template]; // recover strip template + if (!stripString || stripString.trim() == "") { + console.error("No template found for type '" + template + "'"); + return $("<div>"); + + } else { + stripString = fillStripData(stripString, jsData); //replace placeholders with data + } + } + + var ret = $(stripString);// create a jquery object in memory + ret.attr("__template", template); // set __template attribute + + //decorate the strip + var dec = $.JST._decorators[template]; + if (typeof (dec) == "function") + dec(ret, jsData); + + return ret; + }, + + + existsTemplate: function(template) { + return $.JST._templates[template]; + }, + + //decorate function is like function(domElement,jsonData){...} + loadDecorator:function(template, decorator) { + $.JST._decorators[template] = decorator; + }, + + getDecorator:function(template) { + return $.JST._decorators[template]; + }, + + decorateTemplate:function(element) { + var dec = $.JST._decorators[element.attr("__template")]; + if (typeof (dec) == "function") + dec(editor); + }, + + // asynchronous + ajaxLoadAsynchTemplates: function(templateUrl, callback) { + + $.get(templateUrl, function(data) { + + var div = $("<div>"); + div.html(data); + + $.JST.loadTemplates(div); + + if (typeof(callback == "function")) + callback(); + },"html"); + }, + + ajaxLoadTemplates: function(templateUrl) { + $.ajax({ + async:false, + url: templateUrl, + dataType: "html", + success: function(data) { + var div = $("<div>"); + div.html(data); + $.JST.loadTemplates(div); + } + }); + + } + + +}; Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/date.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/date.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/date.js 2013-06-20 14:35:02 UTC (rev 344) @@ -0,0 +1,584 @@ +/** + * Copyright (c)2005-2009 Matt Kruse (javascripttoolbox.com) + * + * Dual licensed under the MIT and GPL licenses. + * This basically means you can use this code however you want for + * free, but don't claim to have written it yourself! + * Donations always accepted: http://www.JavascriptToolbox.com/donate/ + * + * Please do not link to the .js files on javascripttoolbox.com from + * your site. Copy the files locally to your server instead. + * + */ +/* +Date functions + +These functions are used to parse, format, and manipulate Date objects. +See documentation and examples at http://www.JavascriptToolbox.com/lib/date/ + +*/ +Date.$VERSION = 1.02; + +// Utility function to append a 0 to single-digit numbers +Date.LZ = function(x) {return(x<0||x>9?"":"0")+x}; +// Full month names. Change this for local month names +Date.monthNames = new Array('January','February','March','April','May','June','July','August','September','October','November','December'); +// Month abbreviations. Change this for local month names +Date.monthAbbreviations = new Array('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'); +// Full day names. Change this for local month names +Date.dayNames = new Array('Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'); +// Day abbreviations. Change this for local month names +Date.dayAbbreviations = new Array('Sun','Mon','Tue','Wed','Thu','Fri','Sat'); +// Used for parsing ambiguous dates like 1/2/2000 - default to preferring 'American' format meaning Jan 2. +// Set to false to prefer 'European' format meaning Feb 1 +Date.preferAmericanFormat = true; + +// Set to 0=SUn for American 1=Mon for european +Date.firstDayOfWeek = 0; + +//default +Date.defaultFormat="dd/MM/yyyy"; + +// If the getFullYear() method is not defined, create it +if (!Date.prototype.getFullYear) { + Date.prototype.getFullYear = function() { var yy=this.getYear(); return (yy<1900?yy+1900:yy); } ; +} + +// Parse a string and convert it to a Date object. +// If no format is passed, try a list of common formats. +// If string cannot be parsed, return null. +// Avoids regular expressions to be more portable. +Date.parseString = function(val, format) { + // If no format is specified, try a few common formats + if (typeof(format)=="undefined" || format==null || format=="") { + var generalFormats=new Array(Date.defaultFormat,'y-M-d','MMM d, y','MMM d,y','y-MMM-d','d-MMM-y','MMM d','MMM-d','d-MMM'); + var monthFirst=new Array('M/d/y','M-d-y','M.d.y','M/d','M-d'); + var dateFirst =new Array('d/M/y','d-M-y','d.M.y','d/M','d-M'); + var checkList=new Array(generalFormats,Date.preferAmericanFormat?monthFirst:dateFirst,Date.preferAmericanFormat?dateFirst:monthFirst); + for (var i=0; i<checkList.length; i++) { + var l=checkList[i]; + for (var j=0; j<l.length; j++) { + var d=Date.parseString(val,l[j]); + if (d!=null) { + return d; + } + } + } + return null; + }; + + this.isInteger = function(val) { + for (var i=0; i < val.length; i++) { + if ("1234567890".indexOf(val.charAt(i))==-1) { + return false; + } + } + return true; + }; + this.getInt = function(str,i,minlength,maxlength) { + for (var x=maxlength; x>=minlength; x--) { + var token=str.substring(i,i+x); + if (token.length < minlength) { + return null; + } + if (this.isInteger(token)) { + return token; + } + } + return null; + }; + + + this.decodeShortcut=function(str){ + var dateUpper = str.trim().toUpperCase(); + var ret=new Date(); + ret.clearTime(); + + if (["NOW","N"].indexOf(dateUpper)>=0) { + ret= new Date(); + + } else if (["TODAY","T"].indexOf(dateUpper)>=0) { + //do nothing + + } else if (["YESTERDAY","Y"].indexOf(dateUpper)>=0) { + ret.setDate(ret.getDate()-1); + + } else if (["TOMORROW","TO"].indexOf(dateUpper)>=0) { + ret.setDate(ret.getDate()+1); + + } else if (["W", "TW", "WEEK", "THISWEEK", "WEEKSTART", "THISWEEKSTART"].indexOf(dateUpper)>=0) { + ret.setFirstDayOfThisWeek(); + + } else if (["LW", "LASTWEEK", "LASTWEEKSTART"].indexOf(dateUpper)>=0) { + ret.setFirstDayOfThisWeek(); + ret.setDate(ret.getDate()-7); + + } else if (["NW", "NEXTWEEK", "NEXTWEEKSTART"].indexOf(dateUpper)>=0) { + ret.setFirstDayOfThisWeek(); + ret.setDate(ret.getDate()+7); + + } else if (["M", "TM", "MONTH", "THISMONTH", "MONTHSTART", "THISMONTHSTART"].indexOf(dateUpper)>=0) { + ret.setDate(1); + + } else if (["LM", "LASTMONTH", "LASTMONTHSTART"].indexOf(dateUpper)>=0) { + ret.setDate(1); + ret.setMonth(ret.getMonth()-1); + + } else if (["NM", "NEXTMONTH", "NEXTMONTHSTART"].indexOf(dateUpper)>=0) { + ret.setDate(1); + ret.setMonth(ret.getMonth()+1); + + } else if (["Q", "TQ", "QUARTER", "THISQUARTER", "QUARTERSTART", "THISQUARTERSTART"].indexOf(dateUpper)>=0) { + ret.setDate(1); + ret.setMonth(Math.floor((ret.getMonth()) / 3) * 3); + + } else if (["LQ", "LASTQUARTER", "LASTQUARTERSTART"].indexOf(dateUpper)>=0) { + ret.setDate(1); + ret.setMonth(Math.floor((ret.getMonth()) / 3) * 3-3); + + } else if (["NQ", "NEXTQUARTER", "NEXTQUARTERSTART"].indexOf(dateUpper)>=0) { + ret.setDate(1); + ret.setMonth(Math.floor((ret.getMonth()) / 3) * 3+3); + + + } else if (/^-?[0-9]+[DWMY]$/.test(dateUpper)) { + var lastOne = dateUpper.substr(dateUpper.length - 1); + var val = parseInt(dateUpper.substr(0, dateUpper.length - 1)); + if (lastOne=="W") + ret.setDate(ret.getDate()+val*7 ); + else if (lastOne=="M") + ret.setMonth(ret.getMonth()+val ); + else if (lastOne=="Y") + ret.setYear(ret.getYear()+val ); + } else { + ret=undefined; + } + + return ret; + }; + + var ret=this.decodeShortcut(val); + if (ret) + return ret; + + val=val+""; + format=format+""; + var i_val=0; + var i_format=0; + var c=""; + var token=""; + var token2=""; + var x,y; + var year=new Date().getFullYear(); + var month=1; + var date=1; + var hh=0; + var mm=0; + var ss=0; + var ampm=""; + while (i_format < format.length) { + // Get next token from format string + c=format.charAt(i_format); + token=""; + while ((format.charAt(i_format)==c) && (i_format < format.length)) { + token += format.charAt(i_format++); + } + // Extract contents of value based on format token + if (token=="yyyy" || token=="yy" || token=="y") { + if (token=="yyyy") { + x=4;y=4; + } + if (token=="yy") { + x=2;y=2; + } + if (token=="y") { + x=2;y=4; + } + year=this.getInt(val,i_val,x,y); + if (year==null) { + return null; + } + i_val += year.length; + if (year.length==2) { + if (year > 70) { + year=1900+(year-0); + } + else { + year=2000+(year-0); + } + } + +// } else if (token=="MMM" || token=="NNN"){ + } else if (token=="MMM" || token=="MMMM"){ + month=0; + var names = (token=="MMMM"?(Date.monthNames.concat(Date.monthAbbreviations)):Date.monthAbbreviations); + for (var i=0; i<names.length; i++) { + var month_name=names[i]; + if (val.substring(i_val,i_val+month_name.length).toLowerCase()==month_name.toLowerCase()) { + month=(i%12)+1; + i_val += month_name.length; + break; + } + } + if ((month < 1)||(month>12)){ + return null; + } + } + else if (token=="E"||token=="EE"||token=="EEE"||token=="EEEE"){ + var names = (token=="EEEE"?Date.dayNames:Date.dayAbbreviations); + for (var i=0; i<names.length; i++) { + var day_name=names[i]; + if (val.substring(i_val,i_val+day_name.length).toLowerCase()==day_name.toLowerCase()) { + i_val += day_name.length; + break; + } + } + } + else if (token=="MM"||token=="M") { + month=this.getInt(val,i_val,token.length,2); + if(month==null||(month<1)||(month>12)){ + return null; + } + i_val+=month.length; + } + else if (token=="dd"||token=="d") { + date=this.getInt(val,i_val,token.length,2); + if(date==null||(date<1)||(date>31)){ + return null; + } + i_val+=date.length; + } + else if (token=="hh"||token=="h") { + hh=this.getInt(val,i_val,token.length,2); + if(hh==null||(hh<1)||(hh>12)){ + return null; + } + i_val+=hh.length; + } + else if (token=="HH"||token=="H") { + hh=this.getInt(val,i_val,token.length,2); + if(hh==null||(hh<0)||(hh>23)){ + return null; + } + i_val+=hh.length; + } + else if (token=="KK"||token=="K") { + hh=this.getInt(val,i_val,token.length,2); + if(hh==null||(hh<0)||(hh>11)){ + return null; + } + i_val+=hh.length; + hh++; + } + else if (token=="kk"||token=="k") { + hh=this.getInt(val,i_val,token.length,2); + if(hh==null||(hh<1)||(hh>24)){ + return null; + } + i_val+=hh.length; + hh--; + } + else if (token=="mm"||token=="m") { + mm=this.getInt(val,i_val,token.length,2); + if(mm==null||(mm<0)||(mm>59)){ + return null; + } + i_val+=mm.length; + } + else if (token=="ss"||token=="s") { + ss=this.getInt(val,i_val,token.length,2); + if(ss==null||(ss<0)||(ss>59)){ + return null; + } + i_val+=ss.length; + } + else if (token=="a") { + if (val.substring(i_val,i_val+2).toLowerCase()=="am") { + ampm="AM"; + } + else if (val.substring(i_val,i_val+2).toLowerCase()=="pm") { + ampm="PM"; + } + else { + return null; + } + i_val+=2; + } + else { + if (val.substring(i_val,i_val+token.length)!=token) { + return null; + } + else { + i_val+=token.length; + } + } + } + // If there are any trailing characters left in the value, it doesn't match + if (i_val != val.length) { + return null; + } + // Is date valid for month? + if (month==2) { + // Check for leap year + if ( ( (year%4==0)&&(year%100 != 0) ) || (year%400==0) ) { // leap year + if (date > 29){ + return null; + } + } + else { + if (date > 28) { + return null; + } + } + } + if ((month==4)||(month==6)||(month==9)||(month==11)) { + if (date > 30) { + return null; + } + } + // Correct hours value + if (hh<12 && ampm=="PM") { + hh=hh-0+12; + } + else if (hh>11 && ampm=="AM") { + hh-=12; + } + return new Date(year,month-1,date,hh,mm,ss); +}; + +// Check if a date string is valid +Date.isValid = function(val,format) { + return (Date.parseString(val,format) != null); +}; + +// Check if a date object is before another date object +Date.prototype.isBefore = function(date2) { + if (date2==null) { + return false; + } + return (this.getTime()<date2.getTime()); +}; + +// Check if a date object is after another date object +Date.prototype.isAfter = function(date2) { + if (date2==null) { + return false; + } + return (this.getTime()>date2.getTime()); +}; + +// Check if two date objects have equal dates and times +Date.prototype.equals = function(date2) { + if (date2==null) { + return false; + } + return (this.getTime()==date2.getTime()); +}; + +// Check if two date objects have equal dates, disregarding times +Date.prototype.equalsIgnoreTime = function(date2) { + if (date2==null) { + return false; + } + var d1 = new Date(this.getTime()).clearTime(); + var d2 = new Date(date2.getTime()).clearTime(); + return (d1.getTime()==d2.getTime()); +}; + +// Format a date into a string using a given format string +Date.prototype.format = function(format) { + if (!format) + format=Date.defaultFormat; + format=format+""; + var result=""; + var i_format=0; + var c=""; + var token=""; + var y=this.getFullYear()+""; + var M=this.getMonth()+1; + var d=this.getDate(); + var E=this.getDay(); + var H=this.getHours(); + var m=this.getMinutes(); + var s=this.getSeconds(); + // Convert real date parts into formatted versions + var value=new Object(); + if (y.length < 4) { + y=""+(+y+1900); + } + value["y"]=""+y; + value["yyyy"]=y; + value["yy"]=y.substring(2,4); + value["M"]=M; + value["MM"]=Date.LZ(M); + value["MMM"]=Date.monthAbbreviations[M-1]; + value["MMMM"]=Date.monthNames[M-1]; + value["d"]=d; + value["dd"]=Date.LZ(d); + value["E"]=Date.dayAbbreviations[E]; + value["EE"]=Date.dayAbbreviations[E]; + value["EEE"]=Date.dayAbbreviations[E]; + value["EEEE"]=Date.dayNames[E]; + value["H"]=H; + value["HH"]=Date.LZ(H); + if (H==0){ + value["h"]=12; + } + else if (H>12){ + value["h"]=H-12; + } + else { + value["h"]=H; + } + value["hh"]=Date.LZ(value["h"]); + value["K"]=value["h"]-1; + value["k"]=value["H"]+1; + value["KK"]=Date.LZ(value["K"]); + value["kk"]=Date.LZ(value["k"]); + if (H > 11) { + value["a"]="PM"; + } + else { + value["a"]="AM"; + } + value["m"]=m; + value["mm"]=Date.LZ(m); + value["s"]=s; + value["ss"]=Date.LZ(s); + while (i_format < format.length) { + c=format.charAt(i_format); + token=""; + while ((format.charAt(i_format)==c) && (i_format < format.length)) { + token += format.charAt(i_format++); + } + if (typeof(value[token])!="undefined") { + result=result + value[token]; + } + else { + result=result + token; + } + } + return result; +}; + +// Get the full name of the day for a date +Date.prototype.getDayName = function() { + return Date.dayNames[this.getDay()]; +}; + +// Get the abbreviation of the day for a date +Date.prototype.getDayAbbreviation = function() { + return Date.dayAbbreviations[this.getDay()]; +}; + +// Get the full name of the month for a date +Date.prototype.getMonthName = function() { + return Date.monthNames[this.getMonth()]; +}; + +// Get the abbreviation of the month for a date +Date.prototype.getMonthAbbreviation = function() { + return Date.monthAbbreviations[this.getMonth()]; +}; + +// Clear all time information in a date object +Date.prototype.clearTime = function() { + this.setHours(0); + this.setMinutes(0); + this.setSeconds(0); + this.setMilliseconds(0); + return this; +}; + +// Add an amount of time to a date. Negative numbers can be passed to subtract time. +Date.prototype.add = function(interval, number) { + if (typeof(interval)=="undefined" || interval==null || typeof(number)=="undefined" || number==null) { + return this; + } + number = +number; + if (interval=='y') { // year + this.setFullYear(this.getFullYear()+number); + } + else if (interval=='M') { // Month + this.setMonth(this.getMonth()+number); + } + else if (interval=='d') { // Day + this.setDate(this.getDate()+number); + } + else if (interval=='w') { // Weekday + var step = (number>0)?1:-1; + while (number!=0) { + this.add('d',step); + while(this.getDay()==0 || this.getDay()==6) { + this.add('d',step); + } + number -= step; + } + } + else if (interval=='h') { // Hour + this.setHours(this.getHours() + number); + } + else if (interval=='m') { // Minute + this.setMinutes(this.getMinutes() + number); + } + else if (interval=='s') { // Second + this.setSeconds(this.getSeconds() + number); + } + return this; + +}; + +Date.prototype.toInt = function () { + return this.getFullYear()*10000+(this.getMonth()+1)*100+this.getDate(); +}; + +Date.fromInt=function (dateInt){ + var year = parseInt(dateInt/10000); + var month = parseInt((dateInt-year*10000)/100); + var day = parseInt(dateInt-year*10000-month*100); + return new Date(year,month-1,day,12,00,00); +}; + + +Date.prototype.isHoliday=function(){ + return isHoliday(this); +}; + +Date.prototype.isToday=function(){ + return this.toInt()==new Date().toInt(); +}; + + +Date.prototype.incrementDateByWorkingDays=function (days) { + //console.debug("incrementDateByWorkingDays start ",d,days) + var q = Math.abs(days); + while (q > 0) { + this.setDate(this.getDate() + (days > 0 ? 1 : -1)); + if (!this.isHoliday()) + q--; + } + return this; +}; + + +//todo questo può essere poco performante in caso di distanze grandi +Date.prototype.distanceInWorkingDays= function (toDate){ + var pos = new Date(this.getTime()); + pos.setHours(23, 59, 59, 999); + var days = 0; + var nd=new Date(toDate.getTime()); + nd.setHours(23, 59, 59, 999); + var end=nd.getTime(); + while (pos.getTime() <= end) { + days = days + (isHoliday(pos) ? 0 : 1); + pos.setDate(pos.getDate() + 1); + } + return days; +}; + + +Date.prototype.setFirstDayOfThisWeek= function (firstDayOfWeek){ + if (!firstDayOfWeek) + firstDayOfWeek=Date.firstDayOfWeek; + this.setDate(this.getDate() - this.getDay() +firstDayOfWeek - (this.getDay()==0 && firstDayOfWeek!=0 ?7:0)); +} Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/dateField/images/next.png =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/dateField/images/next.png ___________________________________________________________________ Added: svn:mime-type + image/png Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/dateField/images/prev.png =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/dateField/images/prev.png ___________________________________________________________________ Added: svn:mime-type + image/png Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/dateField/jquery.dateField.css =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/dateField/jquery.dateField.css (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/dateField/jquery.dateField.css 2013-06-20 14:35:02 UTC (rev 344) @@ -0,0 +1,88 @@ +.calBox { + background-color: #91B4B7; + padding: 2px; + -moz-border-radius: 4px; + text-align: center; + color: white; + width:200px; + position:absolute; +} + +.calElement,.calDayHeader { + margin: 1px; + display: inline-block; + overflow:hidden; +} + +.calDayHeader { + background-color: #617777 +} + +.calNavBar { + padding: 5px 5px 2px 5px; + margin-bottom: 5px; +} + +.calDay { +} + +.calDay .calElement, .calDay .calDayHeader { + font-size: 11px; + border: 1px dotted #617777; +} + +.calElement.prev, +.calElement.next { + width: 16px; + height: 16px; + border: none; +} +.calElement.prev { + background: transparent url(images/prev.png) no-repeat 10px 0; + float:left; +} + +.calElement.next { + background: transparent url(images/next.png) no-repeat; + float:right; +} + +.calElement.next:hover, .calElement.prev:hover { + background-color: transparent; + border:none; + cursor: pointer; + opacity: 0.6 +} + +.calDay .calElement .dayNumber { + font-size: 20px; +} + +.calDay.calFullMonth .dayNumber { + font-size: 14px; +} +.calDay .calOutOfScope{ + color: #B8D4D6; +} + +.calElement.selected { + background-color: #404040; + border: 1px solid #404040; +} + +.calElement:hover { + background-color: #404040; + border: 1px solid #404040; + cursor: pointer; +} + +.calElement.today { + background-color: #009E94; +} + +.shortCuts span{ + border: 1px solid #999999; + height:14px; + padding-left:2px; + padding-right:2px; +} \ No newline at end of file Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/dateField/jquery.dateField.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/dateField/jquery.dateField.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/dateField/jquery.dateField.js 2013-06-20 14:35:02 UTC (rev 344) @@ -0,0 +1,212 @@ +/* + Copyright (c) 2009 Open Lab + Written by Roberto Bicchierai http://roberto.open-lab.com + 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. +*/ + +jQuery.fn.dateField = function(options) { + + //check if the input field is passed correctly + if (!options.inputField){ + console.error("You must supply an input field"); + return false; + } + + // -------------------------- start default option values -------------------------- + + if (typeof(options.firstDayOfWeek) == "undefined") + options.firstDayOfWeek=Date.firstDayOfWeek; + + if (typeof(options.useWheel) == "undefined") + options.useWheel=true; + + if (typeof(options.dateFormat) == "undefined") + options.dateFormat=Date.defaultFormat; + // -------------------------- end default option values -------------------------- + + + // ------------------ start + if(options.inputField.is("[readonly]") || options.inputField.is("[disabled]")) + return; + + var calendar = {currentDate: new Date()}; + calendar.options = options; + + //build the calendar on the first element in the set of matched elements. + var theOpener = this.eq(0); + var theDiv=$("<div>").addClass("calBox"); + + + //create calendar elements elements + var divNavBar = $("<div>").addClass("calNavBar"); + var divDays = $("<div>").addClass("calDay"); + + divDays.addClass("calFullMonth"); + theDiv.append(divNavBar).append(divDays); + + if (options.isSearchField){ + var divShortcuts=$("<div>").addClass("shortCuts").html("<span title='last quarter'>LQ</span> <span title='last month'>LM</span> <span title='this month'>M</span> <span title='last week'>LW</span> <span title='this week'>W</span> <span title='yesterday'>Y</span> <span title='today'>T</span><span title='tomorrow'>TM</span> <span title='next week'>NW</span> <span title='next month'>NM</span> <span title='this quarter'>Q</span> <span title='next quarter'>NQ</span>"); + divShortcuts.click(function(ev){ + var el=$(ev.target); + if(el.is("span")){ + if (!options.isSearchField) + options.inputField.val(Date.parseString(el.text().trim(),options.dateFormat).format(options.dateFormat)); + else + options.inputField.val(el.text().trim()); + theDiv.remove(); + } + }); + theDiv.append(divShortcuts); + } + + + $("body").append(theDiv); + nearBestPosition(theOpener,theDiv); + theDiv.bringToFront(); + + + //register for click outside. Delayed to avoid it run immediately + $("body").oneTime(100, "regclibodcal", function() { + $("body").bind("click.dateField", function() { + $(this).unbind("click.dateField"); + theDiv.remove(); + }); + }); + + + calendar.drawCalendar = function(date) { + calendar.currentDate = date; + + var fillNavBar = function(date) { + var t = new Date(date.getTime()); + divNavBar.empty(); + + t.setMonth(t.getMonth()-1); + var spanPrev = $("<span>").addClass("calElement noCallback prev").attr("millis", t.getTime()); + t.setMonth(t.getMonth()+1); + var spanMonth = $("<span>").html(t.format("MMMM yyyy")); + t.setMonth(t.getMonth()+1); + var spanNext = $("<span>").addClass("calElement noCallback next").attr("millis", t.getTime()); + + divNavBar.append(spanPrev).append(spanMonth).append(spanNext); + }; + + var fillDaysFullMonth = function(date) { + divDays.empty(); + var t = new Date();//today + var w = parseInt((theDiv.width()-4-(4*7))/7)+"px"; + // draw day headers + var d = new Date(date); + d.setFirstDayOfThisWeek(options.firstDayOfWeek); + for (var i = 0; i < 7; i++) { + var span = $("<span>").addClass("calDayHeader").attr("day", d.getDay()); + span.css("width",w); + span.html(Date.dayAbbreviations[d.getDay()]); + + //call the dayHeaderRenderer + if (typeof(options.dayHeaderRenderer) == "function") + options.dayHeaderRenderer(span,d.getDay()); + + divDays.append(span); + d.setDate(d.getDate()+1); + } + + //draw cells + d = new Date(date); + d.setDate(1); // set day to start of month + d.setFirstDayOfThisWeek(options.firstDayOfWeek);//go to first day of week + + var i=0; + + while ((d.getMonth()<=date.getMonth() && d.getFullYear()<=date.getFullYear()) || d.getFullYear()<date.getFullYear() || (i%7!=0)) { + var span = $("<span>").addClass("calElement day").attr("millis", d.getTime()); + + span.html("<span class=dayNumber>" + d.getDate() + "</span>").css("width",w); + if (d.getYear() == t.getYear() && d.getMonth() == t.getMonth() && d.getDate() == t.getDate()) + span.addClass("today"); + if (d.getYear() == date.getYear() && d.getMonth() == date.getMonth() && d.getDate() == date.getDate()) + span.addClass("selected"); + + if(d.getMonth()!=date.getMonth()) + span.addClass("calOutOfScope"); + + //call the dayRenderer + if (typeof(options.dayRenderer) == "function") + options.dayRenderer(span,d); + + divDays.append(span); + d.setDate(d.getDate()+1); + i++; + } + + }; + + fillNavBar(date); + fillDaysFullMonth(date); + }; + + + theDiv.click(function(ev) { + var el = $(ev.target).closest(".calElement"); + if (el.size() > 0) { + var date = new Date(parseInt(el.attr("millis"))); + if (el.hasClass("day")) { + theDiv.remove(); + if (!el.is(".noCallback")) { + options.inputField.val(date.format(options.dateFormat)).attr("millis", date.getTime()).focus(); + if (typeof(options.callback) == "function") + options.callback(date); + } + } else { + calendar.drawCalendar(date); + } + } + ev.stopPropagation(); + }); + + + //if mousewheel + if ($.event.special.mousewheel && options.useWheel) { + divDays.mousewheel(function(event, delta) { + var d = new Date(calendar.currentDate.getTime()); + d.setMonth(d.getMonth() + delta); + calendar.drawCalendar(d); + return false; + }); + } + + + // start calendar to the date in the input + var dateStr=options.inputField.val(); + + + if (!dateStr || !Date.isValid(dateStr,options.dateFormat)){ + calendar.drawCalendar(new Date()); + } else { + var date = Date.parseString(dateStr,options.dateFormat); + //set date string formatted + if (!options.isSearchField) + options.inputField.val(date.format(options.dateFormat)).attr("millis",date.getTime()); + + calendar.drawCalendar(date); + } + + return calendar; +}; \ No newline at end of file Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/i18nJs.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/i18nJs.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/i18nJs.js 2013-06-20 14:35:02 UTC (rev 344) @@ -0,0 +1,140 @@ + + + + function dateToRelative(localTime){ + var diff=new Date().getTime()-localTime; + var ret=""; + + var min=60000; + var hour=3600000; + var day=86400000; + var wee=604800000; + var mon=2629800000; + var yea=31557600000; + + if (diff<-yea*2) + ret ="in ## years".replace("##",(-diff/yea).toFixed(0)); + + else if (diff<-mon*9) + ret ="in ## months".replace("##",(-diff/mon).toFixed(0)); + + else if (diff<-wee*5) + ret ="in ## weeks".replace("##",(-diff/wee).toFixed(0)); + + else if (diff<-day*2) + ret ="in ## days".replace("##",(-diff/day).toFixed(0)); + + else if (diff<-hour) + ret ="in ## hours".replace("##",(-diff/hour).toFixed(0)); + + else if (diff<-min*35) + ret ="in about one hour"; + + else if (diff<-min*25) + ret ="in about half hour"; + + else if (diff<-min*10) + ret ="in some minutes"; + + else if (diff<-min*2) + ret ="in few minutes"; + + else if (diff<=min) + ret ="just now"; + + else if (diff<=min*5) + ret ="few minutes ago"; + + else if (diff<=min*15) + ret ="some minutes ago"; + + else if (diff<=min*35) + ret ="about half hour ago"; + + else if (diff<=min*75) + ret ="about an hour ago"; + + else if (diff<=hour*5) + ret ="few hours ago"; + + else if (diff<=hour*24) + ret ="## hours ago".replace("##",(diff/hour).toFixed(0)); + + else if (diff<=day*7) + ret ="## days ago".replace("##",(diff/day).toFixed(0)); + + else if (diff<=wee*5) + ret ="## weeks ago".replace("##",(diff/wee).toFixed(0)); + + else if (diff<=mon*12) + ret ="## months ago".replace("##",(diff/mon).toFixed(0)); + + else + ret ="## years ago".replace("##",(diff/yea).toFixed(0)); + + return ret; + } + + //override date format i18n + + Date.monthNames = ["January","February","March","April","May","June","July","August","September","October","November","December"]; + // Month abbreviations. Change this for local month names + Date.monthAbbreviations = ["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"]; + // Full day names. Change this for local month names + Date.dayNames =["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"]; + // Day abbreviations. Change this for local month names + Date.dayAbbreviations = ["Sun","Mon","Tue","Wed","Thu","Fri","Sat"]; + // Used for parsing ambiguous dates like 1/2/2000 - default to preferring 'American' format meaning Jan 2. + // Set to false to prefer 'European' format meaning Feb 1 + Date.preferAmericanFormat = false; + + Date.firstDayOfWeek =1; + Date.defaultFormat = "dd/MM/yyyy"; + + + Number.decimalSeparator = "."; + Number.groupingSeparator = ","; + Number.minusSign = "-"; + Number.currencyFormat = "##0.00"; + + + + var millisInWorkingDay =36000000; + var workingDaysPerWeek =5; + + function isHoliday(date) { + var friIsHoly =false; + var satIsHoly =true; + var sunIsHoly =true; + + pad = function (val) { + val = "0" + val; + return val.substr(val.length - 2); + }; + + var holidays = "#01_01#04_25#08_15#11_01#12_25#12_26#06_02#12_08#05_01#2010_04_05#2010_10_19#2010_05_15#2011_04_04#"; + + var ymd = "#" + date.getFullYear() + "_" + pad(date.getMonth() + 1) + "_" + pad(date.getDate()) + "#"; + var md = "#" + pad(date.getMonth() + 1) + "_" + pad(date.getDate()) + "#"; + var day = date.getDay(); + + return (day == 5 && friIsHoly) || (day == 6 && satIsHoly) || (day == 0 && sunIsHoly) || holidays.indexOf(ymd) > -1 || holidays.indexOf(md) > -1; + } + + + + var i18n = { + FORM_IS_CHANGED:"You have some unsaved data on the page!", + YES:"yes", + NO:"no", + FLD_CONFIRM_DELETE:"confirm the deletion?", + INVALID_DATA:"The data inserted are invalid for the field format.", + ERROR_ON_FIELD:"Error on field", + CLOSE_ALL_CONTAINERS:"close all?", + + + + DO_YOU_CONFIRM:"Do you confirm?" + }; + + \ No newline at end of file Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/jquery.livequery.min.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/jquery.livequery.min.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/jquery.livequery.min.js 2013-06-20 14:35:02 UTC (rev 344) @@ -0,0 +1,11 @@ +/* Copyright (c) 2007 Brandon Aaron (brandon.aaron@gmail.com || http://brandonaaron.net) + * Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) + * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. + * + * Version: 1.0.2 + * Requires jQuery 1.1.3+ + * Docs: http://docs.jquery.com/Plugins/livequery + */ +(function($){$.extend($.fn,{livequery:function(type,fn,fn2){var self=this,q;if($.isFunction(type))fn2=fn,fn=type,type=undefined;$.each($.livequery.queries,function(i,query){if(self.selector==query.selector&&self.context==query.context&&type==query.type&&(!fn||fn.$lqguid==query.fn.$lqguid)&&(!fn2||fn2.$lqguid==query.fn2.$lqguid))return(q=query)&&false;});q=q||new $.livequery(this.selector,this.context,type,fn,fn2);q.stopped=false;$.livequery.run(q.id);return this;},expire:function(type,fn,fn2){var self=this;if($.isFunction(type))fn2=fn,fn=type,type=undefined;$.each($.livequery.queries,function(i,query){if(self.selector==query.selector&&self.context==query.context&&(!type||type==query.type)&&(!fn||fn.$lqguid==query.fn.$lqguid)&&(!fn2||fn2.$lqguid==query.fn2.$lqguid)&&!this.stopped)$.livequery.stop(query.id);});return this;}});$.livequery=function(selector,context,type,fn,fn2){this.selector=selector;this.context=context||document;this.type=type;this.fn=fn;this.fn2=fn2;this.elements=[];this.stopped=false;this.id=$.livequery.queries.push(this)-1;fn.$lqguid=fn.$lqguid||$.livequery.guid++;if(fn2)fn2.$lqguid=fn2.$lqguid||$.livequery.guid++;return this;};$.livequery.prototype={stop:function(){var query=this;if(this.type)this.elements.unbind(this.type,this.fn);else if(this.fn2)this.elements.each(function(i,el){query.fn2.apply(el);});this.elements=[];this.stopped=true;},run:function(){if(this.stopped)return;var query=this;var oEls=this.elements,els=$(this.selector,this.context),nEls=els.not(oEls);this.elements=els;if(this.type){nEls.bind(this.type,this.fn);if(oEls.length>0)$.each(oEls,function(i,el){if($.inArray(el,els)<0)$.event.remove(el,query.type,query.fn);});}else{nEls.each(function(){query.fn.apply(this);});if(this.fn2&&oEls.length>0)$.each(oEls,function(i,el){if($.inArray(el,els)<0)query.fn2.apply(el);});}}};$.extend($.livequery,{guid:0,queries:[],queue:[],running:false,timeout:null,checkQueue:function(){if($.livequery.running&&$.livequery.queue.length){var length=$.livequery.queue.length;while(length--)$.livequery.queries[$.livequery.queue.shift()].run();}},pause:function(){$.livequery.running=false;},play:function(){$.livequery.running=true;$.livequery.run();},registerPlugin:function(){$.each(arguments,function(i,n){if(!$.fn[n])return;var old=$.fn[n];$.fn[n]=function(){var r=old.apply(this,arguments);$.livequery.run();return r;}});},run:function(id){if(id!=undefined){if($.inArray(id,$.livequery.queue)<0)$.livequery.queue.push(id);}else +$.each($.livequery.queries,function(id){if($.inArray(id,$.livequery.queue)<0)$.livequery.queue.push(id);});if($.livequery.timeout)clearTimeout($.livequery.timeout);$.livequery.timeout=setTimeout($.livequery.checkQueue,20);},stop:function(id){if(id!=undefined)$.livequery.queries[id].stop();else +$.each($.livequery.queries,function(id){$.livequery.queries[id].stop();});}});$.livequery.registerPlugin('append','prepend','after','before','wrap','attr','removeAttr','addClass','removeClass','toggleClass','empty','remove');$(function(){$.livequery.play();});var init=$.prototype.init;$.prototype.init=function(a,c){var r=init.apply(this,arguments);if(a&&a.selector)r.context=a.context,r.selector=a.selector;if(typeof a=='string')r.context=c||document,r.selector=a;return r;};$.prototype.init.prototype=$.prototype;})(jQuery); \ No newline at end of file Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/jquery.timers.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/jquery.timers.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/jquery.timers.js 2013-06-20 14:35:02 UTC (rev 344) @@ -0,0 +1,142 @@ +jQuery.fn.extend({ + everyTime: function(interval, label, fn, times, belay) { + return this.each(function() { + jQuery.timer.add(this, interval, label, fn, times, belay); + }); + }, + oneTime: function(interval, label, fn) { + return this.each(function() { + jQuery.timer.add(this, interval, label, fn, 1); + }); + }, + stopTime: function(label, fn) { + return this.each(function() { + jQuery.timer.remove(this, label, fn); + }); + } +}); + +jQuery.extend({ + timer: { + guid: 1, + global: {}, + regex: /^([0-9]+)\s*(.*s)?$/, + powers: { + // Yeah this is major overkill... + 'ms': 1, + 'cs': 10, + 'ds': 100, + 's': 1000, + 'das': 10000, + 'hs': 100000, + 'ks': 1000000 + }, + timeParse: function(value) { + if (value == undefined || value == null) + return null; + var result = this.regex.exec(jQuery.trim(value.toString())); + if (result[2]) { + var num = parseInt(result[1], 10); + var mult = this.powers[result[2]] || 1; + return num * mult; + } else { + return value; + } + }, + add: function(element, interval, label, fn, times, belay) { + var counter = 0; + + if (jQuery.isFunction(label)) { + if (!times) + times = fn; + fn = label; + label = interval; + } + + interval = jQuery.timer.timeParse(interval); + + if (typeof interval != 'number' || isNaN(interval) || interval <= 0) + return; + + if (times && times.constructor != Number) { + belay = !!times; + times = 0; + } + + times = times || 0; + belay = belay || false; + + if (!element.$timers) + element.$timers = {}; + + if (!element.$timers[label]) + element.$timers[label] = {}; + + fn.$timerID = fn.$timerID || this.guid++; + + var handler = function() { + if (belay && this.inProgress) + return; + this.inProgress = true; + if ((++counter > times && times !== 0) || fn.call(element, counter) === false) + jQuery.timer.remove(element, label, fn); + this.inProgress = false; + }; + + handler.$timerID = fn.$timerID; + + if (!element.$timers[label][fn.$timerID]) + element.$timers[label][fn.$timerID] = window.setInterval(handler,interval); + + if ( !this.global[label] ) + this.global[label] = []; + this.global[label].push( element ); + + }, + remove: function(element, label, fn) { + var timers = element.$timers, ret; + + if ( timers ) { + + if (!label) { + for ( label in timers ) + this.remove(element, label, fn); + } else if ( timers[label] ) { + if ( fn ) { + if ( fn.$timerID ) { + window.clearInterval(timers[label][fn.$timerID]); + delete timers[label][fn.$timerID]; + } + } else { + for ( var fn in timers[label] ) { + window.clearInterval(timers[label][fn]); + delete timers[label][fn]; + } + } + + for ( ret in timers[label] ) break; + if ( !ret ) { + ret = null; + delete timers[label]; + } + } + + for ( ret in timers ) break; + if ( !ret ) + element.$timers = null; + } + } + } +}); + +if (jQuery.browser.msie) + jQuery(window).one("unload", function() { + var global = jQuery.timer.global; + for ( var label in global ) { + var els = global[label], i = els.length; + while ( --i ) + jQuery.timer.remove(els[i], label); + } + }); + + Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/platform.js =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/platform.js (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/libs/platform.js 2013-06-20 14:35:02 UTC (rev 344) @@ -0,0 +1,954 @@ + +if(!window.console) { + window.console = new function() { + this.log = function(str) {/*alert(str)*/}; + this.debug = function(str) {/*alert(str)*/}; + this.error = function(str) {/*alert(str)*/}; + }; +} +if(!window.console.debug || !window.console.error|| !window.console.log ) { + window.console = new function() { + this.log = function(str) {/*alert(str)*/}; + this.debug = function(str) {/*alert(str)*/}; + this.error = function(str) {/*alert(str)*/}; + }; +} + + +//----------------------------------positioning----------------------------------------------- +$.fn.bringToFront=function(selector){ + var zi=10; + var elements = selector ? $(selector) : $("*"); + elements.each(function() { + if($(this).css("position")!="static"){ + var cur = parseInt($(this).css('zIndex')); + zi = cur > zi ? parseInt($(this).css('zIndex')) : zi; + } + }); + + return $(this).css('zIndex',zi+=10); +}; + +function nearBestPosition(whereId, theObjId, centerOnEl) { + var el=whereId; + var target=theObjId; + + if (typeof whereId != "object"){ el = $("#"+whereId); } + if (typeof theObjId != "object"){target = $("#"+theObjId);} + + if (el) { + target.css("visibility","hidden"); + var hasContainment = false; + + target.parents().each(function(){ + if($(this).css("position")=="static") + return; + hasContainment = true; + + }); + + var trueX = hasContainment ? el.position().left : el.offset().left; + var trueY = hasContainment ? el.position().top : el.offset().top; + var h = el.outerHeight(); + var elHeight = parseFloat(h); + + if (centerOnEl){ + var elWidth = parseFloat(el.outerWidth()); + var targetWidth = parseFloat(target.outerWidth()); + trueX+=(elWidth-targetWidth)/2; + } + + trueY += parseFloat(elHeight); + + var left = trueX; + var top = trueY; + var barHeight = ($.browser.msie) ? 45 : 35; + var barWidth = ($.browser.msie) ? 20 : 0; + + if (trueX && trueY) { + target.css("left", left); + target.css("top", top); + } + + if (target.offset().left >= ($(window).width() - target.outerWidth())) { + left = ($(window).width() - target.outerWidth() - barWidth )+ "px"; + target.css("left", left); + } + + if (target.offset().left < 0) { + left = 0+ "px"; + target.css("left", left); + } + + if ((target.offset().top + target.outerHeight() >= (($(window).height() - barHeight))) && (target.outerHeight() < $(window).height())) { + target.css("margin-top",(-(el.outerHeight() + target.outerHeight())) + "px"); + } + + target.css("visibility","visible"); + } +} + + +String.prototype.trim = function () { + return this.replace(/^\s*(\S*(\s+\S+)*)\s*$/, "$1"); +}; + +String.prototype.startsWith = function(t, i) { + if (!i) { + return (t == this.substring(0, t.length)); + } else { + return (t.toLowerCase()== this.substring(0, t.length).toLowerCase()); + } +}; + +String.prototype.endsWith = function(t, i) { + if (!i) { + return (t== this.substring(this.length - t.length)); + } else { + return (t.toLowerCase() == this.substring(this.length -t.length).toLowerCase()); + } +}; + +// leaves only char from A to Z, numbers, _ -> valid ID +String.prototype.asId = function () { + return this.replace(/[^a-zA-Z0-9_]+/g, ''); +}; + +String.prototype.replaceAll= function(from, to){ + return this.replace(new RegExp(RegExp.quote(from), 'g'),to); +}; + + +if (!Array.prototype.indexOf) { + Array.prototype.indexOf = function (searchElement, fromIndex) { + if (this == null) { + throw new TypeError(); + } + var t = Object(this); + var len = t.length >>> 0; + if (len === 0) { + return -1; + } + var n = 0; + if (arguments.length > 0) { + n = Number(arguments[1]); + if (n != n) { // shortcut for verifying if it's NaN + n = 0; + } else if (n != 0 && n != Infinity && n != -Infinity) { + n = (n > 0 || -1) * Math.floor(Math.abs(n)); + } + } + if (n >= len) { + return -1; + } + var k = n >= 0 ? n : Math.max(len - Math.abs(n), 0); + for (; k < len; k++) { + if (k in t && t[k] === searchElement) { + return k; + } + } + return -1; + }; +} + + +Object.size = function(obj) { + var size = 0, key; + for (key in obj) { + if (obj.hasOwnProperty(key)) size++; + } + return size; +}; + + +// transform string values to printable: \n in <br> +function transformToPrintable(data){ + for (var prop in data) { + var value = data[prop]; + if (typeof(value)=="string") + data[prop]=(value + "").replace(/\n/g, "<br>"); + } + return data; +} + + +/* Types Function */ + +function isValidURL(url){ + var RegExp = /^(([\w]+:)?\/\/)?(([\d\w]|%[a-fA-f\d]{2,2})+(:([\d\w]|%[a-fA-f\d]{2,2})+)?@)?([\d\w][-\d\w]{0,253}[\d\w]\.)+[\w]{2,4}(:[\d]+)?(\/([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)*(\?(&?([-+_~.\d\w]|%[a-fA-f\d]{2,2})=?)*)?(#([-+_~.\d\w]|%[a-fA-f\d]{2,2})*)?$/; + return RegExp.test(url); +} + +function isValidEmail(email){ + var RegExp = /^((([a-z]|[0-9]|!|#|$|%|&|'|\*|\+|\-|\/|=|\?|\^|_|`|\{|\||\}|~)+(\.([a-z]|[0-9]|!|#|$|%|&|'|\*|\+|\-|\/|=|\?|\^|_|`|\{|\||\}|~)+)*)@((((([a-z]|[0-9])([a-z]|[0-9]|\-){0,61}([a-z]|[0-9])\.))*([a-z]|[0-9])([a-z]|[0-9]|\-){0,61}([a-z]|[0-9])\.)[\w]{2,4}|(((([0-9]){1,3}\.){3}([0-9]){1,3}))|(\[((([0-9]){1,3}\.){3}([0-9]){1,3})\])))$/; + return RegExp.test(email); +} + +function isValidInteger(n) { + reg = new RegExp("^[-+]{0,1}[0-9]*$"); + return reg.test(n); +} + +function isValidDouble(n) { + var sep = Number.decimalSeparator; + reg = new RegExp("^[-+]{0,1}[0-9]*[" + sep + "]{0,1}[0-9]*$"); + return reg.test(n); +} + +function isValidTime(n) { + return !isNaN(millisFromHourMinute(n)); +} + +function isValidDurationDays(n) { + return !isNaN(daysFromString(n)); +} + +function isValidDurationMillis(n) { + return !isNaN(millisFromString(n)); +} + +function isValidDurationMillis(n) { + return !isNaN(millisFromString(n)); +} + + +/* + supports almost all Java currency format e.g.: ###,##0.00EUR €#,###.00 #,###.00€ -$#,###.00 $-#,###.00 + */ +function isValidCurrency(numStr){ + //first try to convert format in a regex + var regex=""; + var format=Number.currencyFormat+""; + + var minusFound=false; + var numFound=false; + var currencyString=""; + var numberRegex="[0-9\\"+Number.groupingSeparator+"]+[\\"+Number.decimalSeparator+"]?[0-9]*"; + + for (var i=0; i<format.length; i++){ + var ch= format.charAt(i); + + if (ch=="." || ch=="," || ch=="0"){ + //skip it + if(currencyString!=""){ + regex=regex+"(?:"+RegExp.quote(currencyString)+")?"; + currencyString=""; + } + + } else if (ch=="#") { + if(currencyString!=""){ + regex=regex+"(?:"+RegExp.quote(currencyString)+")?"; + currencyString=""; + } + + if (!numFound){ + numFound=true; + regex=regex+numberRegex; + } + + } else if (ch=="-"){ + if(currencyString!=""){ + regex=regex+"(?:"+RegExp.quote(currencyString)+")?"; + currencyString=""; + } + if (!minusFound){ + minusFound=true; + regex=regex+ "[-]?"; + } + + } else { + currencyString=currencyString+ch; + } + } + if (!minusFound) + regex="[-]?"+regex; + + if(currencyString!="") + regex=regex+"(?:"+RegExp.quote(currencyString)+")?"; + + regex="^"+regex+"$"; + + var rg=new RegExp(regex); + return rg.test(numStr); +} + +function getCurrencyValue(numStr){ + if (!isValidCurrency(numStr)) + return NaN; + + return parseFloat(numStr.replaceAll(Number.groupingSeparator,"").replaceAll(Number.decimalSeparator,".").replace(/[^0123456789.]/,"")); +} + + +function formatCurrency(numberString) { + return formatNumber(numberString, Number.currencyFormat); +} + + +function formatNumber(numberString, format) { + if (!format) + format="##0.00"; + + var dec = Number.decimalSeparator; + var group = Number.groupingSeparator; + var neg = Number.minusSign; + + var round = true; + + var validFormat = "0#-,."; + + // strip all the invalid characters at the beginning and the end + // of the format, and we'll stick them back on at the end + // make a special case for the negative sign "-" though, so + // we can have formats like -$23.32 + var prefix = ""; + var negativeInFront = false; + for (var i = 0; i < format.length; i++) { + if (validFormat.indexOf(format.charAt(i)) == -1) { + prefix = prefix + format.charAt(i); + } else { + if (i == 0 && format.charAt(i) == '-') { + negativeInFront = true; + } else { + break; + } + } + } + var suffix = ""; + for (var i = format.length - 1; i >= 0; i--) { + if (validFormat.indexOf(format.charAt(i)) == -1) + suffix = format.charAt(i) + suffix; + else + break; + } + + format = format.substring(prefix.length); + format = format.substring(0, format.length - suffix.length); + + // now we need to convert it into a number + //while (numberString.indexOf(group) > -1) + // numberString = numberString.replace(group, ''); + //var number = new Number(numberString.replace(dec, ".").replace(neg, "-")); + var number = new Number(numberString); + + + var forcedToZero = false; + if (isNaN(number)) { + number = 0; + forcedToZero = true; + } + + // special case for percentages + if (suffix == "%") + number = number * 100; + + var returnString = ""; + if (format.indexOf(".") > -1) { + var decimalPortion = dec; + var decimalFormat = format.substring(format.lastIndexOf(".") + 1); + + // round or truncate number as needed + if (round) + number = new Number(number.toFixed(decimalFormat.length)); + else { + var numStr = number.toString(); + numStr = numStr.substring(0, numStr.lastIndexOf('.') + decimalFormat.length + 1); + number = new Number(numStr); + } + + var decimalValue = number % 1; + var decimalString = new String(decimalValue.toFixed(decimalFormat.length)); + decimalString = decimalString.substring(decimalString.lastIndexOf(".") + 1); + + for (var i = 0; i < decimalFormat.length; i++) { + if (decimalFormat.charAt(i) == '#' && decimalString.charAt(i) != '0') { + decimalPortion += decimalString.charAt(i); + } else if (decimalFormat.charAt(i) == '#' && decimalString.charAt(i) == '0') { + var notParsed = decimalString.substring(i); + if (notParsed.match('[1-9]')) { + decimalPortion += decimalString.charAt(i); + } else{ + break; + } + } else if (decimalFormat.charAt(i) == "0"){ + decimalPortion += decimalString.charAt(i); + } + } + returnString += decimalPortion; + } else{ + number = Math.round(number); + } + var ones = Math.floor(number); + if (number < 0) + ones = Math.ceil(number); + + var onesFormat = ""; + if (format.indexOf(".") == -1) + onesFormat = format; + else + onesFormat = format.substring(0, format.indexOf(".")); + + var onePortion = ""; + if (!(ones == 0 && onesFormat.substr(onesFormat.length - 1) == '#') || forcedToZero) { + // find how many digits are in the group + var oneText = new String(Math.abs(ones)); + var groupLength = 9999; + if (onesFormat.lastIndexOf(",") != -1) + groupLength = onesFormat.length - onesFormat.lastIndexOf(",") - 1; + var groupCount = 0; + for (var i = oneText.length - 1; i > -1; i--) { + onePortion = oneText.charAt(i) + onePortion; + groupCount++; + if (groupCount == groupLength && i != 0) { + onePortion = group + onePortion; + groupCount = 0; + } + } + + // account for any pre-data padding + if (onesFormat.length > onePortion.length) { + var padStart = onesFormat.indexOf('0'); + if (padStart != -1) { + var padLen = onesFormat.length - padStart; + + // pad to left with 0's or group char + var pos = onesFormat.length - onePortion.length - 1; + while (onePortion.length < padLen) { + var padChar = onesFormat.charAt(pos); + // replace with real group char if needed + if (padChar == ',') + padChar = group; + onePortion = padChar + onePortion; + pos--; + } + } + } + } + + if (!onePortion && onesFormat.indexOf('0', onesFormat.length - 1) !== -1) + onePortion = '0'; + + returnString = onePortion + returnString; + + // handle special case where negative is in front of the invalid characters + if (number < 0 && negativeInFront && prefix.length > 0) + prefix = neg + prefix; + else if (number < 0) + returnString = neg + returnString; + + if (returnString.lastIndexOf(dec) == returnString.length - 1) { + returnString = returnString.substring(0, returnString.length - 1); + } + returnString = prefix + returnString + suffix; + return returnString; +} + + + + +RegExp.quote = function(str) { + return str.replace(/([.?*+^$[\]\\(){}-])/g, "\\$1"); +}; + +/* ----- millis format --------- */ +/** + * @param str - Striga da riempire + * @param len - Numero totale di caratteri, comprensivo degli "zeri" + * @param ch - Carattere usato per riempire + */ +function pad(str, len, ch){ + if ((str+"").length<len){ + return new Array(len-(''+str).length+1).join(ch) + str; + } else{ + return str + } +} + +function getMillisInHours(millis) { + if (!millis) + return ""; + var sgn=millis>=0?1:-1; + var hour = Math.floor(millis / 3600000); + return (sgn>0?"":"-")+pad(hour,2,"0"); +} +function getMillisInHoursMinutes(millis) { + if (typeof(millis)!="number" ) + return ""; + + var sgn=millis>=0?1:-1; + millis=Math.abs(millis); + var hour = Math.floor(millis / 3600000); + var min = Math.floor((millis % 3600000) / 60000); + return (sgn>0?"":"-")+pad(hour,1,"0") + ":" + pad(min,2,"0"); +} + +function getMillisInDaysHoursMinutes(millis) { + if (!millis) + return ""; + // millisInWorkingDay is set on partHeaderFooter + var sgn=millis>=0?1:-1; + millis=Math.abs(millis); + var days = Math.floor(millis / millisInWorkingDay); + var hour = Math.floor((millis % millisInWorkingDay) / 3600000); + var min = Math.floor((millis-days*millisInWorkingDay-hour*3600000) / 60000); + return (sgn>=0?"":"-")+(days > 0 ? days + " " : "") + pad(hour,1,"0") + ":" + pad(min,2,"0"); +} + +function millisFromHourMinute(stringHourMinutes) { //All this format are valid: "12:58" "13.75" "63635676000" (this is already in milliseconds) + var result = 0; + stringHourMinutes.replace(",","."); + var semiColSeparator = stringHourMinutes.indexOf(":"); + var dotSeparator = stringHourMinutes.indexOf("."); + + if (semiColSeparator < 0 && dotSeparator < 0 && stringHourMinutes.length > 5) { + return parseInt(stringHourMinutes, 10); //already in millis + } else { + + if (dotSeparator > -1) { + var d = parseFloat(stringHourMinutes); + result = d * 3600000; + } else { + var hour = 0; + var minute = 0; + if (semiColSeparator == -1) + hour = parseInt(stringHourMinutes, 10); + else { + hour = parseInt(stringHourMinutes.substring(0, semiColSeparator), 10); + minute = parseInt(stringHourMinutes.substring(semiColSeparator + 1), 10); + } + result = hour * 3600000 + minute * 60000; + } + if (typeof(result)!="number") + result=NaN; + return result; + } +} + + +/** + * @param string "3y 4d", "4D:08:10", "12M/3d", "2H4D", "3M4d,2h", "12:30", "11", "3", "1.5", "2m/3D", "12/3d", "1234" + * by default 2 means 2 hours 1.5 means 1:30 + * @param considerWorkingdays if true day lenght is from global.properties CompanyCalendar.MILLIS_IN_WORKING_DAY otherwise in 24 + * @return milliseconds. 0 if invalid string + */ +function millisFromString(string,considerWorkingdays) { + if (!string) + return 0; + + var regex = new RegExp("(\\d+[Yy])|(\\d+[M])|(\\d+[Ww])|(\\d+[Dd])|(\\d+[Hh])|(\\d+[m])|(\\d+[Ss])|(\\d+:\\d+)|(:\\d+)|(\\d*[\\.,]\\d+)|(\\d+)","g"); + + var matcher = regex.exec(string); + var totMillis=0; + + if (!matcher) + return NaN; + + while (matcher!=null) { + for (var i = 1; i < matcher.length; i++) { + var match = matcher[i]; + if (match) { + var number = 0; + try { + number = parseInt(match); + } catch (e) { + } + if (i == 1) { // years + totMillis = totMillis + number * (considerWorkingdays ? millisInWorkingDay * workingDaysPerWeek * 52 : 3600000 * 24 * 365); + } else if (i == 2) { // months + totMillis = totMillis + number * (considerWorkingdays ? millisInWorkingDay * workingDaysPerWeek * 4 : 3600000 * 24 * 30); + } else if (i == 3) { // weeks + totMillis = totMillis + number * (considerWorkingdays ? millisInWorkingDay * workingDaysPerWeek : 3600000 * 24 * 7); + } else if (i == 4) { // days + totMillis = totMillis + number * (considerWorkingdays ? millisInWorkingDay : 3600000 * 24); + } else if (i == 5) { // hours + totMillis = totMillis + number * 3600000; + } else if (i == 6) { // minutes + totMillis = totMillis + number * 60000; + } else if (i == 7) { // seconds + totMillis = totMillis + number * 1000; + } else if (i == 8) { // hour:minutes + totMillis = totMillis + millisFromHourMinute(match); + } else if (i == 9) { // :minutes + totMillis = totMillis + millisFromHourMinute(match); + } else if (i == 10) { // hour.minutes + totMillis = totMillis + millisFromHourMinute(match); + } else if (i == 11) { // hours + totMillis = totMillis + number * 3600000; + } + } + } + matcher=regex.exec(string); + } + + return totMillis; +} + +/** + * @param string "3y 4d", "4D:08:10", "12M/3d", "2H4D", "3M4d,2h", "12:30", "11", "3", "1.5", "2m/3D", "12/3d", "1234" + * by default 2 means 2 hours 1.5 means 1:30 + * @param considerWorkingdays if true day lenght is from global.properties CompanyCalendar.MILLIS_IN_WORKING_DAY otherwise in 24 + * @return milliseconds. 0 if invalid string + */ +function daysFromString(string,considerWorkingdays) { + if (!string) + return undefined; + + var regex = new RegExp("(\\d+[Yy])|(\\d+[Mm])|(\\d+[Ww])|(\\d+[Dd])|(\\d*[\\.,]\\d+)|(\\d+)","g"); + + var matcher = regex.exec(string); + var totDays=0; + + if (!matcher) + return NaN; + + while (matcher != null) { + for (var i = 1; i < matcher.length; i++) { + var match = matcher[i]; + if (match) { + var number = 0; + try { + number = parseInt(match); + } catch (e) { + } + if (i == 1) { // years + totDays = totDays + number * (considerWorkingdays ? workingDaysPerWeek * 52 : 365); + } else if (i == 2) { // months + totDays = totDays + number * (considerWorkingdays ? workingDaysPerWeek * 4 : 30); + } else if (i == 3) { // weeks + totDays = totDays + number * (considerWorkingdays ? workingDaysPerWeek : 7); + } else if (i == 4) { // days + totDays = totDays + number; + } else if (i == 5) { // days.minutes + totDays = totDays + number; + } else if (i == 6) { // days + totDays = totDays + number; + } + } + } + matcher=regex.exec(string); + } + + return totDays; +} + + + +/* Object Functions */ + +function stopBubble(e) { + if ($.browser.msie && event){ + event.cancelBubble = true; + event.returnValue = false; + + }else if (e){ + e.stopPropagation(); + e.preventDefault(); + } + return false; +} + +//validation functions - used by textfield and datefield +function validateField(ev) { + var el = $(this); + var rett=true; + el.clearErrorAlert(); + // check serverside only if not empty + var value = el.val(); + if (value) { + + var type = el.attr('entryType').toUpperCase(); + + if (type == "INTEGER") { + rett = isValidInteger(value); + } else if (type == "DOUBLE") { + rett = isValidDouble(value); + } else if (type == "PERCENTILE") { + rett = isValidDouble(value); + } else if (type == "URL") { + rett = isValidURL(value); + } else if (type == "EMAIL") { + rett = isValidEmail(value); + } else if (type == "DURATIONMILLIS") { + rett = isValidDurationMillis(value); + } else if (type == "DURATIONDAYS") { + rett = isValidDurationDays(value); + } else if (type == "DATE") { + rett = Date.isValid(value,el.attr("format")); + } else if (type == "TIME") { + rett = isValidTime(value); + } else if (type == "CURRENCY") { + rett = isValidCurrency(value); + } + + if (!rett) { + el.createErrorAlert(i18n.ERROR_ON_FIELD,i18n.INVALID_DATA); + } + } + return rett; +} + +jQuery.fn.clearErrorAlert= function(){ + this.each(function(){ + var el = $(this); + el.removeAttr("invalid").removeClass("formElementsError"); + $("#"+el.attr("id")+"error").remove(); + }); + return this; +}; + +jQuery.fn.createErrorAlert = function(errorCode, message) { + this.each(function() { + var el = $(this); + el.attr("invalid", "true").addClass("formElementsError"); + if ($("#" + el.attr("id") + "error").size() <= 0) { + var errMess = (errorCode?errorCode:"") + ": " + (message?message:""); + var err = "<img width='17' heigh='17' id=\"" + el.attr("id") + "error\" error='1'"; + err += " onclick=\"alert($(this).attr('title'))\" border='0' align='absmiddle'>"; + err=$(err); + err.attr("title",errMess).attr("src",skinImgPath+"alert.gif"); + el.after(err); + } + }); + return this; +}; + + +//errors =[{ceName:ceErrorCode},...] +function jsonErrorHandling(response){ + if (!response.ok){ + if (response.message) + alert("ERROR:\n"+ response.message); + for (var i in response.clientEntryErrors){ + var err=response.clientEntryErrors[i]; + $(":input[name="+err.name+"]").createErrorAlert(err.error); + } + } +} + + + +// ---------------------------------- oldvalues management +// update all values selected +jQuery.fn.updateOldValue= function(){ + this.each(function(){ + var el = $(this); + el.data("_oldvalue",el.val()); + }); + return this; +}; + +// return true if at least one element has changed +jQuery.fn.isValueChanged=function (){ + var ret=false; + this.each(function(){ + var el = $(this); + if (el.val()+"" != el.data("_oldvalue") + ""){ + //console.debug("io sono diverso "+el.attr("id")+ " :"+el.val()+" != "+el.data("_oldvalue")); + ret=true; + return false; + } + }); + return ret; +}; + +jQuery.fn.getOldValue=function(){ + return $(this).data("_oldvalue"); +}; + + + + +$.fn.unselectable=function(){ + this.each(function(){ + $(this).addClass("unselectable").attr("unselectable","on"); + }); + return $(this); +}; + +$.fn.clearUnselectable=function(){ + this.each(function(){ + $(this).removeClass("unselectable").removeAttr("unselectable"); + }); + return $(this); +}; + + +// ---------------------------------- PROFILING ------------------------------------------ +var __profiler = {}; +/** + * usage: instantiate a new Profiler("a name") at the beginning of the code you want to profile var p= new Profiler("testLoop") + * call p.stop() at the end of the code you want test. + * call Profiler.displayAll() or p.display() to see how many times the code has been executed and millisecond spent. + * call Profiler.resetAll() or p.reset() to restart profiler. + * @param name + */ +function Profiler(name) { + this.startTime = new Date().getTime(); + this.name = name; + + this.stop = function() { + if (!__profiler[this.name]) + __profiler[this.name] = {millis:0,count:0}; + __profiler[this.name].millis += new Date().getTime() - this.startTime; + __profiler[this.name].count++; + }; + this.display = function() { + console.debug(__profiler[this.name]); + }; + + this.reset = function() { + delete __profiler[this.name]; + }; +} + +Profiler.reset = function() { + __profiler = {}; +}; + +Profiler.displayAll = function() { + var ret = ""; + var totMillis = 0; + for (var key in __profiler) { + var p = __profiler[key]; + var extraspace=" ".substr(0,30-key.length); + ret += key + extraspace+ "\t millis:" + p.millis+"\t count:" + p.count + "\n"; + totMillis += p.millis; + } + console.debug(ret); +}; + + +$(document).ready(function() { + $(":input[oldValue]").livequery(function(){$(this).updateOldValue();}); + $('.validated').livequery('blur', validateField); +}); + +function openBlackPopup(url,width,height,onCloseCallBack,iframeId){ + if(!iframeId) + iframeId="bwinPopup"; + + if (!width) + width='900px'; + if (!height) + height='730px'; + + $("#__blackpopup__").remove(); + + var bg=$("<div>").attr("id","__blackpopup__"); + //bg.css({position:'fixed',top:0, left:0,width:'100%',height:'100%', backgroundImage:"url('"+contextPath+"/applications/teamwork/images/black_70.png')",textAlign:'center'}); + bg.css({position:'fixed',top:0, left:0,width:'100%',height:'100%',textAlign:'center'}); + + //add black only if not already in blackpupup + if(window.name!=iframeId) + bg.css({backgroundImage:"url('"+contextPath+"/applications/teamwork/images/black_70.png')"}); + + bg.append("<iframe id='"+iframeId+"' name='"+iframeId+"' frameborder='0'></iframe>"); + bg.bringToFront(); + + + //close call callback + bg.bind("close",function(){ + bg.slideUp(300,function(){ + bg.remove(); + if (typeof(onCloseCallBack)=="function") + onCloseCallBack(); + }); + }); + + //destroy do not call callback + bg.bind("destroy",function(){ + bg.remove(); + }); + + bg.find("iframe:first").attr("src",url).css({width:width, height:height,top:100,border:'8px solid #909090', backgroundColor:'#ffffff'}); + + var bdiv= $("<div>").css({width:width,position:"relative",height:"5px", textAlign:"right", margin:"auto" }); + bdiv.append("<img src=\"closeBig.png\" style='cursor:pointer;position:absolute;right:-40px;top:30px;'>"); + bdiv.find("img:first").click( function(){ + bg.trigger("close"); + + }); + + bg.prepend(bdiv); + $("body").append(bg); +} + + +//returns a jquery object where to write content +function createBlackPage(width,height,onCloseCallBack){ + if (!width) + width='900px'; + if (!height) + height='730px'; + + $("#__blackpopup__").remove(); + + var bg=$("<div>").attr("id","__blackpopup__"); + bg.css({position:'fixed',top:"0px",paddingTop:"50px", left:0,width:'100%',height:'100%', backgroundImage:"url('/applications/teamwork/images/black_70.png')"}); + bg.append("<div id='bwinPopupd' name='bwinPopupd'></div>"); + bg.bringToFront(); + + var ret=bg.find("#bwinPopupd"); + ret.css({width:width, height:height,top:10, "-moz-box-shadow":'1px 1px 6px #333333',overflow:'auto',"-webkit-box-shadow":'1px 1px 6px #333333', border:'8px solid #777', backgroundColor:"#fff", margin:"auto" }); + + var bdiv= $("<div>").css({width:width,position:"relative",height:"0px", textAlign:"right", margin:"auto" }); + var img=$("<img src='closeBig.png' style='cursor:pointer;position:absolute;right:-40px;top:5px;' title='close'>"); + bdiv.append(img); + img.click( function(){ + bg.trigger("close"); + }); + + bg.prepend(bdiv); + $("body").append(bg); + + //close call callback + bg.bind("close",function(){ + bg.slideUp(300,function(){ + bg.remove(); + if (typeof(onCloseCallBack)=="function") + onCloseCallBack(); + }); + }); + + //destroy do not call callback + bg.bind("destroy",function(){ + bg.remove(); + }); + return ret; +} + + +function getBlackPopup(){ + var ret=$("#__blackpopup__"); + if (typeof(top)!="undefined"){ + ret= window.parent.$("#__blackpopup__"); + } + return ret; +} + + +function closeBlackPopup(){ + getBlackPopup().trigger("close"); +} + + + +//------------------------------------------------ TEAMWORK SPECIFIC FUNCTIONS -------------------------------------------------------- +function openIssueEditorInBlack(issueId,command,params){ + if (!command) + command="ED"; + var editUrl=contextPath+"/applications/teamwork/issue/issueEditor.jsp?CM="+command+"&OBJID="+issueId; + if (params) + editUrl=editUrl+params; + openBlackPopup(editUrl,1020,$(window).height()-50, function(){$("#"+issueId).effect("highlight", { color: "yellow" }, 1500);}); +} + +function openBoardInBlack(boardId,command,params,callback){ + if (!command) + command="ED"; + var editUrl=contextPath+"/applications/teamwork/board/boardEditor.jsp?CM="+command+"&OBJID="+boardId; + if (params) + editUrl=editUrl+params; + openBlackPopup(editUrl,$(window).width()-100,$(window).height()-50,callback ); +} + Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/linkArrow.png =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/linkArrow.png ___________________________________________________________________ Added: svn:mime-type + image/png Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/milestone.png =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/milestone.png ___________________________________________________________________ Added: svn:mime-type + image/png Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/platform.css =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/platform.css (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/platform.css 2013-06-20 14:35:02 UTC (rev 344) @@ -0,0 +1,346 @@ +@font-face { + font-family: 'TeamworkRegular'; + src: url('teamwork-regular-webfont.eot'); + src: url('teamwork-regular-webfont.eot?#iefix') format('embedded-opentype'), + url('teamwork-regular-webfont.woff') format('woff'), + url('teamwork-regular-webfont.ttf') format('truetype'), + url('teamwork-regular-webfont.otf') format('opentype'), + url('teamwork-regular-webfont.svg#TeamworkRegular') format('svg'); + font-weight: normal; + font-style: normal; +} + + + +*{ + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + -o-box-sizing:border-box; + box-sizing: border-box; +} + + /* + ------------------------------------------------------- + body styles + ------------------------------------------------------- + */ +BODY, TBODY { + font-family: arial; + font-size: 14px; + margin: 0; + color: #000000; + text-decoration: none; +} + +BODY { + background-color: #91B4B7; +} + +a { + text-decoration: none; + font-weight: bold; + color: #16ABDD; +} + +a:hover, a.aHover { + color: #0C85AD; + text-decoration: underline; +} + +h1 { + font-size:32px; + line-height: 34px; + color:#91B4B7; + font-weight:normal; + margin:0 0 10px 0 +} + +h2 { + font-size:22px; + color:#373737; + font-weight:normal; + margin:0 0 10px 0 +} + +h3 { + text-decoration: none; + color: #000000; + font-size: 16px; + margin: 0; +} + +h4 { + font-size:16px; + padding:5px 0; + color:#617777; + margin:0 +} + +#savingMessage{ + background-color:#E3EDED; + display:none; + color:#617777; + font-weight:bolder; + position:fixed; + top:0; + left:50%; + width:200px; + text-align:center; + margin-left:-100px; + padding:5px 0; + z-index:1000000; + box-shadow:0 3px 2px rgba(0,0,0,0.4); + -moz-box-shadow:0 3px 2px rgba(0,0,0,0.4); + -webkit-box-shadow:0 3px 2px rgba(0,0,0,0.4); + -o-box-shadow:0 3px 2px rgba(0,0,0,0.4); +} + +.waiting{ + cursor:progress; +} + +/* + ------------------------------------------------------- + teamwork icon + ------------------------------------------------------- + */ + + +.teamworkIcon { + font-family: 'TeamworkRegular', arial, sans-serif; + color:#617777; + font-weight:normal; + font-size:120% +} + +.teamworkIcon.withLabel { + padding-right:5px; +} + + +.button:hover .teamworkIcon { + opacity: 0.8 +} + +.teamworkIcon.alert { + color:#B61E2D; +} + +.cvcColorSquare{ + display:inline-block; + text-align:left; + border:#fff 0px solid; + box-shadow:0px 0px 5px #999; + -moz-box-shadow:2px 2px 2px #999; + -webkit-box-shadow:0px 0px 5px #999; + -o-box-shadow:0px 0px 5px #999; + text-indent:10px; + border-radius:5px +} + +.cvcColorSquare:hover{ + opacity:0.8 +} + + + +.unselectable { + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -o-user-select: none; + user-select: none; +} + + +.ui-resizable { position: relative;} +.ui-resizable-handle { position: absolute;font-size: 0.1px;z-index: 99999; display: block; } +.ui-resizable-disabled .ui-resizable-handle, .ui-resizable-autohide .ui-resizable-handle { display: none; } +.ui-resizable-n { cursor: n-resize; height: 7px; width: 100%; top: -5px; left: 0; } +.ui-resizable-s { cursor: s-resize; height: 7px; width: 100%; bottom: -5px; left: 0; } +.ui-resizable-e { cursor: e-resize; width: 7px; right: -5px; top: 0; height: 100%; } +.ui-resizable-w { cursor: w-resize; width: 7px; left: -5px; top: 0; height: 100%; } +.ui-resizable-se { cursor: se-resize; width: 12px; height: 12px; right: 1px; bottom: 1px; } +.ui-resizable-sw { cursor: sw-resize; width: 9px; height: 9px; left: -5px; bottom: -5px; } +.ui-resizable-nw { cursor: nw-resize; width: 9px; height: 9px; left: -5px; top: -5px; } +.ui-resizable-ne { cursor: ne-resize; width: 9px; height: 9px; right: -5px; top: -5px;} + + + /* + ------------------------------------------------------- + table styles + ------------------------------------------------------- + */ + +.table { + width: 100%; +} + +TH, .tableHeader { + font-weight: normal; + color: #FFFFFF; + border: none; + background-color: #91B4B7; + padding:2px +} + +TH a { + color: #FFFFFF; +} + + + + /* + ------------------------------------------------------- + Buttons + ------------------------------------------------------- + */ + +.button { + display:inline-block; + font-size:110%; + font-family:Arial, sans-serif; + color:#fff; + cursor:pointer; + background-color:#373737; + -moz-box-shadow:2px 2px 2px #999; + -webkit-box-shadow:2px 2px 2px #999; + -o-box-shadow:2px 2px 2px #999; + box-shadow:2px 2px 2px #999; + -moz-border-radius:5px; + -webkit-border-radius:5px; + -o-border-radius:5px; + border-radius:5px; + border:none; + padding:7px 10px; + margin-bottom:10px; + margin-right:10px; + text-align: center +} + + +.button.first { + background-color:#FEA730; + font-weight: bold; +} +.button.first:hover { + background-color:#D98E2A; +} + + + +.button[disabled] { + cursor: default; + opacity:0.4 +} + + +.button:hover[disabled] { + background-color:#BABABA +} + +.button.textual, .button.buttonImg { + border: none; + background-color: transparent; + color:#68979B; + -moz-box-shadow:0 0 0 #999; + -webkit-box-shadow:0 0 0 #999; + -o-box-shadow:0 0 0 #999; + box-shadow:0 0 0 #999; + -moz-border-radius:0; + -webkit-border-radius:0; + -o-border-radius:0; + border-radius:0; + padding:0; + margin:0; + text-align: left +} + + + +.button.edit { + color:#009E94; + padding:0; + margin:0 +} + +.button.delete { + color:#B61E2D; + padding:0; + margin:0 +} + +.button:hover { + background-color: #4C4C4C; +} +.button.textual:hover, .button.buttonImg:hover { + background-color: transparent; +} + +span.separator { + display: inline-block; +} + +.button.add { + color:#009E94; +} + +.button.add .teamworkIcon { + color:#009E94; +} + + +form { + margin: 0; + padding: 0; +} + +select { + border:1px solid #91B4B7; + padding: 4px; + font-size: 16px; + font-family: Arial, Helvetica, sans-serif; +} + +.formElements { + background-color: white; + padding: 4px; + font-size: 16px; + border:1px solid #91B4B7; + -moz-border-radius:3px; + -webkit-border-radius:3px; + -o-border-radius:3px; + border-radius:3px; + font-family: Arial, Helvetica, sans-serif; +} + +.formElementsError { + border: 1px solid #ff0000; +} + +.formElementExclamation { + width: 15px; + height: 25px; + mmmargin-left: -20px; + mmmposition: absolute; + background: url("alert.gif") no-repeat; +} + + +span#FLD_LOGIN_NAMEerror, span#FLD_PWDerror { + margin-left: -23px; + margin-top: 2px; +} + + +input { + background-color: white; + padding: 4px; + font-size: 16px; + border:1px solid #91B4B7; + -moz-border-radius:3px; + -webkit-border-radius:3px; + -o-border-radius:3px; + border-radius:3px; + font-family: Arial, Helvetica, sans-serif; +} Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamwork-regular-webfont.eot =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamwork-regular-webfont.eot ___________________________________________________________________ Added: svn:mime-type + application/vnd.ms-fontobject Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamwork-regular-webfont.otf =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamwork-regular-webfont.otf ___________________________________________________________________ Added: svn:mime-type + application/vnd.ms-opentype Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamwork-regular-webfont.svg =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamwork-regular-webfont.svg ___________________________________________________________________ Added: svn:mime-type + image/svg+xml Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamwork-regular-webfont.ttf =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamwork-regular-webfont.ttf ___________________________________________________________________ Added: svn:mime-type + application/x-font-ttf Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamwork-regular-webfont.woff =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamwork-regular-webfont.woff ___________________________________________________________________ Added: svn:mime-type + application/octet-stream Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamworkFont.css =================================================================== --- trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamworkFont.css (rev 0) +++ trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/teamworkFont.css 2013-06-20 14:35:02 UTC (rev 344) @@ -0,0 +1,16 @@ + +@font-face { + font-family: 'TeamworkRegular'; + src: url('teamwork-regular-webfont.eot'); + src: url('teamwork-regular-webfont.eot?#iefix') format('embedded-opentype'), + url('teamwork-regular-webfont.woff') format('woff'), + url('teamwork-regular-webfont.ttf') format('truetype'), + url('teamwork-regular-webfont.otf') format('opentype'), + url('teamwork-regular-webfont.svg#TeamworkRegular') format('svg'); + font-weight: normal; + font-style: normal; + +} + + + Added: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/twGanttSmall.png =================================================================== (Binary files differ) Property changes on: trunk/chorem-webmotion/src/main/webapp/js/jQueryGantt/twGanttSmall.png ___________________________________________________________________ Added: svn:mime-type + image/png
participants (1)
-
meynier@users.chorem.org