├── .npmignore
├── docs
├── demo.gif
├── demo.png
├── home-bg.jpg
├── fixes
│ ├── planend.json
│ ├── two-weaksafter.html
│ ├── bug141.json
│ ├── bug318.json
│ ├── bug277.json
│ ├── general.html
│ ├── data-plan-color.json
│ ├── lang.html
│ └── data.json
├── main.css
├── main.js
├── project.xml
├── demobigdata.html
├── demo-plan-color.html
├── demo.html
├── index.js
└── DotNet.md
├── .gitignore
├── index.ts
├── .github
└── ISSUE_TEMPLATE
│ ├── custom.md
│ ├── feature_request.md
│ └── bug_report.md
├── e2e
├── tsconfig.e2e.json
├── app.po.ts
└── app.e2e-spec.ts
├── test
├── index.ts
└── index.html
├── tsconfig.json
├── .vscode
└── launch.json
├── .travis.yml
├── protractor.conf.js
├── package.json
├── LICENSE
├── tslint.json
├── src
├── draw_dependencies.ts
├── json.ts
├── jsgantt.ts
├── utils
│ ├── draw_utils.ts
│ └── date_utils.ts
├── draw_columns.ts
├── options.ts
├── xml.ts
└── events.ts
├── README.md
└── CONTRIBUTING.md
/.npmignore:
--------------------------------------------------------------------------------
1 | *.ts
2 |
--------------------------------------------------------------------------------
/docs/demo.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/HEAD/docs/demo.gif
--------------------------------------------------------------------------------
/docs/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/HEAD/docs/demo.png
--------------------------------------------------------------------------------
/docs/home-bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jsGanttImproved/jsgantt-improved/HEAD/docs/home-bg.jpg
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | *.log
3 | package-lock.json
4 | /jsgantt.js
5 | .idea
6 | dist/src
7 | dist/e2e
8 | dist/test
9 | dist/index.*
--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------
1 | import
2 | * as jsGantt
3 | from './src/jsgantt';
4 |
5 | declare var module: any;
6 | module.exports = jsGantt.JSGantt;
7 |
8 | export const JSGantt = jsGantt.JSGantt;
9 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/custom.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Custom issue template
3 | about: Describe this issue template's purpose here.
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/e2e",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": [
8 | "jasmine",
9 | "node"
10 | ]
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/test/index.ts:
--------------------------------------------------------------------------------
1 |
2 | import * as JSGantt from '../index';
3 | import { expect } from 'chai';
4 | import { browser, by, element } from 'protractor';
5 |
6 | const dv = browser.driver;
7 |
8 | describe('Browser test', () => {
9 | it('JSGantt exists', () => {
10 | expect(JSGantt).to.exist;
11 | });
12 |
13 | it('Driver exists', () => {
14 | expect(dv).to.exist;
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/docs/fixes/planend.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "pid": 1,
4 | "pStart": "2019-03-07",
5 | "pEnd": "2020-07-31",
6 | "pPlanEnd": "2022-03-22",
7 | "pClass": "ggroupblack",
8 | "pName": "step 1"
9 | },
10 | {
11 |
12 | "pid": 2,
13 | "pStart": "2022-03-23",
14 | "pEnd": "2023-12-14",
15 | "pPlanEnd": "2024-03-06",
16 | "pClass": "ggroupblack",
17 | "pName": "step 2"
18 | },
19 | {
20 | "pid": 3,
21 | "pStart": "2024-03-07",
22 | "pEnd": "2025-08-26",
23 | "pPlanEnd": "2026-08-12",
24 | "pClass": "ggroupblack",
25 | "pName": "step 3"
26 | }
27 | ]
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "outDir": "./dist",
5 | "baseUrl": "src",
6 | "sourceMap": true,
7 | "declaration": false,
8 | "moduleResolution": "node",
9 | "emitDecoratorMetadata": true,
10 | "experimentalDecorators": true,
11 | "target": "es5",
12 | "typeRoots": [
13 | "node_modules/@types"
14 | ],
15 | "lib": [
16 | "es2016",
17 | "dom"
18 | ],
19 | "paths": {
20 | "jsgantt-improved": [ "dist/jsgantt-improved" ]
21 | }
22 | },
23 | "exclude": [
24 | ".ng_build"
25 | ]
26 | }
27 |
--------------------------------------------------------------------------------
/e2e/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class NgPackagedPage {
4 | navigateTo() {
5 | return browser.get('/docs/demo.html');
6 | }
7 |
8 | getParagraphText() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 |
12 | getById(id) {
13 | return element(by.id(id)).getText();
14 | }
15 |
16 | getValueById(id) {
17 | return element(by.id(id)).getAttribute('value');
18 | }
19 |
20 | sendKeys(id, val) {
21 | element(by.id(id)).sendKeys(val);
22 | }
23 |
24 | submit() {
25 | element(by.id('submit')).click();
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": "ng e2e",
9 | "type": "node",
10 | "request": "launch",
11 | "program": "${workspaceFolder}/node_modules/protractor/bin/protractor",
12 | "protocol": "inspector",
13 | "args": ["${workspaceFolder}/protractor.conf.js"]
14 | }
15 | ]
16 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/e2e/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { NgPackagedPage } from './app.po';
2 |
3 | import 'mocha';
4 | import { element, by } from 'protractor';
5 |
6 | describe('ng-packaged App', () => {
7 | let page: NgPackagedPage;
8 |
9 | beforeEach(() => {
10 | page = new NgPackagedPage();
11 | return page.navigateTo();
12 | });
13 |
14 | afterEach(() => {
15 | page.navigateTo();
16 | });
17 |
18 | it('it should change language from pt to en', () => {
19 | element(by.css('.gtaskheading.gres')).getText()
20 | .then(t=>{
21 | expect(t).toEqual('Resource');
22 | element(by.cssContainingText('option', 'pt')).click();
23 | return element(by.css('.gtaskheading.gres')).getText()
24 | })
25 | .then(t=>{
26 | expect(t).toEqual('Responsável');
27 | });
28 | });
29 | });
30 |
31 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js:
3 | - "9"
4 | addons:
5 | chrome: stable
6 | services:
7 | - xvfb
8 | before_script:
9 | - "export DISPLAY=:99.0"
10 | # - "sh -e /etc/init.d/xvfb start"
11 | - sleep 3 # give xvfb some time to start
12 | - npm run dist
13 | - http-server & # start a Web server
14 | - sleep 3 # give Web server some time to bind to sockets, etc
15 |
16 | before_install:
17 | - npm install -g http-server browserify webdriver-manager
18 | - "/sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16"
19 | - google-chrome-stable --headless --disable-gpu --remote-debugging-port=9222 http://localhost &
20 |
21 | install:
22 | - npm install
23 | - ./node_modules/protractor/bin/webdriver-manager update --versions.chrome 2.35
24 |
--------------------------------------------------------------------------------
/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
3 |
4 | const { SpecReporter } = require('jasmine-spec-reporter');
5 |
6 | exports.config = {
7 | allScriptsTimeout: 11000,
8 | specs: [
9 | './e2e/**/*.e2e-spec.ts'
10 | ],
11 | capabilities: {
12 | 'browserName': 'chrome'
13 | },
14 | directConnect: true,
15 | baseUrl: 'http://localhost:8080/',
16 | framework: 'jasmine',
17 | jasmineNodeOpts: {
18 | showColors: true,
19 | defaultTimeoutInterval: 30000,
20 | print: function() {}
21 | },
22 | onPrepare() {
23 | browser.ignoreSynchronization = true;
24 | require('ts-node').register({
25 | project: 'e2e/tsconfig.e2e.json'
26 | });
27 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
28 | }
29 | };
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/docs/fixes/two-weaksafter.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/docs/fixes/bug141.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "pID": 1,
4 | "pName": "Define Chart API",
5 | "pStart": "",
6 | "pEnd": "",
7 | "pPlanStart": "",
8 | "pPlanEnd": "",
9 | "pClass": "ggroupblack",
10 | "pLink": "",
11 | "pMile": 0,
12 | "pRes": "Mario",
13 | "pComp": 0,
14 | "pGroup": 1,
15 | "pParent": 0,
16 | "pOpen": 1,
17 | "pDepend": "",
18 | "pCaption": "",
19 | "pNotes": "",
20 | "category": "",
21 | "sector": ""
22 | },
23 | {
24 | "pID": 11,
25 | "pName": "Chart Object",
26 | "pStart": "2018-06-20",
27 | "pEnd": "2018-07-20",
28 | "pClass": "gtaskpink",
29 | "pLink": "",
30 | "pMile": 0,
31 | "pRes": "Henrique",
32 | "pComp": 100,
33 | "pGroup": 0,
34 | "pParent": 1,
35 | "pOpen": 1,
36 | "pDepend": "",
37 | "pCaption": "",
38 | "pNotes": "",
39 | "category": "",
40 | "sector": ""
41 | },
42 | {
43 | "pID": 12,
44 | "pName": "Task Objects",
45 | "pStart": "2018-08-10",
46 | "pEnd": "2018-09-10",
47 | "pClass": "gtaskblue",
48 | "pLink": "",
49 | "pMile": 0,
50 | "pRes": "Henrique",
51 | "pComp": 40,
52 | "pGroup": 0,
53 | "pParent": 1,
54 | "pOpen": 1,
55 | "pDepend": "",
56 | "pCaption": "",
57 | "pNotes": ""
58 | }
59 | ]
--------------------------------------------------------------------------------
/docs/fixes/bug318.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "pID": 1,
4 | "pName": "Define Chart API",
5 | "pStart": "2018-02-25",
6 | "pEnd": "2018-03-17",
7 | "pPlanStart": "2018-04-01",
8 | "pPlanEnd": "2018-04-15 12:00",
9 | "pClass": "ggroupblack",
10 | "pLink": "",
11 | "pMile": 0,
12 | "pRes": "Mario",
13 | "pComp": 0,
14 | "pGroup": 1,
15 | "pParent": 0,
16 | "pOpen": 1,
17 | "pDepend": "",
18 | "pCaption": "",
19 | "pNotes": "",
20 | "category": "",
21 | "sector": ""
22 | },
23 | {
24 | "pID": 11,
25 | "pName": "Chart Object",
26 | "pStart": "2018-06-20",
27 | "pEnd": "2018-07-20",
28 | "pClass": "gtaskpink",
29 | "pLink": "",
30 | "pMile": 0,
31 | "pRes": "Henrique",
32 | "pComp": 100,
33 | "pGroup": 0,
34 | "pParent": 1,
35 | "pOpen": 1,
36 | "pDepend": "",
37 | "pCaption": "",
38 | "pNotes": "",
39 | "category": "",
40 | "sector": ""
41 | },
42 | {
43 | "pID": 12,
44 | "pName": "Task Objects",
45 | "pStart": "2018-08-10",
46 | "pEnd": "2018-09-10",
47 | "pClass": "gtaskblue",
48 | "pLink": "",
49 | "pMile": 0,
50 | "pRes": "Henrique",
51 | "pComp": 40,
52 | "pGroup": 0,
53 | "pParent": 1,
54 | "pOpen": 1,
55 | "pDepend": "",
56 | "pCaption": "",
57 | "pNotes": ""
58 | }
59 | ]
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "jsgantt-improved",
3 | "version": "2.8.10",
4 | "description": "jsgantt-improved",
5 | "main": "dist/index.js",
6 | "scripts": {
7 | "start": "http-server",
8 | "build": "tsc",
9 | "watch:build": "onchange 'src/**' '*.*' -- npm run build",
10 | "watch": "onchange 'src/**' '*.ts' -- npm run dist",
11 | "watch:test": "onchange 'src/**/*.ts' '*.ts' 'e2e/**/*.ts' -- npm run test",
12 | "test": "npm start & node node_modules/.bin/protractor protractor.conf.js",
13 | "test-unit": "mocha -r ts-node/register test/**.ts",
14 | "webdriver": "./node_modules/protractor/bin/webdriver-manager update",
15 | "browserify": "browserify dist/index.js --standalone JSGantt > dist/jsgantt.js",
16 | "dist": "npm run build && npm run browserify && cp src/jsgantt.css dist/ && echo 'DIST finished'",
17 | "publishnpm": "npm run dist && npm publish",
18 | "demo-full": "npm run dist && npm run start",
19 | "e2e-prepare": "npm i -g webdriver-manager && webdriver-manager update && ./node_modules/protractor/node_modules/webdriver-manager/bin/webdriver-manager update"
20 | },
21 | "repository": {
22 | "type": "git",
23 | "url": "git+https://github.com/jsGanttImproved/jsgantt-improved"
24 | },
25 | "author": "Mario Mol , Eduardo Rodrigues, Ricardo Cardoso",
26 | "license": "ISC",
27 | "bugs": {
28 | "url": "https://github.com/jsGanttImproved/jsgantt-improved/issues"
29 | },
30 | "homepage": "https://jsganttimproved.github.io/jsgantt-improved/docs/",
31 | "dependencies": {
32 | "@types/node": "^12.0.10",
33 | "webdriver-manager": "^13.0.0"
34 | },
35 | "devDependencies": {
36 | "@types/chai": "^4.1.5",
37 | "@types/jasmine": "^3.3.0",
38 | "chai": "^4.1.2",
39 | "http-server": "^0.11.1",
40 | "jasmine": "^3.3.0",
41 | "jasmine-core": "^3.3.0",
42 | "jasmine-spec-reporter": "^4.2.1",
43 | "mocha": "^5.2.0",
44 | "protractor": "^5.4.1",
45 | "selenium-webdriver": "^4.0.0-alpha.1",
46 | "ts-node": "^7.0.1",
47 | "typescript": "^3.0.3"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/docs/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | position: relative;
3 | }
4 |
5 | .product-name {
6 | font-family: Satisfy;
7 | }
8 |
9 | #my-navbar-nav {
10 | font-size: 16px;
11 | }
12 |
13 | #my-nav-brand {
14 | font-size: 24px;
15 | margin: 2px 40px 0 10px;
16 | color: #ffffff;
17 | }
18 |
19 | #home {
20 | background: #323232 url(home-bg.jpg) center 0 no-repeat;
21 | background-attachment: fixed;
22 | background-size: cover;
23 | min-height: 660px;
24 | color: #ffffff;
25 | }
26 | #home-title {
27 | font-size: 70px;
28 | margin-top: 180px;
29 | }
30 | #home-subtitle {
31 | font-size: 36px;
32 | margin-top: 40px;
33 | margin-bottom: 50px;
34 | }
35 | #learn-more {
36 | padding: 0;
37 | width: 50px;
38 | height: 50px;
39 | border-radius: 50%;
40 | font-size: 38px;
41 | margin-top: 60px;
42 | }
43 | @media screen and (max-width: 991px) {
44 | #learn-more {
45 | display: none;
46 | }
47 | }
48 |
49 | .section {
50 | margin: 0 15vw;
51 | padding: 5px 0 0;
52 | }
53 |
54 | h1 {
55 | font-family: Kelly Slab;
56 | font-size: 56px;
57 | margin: 50px 0 20px;
58 | }
59 |
60 | h2 {
61 | font-family: Kelly Slab;
62 | margin: 20px 0 10px;
63 | }
64 |
65 | h3 {
66 | font-family: Kelly Slab;
67 | }
68 | #embedded-Gantt, #external-Gantt {
69 | }
70 |
71 | .code-block {
72 | background-color: #222222;
73 | }
74 |
75 | .contact-card {
76 | max-width: 200px;
77 | margin: auto;
78 | }
79 |
80 | .contact-link {
81 | font-size: 28px;
82 | }
83 |
84 | .footer {
85 | margin-top: 10px;
86 | padding-top: 10px;
87 | border-top: solid #bbbbbb 1px;
88 | }
89 |
90 | #slide-card {
91 | background: transparent;
92 | border: none;
93 | margin: 180px 50px 50px;
94 | }
95 | #slide-dots {
96 | background: transparent;
97 | border: none;
98 | }
99 | .dot {
100 | cursor:pointer;
101 | height: 13px;
102 | width: 13px;
103 | margin: 0 2px;
104 | background-color: #ffffff;
105 | opacity: 0.3;
106 | border-radius: 50%;
107 | display: inline-block;
108 | -webkit-transition: all .5s; /* Safari */
109 | transition: all .5s;
110 | }
111 | .dot:hover {
112 | opacity: 1;
113 | }
114 | .dot.active {
115 | opacity: 1;
116 | }
117 | .slide {
118 | width: 100%;
119 | height: 150px;
120 | font-size: 20px;
121 | display: none;
122 | opacity: 0;
123 | }
124 | .slide-icon {
125 | font-size: 60px;
126 | }
127 |
--------------------------------------------------------------------------------
/docs/main.js:
--------------------------------------------------------------------------------
1 | $(document).ready(function() {
2 |
3 | // Scrollspy changes navbar active links
4 | $("body").scrollspy({target:"#my-navbar-nav", offset:50});
5 |
6 | // Smooth scroll
7 | $("a").click(function(event) {
8 | if (this.hash !== "") {
9 | event.preventDefault();
10 | var linkOffset = 0;
11 | if ($.inArray(this.hash,["#options","#xmlExport","#optionsLanguage","#setDayMajorDateDisplayFormat"]) != -1) {
12 | linkOffset = -25;
13 | }
14 | $("html, body").animate({
15 | scrollTop: $(this.hash).offset().top - $(".navbar").height() + linkOffset
16 | }, 600);
17 | }
18 | });
19 |
20 | // Demo buttons
21 | $("#embedded-Gantt").hide(0);
22 | $("#external-Gantt").hide(0);
23 |
24 | $(".btn-demo").click(function() {
25 | if ($(this).html().indexOf("Embedded Code") != -1) {
26 | if ($("#external-Gantt").is(":visible")) {
27 | $("#external-Gantt").animate({
28 | height: "toggle",
29 | opacity: "toggle"}, 300, function () {
30 | $("#embedded-Gantt").animate({
31 | height: "toggle",
32 | opacity: "toggle"}, 600
33 | );
34 | }
35 | );
36 | $(".btn-demo:nth-child(2)").removeClass("active");
37 | } else {
38 | $("#embedded-Gantt").animate({
39 | height: "toggle",
40 | opacity: "toggle"}, 600
41 | );
42 | }
43 | } else {
44 | if ($("#embedded-Gantt").is(":visible")) {
45 | $("#embedded-Gantt").animate({
46 | height: "toggle",
47 | opacity: "toggle"}, 300, function() {
48 | $("#external-Gantt").animate({
49 | height: "toggle",
50 | opacity: "toggle"}, 600
51 | );
52 | }
53 | );
54 | $(".btn-demo:nth-child(1)").removeClass("active");
55 | } else {
56 | $("#external-Gantt").animate({
57 | height: "toggle",
58 | opacity: "toggle"}, 600
59 | );
60 | }
61 | }
62 | });
63 |
64 | // Slideshow
65 | var slideIndex = 0;
66 | carousel();
67 |
68 | function carousel() {
69 | var i;
70 | var x = document.getElementsByClassName("slide");
71 | var d = document.getElementsByClassName("dot");
72 | for (i = 0; i < x.length; i++) {
73 | x[i].style.display = "none";
74 | }
75 | slideIndex++;
76 | if (slideIndex > x.length) {slideIndex = 1}
77 | x[slideIndex-1].style.display = "inline-block";
78 | $(".slide:nth-child(" + (slideIndex).toString() + ")").animate({
79 | opacity: 1
80 | }, 500);
81 | $(".dot").removeClass("active");
82 | $(".dot:nth-child(" + (slideIndex).toString() + ")").addClass("active");
83 | setTimeout(carousel, 2000); // Change image every 2 seconds
84 | }
85 | });
86 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | * Copyright (c) 2013-2018, Paul Geldart, Eduardo Rodrigues and Ricardo Cardoso.
2 | *
3 | * Redistribution and use in source and binary forms, with or without
4 | * modification, are permitted provided that the following conditions are met:
5 | * * Redistributions of source code must retain the above copyright
6 | * notice, this list of conditions and the following disclaimer.
7 | * * Redistributions in binary form must reproduce the above copyright
8 | * notice, this list of conditions and the following disclaimer in the
9 | * documentation and/or other materials provided with the distribution.
10 | * * Neither the name of Paul Geldart, Eduardo Rodrigues and Ricardo Cardoso nor the names of its contributors
11 | * may be used to endorse or promote products derived from this software
12 | * without specific prior written permission.
13 | *
14 | * THIS SOFTWARE IS PROVIDED BY PAUL GELDART, EDUARDO RODRIGUES AND RICARDO CARDOSO ''AS IS'' AND ANY EXPRESS OR
15 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 | * IN NO EVENT SHALL PAUL GELDART, EDUARDO RODRIGUES AND RICARDO CARDOSO BE LIABLE FOR ANY DIRECT,
18 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 |
25 | This project is based on jsGantt 1.2, the original project license follows:
26 |
27 | * Copyright (c) 2008, Shlomy Gantz/BlueBrick Inc.
28 | *
29 | * Redistribution and use in source and binary forms, with or without
30 | * modification, are permitted provided that the following conditions are met:
31 | * * Redistributions of source code must retain the above copyright
32 | * notice, this list of conditions and the following disclaimer.
33 | * * Redistributions in binary form must reproduce the above copyright
34 | * notice, this list of conditions and the following disclaimer in the
35 | * documentation and/or other materials provided with the distribution.
36 | * * Neither the name of Shlomy Gantz or BlueBrick Inc. nor the
37 | * names of its contributors may be used to endorse or promote products
38 | * derived from this software without specific prior written permission.
39 | *
40 | * THIS SOFTWARE IS PROVIDED BY SHLOMY GANTZ/BLUEBRICK INC. ''AS IS'' AND ANY
41 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
42 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
43 | * DISCLAIMED. IN NO EVENT SHALL SHLOMY GANTZ/BLUEBRICK INC. BE LIABLE FOR ANY
44 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
45 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
46 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
47 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
48 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
49 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
50 |
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "callable-types": true,
7 | "class-name": true,
8 | "comment-format": [
9 | true,
10 | "check-space"
11 | ],
12 | "curly": true,
13 | "eofline": true,
14 | "forin": true,
15 | "import-blacklist": [
16 | true,
17 | "rxjs"
18 | ],
19 | "import-spacing": true,
20 | "indent": [
21 | true,
22 | "spaces"
23 | ],
24 | "interface-over-type-literal": true,
25 | "label-position": true,
26 | "max-line-length": [
27 | true,
28 | 140
29 | ],
30 | "member-access": false,
31 | "member-ordering": [
32 | true,
33 | "static-before-instance",
34 | "variables-before-functions"
35 | ],
36 | "no-arg": true,
37 | "no-bitwise": true,
38 | "no-console": [
39 | true,
40 | "debug",
41 | "info",
42 | "time",
43 | "timeEnd",
44 | "trace"
45 | ],
46 | "no-construct": true,
47 | "no-debugger": true,
48 | "no-empty": false,
49 | "no-empty-interface": true,
50 | "no-eval": true,
51 | "no-inferrable-types": [
52 | true,
53 | "ignore-params"
54 | ],
55 | "no-shadowed-variable": true,
56 | "no-string-literal": false,
57 | "no-string-throw": true,
58 | "no-switch-case-fall-through": true,
59 | "no-trailing-whitespace": true,
60 | "no-unused-expression": true,
61 | "no-use-before-declare": true,
62 | "no-var-keyword": true,
63 | "object-literal-sort-keys": false,
64 | "one-line": [
65 | true,
66 | "check-open-brace",
67 | "check-catch",
68 | "check-else",
69 | "check-whitespace"
70 | ],
71 | "prefer-const": true,
72 | "quotemark": [
73 | true,
74 | "single"
75 | ],
76 | "radix": true,
77 | "semicolon": [
78 | "always"
79 | ],
80 | "triple-equals": [
81 | true,
82 | "allow-null-check"
83 | ],
84 | "typedef-whitespace": [
85 | true,
86 | {
87 | "call-signature": "nospace",
88 | "index-signature": "nospace",
89 | "parameter": "nospace",
90 | "property-declaration": "nospace",
91 | "variable-declaration": "nospace"
92 | }
93 | ],
94 | "typeof-compare": true,
95 | "unified-signatures": true,
96 | "variable-name": false,
97 | "whitespace": [
98 | true,
99 | "check-branch",
100 | "check-decl",
101 | "check-operator",
102 | "check-separator",
103 | "check-type"
104 | ],
105 | "directive-selector": [
106 | true,
107 | "attribute",
108 | "app",
109 | "camelCase"
110 | ],
111 | "component-selector": [
112 | true,
113 | "element",
114 | "app",
115 | "kebab-case"
116 | ],
117 | "use-input-property-decorator": true,
118 | "use-output-property-decorator": true,
119 | "use-host-property-decorator": true,
120 | "no-input-rename": true,
121 | "no-output-rename": true,
122 | "use-life-cycle-interface": true,
123 | "use-pipe-transform-interface": true,
124 | "component-class-suffix": true,
125 | "directive-class-suffix": true,
126 | "no-access-missing-member": true,
127 | "templates-use-public": true,
128 | "invoke-injectable": true
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/docs/project.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 10
5 | WCF Changes
6 |
7 |
8 | ggroupblack
9 |
10 | 0
11 |
12 | 0
13 | 1
14 | 0
15 | 1
16 |
17 |
18 |
19 | 20
20 | Move to WCF from remoting
21 | 2017-05-11 09:00
22 | 2017-05-15
23 | gtaskblue
24 |
25 | 0
26 | Paul
27 | 10
28 | 0
29 | 10
30 | 1
31 |
32 | Paul
33 | This text is only available in tool tips
34 |
35 |
36 | 30
37 | add Auditing
38 | 2017-05-18 10:30
39 | 2017-05-20 12:00
40 | gtaskblue
41 |
42 | 0
43 | Eduardo
44 | 50
45 | 0
46 | 10
47 | 1
48 | 20
49 | Eduardo
50 |
51 |
52 | 40
53 | Yet another task
54 | 2017-05-24
55 | 2017-05-25
56 | gtaskblue
57 |
58 | 0
59 | Ricardo
60 | 30
61 | 0
62 | 0
63 | 1
64 | 20,30
65 | Ricardo
66 |
67 |
68 | 50
69 | Another Group
70 |
71 |
72 | ggroupblack
73 |
74 | 0
75 |
76 | 0
77 | 1
78 | 0
79 | 1
80 |
81 |
82 |
83 | 60
84 | Move to GitHub
85 | 2017-05-14 09:00
86 | 2017-05-16
87 | gtaskblue
88 |
89 | 0
90 | Ricardo
91 | 10
92 | 0
93 | 50
94 | 1
95 |
96 | Ricardo
97 | This text is only available in tool tips
98 |
99 |
100 | 70
101 | Updating files
102 | 2017-05-18 10:30
103 | 2017-05-21 12:00
104 | gtaskred
105 |
106 | 0
107 | Paul
108 | 50
109 | 0
110 | 50
111 | 1
112 | 60
113 | Paul
114 |
115 |
116 | 80
117 | Yet another task
118 | 2017-05-23
119 | 2017-05-25
120 | gtaskyellow
121 |
122 | 0
123 | Eduardo
124 | 30
125 | 0
126 | 50
127 | 1
128 | 60,70
129 | Eduardo
130 | gtaskyellow
131 |
132 |
133 |
--------------------------------------------------------------------------------
/docs/fixes/bug277.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "pID": 206,
4 | "pName": "Graphs Test",
5 | "pStart": "2019-01-01",
6 | "pEnd": "2019-12-31",
7 | "pClass": "gnogantt",
8 | "pLink": "",
9 | "pMile": 0,
10 | "pComp": 0,
11 | "pGroup": 1,
12 | "pParent": 0,
13 | "pOpen": 1,
14 | "pCaption": "Proj",
15 | "predecessor": ""
16 | },
17 | {
18 | "pID": 275,
19 | "pName": "Pre-Project",
20 | "pStart": "2019-01-01",
21 | "pEnd": "2020-01-01",
22 | "pClass": "gnogantt",
23 | "pLink": "",
24 | "pMile": 1,
25 | "pComp": 0,
26 | "pGroup": 1,
27 | "pParent": 206,
28 | "pOpen": 1,
29 | "pCaption": "Mile",
30 | "pNotes": "",
31 | "predecessor": ""
32 | },
33 | {
34 | "pID": -2050515367,
35 | "pName": "Teste1",
36 | "pStart": "2019-01-01",
37 | "pEnd": "2019-01-11",
38 | "pPlanStart": "2019-01-01",
39 | "pPlanEnd": "2019-01-11",
40 | "pClass": "gtaskblue",
41 | "pLink": "",
42 | "pMile": 0,
43 | "pComp": 0,
44 | "pGroup": 2,
45 | "pParent": 275,
46 | "pOpen": 1,
47 | "pCaption": "Task",
48 | "pNotes": "",
49 | "predecessor": "",
50 | "appendedTaskId": 1208
51 | },
52 | {
53 | "pID": 1208,
54 | "pName": "Teste1",
55 | "pStart": "2019-01-01",
56 | "pEnd": "2019-01-11",
57 | "pClass": "gtaskblue",
58 | "pLink": "",
59 | "pMile": 0,
60 | "pComp": 0,
61 | "pGroup": 0,
62 | "pParent": -2050515367,
63 | "pDepend": "",
64 | "pOpen": 1,
65 | "pCaption": "Task",
66 | "pNotes": "",
67 | "project": "206"
68 | },
69 | {
70 | "pID": 12098745,
71 | "pName": "Teste2",
72 | "pStart": "2019-01-12",
73 | "pEnd": "2019-01-16",
74 | "pPlanStart": "2019-01-12",
75 | "pPlanEnd": "2019-01-16",
76 | "pClass": "gtaskblue",
77 | "pLink": "",
78 | "pMile": 0,
79 | "pComp": 0,
80 | "pGroup": 2,
81 | "pParent": 275,
82 | "pOpen": 1,
83 | "pCaption": "Task",
84 | "pNotes": "",
85 | "predecessor": "1208",
86 | "appendedTaskId": 1209
87 | },
88 | {
89 | "pID": 8745,
90 | "pName": "Appointments Interval",
91 | "pStart": "2019-01-11",
92 | "pEnd": "2019-01-12",
93 | "pClass": "gappointment",
94 | "pLink": "",
95 | "pMile": 0,
96 | "pComp": 0,
97 | "pGroup": 0,
98 | "pParent": 12098745,
99 | "pDepend": "",
100 | "pOpen": 1,
101 | "pCaption": "Appointments Interval",
102 | "pNotes": "",
103 | "project": "206"
104 | },
105 | {
106 | "pID": 1209,
107 | "pName": "Teste2",
108 | "pStart": "2019-01-12",
109 | "pEnd": "2019-01-16",
110 | "pClass": "gtaskblue",
111 | "pLink": "",
112 | "pMile": 0,
113 | "pComp": 0,
114 | "pGroup": 0,
115 | "pParent": 12098745,
116 | "pDepend": "1208",
117 | "pOpen": 1,
118 | "pCaption": "Task",
119 | "pNotes": "",
120 | "project": "206"
121 | },
122 | {
123 | "pID": 1220317130,
124 | "pName": "Teste3",
125 | "pStart": "2019-01-17",
126 | "pEnd": "2019-01-17",
127 | "pPlanStart": "2019-01-17",
128 | "pPlanEnd": "2019-01-17",
129 | "pClass": "gtaskblue",
130 | "pLink": "",
131 | "pMile": 0,
132 | "pComp": 0,
133 | "pGroup": 2,
134 | "pParent": 275,
135 | "pOpen": 1,
136 | "pCaption": "Task",
137 | "pNotes": "",
138 | "predecessor": "1209",
139 | "appendedTaskId": 1210
140 | },
141 | {
142 | "pID": 1210,
143 | "pName": "Teste3",
144 | "pStart": "2019-01-17",
145 | "pEnd": "2019-01-17",
146 | "pClass": "gtaskblue",
147 | "pLink": "",
148 | "pMile": 0,
149 | "pComp": 0,
150 | "pGroup": 0,
151 | "pParent": 1220317130,
152 | "pDepend": "1209",
153 | "pOpen": 1,
154 | "pCaption": "Task",
155 | "pNotes": "",
156 | "project": "206"
157 | }
158 | ]
--------------------------------------------------------------------------------
/src/draw_dependencies.ts:
--------------------------------------------------------------------------------
1 | export const drawDependency = function (x1, y1, x2, y2, pType, pClass) {
2 | let vDir = 1;
3 | let vBend = false;
4 | let vShort = 4;
5 | let vRow = Math.floor(this.getRowHeight() / 2);
6 |
7 | if (y2 < y1) vRow *= -1;
8 |
9 | switch (pType) {
10 | case 'SF':
11 | vShort *= -1;
12 | if (x1 - 10 <= x2 && y1 != y2) vBend = true;
13 | vDir = -1;
14 | break;
15 | case 'SS':
16 | if (x1 < x2) vShort *= -1;
17 | else vShort = x2 - x1 - (2 * vShort);
18 | break;
19 | case 'FF':
20 | if (x1 <= x2) vShort = x2 - x1 + (2 * vShort);
21 | vDir = -1;
22 | break;
23 | default:
24 | if (x1 + 10 >= x2 && y1 != y2) vBend = true;
25 | break;
26 | }
27 |
28 | if (vBend) {
29 | this.sLine(x1, y1, x1 + vShort, y1, pClass);
30 | this.sLine(x1 + vShort, y1, x1 + vShort, y2 - vRow, pClass);
31 | this.sLine(x1 + vShort, y2 - vRow, x2 - (vShort * 2), y2 - vRow, pClass);
32 | this.sLine(x2 - (vShort * 2), y2 - vRow, x2 - (vShort * 2), y2, pClass);
33 | this.sLine(x2 - (vShort * 2), y2, x2 - (1 * vDir), y2, pClass);
34 | }
35 | else if (y1 != y2) {
36 | this.sLine(x1, y1, x1 + vShort, y1, pClass);
37 | this.sLine(x1 + vShort, y1, x1 + vShort, y2, pClass);
38 | this.sLine(x1 + vShort, y2, x2 - (1 * vDir), y2, pClass);
39 | }
40 | else this.sLine(x1, y1, x2 - (1 * vDir), y2, pClass);
41 |
42 | let vTmpDiv = this.sLine(x2, y2, x2 - 3 - ((vDir < 0) ? 1 : 0), y2 - 3 - ((vDir < 0) ? 1 : 0), pClass + "Arw");
43 | vTmpDiv.style.width = '0px';
44 | vTmpDiv.style.height = '0px';
45 | };
46 |
47 | export const DrawDependencies = function (vDebug = false) {
48 | if (this.getShowDeps() == 1) {
49 |
50 | this.CalcTaskXY(); //First recalculate the x,y
51 | this.clearDependencies();
52 |
53 | let vList = this.getList();
54 | for (let i = 0; i < vList.length; i++) {
55 | let vDepend = vList[i].getDepend();
56 | let vDependType = vList[i].getDepType();
57 | let n = vDepend.length;
58 |
59 | if (n > 0 && vList[i].getVisible() == 1) {
60 | for (let k = 0; k < n; k++) {
61 | let vTask = this.getArrayLocationByID(vDepend[k]);
62 | if (vTask >= 0 && vList[vTask].getGroup() != 2) {
63 | if (vList[vTask].getVisible() == 1) {
64 | if (vDebug) {
65 | console.info(`init drawDependency `, vList[vTask].getID(), new Date());
66 | }
67 | var cssClass = 'gDepId' + vList[vTask].getID() +
68 | ' ' + 'gDepNextId' + vList[i].getID();
69 |
70 | var dependedData = vList[vTask].getDataObject();
71 | var nextDependedData = vList[i].getDataObject();
72 | if (dependedData && dependedData.pID && nextDependedData && nextDependedData.pID) {
73 | cssClass += ' gDepDataId' + dependedData.pID + ' ' + 'gDepNextDataId' + nextDependedData.pID;
74 | }
75 |
76 | if (vDependType[k] == 'SS') this.drawDependency(vList[vTask].getStartX() - 1, vList[vTask].getStartY(), vList[i].getStartX() - 1, vList[i].getStartY(), 'SS', cssClass + ' gDepSS');
77 | else if (vDependType[k] == 'FF') this.drawDependency(vList[vTask].getEndX(), vList[vTask].getEndY(), vList[i].getEndX(), vList[i].getEndY(), 'FF', cssClass + ' gDepFF');
78 | else if (vDependType[k] == 'SF') this.drawDependency(vList[vTask].getStartX() - 1, vList[vTask].getStartY(), vList[i].getEndX(), vList[i].getEndY(), 'SF', cssClass + ' gDepSF');
79 | else if (vDependType[k] == 'FS') this.drawDependency(vList[vTask].getEndX(), vList[vTask].getEndY(), vList[i].getStartX() - 1, vList[i].getStartY(), 'FS', cssClass + ' gDepFS');
80 | }
81 | }
82 | }
83 | }
84 | }
85 | }
86 | // draw the current date line
87 | if (this.vTodayPx >= 0) {
88 | this.sLine(this.vTodayPx, 0, this.vTodayPx, this.getChartTable().offsetHeight - 1, 'gCurDate');
89 | }
90 | };
91 |
--------------------------------------------------------------------------------
/src/json.ts:
--------------------------------------------------------------------------------
1 | import { TaskItem } from "./task";
2 | import { makeRequest } from "./utils/general_utils";
3 |
4 | /**
5 | *
6 | * @param pFile
7 | * @param pGanttlet
8 | */
9 | export const parseJSON = async function (pFile, pGanttVar, vDebug = false, redrawAfter = true) {
10 | const jsonObj = await makeRequest(pFile, true, true);
11 | let bd;
12 | if (vDebug) {
13 | bd = new Date();
14 | console.info('before jsonparse', bd);
15 | }
16 | addJSONTask(pGanttVar, jsonObj);
17 | if (this.vDebug) {
18 | const ad = new Date();
19 | console.info('after addJSONTask', ad, (ad.getTime() - bd.getTime()));
20 | }
21 | if(redrawAfter){
22 | pGanttVar.Draw();
23 | }
24 | return jsonObj;
25 | };
26 |
27 | export const parseJSONString = function (pStr, pGanttVar) {
28 | addJSONTask(pGanttVar, JSON.parse(pStr));
29 | };
30 |
31 | export const addJSONTask = function (pGanttVar, pJsonObj) {
32 | for (let index = 0; index < pJsonObj.length; index++) {
33 | let id;
34 | let name;
35 | let start;
36 | let end;
37 | let planstart;
38 | let planend;
39 | let itemClass;
40 | let planClass;
41 | let link = '';
42 | let milestone = 0;
43 | let resourceName = '';
44 | let completion;
45 | let group = 0;
46 | let parent;
47 | let open;
48 | let dependsOn = '';
49 | let caption = '';
50 | let notes = '';
51 | let cost;
52 | let duration = '';
53 | let bartext = '';
54 | const additionalObject = {};
55 |
56 | for (let prop in pJsonObj[index]) {
57 | let property = prop;
58 | let value = pJsonObj[index][property];
59 | switch (property.toLowerCase()) {
60 | case 'pid':
61 | case 'id':
62 | id = value;
63 | break;
64 | case 'pname':
65 | case 'name':
66 | name = value;
67 | break;
68 | case 'pstart':
69 | case 'start':
70 | start = value;
71 | break;
72 | case 'pend':
73 | case 'end':
74 | end = value;
75 | break;
76 | case 'pplanstart':
77 | case 'planstart':
78 | planstart = value;
79 | break;
80 | case 'pplanend':
81 | case 'planend':
82 | planend = value;
83 | break;
84 | case 'pclass':
85 | case 'class':
86 | itemClass = value;
87 | break;
88 | case 'pplanclass':
89 | case 'planclass':
90 | planClass = value;
91 | break;
92 | case 'plink':
93 | case 'link':
94 | link = value;
95 | break;
96 | case 'pmile':
97 | case 'mile':
98 | milestone = value;
99 | break;
100 | case 'pres':
101 | case 'res':
102 | resourceName = value;
103 | break;
104 | case 'pcomp':
105 | case 'comp':
106 | completion = value;
107 | break;
108 | case 'pgroup':
109 | case 'group':
110 | group = value;
111 | break;
112 | case 'pparent':
113 | case 'parent':
114 | parent = value;
115 | break;
116 | case 'popen':
117 | case 'open':
118 | open = value;
119 | break;
120 | case 'pdepend':
121 | case 'depend':
122 | dependsOn = value;
123 | break;
124 | case 'pcaption':
125 | case 'caption':
126 | caption = value;
127 | break;
128 | case 'pnotes':
129 | case 'notes':
130 | notes = value;
131 | break;
132 | case 'pcost':
133 | case 'cost':
134 | cost = value;
135 | break;
136 | case 'duration':
137 | case 'pduration':
138 | duration = value;
139 | break;
140 | case 'bartext':
141 | case 'pbartext':
142 | bartext = value;
143 | break;
144 | default:
145 | additionalObject[property.toLowerCase()] = value;
146 | }
147 | }
148 |
149 | //if (id != undefined && !isNaN(parseInt(id)) && isFinite(id) && name && start && end && itemClass && completion != undefined && !isNaN(parseFloat(completion)) && isFinite(completion) && !isNaN(parseInt(parent)) && isFinite(parent)) {
150 | pGanttVar.AddTaskItem(new TaskItem(id, name, start, end, itemClass, link,
151 | milestone, resourceName, completion, group, parent, open, dependsOn,
152 | caption, notes, pGanttVar, cost, planstart, planend, duration, bartext,
153 | additionalObject, planClass));
154 | //}
155 | }
156 | };
157 |
--------------------------------------------------------------------------------
/src/jsgantt.ts:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2013-2018, Paul Geldart, Eduardo Rodrigues, Ricardo Cardoso and Mario Mol.
3 | *
4 | * Redistribution and use in source and binary forms, with or without
5 | * modification, are permitted provided that the following conditions are met:
6 | * * Redistributions of source code must retain the above copyright
7 | * notice, this list of conditions and the following disclaimer.
8 | * * Redistributions in binary form must reproduce the above copyright
9 | * notice, this list of conditions and the following disclaimer in the
10 | * documentation and/or other materials provided with the distribution.
11 | * * Neither the name of AUTHORS nor the names of its contributors
12 | * may be used to endorse or promote products derived from this software
13 | * without specific prior written permission.
14 | *
15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ''AS IS'' AND ANY EXPRESS OR
16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 | * IN NO EVENT SHALL AUTHORS BE LIABLE FOR ANY DIRECT,
19 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 |
26 | This project is based on jsGantt 1.2, (which can be obtained from
27 | https://code.google.com/p/jsgantt/) and remains under the original BSD license.
28 | Copyright (c) 2009, Shlomy Gantz BlueBrick Inc.
29 | */
30 |
31 | import {
32 | showToolTip, addTooltipListeners, addThisRowListeners, addFormatListeners,
33 | folder, hide, show,
34 | addScrollListeners, addFolderListeners, addListener
35 | } from "./events";
36 | import {
37 | findObj, changeFormat,
38 | stripIds, stripUnwanted, delayedHide, getOffset,
39 | getScrollPositions, isIE, benchMark, getZoomFactor, hideToolTip, fadeToolTip, criticalPath, updateFlyingObj, moveToolTip,
40 | } from "./utils/general_utils";
41 | import { parseXML, parseXMLString, findXMLNode, getXMLNodeValue, AddXMLTask } from './xml';
42 | import { taskLink, sortTasks, TaskItem, processRows } from "./task";
43 | import { GanttChart } from "./draw";
44 | import { parseJSON, parseJSONString, addJSONTask } from "./json";
45 | import { getMinDate, getMaxDate, parseDateStr, formatDateStr, parseDateFormatStr, getIsoWeek } from "./utils/date_utils";
46 |
47 | export let JSGantt; if (!JSGantt) JSGantt = {};
48 |
49 | JSGantt.isIE = isIE;
50 | JSGantt.TaskItem = TaskItem;
51 | JSGantt.GanttChart = GanttChart;
52 | JSGantt.updateFlyingObj = updateFlyingObj;
53 | JSGantt.showToolTip = showToolTip;
54 |
55 | JSGantt.stripIds = stripIds;
56 | JSGantt.stripUnwanted = stripUnwanted;
57 | JSGantt.delayedHide = delayedHide;
58 |
59 | JSGantt.hideToolTip = hideToolTip;
60 | JSGantt.fadeToolTip = fadeToolTip;
61 | JSGantt.moveToolTip = moveToolTip;
62 |
63 | JSGantt.getZoomFactor = getZoomFactor;
64 |
65 | JSGantt.getOffset = getOffset;
66 | JSGantt.getScrollPositions = getScrollPositions;
67 | JSGantt.processRows = processRows;
68 | JSGantt.sortTasks = sortTasks;
69 |
70 | // Used to determine the minimum date of all tasks and set lower bound based on format
71 | JSGantt.getMinDate = getMinDate;
72 |
73 | // Used to determine the maximum date of all tasks and set upper bound based on format
74 | JSGantt.getMaxDate = getMaxDate;
75 |
76 | // This function finds the document id of the specified object
77 | JSGantt.findObj = findObj;
78 |
79 | JSGantt.changeFormat = changeFormat;
80 |
81 | // Tasks
82 | JSGantt.folder = folder;
83 | JSGantt.hide = hide;
84 | JSGantt.show = show;
85 | JSGantt.taskLink = taskLink;
86 |
87 | JSGantt.parseDateStr = parseDateStr;
88 | JSGantt.formatDateStr = formatDateStr;
89 | JSGantt.parseDateFormatStr = parseDateFormatStr;
90 |
91 | // XML
92 | JSGantt.parseXML = parseXML;
93 | JSGantt.parseXMLString = parseXMLString;
94 | JSGantt.findXMLNode = findXMLNode;
95 | JSGantt.getXMLNodeValue = getXMLNodeValue;
96 | JSGantt.AddXMLTask = AddXMLTask;
97 |
98 | // JSON
99 | JSGantt.parseJSON = parseJSON;
100 | JSGantt.parseJSONString = parseJSONString;
101 | JSGantt.addJSONTask = addJSONTask;
102 |
103 | JSGantt.benchMark = benchMark;
104 | JSGantt.getIsoWeek = getIsoWeek;
105 |
106 | JSGantt.addListener = addListener;
107 | JSGantt.addTooltipListeners = addTooltipListeners;
108 | JSGantt.addThisRowListeners = addThisRowListeners;
109 | JSGantt.addFolderListeners = addFolderListeners;
110 | JSGantt.addFormatListeners = addFormatListeners;
111 | JSGantt.addScrollListeners = addScrollListeners;
112 |
113 | JSGantt.criticalPath = criticalPath;
114 |
--------------------------------------------------------------------------------
/docs/fixes/general.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | jsGantt Improved
10 |
11 |
12 |
13 |
14 |
15 |
16 |
18 |
20 |
21 |
22 |
23 |
25 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | Choose a language:
43 |
44 | pt
45 | en
46 | ru
47 | es
48 | fr
49 | de
50 |
51 |
52 |
53 | Data getting from a URL, JSON Data
54 |
55 |
56 |
57 | Delay for tooltip to hide (in ms):
58 |
59 |
60 |
61 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/test/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | jsGantt Improved
10 |
11 |
12 |
13 |
22 |
23 |
24 |
26 |
28 |
29 |
30 |
31 |
33 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
110 |
111 |
112 |
113 |
114 | Choose a language:
115 |
116 | pt
117 | en
118 | ru
119 | es
120 | fr
121 | de
122 |
123 |
124 |
125 | Data getting from a URL, JSON Data
126 |
127 |
128 |
129 | Delay for tooltip to hide (in ms):
130 |
131 | UseSingleCell:
132 |
133 |
134 | * Click events in table are binded to console.log for testing
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
--------------------------------------------------------------------------------
/docs/fixes/data-plan-color.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "pID": 1,
4 | "pName": "Bridge",
5 | "pClass": "ggroupblack",
6 | "pPlanClass": "ggroupblack",
7 | "pGroup": 1,
8 | "pOpen": 1,
9 | "pParent": 0,
10 | "pComp": 0
11 | },
12 | {
13 | "pID": 2,
14 | "pName": "Preconstruction",
15 | "pClass": "ggroupblack",
16 | "pPlanClass": "ggroupblack",
17 | "pGroup": 1,
18 | "pOpen": 1,
19 | "pParent": 1,
20 | "pComp": 0
21 | },
22 | {
23 | "pID": 4,
24 | "pName": "Sample Approval",
25 | "pClass": "gtaskred",
26 | "pPlanClass": "gtaskred",
27 | "pGroup": 0,
28 | "pOpen": 1,
29 | "pParent": 2,
30 | "pComp": 0,
31 | "pStart": "2021-06-11T02:00:00.000Z",
32 | "pEnd": "2021-07-02T19:00:00.000Z",
33 | "pPlanStart": "2020-11-20T02:00:00.000Z",
34 | "pPlanEnd": "2020-12-11T19:00:00.000Z",
35 | "pNotes": "Start: 2020-11-20T02:00:00Z -> 2021-06-11T02:00:00Z \\n End: 2020-12-11T19:00:00Z -> 2021-07-02T19:00:00Z "
36 | },
37 | {
38 | "pID": 3,
39 | "pName": "Design and Planning",
40 | "pClass": "gtaskred",
41 | "pPlanClass": "gtaskgreen",
42 | "pGroup": 0,
43 | "pOpen": 1,
44 | "pParent": 2,
45 | "pComp": 0,
46 | "pStart": "2021-03-19T02:00:00.000Z",
47 | "pEnd": "2021-06-10T19:00:00.000Z",
48 | "pPlanStart": "2020-09-01T02:00:00.000Z",
49 | "pPlanEnd": "2020-11-23T19:00:00.000Z",
50 | "pNotes": "Start: 2020-09-01T02:00:00Z -> 2021-03-19T02:00:00Z \\n End: 2020-11-23T19:00:00Z -> 2021-06-10T19:00:00Z "
51 | },
52 | {
53 | "pID": 5,
54 | "pName": "Contract execution -> Contract exection",
55 | "pClass": "gtaskred",
56 | "pPlanClass": "gtaskred",
57 | "pGroup": 0,
58 | "pOpen": 1,
59 | "pParent": 2,
60 | "pComp": 0,
61 | "pStart": "2021-07-05T02:00:00.000Z",
62 | "pEnd": "2021-07-05T19:00:00.000Z",
63 | "pPlanStart": "2020-12-14T02:00:00.000Z",
64 | "pPlanEnd": "2020-12-14T19:00:00.000Z",
65 | "pNotes": "Start: 2020-12-14T02:00:00Z -> 2021-07-05T02:00:00Z \\n End: 2020-12-14T19:00:00Z -> 2021-07-05T19:00:00Z "
66 | },
67 | {
68 | "pID": 6,
69 | "pName": "Construction",
70 | "pClass": "ggroupblack",
71 | "pPlanClass": "ggroupblack",
72 | "pGroup": 1,
73 | "pOpen": 1,
74 | "pParent": 1,
75 | "pComp": 0
76 | },
77 | {
78 | "pID": 7,
79 | "pName": "Demo",
80 | "pClass": "gtaskred",
81 | "pPlanClass": "gtaskred",
82 | "pGroup": 0,
83 | "pOpen": 1,
84 | "pParent": 6,
85 | "pComp": 0,
86 | "pStart": "2021-06-01T02:00:00.000Z",
87 | "pEnd": "2021-06-01T19:00:00.000Z",
88 | "pPlanStart": "2021-08-25T02:00:00.000Z",
89 | "pPlanEnd": "2021-08-25T19:00:00.000Z",
90 | "pNotes": "Start: 2021-08-25T02:00:00Z -> 2021-06-01T02:00:00Z \\n End: 2021-08-25T19:00:00Z -> 2021-06-01T19:00:00Z "
91 | },
92 | {
93 | "pID": 8,
94 | "pName": "Mobilization",
95 | "pClass": "gtaskred",
96 | "pPlanClass": "gtaskgreen",
97 | "pGroup": 0,
98 | "pOpen": 1,
99 | "pParent": 6,
100 | "pComp": 0,
101 | "pStart": "2021-07-06T02:00:00.000Z",
102 | "pEnd": "2021-08-16T19:00:00.000Z",
103 | "pPlanStart": "2021-07-06T02:00:00.000Z",
104 | "pPlanEnd": "2021-08-16T19:00:00.000Z",
105 | "pNotes": "Start: 2021-09-01T02:00:00Z -> 2021-07-06T02:00:00Z \\n End: 2021-10-12T19:00:00Z -> 2021-08-16T19:00:00Z "
106 | },
107 | {
108 | "pID": 20,
109 | "pName": "Finishes",
110 | "pClass": "gtaskgreen",
111 | "pPlanClass": "gtaskgreen",
112 | "pGroup": 0,
113 | "pOpen": 1,
114 | "pParent": 6,
115 | "pComp": 0,
116 | "pStart": "2022-10-18T02:00:00.000Z",
117 | "pEnd": "2022-11-25T19:00:00.000Z",
118 | "pPlanStart": "2022-01-03T02:00:00.000Z",
119 | "pPlanEnd": "2022-02-10T19:00:00.000Z",
120 | "pNotes": "Start: 2022-01-03T02:00:00Z -> 2022-10-18T02:00:00Z \\n End: 2022-02-10T19:00:00Z -> 2022-11-25T19:00:00Z "
121 | },
122 | {
123 | "pID": 21,
124 | "pName": "Close-out",
125 | "pClass": "ggroupblack",
126 | "pPlanClass": "ggroupblack",
127 | "pGroup": 1,
128 | "pOpen": 1,
129 | "pParent": 1,
130 | "pComp": 0
131 | },
132 | {
133 | "pID": 22,
134 | "pName": "Punch list",
135 | "pClass": "gtaskgreen",
136 | "pPlanClass": "gtaskgreen",
137 | "pGroup": 0,
138 | "pOpen": 1,
139 | "pParent": 21,
140 | "pComp": 0,
141 | "pStart": "2022-11-28T02:00:00.000Z",
142 | "pEnd": "2022-12-15T19:00:00.000Z",
143 | "pPlanStart": "2022-02-11T02:00:00.000Z",
144 | "pPlanEnd": "2022-03-02T19:00:00.000Z",
145 | "pNotes": "Start: 2022-02-11T02:00:00Z -> 2022-11-28T02:00:00Z \\n End: 2022-03-02T19:00:00Z -> 2022-12-15T19:00:00Z "
146 | },
147 | {
148 | "pID": 23,
149 | "pName": "Final inspections",
150 | "pClass": "gtaskgreen",
151 | "pPlanClass": "gtaskgreen",
152 | "pGroup": 0,
153 | "pOpen": 1,
154 | "pParent": 21,
155 | "pComp": 0,
156 | "pStart": "2022-12-16T02:00:00.000Z",
157 | "pEnd": "2022-12-19T19:00:00.000Z",
158 | "pPlanStart": "2022-03-03T02:00:00.000Z",
159 | "pPlanEnd": "2022-03-04T19:00:00.000Z",
160 | "pNotes": "Start: 2022-03-03T02:00:00Z -> 2022-12-16T02:00:00Z \\n End: 2022-03-04T19:00:00Z -> 2022-12-19T19:00:00Z "
161 | },
162 | {
163 | "pID": 24,
164 | "pName": "Close-out documents",
165 | "pClass": "gtaskgreen",
166 | "pPlanClass": "gtaskgreen",
167 | "pGroup": 0,
168 | "pOpen": 1,
169 | "pParent": 21,
170 | "pComp": 0,
171 | "pStart": "2022-12-20T02:00:00.000Z",
172 | "pEnd": "2023-01-06T19:00:00.000Z",
173 | "pPlanStart": "2022-03-07T02:00:00.000Z",
174 | "pPlanEnd": "2022-03-24T19:00:00.000Z",
175 | "pNotes": "Start: 2022-03-07T02:00:00Z -> 2022-12-20T02:00:00Z \\n End: 2022-03-24T19:00:00Z -> 2023-01-06T19:00:00Z "
176 | }
177 | ]
--------------------------------------------------------------------------------
/docs/demobigdata.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | jsGantt Improved
10 |
11 |
12 |
13 |
22 |
23 |
24 |
26 |
28 |
29 |
30 |
31 |
33 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
113 |
114 |
115 |
116 |
117 | Choose a language:
118 |
119 | pt
120 | en
121 | ru
122 | es
123 | fr
124 | de
125 | cn
126 | sv
127 | nl
128 | id
129 | cs
130 |
131 |
132 |
133 | Data getting from a URL, JSON Data . Use `bigdata.json` to test with more data.
134 |
135 |
136 |
137 | Delay for tooltip to hide (in ms):
138 |
139 | UseSingleCell:
140 |
141 |
142 | * Click events in table are binded to console.log for testing
143 |
144 |
145 |
146 |
147 | Configure debug and see in console:
148 |
149 |
150 |
151 |
152 |
153 | Custom Tooltip Template:
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://travis-ci.com/jsGanttImproved/jsgantt-improved)
2 |
3 |
4 | A fully featured gantt chart component built entirely in Javascript, CSS and AJAX. It is lightweight and there is no need of external libraries or additional images.
5 |
6 |
7 | 
8 |
9 |
10 | Start using with including the files `jsgantt.js` and `jsgantt.css` that are inside `dist/` folder.
11 |
12 | Or install and use in JS
13 |
14 | `npm install jsgantt-improved`
15 |
16 | Import in your JS `import {JSGantt} from 'jsgantt-improved';`
17 |
18 | See the [FULL DOCUMENTATION](./Documentation.md) for more details in all features.
19 |
20 | For **Angular** use the component [ng-gantt](https://github.com/jsGanttImproved/ng-gantt)
21 |
22 | For **React** use the component [react-jsgantt](https://github.com/jsGanttImproved/react-jsgantt)
23 |
24 | For **Vue** , see this example: https://stackblitz.com/edit/vue-jsgantt-3
25 |
26 | For **.NET** , see this example: [.NET Documentation](./docs/DotNet.md)
27 |
28 |
29 | ## Example
30 |
31 |
32 | You can view a Solo live example at:
33 |
34 | * https://jsganttimproved.github.io/jsgantt-improved/docs/demo.html
35 |
36 | Or use a live coding example at Codenpen:
37 |
38 | * https://codepen.io/mariomol/pen/mQzBPV
39 |
40 |
41 | ## Easy to Use
42 |
43 | ```html
44 |
45 |
46 |
47 |
48 |
49 |
105 | ```
106 |
107 | ## Features
108 |
109 | * Tasks & Collapsible Task Groups
110 | * Dependencies and Highlight when hover a task
111 | * Edit data in gantt table with list of responsible
112 | * Task Completion
113 | * Table with Additional Columns
114 | * Task Styling or as HTML tags
115 | * Milestones
116 | * Resources
117 | * Costs
118 | * Plan Start and End Dates
119 | * Gantt with Planned vs Executed
120 | * Dynamic Loading of Tasks
121 | * Dynamic change of format: Hour, Day, Week, Month, Quarter
122 | * Load Gantt from JSON and XML
123 | * From external files (including experimental support for MS Project XML files)
124 | * From JavaScript Strings
125 |
126 | ### Internationalization
127 |
128 | Support for languages below:
129 |
130 | * Arabic (ar)
131 | * Chinese (cn)
132 | * Czech (cs)
133 | * Dutch (Standard)
134 | * English (en)
135 | * French (fr)
136 | * Finnish (fi)
137 | * German (de)
138 | * Hebrew (he)
139 | * Hungarian (hu)
140 | * Korean (ko)
141 | * Indonesian (id)
142 | * Italian (it)
143 | * Japanese (ja)
144 | * Portuguese (pt)
145 | * Polish (pl)
146 | * Russian (ru)
147 | * Spanish (es)
148 | * Swedish (sv)
149 | * Turkish (tr)
150 | * Ukraininan (ua)
151 |
152 | ## Documentation
153 |
154 | See the [Documentation](./Documentation.md) wiki page or the included ``docs/index.html`` file for instructions on use.
155 |
156 | Project based on https://code.google.com/p/jsgantt/.
157 |
158 |
159 | ## Want to Collaborate?
160 |
161 | Its easy to get it set:
162 |
163 | * Clone this repo
164 | * Install lib dependencies: `npm i`
165 | * Install global dependencies: `npm i -g browserify nodemon onchange tsc`
166 | * Compile final js to be used on demo: `npm run dist`
167 | * Run the demo with a live example: `npm start`.
168 | * You can check the demo gantt that we use for testing features at: `http://127.0.0.1:8080/docs/demo.html`
169 | * Use `npm run watch` or do your change in `src` and restart this command refresh the changes.
170 |
171 | For testing:
172 | * Install global dependencies: `npm i -g webdriver-manager`
173 | * Install selenium webdriver: `npm run webdriver`, it will install something like node_modules/webdriver-manager/selenium/chromedriver_88.0.4324.96.zip
174 |
175 | node node_modules/protractor/bin/webdriver-manager update
176 | apt install chromium
177 |
178 | apt install chromium-bsu
179 |
180 | * Use `npm run test` with e2e tests.
181 | * Or use `npm run watch:test` to keep watching the tests
182 |
183 | For new release:
184 | * Increment the version number on package.json
185 | * Run `npm run publishnpm`
186 |
187 |
188 | Or help us donating...
189 |
190 | [](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=S7B43P63C5QEN)
191 |
192 |
193 |
--------------------------------------------------------------------------------
/src/utils/draw_utils.ts:
--------------------------------------------------------------------------------
1 | import { addFormatListeners } from "../events";
2 |
3 | export const makeInput = function (formattedValue, editable, type = 'text', value = null, choices = null) {
4 | if (!value) {
5 | value = formattedValue;
6 | }
7 | if (editable) {
8 | switch (type) {
9 | case 'date':
10 | // Take timezone into account before converting to ISO String
11 | value = value ? new Date(value.getTime() - (value.getTimezoneOffset() * 60000)).toISOString().split('T')[0] : '';
12 | return ` `;
13 | case 'resource':
14 | if (choices) {
15 | const found = choices.filter(c => c.id == value || c.name == value);
16 | if (found && found.length > 0) {
17 | value = found[0].id;
18 | } else {
19 | choices.push({ id: value, name: value });
20 | }
21 | return `` + choices.map(c => `${c.name} `).join('') + ` `;
22 | } else {
23 | return ` `;
24 | }
25 | case 'cost':
26 | return ` `;
27 | default:
28 | return ` `;
29 | }
30 | } else {
31 | return formattedValue;
32 | }
33 | }
34 |
35 | export const newNode = function (pParent, pNodeType, pId = null, pClass = null, pText = null,
36 | pWidth = null, pLeft = null, pDisplay = null, pColspan = null, pAttribs = null) {
37 | let vNewNode = pParent.appendChild(document.createElement(pNodeType));
38 | if (pAttribs) {
39 | for (let i = 0; i + 1 < pAttribs.length; i += 2) {
40 | vNewNode.setAttribute(pAttribs[i], pAttribs[i + 1]);
41 | }
42 | }
43 | if (pId) vNewNode.id = pId; // I wish I could do this with setAttribute but older IEs don't play nice
44 | if (pClass) vNewNode.className = pClass;
45 | if (pWidth) vNewNode.style.width = (isNaN(pWidth * 1)) ? pWidth : pWidth + 'px';
46 | if (pLeft) vNewNode.style.left = (isNaN(pLeft * 1)) ? pLeft : pLeft + 'px';
47 | if (pText) {
48 | if (pText.indexOf && pText.indexOf('<') === -1) {
49 | vNewNode.appendChild(document.createTextNode(pText));
50 | } else {
51 | vNewNode.insertAdjacentHTML('beforeend', pText);
52 | }
53 | }
54 | if (pDisplay) vNewNode.style.display = pDisplay;
55 | if (pColspan) vNewNode.colSpan = pColspan;
56 | return vNewNode;
57 | };
58 |
59 |
60 |
61 | export const getArrayLocationByID = function (pId) {
62 | let vList = this.getList();
63 | for (let i = 0; i < vList.length; i++) {
64 | if (vList[i].getID() == pId)
65 | return i;
66 | }
67 | return -1;
68 | };
69 |
70 | export const CalcTaskXY = function () {
71 | let vID;
72 | let vList = this.getList();
73 | let vBarDiv;
74 | let vTaskDiv;
75 | let vParDiv;
76 | let vLeft, vTop, vWidth;
77 | let vHeight = Math.floor((this.getRowHeight() / 2));
78 |
79 | for (let i = 0; i < vList.length; i++) {
80 | vID = vList[i].getID();
81 | vBarDiv = vList[i].getBarDiv();
82 | vTaskDiv = vList[i].getTaskDiv();
83 | if ((vList[i].getParItem() && vList[i].getParItem().getGroup() == 2)) {
84 | vParDiv = vList[i].getParItem().getChildRow();
85 | }
86 | else vParDiv = vList[i].getChildRow();
87 |
88 | if (vBarDiv) {
89 | vList[i].setStartX(vBarDiv.offsetLeft + 1);
90 | vList[i].setStartY(vParDiv.offsetTop + vBarDiv.offsetTop + vHeight - 1);
91 | vList[i].setEndX(vBarDiv.offsetLeft + vBarDiv.offsetWidth + 1);
92 | vList[i].setEndY(vParDiv.offsetTop + vBarDiv.offsetTop + vHeight - 1);
93 | }
94 | }
95 | };
96 |
97 | export const sLine = function (x1, y1, x2, y2, pClass) {
98 | let vLeft = Math.min(x1, x2);
99 | let vTop = Math.min(y1, y2);
100 | let vWid = Math.abs(x2 - x1) + 1;
101 | let vHgt = Math.abs(y2 - y1) + 1;
102 |
103 | let vTmpDiv = document.createElement('div');
104 | vTmpDiv.id = this.vDivId + 'line' + this.vDepId++;
105 | vTmpDiv.style.position = 'absolute';
106 | vTmpDiv.style.overflow = 'hidden';
107 | vTmpDiv.style.zIndex = '0';
108 | vTmpDiv.style.left = vLeft + 'px';
109 | vTmpDiv.style.top = vTop + 'px';
110 | vTmpDiv.style.width = vWid + 'px';
111 | vTmpDiv.style.height = vHgt + 'px';
112 |
113 | vTmpDiv.style.visibility = 'visible';
114 |
115 | if (vWid == 1) vTmpDiv.className = 'glinev';
116 | else vTmpDiv.className = 'glineh';
117 |
118 | if (pClass) vTmpDiv.className += ' ' + pClass;
119 |
120 | this.getLines().appendChild(vTmpDiv);
121 |
122 | if (this.vEvents.onLineDraw && typeof this.vEvents.onLineDraw === 'function' ) {
123 | this.vEvents.onLineDraw(vTmpDiv);
124 | }
125 |
126 | return vTmpDiv;
127 | };
128 |
129 | export const drawSelector = function (pPos) {
130 | let vOutput = document.createDocumentFragment();
131 | let vDisplay = false;
132 |
133 | for (let i = 0; i < this.vShowSelector.length && !vDisplay; i++) {
134 | if (this.vShowSelector[i].toLowerCase() == pPos.toLowerCase()) vDisplay = true;
135 | }
136 |
137 | if (vDisplay) {
138 | let vTmpDiv = newNode(vOutput, 'div', null, 'gselector', this.vLangs[this.vLang]['format'] + ':');
139 |
140 | if (this.vFormatArr.join().toLowerCase().indexOf('hour') != -1)
141 | addFormatListeners(this, 'hour', newNode(vTmpDiv, 'span', this.vDivId + 'formathour' + pPos, 'gformlabel' + ((this.vFormat == 'hour') ? ' gselected' : ''), this.vLangs[this.vLang]['hour']));
142 |
143 | if (this.vFormatArr.join().toLowerCase().indexOf('day') != -1)
144 | addFormatListeners(this, 'day', newNode(vTmpDiv, 'span', this.vDivId + 'formatday' + pPos, 'gformlabel' + ((this.vFormat == 'day') ? ' gselected' : ''), this.vLangs[this.vLang]['day']));
145 |
146 | if (this.vFormatArr.join().toLowerCase().indexOf('week') != -1)
147 | addFormatListeners(this, 'week', newNode(vTmpDiv, 'span', this.vDivId + 'formatweek' + pPos, 'gformlabel' + ((this.vFormat == 'week') ? ' gselected' : ''), this.vLangs[this.vLang]['week']));
148 |
149 | if (this.vFormatArr.join().toLowerCase().indexOf('month') != -1)
150 | addFormatListeners(this, 'month', newNode(vTmpDiv, 'span', this.vDivId + 'formatmonth' + pPos, 'gformlabel' + ((this.vFormat == 'month') ? ' gselected' : ''), this.vLangs[this.vLang]['month']));
151 |
152 | if (this.vFormatArr.join().toLowerCase().indexOf('quarter') != -1)
153 | addFormatListeners(this, 'quarter', newNode(vTmpDiv, 'span', this.vDivId + 'formatquarter' + pPos, 'gformlabel' + ((this.vFormat == 'quarter') ? ' gselected' : ''), this.vLangs[this.vLang]['quarter']));
154 | }
155 | else {
156 | newNode(vOutput, 'div', null, 'gselector');
157 | }
158 | return vOutput;
159 | };
160 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to jsGanttImproved
2 |
3 | We'd love for you to contribute to our source code and to make jsGanttImproved even better than it is
4 | today! Here are the guidelines we'd like you to follow:
5 |
6 | - [Issues and Bugs](#issue)
7 | - [Feature Requests](#feature)
8 | - [Submission Guidelines](#submit)
9 | - [Coding Rules](#rules)
10 | - [Commit Message Guidelines](#commit)
11 | - [Further Info](#info)
12 |
13 | ## Found an Issue?
14 |
15 | If you find a bug in the source code or a mistake in the documentation, you can help us by
16 | submitting an issue to our [GitHub Repository][github]. Even better you can submit a Pull Request
17 | with a fix.
18 |
19 | **Please see the [Submission Guidelines](#submit) below.**
20 |
21 | ## Want a Feature?
22 |
23 | You can request a new feature by submitting an issue to our [GitHub Repository][github]. If you
24 | would like to implement a new feature then it can be crafted and submitted as a Pull Request.
25 |
26 | ## Submission Guidelines
27 |
28 | ### Submitting an Issue
29 | Before you submit your issue search the archive, maybe your question was already answered.
30 |
31 | If your issue appears to be a bug, and hasn't been reported, open a new issue. Help us to maximize
32 | the effort we can spend fixing issues and adding new features, by not reporting duplicate issues.
33 | Providing the following information will increase the chances of your issue being dealt with
34 | quickly:
35 |
36 | * **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps
37 | * **Motivation for or Use Case** - explain why this is a bug for you
38 | * **Version(s)** - what is the version(s) being used?
39 | * **Browsers and Operating System** - is this a problem with all browsers or only specific ones?
40 | * **Reproduce the Error** - provide a live example (using [Plunker][plunker] or
41 | [JSFiddle][jsfiddle]) or an unambiguous set of steps.
42 | * **Related Issues** - has a similar issue been reported before?
43 | * **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be
44 | causing the problem (line of code or commit)
45 |
46 | ### Submitting a Pull Request
47 | Before you submit your pull request consider the following guidelines:
48 |
49 | * Search [GitHub](https://github.com/jsGanttImproved/jsgantt-improved/pulls) for an open or closed Pull Request
50 | that relates to your submission. You don't want to duplicate effort.
51 | * Make your changes in a new git branch:
52 |
53 | ```shell
54 | git checkout -b my-fix-branch master
55 | ```
56 |
57 | * Create your patch.
58 | * Follow our [Coding Rules](#rules).
59 | * Commit your changes using a descriptive commit message that follows our
60 | [commit message conventions](#commit).
61 |
62 | ```shell
63 | git commit -a
64 | ```
65 | Note: the optional commit `-a` command line option will automatically "add" and "rm" edited files.
66 |
67 | * Push your branch to GitHub:
68 |
69 | ```shell
70 | git push origin my-fix-branch
71 | ```
72 |
73 | In GitHub, send a pull request to `master`.
74 | If we suggest changes, then:
75 |
76 | * Make the required updates.
77 | * Commit your changes to your branch (e.g. `my-fix-branch`).
78 | * Push the changes to your GitHub repository (this will update your Pull Request).
79 |
80 | If the PR gets too outdated we may ask you to rebase and force push to update the PR:
81 |
82 | ```shell
83 | git rebase master -i
84 | git push origin my-fix-branch -f
85 | ```
86 |
87 | _WARNING: Squashing or reverting commits and force-pushing thereafter may remove GitHub comments
88 | on code that were previously made by you or others in your commits. Avoid any form of rebasing
89 | unless necessary._
90 |
91 | That's it! Thank you for your contribution!
92 |
93 | #### After your pull request is merged
94 |
95 | After your pull request is merged, you can safely delete your branch and pull the changes
96 | from the main (upstream) repository:
97 |
98 | * Delete the remote branch on GitHub either through the GitHub web UI or your local shell as follows:
99 |
100 | ```shell
101 | git push origin --delete my-fix-branch
102 | ```
103 |
104 | * Check out the master branch:
105 |
106 | ```shell
107 | git checkout master -f
108 | ```
109 |
110 | * Delete the local branch:
111 |
112 | ```shell
113 | git branch -D my-fix-branch
114 | ```
115 |
116 | * Update your master with the latest upstream version:
117 |
118 | ```shell
119 | git pull --ff upstream master
120 | ```
121 |
122 | ## Coding Rules
123 |
124 | To ensure consistency throughout the source code, keep these rules in mind as you are working:
125 |
126 | * With the exceptions listed below, we follow the rules contained in
127 | [Google's JavaScript Style Guide][js-style-guide]:
128 | * Wrap all code at **100 characters**.
129 |
130 | ## Git Commit Guidelines
131 |
132 | We have very precise rules over how our git commit messages can be formatted. This leads to **more
133 | readable messages** that are easy to follow when looking through the **project history**.
134 |
135 | ### Commit Message Format
136 | Each commit message consists of a **subject**, a **body** and a **footer**:
137 |
138 | ```
139 |
140 |
141 |
142 |
143 |
144 | ```
145 |
146 | The **subject** is mandatory.
147 |
148 | Any line of the commit message cannot be longer 100 characters! This allows the message to be easier
149 | to read on GitHub as well as in various git tools.
150 |
151 | ### Revert
152 | If the commit reverts a previous commit, it should begin with `revert: `, followed by the header of the reverted commit.
153 | In the body it should say: `This reverts commit .`, where the hash is the SHA of the commit being reverted.
154 |
155 | ### Subject
156 | The subject contains succinct description of the change:
157 |
158 | * use the imperative, present tense: "change" not "changed" nor "changes"
159 | * don't capitalize first letter
160 | * no dot (.) at the end
161 |
162 | ### Body
163 | Just as in the **subject**, use the imperative, present tense: "change" not "changed" nor "changes".
164 | The body should include the motivation for the change and contrast this with previous behavior.
165 |
166 | ### Footer
167 | The footer should contain any information about **Breaking Changes** and is also the place to
168 | [reference GitHub issues that this commit closes][closing-issues].
169 |
170 | **Breaking Changes** should start with the word `BREAKING CHANGE:` with a space or two newlines.
171 | The rest of the commit message is then used for this.
172 |
173 | ## Further Information
174 | You can find out more detailed information about contributing in the [Documentation][docs].
175 |
176 |
177 | [github]: https://github.com/jsGanttImproved/jsgantt-improved
178 | [js-style-guide]: https://google.github.io/styleguide/jsguide.html
179 | [closing-issues]: https://help.github.com/articles/closing-issues-via-commit-messages/
180 | [docs]: https://github.com/jsGanttImproved/jsgantt-improved/wiki/Documentation
181 |
--------------------------------------------------------------------------------
/docs/fixes/lang.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | jsGantt Improved
10 |
11 |
12 |
13 |
14 |
15 |
16 |
18 |
20 |
21 |
22 |
23 |
25 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 | Choose a language:
43 |
44 | pt
45 | en
46 | ru
47 | es
48 | fr
49 | de
50 |
51 |
52 |
53 |
54 |
99 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/src/draw_columns.ts:
--------------------------------------------------------------------------------
1 | import { formatDateStr } from './utils/date_utils';
2 | import { AddTaskItemObject } from './task';
3 | import { addListenerInputCell, addListenerClickCell } from './events';
4 | import { newNode, makeInput } from './utils/draw_utils';
5 |
6 | export const COLUMN_ORDER = [
7 | 'vShowRes',
8 | 'vShowDur',
9 | 'vShowComp',
10 | 'vShowStartDate',
11 | 'vShowEndDate',
12 | 'vShowPlanStartDate',
13 | 'vShowPlanEndDate',
14 | 'vShowCost',
15 | 'vAdditionalHeaders',
16 | 'vShowAddEntries'
17 | ];
18 |
19 | const COLUMNS_TYPES = {
20 | 'vShowRes': 'res',
21 | 'vShowDur': 'dur',
22 | 'vShowComp': 'comp',
23 | 'vShowStartDate': 'startdate',
24 | 'vShowEndDate': 'enddate',
25 | 'vShowPlanStartDate': 'planstartdate',
26 | 'vShowPlanEndDate': 'planenddate',
27 | 'vShowCost': 'cost',
28 | 'vShowAddEntries': 'addentries'
29 | }
30 |
31 | export const draw_header = function (column, i, vTmpRow, vTaskList, vEditable, vEventsChange, vEvents,
32 | vDateTaskTableDisplayFormat, vAdditionalHeaders, vFormat, vLangs, vLang, vResources, Draw) {
33 | let vTmpCell, vTmpDiv;
34 |
35 | if ('vShowRes' === column) {
36 | vTmpCell = newNode(vTmpRow, 'td', null, 'gres');
37 | const text = makeInput(vTaskList[i].getResource(), vEditable, 'resource', vTaskList[i].getResource(), vResources);
38 | vTmpDiv = newNode(vTmpCell, 'div', null, null, text);
39 | const callback = (task, e) => task.setResource(e.target.value);
40 | addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'res', Draw, 'change');
41 | addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'res');
42 | }
43 | if ('vShowDur' === column) {
44 | vTmpCell = newNode(vTmpRow, 'td', null, 'gdur');
45 | const text = makeInput(vTaskList[i].getDuration(vFormat, vLangs[vLang]), vEditable, 'text', vTaskList[i].getDuration());
46 | vTmpDiv = newNode(vTmpCell, 'div', null, null, text);
47 | const callback = (task, e) => task.setDuration(e.target.value);
48 | addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'dur', Draw);
49 | addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'dur');
50 | }
51 | if ('vShowComp' === column) {
52 | vTmpCell = newNode(vTmpRow, 'td', null, 'gcomp');
53 | const text = makeInput(vTaskList[i].getCompStr(), vEditable, 'percentage', vTaskList[i].getCompVal());
54 | vTmpDiv = newNode(vTmpCell, 'div', null, null, text);
55 | const callback = (task, e) => { task.setComp(e.target.value); task.setCompVal(e.target.value); }
56 | addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'comp', Draw);
57 | addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'comp');
58 | }
59 | if ('vShowStartDate' === column) {
60 | vTmpCell = newNode(vTmpRow, 'td', null, 'gstartdate');
61 | const v = formatDateStr(vTaskList[i].getStartVar(), vDateTaskTableDisplayFormat, vLangs[vLang]);
62 | const text = makeInput(v, vEditable, 'date', vTaskList[i].getStartVar());
63 | vTmpDiv = newNode(vTmpCell, 'div', null, null, text);
64 | const callback = (task, e) => task.setStart(e.target.value);
65 | addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'start', Draw);
66 | addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'start');
67 | }
68 | if ('vShowEndDate' === column) {
69 | vTmpCell = newNode(vTmpRow, 'td', null, 'genddate');
70 | const v = formatDateStr(vTaskList[i].getEndVar(), vDateTaskTableDisplayFormat, vLangs[vLang]);
71 | const text = makeInput(v, vEditable, 'date', vTaskList[i].getEndVar());
72 | vTmpDiv = newNode(vTmpCell, 'div', null, null, text);
73 | const callback = (task, e) => task.setEnd(e.target.value);
74 | addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'end', Draw);
75 | addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'end');
76 | }
77 | if ('vShowPlanStartDate' === column) {
78 | vTmpCell = newNode(vTmpRow, 'td', null, 'gplanstartdate');
79 | const v = vTaskList[i].getPlanStart() ? formatDateStr(vTaskList[i].getPlanStart(), vDateTaskTableDisplayFormat, vLangs[vLang]) : '';
80 | const text = makeInput(v, vEditable, 'date', vTaskList[i].getPlanStart());
81 | vTmpDiv = newNode(vTmpCell, 'div', null, null, text);
82 | const callback = (task, e) => task.setPlanStart(e.target.value);
83 | addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'planstart', Draw);
84 | addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'planstart');
85 | }
86 | if ('vShowPlanEndDate' === column) {
87 | vTmpCell = newNode(vTmpRow, 'td', null, 'gplanenddate');
88 | const v = vTaskList[i].getPlanEnd() ? formatDateStr(vTaskList[i].getPlanEnd(), vDateTaskTableDisplayFormat, vLangs[vLang]) : '';
89 | const text = makeInput(v, vEditable, 'date', vTaskList[i].getPlanEnd());
90 | vTmpDiv = newNode(vTmpCell, 'div', null, null, text);
91 | const callback = (task, e) => task.setPlanEnd(e.target.value);
92 | addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'planend', Draw);
93 | addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'planend');
94 | }
95 | if ('vShowCost' === column) {
96 | vTmpCell = newNode(vTmpRow, 'td', null, 'gcost');
97 | const text = makeInput(vTaskList[i].getCost(), vEditable, 'cost');
98 | vTmpDiv = newNode(vTmpCell, 'div', null, null, text);
99 | const callback = (task, e) => task.setCost(e.target.value);
100 | addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'cost', Draw);
101 | addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'cost');
102 | }
103 |
104 | if ('vAdditionalHeaders' === column && vAdditionalHeaders) {
105 | for (const key in vAdditionalHeaders) {
106 | const header = vAdditionalHeaders[key];
107 | const css = header.class ? header.class : `gadditional-${key}`;
108 | const data = vTaskList[i].getDataObject();
109 | vTmpCell = newNode(vTmpRow, 'td', null, `gadditional ${css}`);
110 | vTmpDiv = newNode(vTmpCell, 'div', null, null, data ? data[key] : '');
111 | addListenerClickCell(vTmpCell, vEvents, vTaskList[i], `additional_${key}`);
112 | // const callback = (task, e) => task.setCost(e.target.value);
113 | // addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'costdate');
114 | }
115 | }
116 |
117 | if ('vShowAddEntries' === column) {
118 | vTmpCell = newNode(vTmpRow, 'td', null, 'gaddentries');
119 | const button = "+ ";
120 | vTmpDiv = newNode(vTmpCell, 'div', null, null, button);
121 |
122 | const callback = (task, e) => {
123 | AddTaskItemObject({
124 | vParent: task.getParent()
125 | });
126 | }
127 | addListenerInputCell(vTmpCell, vEventsChange, callback, vTaskList, i, 'addentries', Draw.bind(this));
128 | addListenerClickCell(vTmpCell, vEvents, vTaskList[i], 'addentries');
129 | }
130 | };
131 |
132 | export const draw_bottom = function (column, vTmpRow, vAdditionalHeaders) {
133 | if ('vAdditionalHeaders' === column && vAdditionalHeaders) {
134 | for (const key in vAdditionalHeaders) {
135 | const header = vAdditionalHeaders[key];
136 | const css = header.class ? header.class : `gadditional-${key}`;
137 | newNode(vTmpRow, 'td', null, `gspanning gadditional ${css}`, '\u00A0');
138 | }
139 | } else {
140 | const type = COLUMNS_TYPES[column];
141 | newNode(vTmpRow, 'td', null, `gspanning g${type}`, '\u00A0');
142 | }
143 | }
144 |
145 | // export const draw_list_headings = function (column, vTmpRow, vAdditionalHeaders, vEvents) {
146 | // let nodeCreated;
147 | // if ('vAdditionalHeaders' === column && vAdditionalHeaders) {
148 | // for (const key in vAdditionalHeaders) {
149 | // const header = vAdditionalHeaders[key];
150 | // const css = header.class ? header.class : `gadditional-${key}`;
151 | // newNode(vTmpRow, 'td', null, `gspanning gadditional ${css}`, '\u00A0');
152 | // }
153 | // } else {
154 | // const type = COLUMNS_TYPES[column];
155 | // nodeCreated = newNode(vTmpRow, 'td', null, `gspanning g${type}`, '\u00A0');
156 | // addListenerClickCell(nodeCreated, vEvents, { hader: true, column }, type);
157 | // }
158 | // }
159 |
160 | export const draw_task_headings = function (column, vTmpRow, vLangs, vLang, vAdditionalHeaders, vEvents) {
161 | let nodeCreated;
162 | if ('vAdditionalHeaders' === column && vAdditionalHeaders) {
163 | for (const key in vAdditionalHeaders) {
164 | const header = vAdditionalHeaders[key];
165 | const text = header.translate ? vLangs[vLang][header.translate] : header.title;
166 | const css = header.class ? header.class : `gadditional-${key}`;
167 | nodeCreated = newNode(vTmpRow, 'td', null, `gtaskheading gadditional ${css}`, text);
168 | }
169 | }
170 | else {
171 | const type = COLUMNS_TYPES[column];
172 | nodeCreated = newNode(vTmpRow, 'td', null, `gtaskheading g${type}`, vLangs[vLang][type]);
173 | addListenerClickCell(nodeCreated, vEvents, { hader: true, column }, type);
174 | }
175 | }
176 |
--------------------------------------------------------------------------------
/docs/fixes/data.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "pID": 1,
4 | "pName": "Define Chart API",
5 | "pStart": "",
6 | "pEnd": "",
7 | "pPlanStart": "",
8 | "pPlanEnd": "",
9 | "pClass": "ggroupblack",
10 | "pLink": "",
11 | "pMile": 0,
12 | "pRes": "Mario",
13 | "pComp": 0,
14 | "pGroup": 1,
15 | "pParent": 0,
16 | "pOpen": 1,
17 | "pDepend": "",
18 | "pCaption": "",
19 | "pNotes": "Some Notes text",
20 | "category": "Planning",
21 | "sector": "CMO"
22 | },
23 | {
24 | "pID": 11,
25 | "pName": "Chart Object",
26 | "pStart": "2019-06-20",
27 | "pEnd": "2019-07-20",
28 | "pClass": "gmilestone",
29 | "pLink": "",
30 | "pMile": 1,
31 | "pRes": "Henrique",
32 | "pComp": 100,
33 | "pGroup": 0,
34 | "pParent": 1,
35 | "pOpen": 1,
36 | "pDepend": "",
37 | "pCaption": "",
38 | "pNotes": "",
39 | "category": "Executive",
40 | "sector": "CEO"
41 | },
42 | {
43 | "pID": 12,
44 | "pName": "Task Objects",
45 | "pStart": "",
46 | "pEnd": "",
47 | "pClass": "ggroupblack",
48 | "pLink": "",
49 | "pMile": 0,
50 | "pRes": "Henrique",
51 | "pComp": 40,
52 | "pGroup": 1,
53 | "pParent": 1,
54 | "pOpen": 1,
55 | "pDepend": "",
56 | "pCaption": "",
57 | "pNotes": ""
58 | },
59 | {
60 | "pID": 121,
61 | "pName": "Constructor Proc #1234 of February 2019 ",
62 | "pStart": "2019-06-21",
63 | "pEnd": "",
64 | "pClass": "gtaskblue",
65 | "pLink": "",
66 | "pMile": 0,
67 | "pRes": "Pedro",
68 | "pComp": 60,
69 | "pGroup": 0,
70 | "pParent": 12,
71 | "pOpen": 1,
72 | "pDepend": "",
73 | "pCaption": "",
74 | "pNotes": "",
75 | "pDuration": "3 days",
76 | "deadline": "2019-07-01"
77 | },
78 | {
79 | "pID": 122,
80 | "pName": "Task Variables",
81 | "pStart": "2019-07-06",
82 | "pEnd": "2019-07-11",
83 | "pPlanStart": "2019-07-03",
84 | "pPlanEnd": "2019-07-09",
85 | "pClass": "gtaskred",
86 | "pLink": "",
87 | "pMile": 0,
88 | "pRes": "Mario John Silva",
89 | "pComp": 60,
90 | "pGroup": 0,
91 | "pParent": 12,
92 | "pOpen": 1,
93 | "pDepend": 121,
94 | "pCaption": "",
95 | "pNotes": "",
96 | "deadline": "2019-07-10"
97 | },
98 | {
99 | "pID": 123,
100 | "pName": "Task by Minute/Hour",
101 | "pStart": "2019-07-01",
102 | "pEnd": "2019-07-15 12:00",
103 | "pClass": "gtaskyellow",
104 | "pLink": "",
105 | "pMile": 0,
106 | "pRes": "Mario",
107 | "pComp": 60,
108 | "pGroup": 0,
109 | "pParent": 12,
110 | "pOpen": 1,
111 | "pDepend": "",
112 | "pCaption": "",
113 | "pNotes": "",
114 | "pCost": 1000
115 | },
116 | {
117 | "pID": 124,
118 | "pName": "Test Plan End",
119 | "pStart": "2019-07-09",
120 | "pEnd": "2019-07-29",
121 | "pPlanStart": "2019-07-09",
122 | "pPlanEnd": "2019-09-29",
123 | "pClass": "gtaskred",
124 | "pLink": "",
125 | "pMile": 0,
126 | "pRes": "Anyone",
127 | "pComp": 60,
128 | "pGroup": 0,
129 | "pParent": 12,
130 | "pOpen": 1,
131 | "pDepend": "123SS",
132 | "pCaption": "This is a caption",
133 | "pNotes": null,
134 | "pCost": 34,
135 | "deadline": "2019-09-05"
136 | },
137 | {
138 | "pID": 2,
139 | "pName": "Create HTML Shell",
140 | "pStart": "2019-07-24",
141 | "pEnd": "2019-07-24",
142 | "pClass": "gtaskyellow",
143 | "pLink": "",
144 | "pMile": 0,
145 | "pRes": "Mario",
146 | "pComp": 20,
147 | "pGroup": 0,
148 | "pParent": 0,
149 | "pOpen": 1,
150 | "pDepend": 122,
151 | "pCaption": "",
152 | "pNotes": ""
153 | },
154 | {
155 | "pID": 3,
156 | "pName": "Code Javascript",
157 | "pStart": "",
158 | "pEnd": "",
159 | "pClass": "ggroupblack",
160 | "pLink": "",
161 | "pMile": 0,
162 | "pRes": "Mario",
163 | "pComp": 0,
164 | "pGroup": 1,
165 | "pParent": 0,
166 | "pOpen": 1,
167 | "pDepend": "",
168 | "pCaption": "",
169 | "pNotes": ""
170 | },
171 | {
172 | "pID": 31,
173 | "pName": "Define Variables",
174 | "pStart": "2019-06-25",
175 | "pEnd": "2019-07-17",
176 | "pPlanStart": "2019-06-24",
177 | "pPlanEnd": "2019-07-15 12:00",
178 | "pClass": "gtaskpurple",
179 | "pLink": "",
180 | "pMile": 0,
181 | "pRes": "Mario",
182 | "pComp": 30,
183 | "pGroup": 0,
184 | "pParent": 3,
185 | "pOpen": 1,
186 | "pDepend": "",
187 | "pCaption": "",
188 | "pNotes": ""
189 | },
190 | {
191 | "pID": 32,
192 | "pName": "Calculate One Day",
193 | "pStart": "2019-07-15 00:00",
194 | "pEnd": "2019-07-16 00:00",
195 | "pClass": "gtaskgreen",
196 | "pLink": "",
197 | "pMile": 0,
198 | "pRes": "Henrique",
199 | "pComp": 40,
200 | "pGroup": 0,
201 | "pParent": 3,
202 | "pOpen": 1,
203 | "pDepend": "",
204 | "pCaption": "",
205 | "pNotes": ""
206 | },
207 | {
208 | "pID": 33,
209 | "pName": "Draw Task Items",
210 | "pStart": "",
211 | "pEnd": "",
212 | "pClass": "ggroupblack",
213 | "pLink": "",
214 | "pMile": 0,
215 | "pRes": "Someone",
216 | "pComp": 40,
217 | "pGroup": 2,
218 | "pParent": 3,
219 | "pOpen": 1,
220 | "pDepend": "",
221 | "pCaption": "",
222 | "pNotes": ""
223 | },
224 | {
225 | "pID": 332,
226 | "pName": "Task Label Table",
227 | "pStart": "2019-07-06",
228 | "pEnd": "2019-07-09",
229 | "pClass": "gtaskblue",
230 | "pLink": "",
231 | "pMile": 0,
232 | "pRes": "Mario",
233 | "pComp": 60,
234 | "pGroup": 0,
235 | "pParent": 33,
236 | "pOpen": 1,
237 | "pDepend": "",
238 | "pCaption": "",
239 | "pNotes": ""
240 | },
241 | {
242 | "pID": 333,
243 | "pName": "Task Scrolling Grid",
244 | "pStart": "2019-07-11",
245 | "pEnd": "2019-07-20",
246 | "pClass": "gtaskblue",
247 | "pLink": "",
248 | "pMile": 0,
249 | "pRes": "Mario",
250 | "pComp": 0,
251 | "pGroup": 0,
252 | "pParent": 33,
253 | "pOpen": 1,
254 | "pDepend": "332",
255 | "pCaption": "",
256 | "pNotes": ""
257 | },
258 | {
259 | "pID": 34,
260 | "pName": "Draw Task Bars ",
261 | "pStart": "",
262 | "pEnd": "",
263 | "pClass": "ggroupblack",
264 | "pLink": "",
265 | "pMile": 0,
266 | "pRes": "Anybody",
267 | "pComp": 67,
268 | "pGroup": 1,
269 | "pParent": 3,
270 | "pOpen": 0,
271 | "pDepend": "",
272 | "pCaption": "",
273 | "pNotes": ""
274 | },
275 | {
276 | "pID": 341,
277 | "pName": "Test Bar Task",
278 | "pStart": "2019-03-26",
279 | "pEnd": "2019-04-11",
280 | "pClass": "gtaskred",
281 | "pLink": "",
282 | "pMile": 0,
283 | "pRes": "Mario",
284 | "pComp": 60,
285 | "pGroup": 0,
286 | "pParent": 34,
287 | "pOpen": 1,
288 | "pDepend": "",
289 | "pCaption": "",
290 | "pNotes": "",
291 | "pBarText": "this is a bar text"
292 | },
293 | {
294 | "pID": "A342",
295 | "pName": "Calculate Start/Stop",
296 | "pStart": "2019-04-12",
297 | "pEnd": "2019-05-18",
298 | "pClass": "gtaskpink",
299 | "pLink": "",
300 | "pMile": 0,
301 | "pRes": "Mario",
302 | "pComp": 60,
303 | "pGroup": 0,
304 | "pParent": 34,
305 | "pOpen": 1,
306 | "pDepend": "",
307 | "pCaption": "",
308 | "pNotes": ""
309 | },
310 | {
311 | "pID": 343,
312 | "pName": "Draw Task Div",
313 | "pStart": "2019-05-13",
314 | "pEnd": "2019-05-17",
315 | "pClass": "gtaskred",
316 | "pLink": "",
317 | "pMile": 0,
318 | "pRes": "Mario",
319 | "pComp": 60,
320 | "pGroup": 0,
321 | "pParent": 34,
322 | "pOpen": 1,
323 | "pDepend": "",
324 | "pCaption": "",
325 | "pNotes": ""
326 | },
327 | {
328 | "pID": 344,
329 | "pName": "Draw Completion Div",
330 | "pStart": "2019-05-17",
331 | "pEnd": "2019-06-04",
332 | "pClass": "gtaskred",
333 | "pLink": "",
334 | "pMile": 0,
335 | "pRes": "Mario",
336 | "pComp": 60,
337 | "pGroup": 0,
338 | "pParent": 34,
339 | "pOpen": 1,
340 | "pDepend": "A342,343",
341 | "pCaption": "",
342 | "pNotes": ""
343 | },
344 | {
345 | "pID": 35,
346 | "pName": "Just plan dates",
347 | "pPlanStart": "2019-07-17",
348 | "pPlanEnd": "2021-09-15",
349 | "pClass": "gtaskpurple",
350 | "pLink": "",
351 | "pMile": 0,
352 | "pRes": "Mario",
353 | "pComp": 0,
354 | "pGroup": 0,
355 | "pParent": 3,
356 | "pOpen": 1,
357 | "pDepend": "333",
358 | "pCaption": "",
359 | "pNotes": ""
360 | },
361 | {
362 | "pID": 36,
363 | "pName": "Just dates",
364 | "pStart": "2019-07-17",
365 | "pEnd": "2021-09-15",
366 | "pClass": "gtaskpurple",
367 | "pLink": "",
368 | "pMile": 0,
369 | "pRes": "Mario",
370 | "pComp": 0,
371 | "pGroup": 0,
372 | "pParent": 3,
373 | "pOpen": 1,
374 | "pDepend": "333",
375 | "pCaption": "",
376 | "pNotes": ""
377 | }
378 | ]
--------------------------------------------------------------------------------
/docs/demo-plan-color.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | jsGantt Improved
10 |
11 |
12 |
13 |
41 |
42 |
43 |
46 |
49 |
50 |
51 |
52 |
54 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
Chart
71 |
72 |
73 |
74 |
75 |
138 |
139 |
140 |
141 |
142 | Choose a language:
143 |
144 | Chinese (cn)
145 | Czech (cs)
146 | Dutch (Standard)
147 | English (en)
148 | French (fr)
149 | German (de)
150 | Hungarian (hu)
151 | Indonesian (id)
152 | Japanese (ja)
153 | Portuguese (pt)
154 | Russian (ru)
155 | Spanish (es)
156 | Swedish (sv)
157 | Turkish (tr)
158 | Ukraininan (ua)
159 |
160 |
161 |
162 | Data getting from a URL,
JSON Data . Use `bigdata.json` to test with more
163 | data.
164 |
165 |
166 |
167 | Delay for tooltip to hide (in ms):
168 |
169 | UseSingleCell:
170 |
171 |
172 |
173 |
174 |
175 | Configure debug and see in console:
176 |
177 |
178 |
179 |
180 |
181 |
182 | Configure editable:
183 |
184 |
185 |
186 |
187 |
188 |
189 | Configure sortTasks:
190 |
191 |
192 |
193 |
194 | Show Weekenddays:
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
Height:
206 |
207 | (CSS style height like "300px". Empty for auto height)
208 |
209 |
210 |
211 |
212 | Custom elements (black lines for deadlines):
213 |
214 |
215 | Min Date:
216 |
217 |
218 | Max Date:
219 |
220 |
221 |
222 |
223 |
224 |
225 | Custom Tooltip Template:
226 |
228 |
229 | Dynamic tooltip:
230 |
231 |
232 |
233 |
234 |
235 |
236 | Custom columns order:
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 | Clear all tasks
247 |
248 |
249 |
250 | Print data in console
251 |
252 |
253 |
254 |
255 |
256 | A4 Portrait
257 | A4 Landscape
258 | A3 Portrait
259 | A3 Landscape
260 | A2 Portrait
261 | A2 Landscape
262 | A1 Portrait
263 | A1 Landscape
264 | A0 Portrait
265 | A0 Landscape
266 |
267 |
Print
268 |
269 |
270 |
271 |
272 | * Click events in table are binded to console.log for testing
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
--------------------------------------------------------------------------------
/docs/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | jsGantt Improved
10 |
11 |
12 |
13 |
40 |
41 |
42 |
45 |
48 |
49 |
50 |
51 |
53 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
--------------------------------------------------------------------------------
/docs/index.js:
--------------------------------------------------------------------------------
1 | let dataurl;
2 | let jsonObj;
3 | let g;
4 |
5 | function start(e) {
6 | g = new JSGantt.GanttChart(document.getElementById("embedded-Gantt"), "week");
7 | if (g.getDivId() != null) {
8 | const newDataurl = document.getElementById("dataurl").value ? document.getElementById("dataurl").value : "./fixes/data.json";
9 | const vDebug = document.querySelector("#debug:checked") ? true : false;
10 | //vDebug = true;
11 | const vEditable = document.querySelector("#editable:checked") ? true : false;
12 | const vUseSort = document.querySelector("#sort:checked") ? true : false;
13 | const newtooltiptemplate = document.getElementById("tooltiptemplate").value ? document.getElementById("tooltiptemplate").value : null;
14 | let vColumnOrder;
15 | if (document.querySelector("#vColumnOrder").value) {
16 | vColumnOrder = document.querySelector("#vColumnOrder").value.split(",");
17 | }
18 |
19 | const vScrollTo = "today"; // or new Date() or a Date object with a specific date
20 |
21 | // SET LANG FROM INPUT
22 | lang = e && e.target ? e.target.value : "en";
23 | delay = document.getElementById("delay").value;
24 |
25 | vUseSingleCell = document.getElementById("useSingleCell").value;
26 | vShowRes = document.querySelector("#vShowRes:checked") ? 1 : 0;
27 | vShowCost = document.querySelector("#vShowCost:checked") ? 1 : 0;
28 | vShowAddEntries = document.querySelector("#vShowAddEntries:checked") ? 1 : 0;
29 | vShowComp = document.querySelector("#vShowComp:checked") ? 1 : 0;
30 | vShowDur = document.querySelector("#vShowDur:checked") ? 1 : 0;
31 | vShowStartDate = document.querySelector("#vShowStartDate:checked") ? 1 : 0;
32 | vShowEndDate = document.querySelector("#vShowEndDate:checked") ? 1 : 0;
33 | vShowPlanStartDate = document.querySelector("#vShowPlanStartDate:checked") ? 1 : 0;
34 | vShowPlanEndDate = document.querySelector("#vShowPlanEndDate:checked") ? 1 : 0;
35 | vShowTaskInfoLink = document.querySelector("#vShowTaskInfoLink:checked") ? 1 : 0;
36 | vShowEndWeekDate = document.querySelector("#vShowEndWeekDate:checked") ? 1 : 0;
37 | vTotalHeight = document.querySelector("#vTotalHeight").value || undefined;
38 |
39 | vShowWeekends = document.querySelector("#vShowWeekends:checked") ? 1 : 0;
40 |
41 | vMinDate = document.querySelector("#vMinDate").value;
42 | vMaxDate = document.querySelector("#vMaxDate").value;
43 |
44 | vAdditionalHeaders = {
45 | category: {
46 | title: "Category",
47 | },
48 | sector: {
49 | title: "Sector",
50 | },
51 | };
52 |
53 | g.setOptions({
54 | vCaptionType: "Complete", // Set to Show Caption : None,Caption,Resource,Duration,Complete,
55 | vQuarterColWidth: 36,
56 | vDateTaskDisplayFormat: "day dd month yyyy", // Shown in tool tip box
57 | vDayMajorDateDisplayFormat: "mon yyyy - Week ww", // Set format to display dates in the "Major" header of the "Day" view
58 | vWeekMinorDateDisplayFormat: "dd mon", // Set format to display dates in the "Minor" header of the "Week" view
59 | vLang: lang,
60 | vUseSingleCell, // Set the threshold at which we will only use one cell per table row (0 disables). Helps with rendering performance for large charts.
61 | vShowRes,
62 | vShowCost,
63 | vShowAddEntries,
64 | vShowComp,
65 | vShowDur,
66 | vShowStartDate,
67 | vShowEndDate,
68 | vShowPlanStartDate,
69 | vShowPlanEndDate,
70 | vTotalHeight,
71 | vMinDate,
72 | vMaxDate,
73 | // EVENTs
74 | vEvents: {
75 | taskname: console.log,
76 | res: console.log,
77 | dur: console.log,
78 | comp: console.log,
79 | start: console.log,
80 | end: console.log,
81 | planstart: console.log,
82 | planend: console.log,
83 | cost: console.log,
84 | additional_category: console.log,
85 | beforeDraw: () => console.log("before draw listener"),
86 | afterDraw: () => {
87 | console.log("after draw listener");
88 | if (document.querySelector("#customElements:checked")) {
89 | drawCustomElements(g);
90 | }
91 | },
92 | },
93 | vEventsChange: {
94 | taskname: editValue, // if you need to use the this scope, do: editValue.bind(this)
95 | res: editValue,
96 | dur: editValue,
97 | comp: editValue,
98 | start: editValue,
99 | end: editValue,
100 | planstart: editValue,
101 | planend: editValue,
102 | cost: editValue,
103 | },
104 | vEventClickRow: console.log,
105 | vEventClickCollapse: console.log,
106 |
107 | vResources: [
108 | { id: 0, name: "Anybody" },
109 | { id: 1, name: "Mario" },
110 | { id: 2, name: "Henrique" },
111 | { id: 3, name: "Pedro" },
112 | ],
113 |
114 | vShowTaskInfoLink, // Show link in tool tip (0/1)
115 | vShowEndWeekDate, // Show/Hide the date for the last day of the week in header for daily view (1/0)
116 | vShowWeekends, // Show weekends days in the vFormat day
117 | vTooltipDelay: delay,
118 | vTooltipTemplate: document.querySelector("#dynamicTooltip:checked") ? generateTooltip : newtooltiptemplate,
119 | vDebug,
120 | vEditable,
121 | vColumnOrder,
122 | vScrollTo,
123 | vUseSort,
124 | vFormat: "week",
125 | vFormatArr: ["Day", "Week", "Month", "Quarter"], // Even with setUseSingleCell using Hour format on such a large chart can cause issues in some browsers
126 | });
127 | //DELAY FROM INPUT
128 |
129 | // Teste manual add task
130 | // g.AddTaskItemObject({
131 | // pID: 100,
132 | // pName: "Task 1",
133 | // pStart: "2018-09-05",
134 | // pEnd: "2018-09-11",
135 | // pLink: "",
136 | // pClass: "gtaskgreen",
137 | // pMile: 0,
138 | // pComp: 100,
139 | // pGroup: 0,
140 | // pParent: 0,
141 | // pOpen: 1,
142 | // pNotes: "",
143 | // category: 'test'
144 | // });
145 |
146 | // Parameters (pID, pName, pStart, pEnd, pStyle, pLink (unused) pLink: pMilpMile: e, pRes, pComp, pGroup, pParent, pOpen, pDepend, pCaption, pNotes, pGantt)
147 | if (dataurl !== newDataurl) {
148 | dataurl = newDataurl;
149 | JSGantt.parseJSON(dataurl, g, vDebug).then((j) => (jsonObj = j));
150 | } else {
151 | JSGantt.addJSONTask(g, jsonObj);
152 | }
153 | /*
154 | // Add Custom tasks programatically
155 | g.AddTaskItem(new JSGantt.TaskItem(1, 'Task Objects', '', '', 'ggroupblack', '', 0, 'Shlomy', 40, 1, 0, '', '', '', '', g));
156 | g.AddTaskItem(new JSGantt.TaskItem(121, 'Constructor Proc', '2019-08-20', '2020-03-06', 'gtaskblue', '', 0, 'Brian T.', 60, 0, 1, 1, '', '', '', g));
157 | g.AddTaskItem(new JSGantt.TaskItem(122, 'Task Variables', '2019-08-20', '2020-03-06', 'gtaskred', '', 0, 'Brian', 60, 0, 1, 1, 121, '', '', g));
158 | g.AddTaskItem(new JSGantt.TaskItem(123, 'Task by Minute/Hour', '2019-08-20', '2020-03-06 12:00', 'gtaskyellow', '', 0, 'Ilan', 60, 0, 1, 1, '', '', '', g));
159 | g.AddTaskItem(new JSGantt.TaskItem(124, 'Task Functions', '2019-08-20', '2020-03-06', 'gtaskred', '', 0, 'Anyone', 60, 0, 1, 1, '123', 'This is a caption', null, g));
160 | */
161 |
162 | if (vDebug) {
163 | bd = new Date();
164 | console.log("before reloading", bd);
165 | }
166 | g.Draw();
167 | //JSGantt.criticalPath(jsonObj)
168 | if (vDebug) {
169 | const ad = new Date();
170 | console.log("after reloading: total time", ad, ad.getTime() - bd.getTime());
171 | }
172 | } else {
173 | alert("Error, unable to create Gantt Chart");
174 | }
175 |
176 | // document.getElementById("idMainLeft").onscroll = () => {
177 | // scrollingTwoMains('idMainLeft', 'idMainRight');
178 | // };
179 |
180 | // document.getElementById('idMainRight').onscroll = () => {
181 | // scrollingTwoMains('idMainRight', 'idMainLeft');
182 | // };
183 | }
184 |
185 | function scrollingTwoMains(mainMoving, mainMoved) {
186 | document.getElementById(mainMoved).scrollTop = document.getElementById(mainMoving).scrollTop;
187 | }
188 |
189 | function clearTasks() {
190 | g.ClearTasks();
191 | g.Draw();
192 | }
193 |
194 | function printTasksInConsole() {
195 | const tasks = g.vTaskList.map((e) => ({ ...e.getAllData(), ...e.getDataObject() }));
196 | console.log(tasks);
197 | }
198 |
199 | function printChart() {
200 | let width, height;
201 | [width, height] = document.querySelector("#print_page_size").value.split(",");
202 | g.printChart(width, height);
203 | }
204 |
205 | function editValue(list, task, event, cell, column) {
206 | console.log(list, task, event, cell, column);
207 | const found = list.find((item) => item.pID == task.getOriginalID());
208 | if (!found) {
209 | return;
210 | } else {
211 | found[column] = event ? event.target.value : "";
212 | }
213 | }
214 |
215 | function drawCustomElements(g) {
216 | for (const item of g.getList()) {
217 | const dataObj = item.getDataObject();
218 | if (dataObj && dataObj.deadline) {
219 | const x = g.chartRowDateToX(new Date(dataObj.deadline));
220 | const td = item.getChildRow().querySelector("td");
221 | td.style.position = "relative";
222 | const div = document.createElement("div");
223 | div.style.left = `${x}px`;
224 | div.classList.add("deadline-line");
225 | td.appendChild(div);
226 | }
227 | }
228 | }
229 |
230 | function generateTooltip(task) {
231 | // default tooltip for level 1
232 | if (task.getLevel() === 1) return;
233 |
234 | // string tooltip for level 2. Show completed/total child count and current timestamp
235 | if (task.getLevel() === 2) {
236 | let childCount = 0;
237 | let complete = 0;
238 | for (const item of g.getList()) {
239 | if (item.getParent() == task.getID()) {
240 | if (item.getCompVal() === 100) {
241 | complete++;
242 | }
243 | childCount++;
244 | }
245 | }
246 | console.log(`Generated dynamic sync template for '${task.getName()}'`);
247 | return `
248 |
249 | Name: {{pName}}
250 | Complete child tasks: ${complete}/${childCount}
251 | Tooltip generated at: ${new Date()}
252 |
253 | `;
254 | }
255 |
256 | // async tooltip for level 3 and below
257 | return new Promise((resolve, reject) => {
258 | const delay = Math.random() * 3000;
259 | setTimeout(() => {
260 | console.log(`Generated dynamic async template for '${task.getName()}'`);
261 | resolve(`Tooltip content from the promise after ${Math.round(delay)}ms`);
262 | }, delay);
263 | });
264 | }
265 |
266 | start("pt");
267 |
--------------------------------------------------------------------------------
/src/utils/date_utils.ts:
--------------------------------------------------------------------------------
1 |
2 | /**
3 | * DATES
4 | */
5 | export const getMinDate = function (pList, pFormat, pMinDate) {
6 | let vDate = new Date();
7 | if (pList.length <= 0) return pMinDate || vDate;
8 |
9 | vDate.setTime((pMinDate && pMinDate.getTime()) || pList[0].getStart().getTime());
10 |
11 | // Parse all Task Start dates to find min
12 | for (let i = 0; i < pList.length; i++) {
13 | if (pList[i].getStart().getTime() < vDate.getTime()) vDate.setTime(pList[i].getStart().getTime());
14 | if (pList[i].getPlanStart() && pList[i].getPlanStart().getTime() < vDate.getTime()) vDate.setTime(pList[i].getPlanStart().getTime());
15 | }
16 |
17 | // Adjust min date to specific format boundaries (first of week or first of month)
18 | if (pFormat == 'day') {
19 | vDate.setDate(vDate.getDate() - 1);
20 | while (vDate.getDay() % 7 != 1) vDate.setDate(vDate.getDate() - 1);
21 | }
22 | else if (pFormat == 'week') {
23 | vDate.setDate(vDate.getDate() - 1);
24 | while (vDate.getDay() % 7 != 1) vDate.setDate(vDate.getDate() - 1);
25 | }
26 | else if (pFormat == 'month') {
27 | vDate.setDate(vDate.getDate() - 15);
28 | while (vDate.getDate() > 1) vDate.setDate(vDate.getDate() - 1);
29 | }
30 | else if (pFormat == 'quarter') {
31 | vDate.setDate(vDate.getDate() - 31);
32 | if (vDate.getMonth() == 0 || vDate.getMonth() == 1 || vDate.getMonth() == 2)
33 | vDate.setFullYear(vDate.getFullYear(), 0, 1);
34 | else if (vDate.getMonth() == 3 || vDate.getMonth() == 4 || vDate.getMonth() == 5)
35 | vDate.setFullYear(vDate.getFullYear(), 3, 1);
36 | else if (vDate.getMonth() == 6 || vDate.getMonth() == 7 || vDate.getMonth() == 8)
37 | vDate.setFullYear(vDate.getFullYear(), 6, 1);
38 | else if (vDate.getMonth() == 9 || vDate.getMonth() == 10 || vDate.getMonth() == 11)
39 | vDate.setFullYear(vDate.getFullYear(), 9, 1);
40 | }
41 | else if (pFormat == 'hour') {
42 | vDate.setHours(vDate.getHours() - 1);
43 | while (vDate.getHours() % 6 != 0) vDate.setHours(vDate.getHours() - 1);
44 | }
45 |
46 | if (pFormat == 'hour') vDate.setMinutes(0, 0);
47 | else vDate.setHours(0, 0, 0);
48 | return (vDate);
49 | };
50 |
51 | export const getMaxDate = function (pList, pFormat, pMaxDate) {
52 | let vDate = new Date();
53 |
54 | if (pList.length <= 0) return pMaxDate || vDate;
55 |
56 | vDate.setTime((pMaxDate && pMaxDate.getTime()) || pList[0].getEnd().getTime());
57 |
58 | // Parse all Task End dates to find max
59 | for (let i = 0; i < pList.length; i++) {
60 | if (pList[i].getEnd().getTime() > vDate.getTime()) vDate.setTime(pList[i].getEnd().getTime());
61 | if (pList[i].getPlanEnd() && pList[i].getPlanEnd().getTime() > vDate.getTime()) vDate.setTime(pList[i].getPlanEnd().getTime());
62 | }
63 |
64 | // Adjust max date to specific format boundaries (end of week or end of month)
65 | if (pFormat == 'day') {
66 | vDate.setDate(vDate.getDate() + 1);
67 | while (vDate.getDay() % 7 != 0) vDate.setDate(vDate.getDate() + 1);
68 | }
69 | else if (pFormat == 'week') {
70 | //For weeks, what is the last logical boundary?
71 | vDate.setDate(vDate.getDate() + 1);
72 |
73 | while (vDate.getDay() % 7 != 0) vDate.setDate(vDate.getDate() + 1);
74 | }
75 | else if (pFormat == 'month') {
76 | // Set to last day of current Month
77 | while (vDate.getDate() > 1) vDate.setDate(vDate.getDate() + 1);
78 | vDate.setDate(vDate.getDate() - 1);
79 | }
80 | else if (pFormat == 'quarter') {
81 | // Set to last day of current Quarter
82 | if (vDate.getMonth() == 0 || vDate.getMonth() == 1 || vDate.getMonth() == 2)
83 | vDate.setFullYear(vDate.getFullYear(), 2, 31);
84 | else if (vDate.getMonth() == 3 || vDate.getMonth() == 4 || vDate.getMonth() == 5)
85 | vDate.setFullYear(vDate.getFullYear(), 5, 30);
86 | else if (vDate.getMonth() == 6 || vDate.getMonth() == 7 || vDate.getMonth() == 8)
87 | vDate.setFullYear(vDate.getFullYear(), 8, 30);
88 | else if (vDate.getMonth() == 9 || vDate.getMonth() == 10 || vDate.getMonth() == 11)
89 | vDate.setFullYear(vDate.getFullYear(), 11, 31);
90 | }
91 | else if (pFormat == 'hour') {
92 | if (vDate.getHours() == 0) vDate.setDate(vDate.getDate() + 1);
93 | vDate.setHours(vDate.getHours() + 1);
94 |
95 | while (vDate.getHours() % 6 != 5) vDate.setHours(vDate.getHours() + 1);
96 | }
97 | return (vDate);
98 | };
99 |
100 | export const coerceDate = function (date) {
101 | if (date instanceof Date) {
102 | return date;
103 | } else {
104 | const temp = new Date(date);
105 | if (temp instanceof Date && !isNaN(temp.valueOf())) {
106 | return temp;
107 | }
108 | }
109 | }
110 |
111 | export const parseDateStr = function (pDateStr, pFormatStr) {
112 | let vDate = new Date();
113 | let vDateParts = pDateStr.split(/[^0-9]/);
114 | if (pDateStr.length >= 10 && vDateParts.length >= 3) {
115 | while (vDateParts.length < 5) vDateParts.push(0);
116 |
117 | switch (pFormatStr) {
118 | case 'mm/dd/yyyy':
119 | vDate = new Date(vDateParts[2], vDateParts[0] - 1, vDateParts[1], vDateParts[3], vDateParts[4]);
120 | break;
121 | case 'dd/mm/yyyy':
122 | vDate = new Date(vDateParts[2], vDateParts[1] - 1, vDateParts[0], vDateParts[3], vDateParts[4]);
123 | break;
124 | case 'yyyy-mm-dd':
125 | vDate = new Date(vDateParts[0], vDateParts[1] - 1, vDateParts[2], vDateParts[3], vDateParts[4]);
126 | break;
127 | case 'yyyy-mm-dd HH:MI:SS':
128 | vDate = new Date(vDateParts[0], vDateParts[1] - 1, vDateParts[2], vDateParts[3], vDateParts[4], vDateParts[5]);
129 | break;
130 | }
131 | }
132 | return (vDate);
133 | };
134 |
135 | export const formatDateStr = function (pDate, pDateFormatArr, pL) {
136 | // Fix on issue #303 - getXMLTask is passing null as pDates
137 | if (!pDate) {
138 | return;
139 | }
140 | let vDateStr = '';
141 |
142 | let vYear2Str = pDate.getFullYear().toString().substring(2, 4);
143 | let vMonthStr = (pDate.getMonth() + 1) + '';
144 | let vMonthArr = new Array(pL['january'], pL['february'], pL['march'], pL['april'], pL['maylong'], pL['june'], pL['july'], pL['august'], pL['september'], pL['october'], pL['november'], pL['december']);
145 | let vDayArr = new Array(pL['sunday'], pL['monday'], pL['tuesday'], pL['wednesday'], pL['thursday'], pL['friday'], pL['saturday']);
146 | let vMthArr = new Array(pL['jan'], pL['feb'], pL['mar'], pL['apr'], pL['may'], pL['jun'], pL['jul'], pL['aug'], pL['sep'], pL['oct'], pL['nov'], pL['dec']);
147 | let vDyArr = new Array(pL['sun'], pL['mon'], pL['tue'], pL['wed'], pL['thu'], pL['fri'], pL['sat']);
148 |
149 | for (let i = 0; i < pDateFormatArr.length; i++) {
150 | switch (pDateFormatArr[i]) {
151 | case 'dd':
152 | if (pDate.getDate() < 10) vDateStr += '0'; // now fall through
153 | case 'd':
154 | vDateStr += pDate.getDate();
155 | break;
156 | case 'day':
157 | vDateStr += vDyArr[pDate.getDay()];
158 | break;
159 | case 'DAY':
160 | vDateStr += vDayArr[pDate.getDay()];
161 | break;
162 | case 'mm':
163 | if (parseInt(vMonthStr, 10) < 10) vDateStr += '0'; // now fall through
164 | case 'm':
165 | vDateStr += vMonthStr;
166 | break;
167 | case 'mon':
168 | vDateStr += vMthArr[pDate.getMonth()];
169 | break;
170 | case 'month':
171 | vDateStr += vMonthArr[pDate.getMonth()];
172 | break;
173 | case 'yyyy':
174 | vDateStr += pDate.getFullYear();
175 | break;
176 | case 'yy':
177 | vDateStr += vYear2Str;
178 | break;
179 | case 'qq':
180 | vDateStr += pL['qtr']; // now fall through
181 | case 'q':
182 | vDateStr += Math.floor(pDate.getMonth() / 3) + 1;
183 | break;
184 | case 'hh':
185 | if ((((pDate.getHours() % 12) == 0) ? 12 : pDate.getHours() % 12) < 10) vDateStr += '0'; // now fall through
186 | case 'h':
187 | vDateStr += ((pDate.getHours() % 12) == 0) ? 12 : pDate.getHours() % 12;
188 | break;
189 | case 'HH':
190 | if ((pDate.getHours()) < 10) vDateStr += '0'; // now fall through
191 | case 'H':
192 | vDateStr += (pDate.getHours());
193 | break;
194 | case 'MI':
195 | if (pDate.getMinutes() < 10) vDateStr += '0'; // now fall through
196 | case 'mi':
197 | vDateStr += pDate.getMinutes();
198 | break;
199 | case 'SS':
200 | if (pDate.getSeconds() < 10)
201 | vDateStr += '0'; // now fall through
202 | case 'ss':
203 | vDateStr += pDate.getSeconds();
204 | break;
205 | case 'pm':
206 | vDateStr += ((pDate.getHours()) < 12) ? 'am' : 'pm';
207 | break;
208 | case 'PM':
209 | vDateStr += ((pDate.getHours()) < 12) ? 'AM' : 'PM';
210 | break;
211 | case 'ww':
212 | if (getIsoWeek(pDate) < 10) vDateStr += '0'; // now fall through
213 | case 'w':
214 | vDateStr += getIsoWeek(pDate);
215 | break;
216 | case 'week':
217 | let vWeekNum = getIsoWeek(pDate);
218 | let vYear = pDate.getFullYear();
219 | let vDayOfWeek = (pDate.getDay() == 0) ? 7 : pDate.getDay();
220 | if (vWeekNum >= 52 && parseInt(vMonthStr, 10) === 1) vYear--;
221 | if (vWeekNum == 1 && parseInt(vMonthStr, 10) === 12) vYear++;
222 | if (vWeekNum < 10) vWeekNum = parseInt('0' + vWeekNum, 10);
223 |
224 | vDateStr += vYear + '-W' + vWeekNum + '-' + vDayOfWeek;
225 | break;
226 | default:
227 | if (pL[pDateFormatArr[i].toLowerCase()]) vDateStr += pL[pDateFormatArr[i].toLowerCase()];
228 | else vDateStr += pDateFormatArr[i];
229 | break;
230 | }
231 | }
232 | return vDateStr;
233 | };
234 |
235 | export const parseDateFormatStr = function (pFormatStr) {
236 | let vComponantStr = '';
237 | let vCurrChar = '';
238 | let vSeparators = new RegExp('[\/\\ -.,\'":]');
239 | let vDateFormatArray = new Array();
240 |
241 | for (let i = 0; i < pFormatStr.length; i++) {
242 | vCurrChar = pFormatStr.charAt(i);
243 | if ((vCurrChar.match(vSeparators)) || (i + 1 == pFormatStr.length)) // separator or end of string
244 | {
245 | if ((i + 1 == pFormatStr.length) && (!(vCurrChar.match(vSeparators)))) // at end of string add any non-separator chars to the current component
246 | {
247 | vComponantStr += vCurrChar;
248 | }
249 | vDateFormatArray.push(vComponantStr);
250 | if (vCurrChar.match(vSeparators)) vDateFormatArray.push(vCurrChar);
251 | vComponantStr = '';
252 | }
253 | else {
254 | vComponantStr += vCurrChar;
255 | }
256 |
257 | }
258 | return vDateFormatArray;
259 | };
260 |
261 | /**
262 | * We have to compare against the monday of the first week of the year containing 04 jan *not* 01/01
263 | * 60*60*24*1000=86400000
264 | * @param pDate
265 | */
266 | export const getIsoWeek = function (pDate) {
267 | let dayMiliseconds = 86400000;
268 | let keyDay = new Date(pDate.getFullYear(), 0, 4, 0, 0, 0);
269 | let keyDayOfWeek = (keyDay.getDay() == 0) ? 6 : keyDay.getDay() - 1; // define monday as 0
270 | let firstMondayYearTime = keyDay.getTime() - (keyDayOfWeek * dayMiliseconds);
271 | let thisDate = new Date(pDate.getFullYear(), pDate.getMonth(), pDate.getDate(), 0, 0, 0); // This at 00:00:00
272 | let thisTime = thisDate.getTime();
273 | let daysFromFirstMonday = Math.round(((thisTime - firstMondayYearTime) / dayMiliseconds));
274 | let lastWeek = 99;
275 | let thisWeek = 99;
276 |
277 | let firstMondayYear = new Date(firstMondayYearTime);
278 |
279 | thisWeek = Math.ceil((daysFromFirstMonday + 1) / 7);
280 |
281 | if (thisWeek <= 0) thisWeek = getIsoWeek(new Date(pDate.getFullYear() - 1, 11, 31, 0, 0, 0));
282 | else if (thisWeek == 53 && (new Date(pDate.getFullYear(), 0, 1, 0, 0, 0)).getDay() != 4 && (new Date(pDate.getFullYear(), 11, 31, 0, 0, 0)).getDay() != 4) thisWeek = 1;
283 | return thisWeek;
284 | };
285 |
--------------------------------------------------------------------------------
/docs/DotNet.md:
--------------------------------------------------------------------------------
1 | ## Page_Load method in Code behind of the asp .NET page
2 |
3 | ```C#
4 | protected void Page_Load(object sender, EventArgs e)
5 | {
6 | projects = sqlManager.ExecutePortfolioView();
7 |
8 | HiddenField.Value = JsonConvert.SerializeObject(projects);
9 | }
10 | ```
11 |
12 |
13 | ## Asp page:
14 |
15 | ```html
16 | <%@ Page Title="" Language="C#" MasterPageFile="~/Site.Master" AutoEventWireup="true" CodeBehind="Gantt.aspx.cs" Inherits="Links2.WebUI.Gannt" %>
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
57 |
58 |
59 | <%-- --%>
60 |
61 |
62 |
63 |
64 |
354 |
355 |
356 | ```
357 |
358 |
--------------------------------------------------------------------------------
/src/options.ts:
--------------------------------------------------------------------------------
1 | import { parseDateFormatStr } from "./utils/date_utils";
2 | import { COLUMN_ORDER } from "./draw_columns";
3 |
4 | export const includeGetSet = function () {
5 | /**
6 | * SETTERS
7 | */
8 | this.setOptions = function (options) {
9 | const keys = Object.keys(options);
10 | for (let i = 0; i < keys.length; i++) {
11 | const key = keys[i];
12 | const val = options[key];
13 | if (key === 'vResources' || key === 'vColumnOrder') {
14 | // ev = `this.set${key.substr(1)}(val)`;
15 | this['set' + key.substr(1)](val);
16 | } else if (val instanceof Array) {
17 | // ev = `this.set${key.substr(1)}(...val)`;
18 | this['set' + key.substr(1)].apply(this, val);
19 | } else {
20 | // ev = `this.set${key.substr(1)}(val)`;
21 | this['set' + key.substr(1)](val);
22 | }
23 | }
24 | }
25 | this.setUseFade = function (pVal) { this.vUseFade = pVal; };
26 | this.setUseMove = function (pVal) { this.vUseMove = pVal; };
27 | this.setUseRowHlt = function (pVal) { this.vUseRowHlt = pVal; };
28 | this.setUseToolTip = function (pVal) { this.vUseToolTip = pVal; };
29 | this.setUseSort = function (pVal) { this.vUseSort = pVal; };
30 | this.setUseSingleCell = function (pVal) { this.vUseSingleCell = pVal * 1; };
31 | this.setFormatArr = function () {
32 | let vValidFormats = 'hour day week month quarter';
33 | this.vFormatArr = new Array();
34 | for (let i = 0, j = 0; i < arguments.length; i++) {
35 | if (vValidFormats.indexOf(arguments[i].toLowerCase()) != -1 && arguments[i].length > 1) {
36 | this.vFormatArr[j++] = arguments[i].toLowerCase();
37 | let vRegExp = new RegExp('(?:^|\s)' + arguments[i] + '(?!\S)', 'g');
38 | vValidFormats = vValidFormats.replace(vRegExp, '');
39 | }
40 | }
41 | };
42 | this.setShowRes = function (pVal) { this.vShowRes = pVal; };
43 | this.setShowDur = function (pVal) { this.vShowDur = pVal; };
44 | this.setShowComp = function (pVal) { this.vShowComp = pVal; };
45 | this.setShowStartDate = function (pVal) { this.vShowStartDate = pVal; };
46 | this.setShowEndDate = function (pVal) { this.vShowEndDate = pVal; };
47 | this.setShowPlanStartDate = function (pVal) { this.vShowPlanStartDate = pVal; };
48 | this.setShowPlanEndDate = function (pVal) { this.vShowPlanEndDate = pVal; };
49 | this.setShowCost = function (pVal) { this.vShowCost = pVal; };
50 | this.setShowAddEntries = function (pVal) { this.vShowAddEntries = pVal; };
51 | this.setShowTaskInfoRes = function (pVal) { this.vShowTaskInfoRes = pVal; };
52 | this.setShowTaskInfoDur = function (pVal) { this.vShowTaskInfoDur = pVal; };
53 | this.setShowTaskInfoComp = function (pVal) { this.vShowTaskInfoComp = pVal; };
54 | this.setShowTaskInfoStartDate = function (pVal) { this.vShowTaskInfoStartDate = pVal; };
55 | this.setShowTaskInfoEndDate = function (pVal) { this.vShowTaskInfoEndDate = pVal; };
56 | this.setShowTaskInfoNotes = function (pVal) { this.vShowTaskInfoNotes = pVal; };
57 | this.setShowTaskInfoLink = function (pVal) { this.vShowTaskInfoLink = pVal; };
58 | this.setShowEndWeekDate = function (pVal) { this.vShowEndWeekDate = pVal; };
59 | this.setShowWeekends = function (pVal) { this.vShowWeekends = pVal; };
60 | this.setShowSelector = function () {
61 | let vValidSelectors = 'top bottom';
62 | this.vShowSelector = new Array();
63 | for (let i = 0, j = 0; i < arguments.length; i++) {
64 | if (vValidSelectors.indexOf(arguments[i].toLowerCase()) != -1 && arguments[i].length > 1) {
65 | this.vShowSelector[j++] = arguments[i].toLowerCase();
66 | let vRegExp = new RegExp('(?:^|\s)' + arguments[i] + '(?!\S)', 'g');
67 | vValidSelectors = vValidSelectors.replace(vRegExp, '');
68 | }
69 | }
70 | };
71 | this.setShowDeps = function (pVal) { this.vShowDeps = pVal; };
72 | this.setDateInputFormat = function (pVal) { this.vDateInputFormat = pVal; };
73 | this.setDateTaskTableDisplayFormat = function (pVal) { this.vDateTaskTableDisplayFormat = parseDateFormatStr(pVal); };
74 | this.setDateTaskDisplayFormat = function (pVal) { this.vDateTaskDisplayFormat = parseDateFormatStr(pVal); };
75 | this.setHourMajorDateDisplayFormat = function (pVal) { this.vHourMajorDateDisplayFormat = parseDateFormatStr(pVal); };
76 | this.setHourMinorDateDisplayFormat = function (pVal) { this.vHourMinorDateDisplayFormat = parseDateFormatStr(pVal); };
77 | this.setDayMajorDateDisplayFormat = function (pVal) { this.vDayMajorDateDisplayFormat = parseDateFormatStr(pVal); };
78 | this.setDayMinorDateDisplayFormat = function (pVal) { this.vDayMinorDateDisplayFormat = parseDateFormatStr(pVal); };
79 | this.setWeekMajorDateDisplayFormat = function (pVal) { this.vWeekMajorDateDisplayFormat = parseDateFormatStr(pVal); };
80 | this.setWeekMinorDateDisplayFormat = function (pVal) { this.vWeekMinorDateDisplayFormat = parseDateFormatStr(pVal); };
81 | this.setMonthMajorDateDisplayFormat = function (pVal) { this.vMonthMajorDateDisplayFormat = parseDateFormatStr(pVal); };
82 | this.setMonthMinorDateDisplayFormat = function (pVal) { this.vMonthMinorDateDisplayFormat = parseDateFormatStr(pVal); };
83 | this.setQuarterMajorDateDisplayFormat = function (pVal) { this.vQuarterMajorDateDisplayFormat = parseDateFormatStr(pVal); };
84 | this.setQuarterMinorDateDisplayFormat = function (pVal) { this.vQuarterMinorDateDisplayFormat = parseDateFormatStr(pVal); };
85 | this.setCaptionType = function (pType) { this.vCaptionType = pType; };
86 | this.setFormat = function (pFormat) {
87 | this.vFormat = pFormat;
88 | this.Draw();
89 | };
90 | this.setWorkingDays = function (workingDays) { this.vWorkingDays = workingDays; };
91 | this.setMinGpLen = function (pMinGpLen) { this.vMinGpLen = pMinGpLen; };
92 | this.setScrollTo = function (pDate) { this.vScrollTo = pDate; };
93 | this.setHourColWidth = function (pWidth) { this.vHourColWidth = pWidth; };
94 | this.setDayColWidth = function (pWidth) { this.vDayColWidth = pWidth; };
95 | this.setWeekColWidth = function (pWidth) { this.vWeekColWidth = pWidth; };
96 | this.setMonthColWidth = function (pWidth) { this.vMonthColWidth = pWidth; };
97 | this.setQuarterColWidth = function (pWidth) { this.vQuarterColWidth = pWidth; };
98 | this.setRowHeight = function (pHeight) { this.vRowHeight = pHeight; };
99 | this.setLang = function (pLang) { if (this.vLangs[pLang]) this.vLang = pLang; };
100 | this.setChartBody = function (pDiv) { if (typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement) this.vChartBody = pDiv; };
101 | this.setChartHead = function (pDiv) { if (typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement) this.vChartHead = pDiv; };
102 | this.setListBody = function (pDiv) { if (typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement) this.vListBody = pDiv; };
103 | this.setChartTable = function (pTable) { if (typeof HTMLTableElement !== 'function' || pTable instanceof HTMLTableElement) this.vChartTable = pTable; };
104 | this.setLines = function (pDiv) { if (typeof HTMLDivElement !== 'function' || pDiv instanceof HTMLDivElement) this.vLines = pDiv; };
105 | this.setLineOptions = function (lineOptions) { this.vLineOptions = lineOptions; };
106 | this.setTimer = function (pVal) { this.vTimer = pVal * 1; };
107 | this.setTooltipDelay = function (pVal) { this.vTooltipDelay = pVal * 1; };
108 | this.setTooltipTemplate = function (pVal) { this.vTooltipTemplate = pVal; };
109 | this.setMinDate = function (pVal) { this.vMinDate = pVal; };
110 | this.setMaxDate = function (pVal) { this.vMaxDate = pVal; };
111 | this.addLang = function (pLang, pVals) {
112 | if (!this.vLangs[pLang]) {
113 | this.vLangs[pLang] = new Object();
114 | for (let vKey in this.vLangs['en']) this.vLangs[pLang][vKey] = (pVals[vKey]) ? document.createTextNode(pVals[vKey]).data : this.vLangs['en'][vKey];
115 | }
116 | };
117 | this.setCustomLang = function (pVals) {
118 | this.vLangs[this.vLang] = new Object();
119 | for (var vKey in this.vLangs['en']) {
120 | this.vLangs[this.vLang][vKey] = (pVals[vKey]) ? document.createTextNode(pVals[vKey]).data : this.vLangs['en'][vKey];
121 | }
122 | };
123 | this.setTotalHeight = function (pVal) { this.vTotalHeight = pVal; };
124 |
125 | // EVENTS
126 | this.setEvents = function (pEvents) { this.vEvents = pEvents; };
127 | this.setEventsChange = function (pEventsChange) { this.vEventsChange = pEventsChange; };
128 | this.setEventClickRow = function (fn) { this.vEventClickRow = fn; };
129 | this.setEventClickCollapse = function (fn) { this.vEventClickCollapse = fn; };
130 |
131 | this.setResources = function (resources) { this.vResources = resources; };
132 | this.setAdditionalHeaders = function (headers) { this.vAdditionalHeaders = headers; };
133 | this.setColumnOrder = function (order) { this.vColumnOrder = order; };
134 |
135 | this.setEditable = function (editable) { this.vEditable = editable; }
136 | this.setDebug = function (debug) { this.vDebug = debug; }
137 |
138 | /**
139 | * GETTERS
140 | */
141 | this.getDivId = function () { return this.vDivId; };
142 | this.getUseFade = function () { return this.vUseFade; };
143 | this.getUseMove = function () { return this.vUseMove; };
144 | this.getUseRowHlt = function () { return this.vUseRowHlt; };
145 | this.getUseToolTip = function () { return this.vUseToolTip; };
146 | this.getUseSort = function () { return this.vUseSort; };
147 | this.getUseSingleCell = function () { return this.vUseSingleCell; };
148 | this.getFormatArr = function () { return this.vFormatArr; };
149 | this.getShowRes = function () { return this.vShowRes; };
150 | this.getShowDur = function () { return this.vShowDur; };
151 | this.getShowComp = function () { return this.vShowComp; };
152 | this.getShowStartDate = function () { return this.vShowStartDate; };
153 | this.getShowEndDate = function () { return this.vShowEndDate; };
154 | this.getShowPlanStartDate = function () { return this.vShowPlanStartDate; };
155 | this.getShowPlanEndDate = function () { return this.vShowPlanEndDate; };
156 | this.getShowCost = function () { return this.vShowCost; };
157 | this.getShowAddEntries = function () { return this.vShowAddEntries; };
158 | this.getShowTaskInfoRes = function () { return this.vShowTaskInfoRes; };
159 | this.getShowTaskInfoDur = function () { return this.vShowTaskInfoDur; };
160 | this.getShowTaskInfoComp = function () { return this.vShowTaskInfoComp; };
161 | this.getShowTaskInfoStartDate = function () { return this.vShowTaskInfoStartDate; };
162 | this.getShowTaskInfoEndDate = function () { return this.vShowTaskInfoEndDate; };
163 | this.getShowTaskInfoNotes = function () { return this.vShowTaskInfoNotes; };
164 | this.getShowTaskInfoLink = function () { return this.vShowTaskInfoLink; };
165 | this.getShowEndWeekDate = function () { return this.vShowEndWeekDate; };
166 | this.getShowWeekends = function () { return this.vShowWeekends; };
167 | this.getShowSelector = function () { return this.vShowSelector; };
168 | this.getShowDeps = function () { return this.vShowDeps; };
169 | this.getDateInputFormat = function () { return this.vDateInputFormat; };
170 | this.getDateTaskTableDisplayFormat = function () { return this.vDateTaskTableDisplayFormat; };
171 | this.getDateTaskDisplayFormat = function () { return this.vDateTaskDisplayFormat; };
172 | this.getHourMajorDateDisplayFormat = function () { return this.vHourMajorDateDisplayFormat; };
173 | this.getHourMinorDateDisplayFormat = function () { return this.vHourMinorDateDisplayFormat; };
174 | this.getDayMajorDateDisplayFormat = function () { return this.vDayMajorDateDisplayFormat; };
175 | this.getDayMinorDateDisplayFormat = function () { return this.vDayMinorDateDisplayFormat; };
176 | this.getWeekMajorDateDisplayFormat = function () { return this.vWeekMajorDateDisplayFormat; };
177 | this.getWeekMinorDateDisplayFormat = function () { return this.vWeekMinorDateDisplayFormat; };
178 | this.getMonthMajorDateDisplayFormat = function () { return this.vMonthMajorDateDisplayFormat; };
179 | this.getMonthMinorDateDisplayFormat = function () { return this.vMonthMinorDateDisplayFormat; };
180 | this.getQuarterMajorDateDisplayFormat = function () { return this.vQuarterMajorDateDisplayFormat; };
181 | this.getQuarterMinorDateDisplayFormat = function () { return this.vQuarterMinorDateDisplayFormat; };
182 | this.getCaptionType = function () { return this.vCaptionType; };
183 | this.getMinGpLen = function () { return this.vMinGpLen; };
184 | this.getScrollTo = function () { return this.vScrollTo; };
185 | this.getHourColWidth = function () { return this.vHourColWidth; };
186 | this.getDayColWidth = function () { return this.vDayColWidth; };
187 | this.getWeekColWidth = function () { return this.vWeekColWidth; };
188 | this.getMonthColWidth = function () { return this.vMonthColWidth; };
189 | this.getQuarterColWidth = function () { return this.vQuarterColWidth; };
190 | this.getRowHeight = function () { return this.vRowHeight; };
191 | this.getChartBody = function () { return this.vChartBody; };
192 | this.getChartHead = function () { return this.vChartHead; };
193 | this.getListBody = function () { return this.vListBody; };
194 | this.getChartTable = function () { return this.vChartTable; };
195 | this.getLines = function () { return this.vLines; };
196 | this.getTimer = function () { return this.vTimer; };
197 | this.getMinDate = function () { return this.vMinDate; };
198 | this.getMaxDate = function () { return this.vMaxDate; };
199 | this.getTooltipDelay = function () { return this.vTooltipDelay; };
200 | this.getList = function () { return this.vTaskList; };
201 |
202 | //EVENTS
203 | this.getEventsClickCell = function () { return this.vEvents; };
204 | this.getEventsChange = function () { return this.vEventsChange; };
205 | this.getEventClickRow = function () { return this.vEventClickRow; };
206 | this.getEventClickCollapse = function () { return this.vEventClickCollapse; };
207 |
208 | this.getResources = function () { return this.vResources; };
209 | this.getAdditionalHeaders = function () { return this.vAdditionalHeaders; };
210 | this.getColumnOrder = function () { return this.vColumnOrder || COLUMN_ORDER; };
211 | }
--------------------------------------------------------------------------------
/src/xml.ts:
--------------------------------------------------------------------------------
1 | import { TaskItem } from "./task";
2 | import { formatDateStr, parseDateFormatStr } from "./utils/date_utils";
3 | import { newNode } from "./utils/draw_utils";
4 | import { makeRequest } from "./utils/general_utils";
5 |
6 | export const parseXML = function (pFile, pGanttVar) {
7 | return makeRequest(pFile, false, false)
8 | .then(xmlDoc => {
9 | AddXMLTask(pGanttVar, xmlDoc);
10 | });
11 | };
12 |
13 | export const parseXMLString = function (pStr, pGanttVar) {
14 | let xmlDoc;
15 | if (typeof (window).DOMParser != 'undefined') {
16 | xmlDoc = (new (window).DOMParser()).parseFromString(pStr, 'text/xml');
17 | } else if (typeof (window).ActiveXObject != 'undefined' &&
18 | new (window).ActiveXObject('Microsoft.XMLDOM')) {
19 | xmlDoc = new (window).ActiveXObject('Microsoft.XMLDOM');
20 | xmlDoc.async = 'false';
21 | xmlDoc.loadXML(pStr);
22 | }
23 |
24 | AddXMLTask(pGanttVar, xmlDoc);
25 | };
26 |
27 |
28 | export const findXMLNode = function (pRoot, pNodeName) {
29 | let vRetValue;
30 |
31 | try {
32 | vRetValue = pRoot.getElementsByTagName(pNodeName);
33 | } catch (error) { ; } // do nothing, we'll return undefined
34 |
35 | return vRetValue;
36 | };
37 |
38 | // pType can be 1=numeric, 2=String, all other values just return raw data
39 | export const getXMLNodeValue = function (pRoot, pNodeName, pType, pDefault) {
40 | let vRetValue;
41 |
42 | try {
43 | vRetValue = pRoot.getElementsByTagName(pNodeName)[0].childNodes[0].nodeValue;
44 | } catch (error) {
45 | if (typeof pDefault != 'undefined') vRetValue = pDefault;
46 | }
47 |
48 | if (typeof vRetValue != 'undefined' && vRetValue != null) {
49 | if (pType == 1) vRetValue *= 1;
50 | else if (pType == 2) vRetValue = vRetValue.toString();
51 | }
52 | return vRetValue;
53 | };
54 |
55 | export const AddXMLTask = function (pGanttVar, pXmlDoc) {
56 | let project = '';
57 | let Task;
58 | let n = 0;
59 | let m = 0;
60 | let i = 0;
61 | let j = 0;
62 | let k = 0;
63 | let maxPID = 0;
64 | let ass = new Array();
65 | let assRes = new Array();
66 | let res = new Array();
67 | let pars = new Array();
68 |
69 | let projNode = findXMLNode(pXmlDoc, 'Project');
70 | if (typeof projNode != 'undefined' && projNode.length > 0) {
71 | project = projNode[0].getAttribute('xmlns');
72 | }
73 |
74 | if (project == 'http://schemas.microsoft.com/project') {
75 | pGanttVar.setDateInputFormat('yyyy-mm-dd');
76 | Task = findXMLNode(pXmlDoc, 'Task');
77 | if (typeof Task == 'undefined') n = 0;
78 | else n = Task.length;
79 |
80 | let resources = findXMLNode(pXmlDoc, 'Resource');
81 | if (typeof resources == 'undefined') { n = 0; m = 0; }
82 | else m = resources.length;
83 |
84 | for (i = 0; i < m; i++) {
85 | let resname = getXMLNodeValue(resources[i], 'Name', 2, '');
86 | let uid = getXMLNodeValue(resources[i], 'UID', 1, -1);
87 |
88 | if (resname.length > 0 && uid > 0) res[uid] = resname;
89 | }
90 |
91 | let assignments = findXMLNode(pXmlDoc, 'Assignment');
92 | if (typeof assignments == 'undefined') j = 0;
93 | else j = assignments.length;
94 |
95 | for (i = 0; i < j; i++) {
96 | let uid;
97 | let resUID = getXMLNodeValue(assignments[i], 'ResourceUID', 1, -1);
98 | uid = getXMLNodeValue(assignments[i], 'TaskUID', 1, -1);
99 |
100 | if (uid > 0) {
101 | if (resUID > 0) assRes[uid] = res[resUID];
102 | ass[uid] = assignments[i];
103 | }
104 | }
105 |
106 | // Store information about parent UIDs in an easily searchable form
107 | for (i = 0; i < n; i++) {
108 | let uid;
109 | uid = getXMLNodeValue(Task[i], 'UID', 1, 0);
110 | let vOutlineNumber;
111 | if (uid != 0) vOutlineNumber = getXMLNodeValue(Task[i], 'OutlineNumber', 2, '0');
112 | if (uid > 0) pars[vOutlineNumber] = uid;
113 | if (uid > maxPID) maxPID = uid;
114 | }
115 |
116 | for (i = 0; i < n; i++) {
117 | // optional parameters may not have an entry
118 | // Task ID must NOT be zero otherwise it will be skipped
119 | let pID = getXMLNodeValue(Task[i], 'UID', 1, 0);
120 |
121 | if (pID != 0) {
122 | let pName = getXMLNodeValue(Task[i], 'Name', 2, 'No Task Name');
123 | let pStart = getXMLNodeValue(Task[i], 'Start', 2, '');
124 | let pEnd = getXMLNodeValue(Task[i], 'Finish', 2, '');
125 | let pPlanStart = getXMLNodeValue(Task[i], 'PlanStart', 2, '');
126 | let pPlanEnd = getXMLNodeValue(Task[i], 'PlanFinish', 2, '');
127 | let pDuration = getXMLNodeValue(Task[i], 'Duration', 2, '');
128 | let pLink = getXMLNodeValue(Task[i], 'HyperlinkAddress', 2, '');
129 | let pMile = getXMLNodeValue(Task[i], 'Milestone', 1, 0);
130 | let pComp = getXMLNodeValue(Task[i], 'PercentWorkComplete', 1, 0);
131 | let pCost = getXMLNodeValue(Task[i], 'Cost', 2, 0);
132 | let pGroup = getXMLNodeValue(Task[i], 'Summary', 1, 0);
133 |
134 | let pParent = 0;
135 |
136 | let vOutlineLevel = getXMLNodeValue(Task[i], 'OutlineLevel', 1, 0);
137 | let vOutlineNumber;
138 | if (vOutlineLevel > 1) {
139 | vOutlineNumber = getXMLNodeValue(Task[i], 'OutlineNumber', 2, '0');
140 | pParent = pars[vOutlineNumber.substr(0, vOutlineNumber.lastIndexOf('.'))];
141 | }
142 |
143 | let pNotes;
144 | try {
145 | pNotes = Task[i].getElementsByTagName('Notes')[0].childNodes[1].nodeValue; //this should be a CDATA node
146 | } catch (error) { pNotes = ''; }
147 |
148 | let pRes;
149 | if (typeof assRes[pID] != 'undefined') pRes = assRes[pID];
150 | else pRes = '';
151 |
152 | let predecessors = findXMLNode(Task[i], 'PredecessorLink');
153 | if (typeof predecessors == 'undefined') j = 0;
154 | else j = predecessors.length;
155 | let pDepend = '';
156 |
157 | for (k = 0; k < j; k++) {
158 | let depUID = getXMLNodeValue(predecessors[k], 'PredecessorUID', 1, -1);
159 | let depType = getXMLNodeValue(predecessors[k], 'Type', 1, 1);
160 |
161 | if (depUID > 0) {
162 | if (pDepend.length > 0) pDepend += ',';
163 | switch (depType) {
164 | case 0: pDepend += depUID + 'FF'; break;
165 | case 1: pDepend += depUID + 'FS'; break;
166 | case 2: pDepend += depUID + 'SF'; break;
167 | case 3: pDepend += depUID + 'SS'; break;
168 | default: pDepend += depUID + 'FS'; break;
169 | }
170 | }
171 | }
172 |
173 | let pOpen = 1;
174 | let pCaption = '';
175 |
176 | let pClass;
177 | if (pGroup > 0) pClass = 'ggroupblack';
178 | else if (pMile > 0) pClass = 'gmilestone';
179 | else pClass = 'gtaskblue';
180 |
181 | // check for split tasks
182 |
183 | let splits = findXMLNode(ass[pID], 'TimephasedData');
184 | if (typeof splits == 'undefined') j = 0;
185 | else j = splits.length;
186 |
187 | let vSplitStart = pStart;
188 | let vSplitEnd = pEnd;
189 | let vSubCreated = false;
190 | let vDepend = pDepend.replace(/,*[0-9]+[FS]F/g, '');
191 |
192 | for (k = 0; k < j; k++) {
193 | let vDuration = getXMLNodeValue(splits[k], 'Value', 2, '0');
194 | //remove all text
195 | vDuration = '0' + vDuration.replace(/\D/g, '');
196 | vDuration *= 1;
197 | if ((vDuration == 0 && !vSubCreated) || (k + 1 == j && pGroup == 2)) {
198 | // No time booked in the given period (or last entry)
199 | // Make sure the parent task is set as a combined group
200 | pGroup = 2;
201 | // Handle last loop
202 | if (k + 1 == j) vDepend = pDepend.replace(/,*[0-9]+[FS]S/g, '');
203 | // Now create a subtask
204 | maxPID++;
205 | vSplitEnd = getXMLNodeValue(splits[k], (k + 1 == j) ? 'Finish' : 'Start', 2, '');
206 | pGanttVar.AddTaskItem(new TaskItem(maxPID, pName, vSplitStart, vSplitEnd, 'gtaskblue',
207 | pLink, pMile, pRes, pComp, 0, pID, pOpen, vDepend, pCaption, pNotes, pGanttVar, pCost,
208 | pPlanStart, pPlanEnd, pDuration));
209 | vSubCreated = true;
210 | vDepend = '';
211 | }
212 | else if (vDuration != 0 && vSubCreated) {
213 | vSplitStart = getXMLNodeValue(splits[k], 'Start', 2, '');
214 | vSubCreated = false;
215 | }
216 | }
217 | if (vSubCreated) pDepend = '';
218 |
219 | // Finally add the task
220 | pGanttVar.AddTaskItem(new TaskItem(pID, pName, pStart, pEnd, pClass, pLink, pMile, pRes, pComp, pGroup,
221 | pParent, pOpen, pDepend, pCaption, pNotes, pGanttVar, pCost, pPlanStart, pPlanEnd, pDuration, undefined, undefined, pClass));
222 | }
223 | }
224 | }
225 | else {
226 | Task = pXmlDoc.getElementsByTagName('task');
227 | n = Task.length;
228 |
229 | for (i = 0; i < n; i++) {
230 | // optional parameters may not have an entry
231 | // Task ID must NOT be zero otherwise it will be skipped
232 | let pID = getXMLNodeValue(Task[i], 'pID', 1, 0);
233 |
234 | if (pID != 0) {
235 | let pName = getXMLNodeValue(Task[i], 'pName', 2, 'No Task Name');
236 | let pStart = getXMLNodeValue(Task[i], 'pStart', 2, '');
237 | let pEnd = getXMLNodeValue(Task[i], 'pEnd', 2, '');
238 | let pPlanStart = getXMLNodeValue(Task[i], 'pPlanStart', 2, '');
239 | let pPlanEnd = getXMLNodeValue(Task[i], 'pPlanEnd', 2, '');
240 | let pDuration = getXMLNodeValue(Task[i], 'pDuration', 2, '');
241 | let pLink = getXMLNodeValue(Task[i], 'pLink', 2, '');
242 | let pMile = getXMLNodeValue(Task[i], 'pMile', 1, 0);
243 | let pComp = getXMLNodeValue(Task[i], 'pComp', 1, 0);
244 | let pCost = getXMLNodeValue(Task[i], 'pCost', 2, 0);
245 | let pGroup = getXMLNodeValue(Task[i], 'pGroup', 1, 0);
246 | let pParent = getXMLNodeValue(Task[i], 'pParent', 1, 0);
247 | let pRes = getXMLNodeValue(Task[i], 'pRes', 2, '');
248 | let pOpen = getXMLNodeValue(Task[i], 'pOpen', 1, 1);
249 | let pDepend = getXMLNodeValue(Task[i], 'pDepend', 2, '');
250 | let pCaption = getXMLNodeValue(Task[i], 'pCaption', 2, '');
251 | let pNotes = getXMLNodeValue(Task[i], 'pNotes', 2, '');
252 | let pClass = getXMLNodeValue(Task[i], 'pClass', 2, '');
253 | let pPlanClass = getXMLNodeValue(Task[i], 'pPlanClass', 2, '');
254 | if (typeof pClass == 'undefined') {
255 | if (pGroup > 0) pClass = 'ggroupblack';
256 | else if (pMile > 0) pClass = 'gmilestone';
257 | else pClass = 'gtaskblue';
258 | }
259 | if (typeof pPlanClass == 'undefined') pPlanClass = pClass;
260 |
261 | // Finally add the task
262 | pGanttVar.AddTaskItem(new TaskItem(pID, pName, pStart, pEnd, pClass, pLink, pMile, pRes, pComp,
263 | pGroup, pParent, pOpen, pDepend, pCaption, pNotes, pGanttVar, pCost, pPlanStart, pPlanEnd, pDuration, undefined, undefined, pPlanClass));
264 | }
265 | }
266 | }
267 |
268 |
269 | };
270 |
271 |
272 | export const getXMLProject = function () {
273 | let vProject = '';
274 | for (let i = 0; i < this.vTaskList.length; i++) {
275 | vProject += this.getXMLTask(i, true);
276 | }
277 | vProject += ' ';
278 | return vProject;
279 | };
280 |
281 | export const getXMLTask = function (pID, pIdx) {
282 | let i = 0;
283 | let vIdx = -1;
284 | let vTask = '';
285 | let vOutFrmt = parseDateFormatStr(this.getDateInputFormat() + ' HH:MI:SS');
286 | if (pIdx === true) vIdx = pID;
287 | else {
288 | for (i = 0; i < this.vTaskList.length; i++) {
289 | if (this.vTaskList[i].getID() == pID) { vIdx = i; break; }
290 | }
291 | }
292 | if (vIdx >= 0 && vIdx < this.vTaskList.length) {
293 | /* Simplest way to return case sensitive node names is to just build a string */
294 | vTask = '';
295 | vTask += '' + this.vTaskList[vIdx].getID() + ' ';
296 | vTask += '' + this.vTaskList[vIdx].getName() + ' ';
297 | vTask += '' + formatDateStr(this.vTaskList[vIdx].getStart(), vOutFrmt, this.vLangs[this.vLang]) + ' ';
298 | vTask += '' + formatDateStr(this.vTaskList[vIdx].getEnd(), vOutFrmt, this.vLangs[this.vLang]) + ' ';
299 | vTask += '' + formatDateStr(this.vTaskList[vIdx].getPlanStart(), vOutFrmt, this.vLangs[this.vLang]) + ' ';
300 | vTask += '' + formatDateStr(this.vTaskList[vIdx].getPlanEnd(), vOutFrmt, this.vLangs[this.vLang]) + ' ';
301 | vTask += '' + this.vTaskList[vIdx].getDuration() + ' ';
302 | vTask += '' + this.vTaskList[vIdx].getClass() + ' ';
303 | vTask += '' + this.vTaskList[vIdx].getLink() + ' ';
304 | vTask += '' + this.vTaskList[vIdx].getMile() + ' ';
305 | if (this.vTaskList[vIdx].getResource() != '\u00A0') vTask += '' + this.vTaskList[vIdx].getResource() + ' ';
306 | vTask += '' + this.vTaskList[vIdx].getCompVal() + ' ';
307 | vTask += '' + this.vTaskList[vIdx].getCost() + ' ';
308 | vTask += '' + this.vTaskList[vIdx].getGroup() + ' ';
309 | vTask += '' + this.vTaskList[vIdx].getParent() + ' ';
310 | vTask += '' + this.vTaskList[vIdx].getOpen() + ' ';
311 | vTask += '';
312 | let vDepList = this.vTaskList[vIdx].getDepend();
313 | for (i = 0; i < vDepList.length; i++) {
314 | if (i > 0) vTask += ',';
315 | if (vDepList[i] > 0) vTask += vDepList[i] + this.vTaskList[vIdx].getDepType()[i];
316 | }
317 | vTask += ' ';
318 | vTask += '' + this.vTaskList[vIdx].getCaption() + ' ';
319 |
320 | let vTmpFrag = document.createDocumentFragment();
321 | let vTmpDiv = newNode(vTmpFrag, 'div', null, null, this.vTaskList[vIdx].getNotes().innerHTML);
322 | vTask += '' + vTmpDiv.innerHTML + ' ';
323 | vTask += '' + this.vTaskList[vIdx].getPlanClass() + ' ';
324 | vTask += ' ';
325 | }
326 | return vTask;
327 | };
328 |
--------------------------------------------------------------------------------
/src/events.ts:
--------------------------------------------------------------------------------
1 | import {
2 | delayedHide, changeFormat, stripIds, isIE, findObj, fadeToolTip, getScrollbarWidth,
3 | isParentElementOrSelf, updateFlyingObj
4 | } from "./utils/general_utils";
5 |
6 | // Function to open/close and hide/show children of specified task
7 | export const folder = function (pID, ganttObj) {
8 | let vList = ganttObj.getList();
9 |
10 | ganttObj.clearDependencies(); // clear these first so slow rendering doesn't look odd
11 |
12 | for (let i = 0; i < vList.length; i++) {
13 | if (vList[i].getID() == pID) {
14 | if (vList[i].getOpen() == 1) {
15 | vList[i].setOpen(0);
16 | hide(pID, ganttObj);
17 |
18 | if (isIE())
19 | vList[i].getGroupSpan().innerText = '+';
20 | else
21 | vList[i].getGroupSpan().textContent = '+';
22 | }
23 | else {
24 | vList[i].setOpen(1);
25 | show(pID, 1, ganttObj);
26 |
27 | if (isIE())
28 | vList[i].getGroupSpan().innerText = '-';
29 | else
30 | vList[i].getGroupSpan().textContent = '-';
31 | }
32 | }
33 | }
34 | let bd;
35 | if (this.vDebug) {
36 | bd = new Date();
37 | console.info('after drawDependency', bd);
38 | }
39 | ganttObj.DrawDependencies(this.vDebug);
40 | if (this.vDebug) {
41 | const ad = new Date();
42 | console.info('after drawDependency', ad, (ad.getTime() - bd.getTime()));
43 | }
44 | };
45 |
46 | export const hide = function (pID, ganttObj) {
47 | let vList = ganttObj.getList();
48 | let vID = 0;
49 |
50 | for (let i = 0; i < vList.length; i++) {
51 | if (vList[i].getParent() == pID) {
52 | vID = vList[i].getID();
53 | // it's unlikely but if the task list has been updated since
54 | // the chart was drawn some of the rows may not exist
55 | if (vList[i].getListChildRow()) vList[i].getListChildRow().style.display = 'none';
56 | if (vList[i].getChildRow()) vList[i].getChildRow().style.display = 'none';
57 | vList[i].setVisible(0);
58 | if (vList[i].getGroup()) hide(vID, ganttObj);
59 | }
60 | }
61 | };
62 |
63 | // Function to show children of specified task
64 | export const show = function (pID, pTop, ganttObj) {
65 | let vList = ganttObj.getList();
66 | let vID = 0;
67 | let vState = '';
68 |
69 | for (let i = 0; i < vList.length; i++) {
70 | if (vList[i].getParent() == pID) {
71 | if (!vList[i].getParItem()) {
72 | console.error(`Cant find parent on who event (maybe problems with Task ID and Parent Id mixes?)`);
73 | }
74 | if (vList[i].getParItem().getGroupSpan()) {
75 | if (isIE()) vState = vList[i].getParItem().getGroupSpan().innerText;
76 | else vState = vList[i].getParItem().getGroupSpan().textContent;
77 | }
78 | i = vList.length;
79 | }
80 | }
81 |
82 | for (let i = 0; i < vList.length; i++) {
83 | if (vList[i].getParent() == pID) {
84 | let vChgState = false;
85 | vID = vList[i].getID();
86 |
87 | if (pTop == 1 && vState == '+') vChgState = true;
88 | else if (vState == '-') vChgState = true;
89 | else if (vList[i].getParItem() && vList[i].getParItem().getGroup() == 2) vList[i].setVisible(1);
90 |
91 | if (vChgState) {
92 | if (vList[i].getListChildRow()) vList[i].getListChildRow().style.display = '';
93 | if (vList[i].getChildRow()) vList[i].getChildRow().style.display = '';
94 | vList[i].setVisible(1);
95 | }
96 | if (vList[i].getGroup()) show(vID, 0, ganttObj);
97 | }
98 | }
99 | };
100 |
101 |
102 | export const mouseOver = function (pObj1, pObj2) {
103 | if (this.getUseRowHlt()) {
104 | pObj1.className += ' gitemhighlight';
105 | pObj2.className += ' gitemhighlight';
106 | }
107 | };
108 |
109 | export const mouseOut = function (pObj1, pObj2) {
110 | if (this.getUseRowHlt()) {
111 | pObj1.className = pObj1.className.replace(/(?:^|\s)gitemhighlight(?!\S)/g, '');
112 | pObj2.className = pObj2.className.replace(/(?:^|\s)gitemhighlight(?!\S)/g, '');
113 | }
114 | };
115 |
116 | export const showToolTip = function (pGanttChartObj, e, pContents, pWidth, pTimer) {
117 | let vTtDivId = pGanttChartObj.getDivId() + 'JSGanttToolTip';
118 | let vMaxW = 500;
119 | let vMaxAlpha = 100;
120 | let vShowing = pContents.id;
121 |
122 | if (pGanttChartObj.getUseToolTip()) {
123 | if (pGanttChartObj.vTool == null) {
124 | pGanttChartObj.vTool = document.createElement('div');
125 | pGanttChartObj.vTool.id = vTtDivId;
126 | pGanttChartObj.vTool.className = 'JSGanttToolTip';
127 | pGanttChartObj.vTool.vToolCont = document.createElement('div');
128 | pGanttChartObj.vTool.vToolCont.id = vTtDivId + 'cont';
129 | pGanttChartObj.vTool.vToolCont.className = 'JSGanttToolTipcont';
130 | pGanttChartObj.vTool.vToolCont.setAttribute('showing', '');
131 | pGanttChartObj.vTool.appendChild(pGanttChartObj.vTool.vToolCont);
132 | document.body.appendChild(pGanttChartObj.vTool);
133 | pGanttChartObj.vTool.style.opacity = 0;
134 | pGanttChartObj.vTool.setAttribute('currentOpacity', 0);
135 | pGanttChartObj.vTool.setAttribute('fadeIncrement', 10);
136 | pGanttChartObj.vTool.setAttribute('moveSpeed', 10);
137 | pGanttChartObj.vTool.style.filter = 'alpha(opacity=0)';
138 | pGanttChartObj.vTool.style.visibility = 'hidden';
139 | pGanttChartObj.vTool.style.left = Math.floor(((e) ? e.clientX : (window.event).clientX) / 2) + 'px';
140 | pGanttChartObj.vTool.style.top = Math.floor(((e) ? e.clientY : (window.event).clientY) / 2) + 'px';
141 | this.addListener('mouseover', function () { clearTimeout(pGanttChartObj.vTool.delayTimeout); }, pGanttChartObj.vTool);
142 | this.addListener('mouseout', function () { delayedHide(pGanttChartObj, pGanttChartObj.vTool, pTimer); }, pGanttChartObj.vTool);
143 | }
144 | clearTimeout(pGanttChartObj.vTool.delayTimeout);
145 |
146 | const newHTML = pContents.innerHTML;
147 |
148 | if (pGanttChartObj.vTool.vToolCont.getAttribute("content") !== newHTML) {
149 | pGanttChartObj.vTool.vToolCont.innerHTML = pContents.innerHTML;
150 | // as we are allowing arbitrary HTML we should remove any tag ids to prevent duplication
151 | stripIds(pGanttChartObj.vTool.vToolCont);
152 | pGanttChartObj.vTool.vToolCont.setAttribute("content", newHTML);
153 | }
154 |
155 | if (pGanttChartObj.vTool.vToolCont.getAttribute('showing') != vShowing || pGanttChartObj.vTool.style.visibility != 'visible') {
156 | if (pGanttChartObj.vTool.vToolCont.getAttribute('showing') != vShowing) {
157 | pGanttChartObj.vTool.vToolCont.setAttribute('showing', vShowing);
158 | }
159 |
160 | pGanttChartObj.vTool.style.visibility = 'visible';
161 | // Rather than follow the mouse just have it stay put
162 | updateFlyingObj(e, pGanttChartObj, pTimer);
163 | pGanttChartObj.vTool.style.width = (pWidth) ? pWidth + 'px' : 'auto';
164 | if (!pWidth && isIE()) {
165 | pGanttChartObj.vTool.style.width = pGanttChartObj.vTool.offsetWidth;
166 | }
167 | if (pGanttChartObj.vTool.offsetWidth > vMaxW) { pGanttChartObj.vTool.style.width = vMaxW + 'px'; }
168 | }
169 |
170 | if (pGanttChartObj.getUseFade()) {
171 | clearInterval(pGanttChartObj.vTool.fadeInterval);
172 | pGanttChartObj.vTool.fadeInterval = setInterval(function () { fadeToolTip(1, pGanttChartObj.vTool, vMaxAlpha); }, pTimer);
173 | }
174 | else {
175 | pGanttChartObj.vTool.style.opacity = vMaxAlpha * 0.01;
176 | pGanttChartObj.vTool.style.filter = 'alpha(opacity=' + vMaxAlpha + ')';
177 | }
178 | }
179 | };
180 |
181 |
182 |
183 |
184 | export const addListener = function (eventName, handler, control) {
185 | // Check if control is a string
186 | if (control === String(control)) control = findObj(control);
187 |
188 | if (control.addEventListener) //Standard W3C
189 | {
190 | return control.addEventListener(eventName, handler, false);
191 | }
192 | else if (control.attachEvent) //IExplore
193 | {
194 | return control.attachEvent('on' + eventName, handler);
195 | }
196 | else {
197 | return false;
198 | }
199 | };
200 |
201 | export const removeListener = function (eventName, handler, control) {
202 | // Check if control is a string
203 | if (control === String(control)) control = findObj(control);
204 | if (control.removeEventListener) {
205 | //Standard W3C
206 | return control.removeEventListener(eventName, handler, false);
207 | } else if (control.detachEvent) {
208 | //IExplore
209 | return control.attachEvent('on' + eventName, handler);
210 | } else {
211 | return false;
212 | }
213 | };
214 |
215 | export const syncScroll = function (elements, attrName) {
216 | let syncFlags = new Map(elements.map(e => [e, false]));
217 |
218 | function scrollEvent(e) {
219 | if (!syncFlags.get(e.target)) {
220 | for (const el of elements) {
221 | if (el !== e.target) {
222 | syncFlags.set(el, true);
223 | el[attrName] = e.target[attrName];
224 | }
225 | }
226 | }
227 |
228 | syncFlags.set(e.target, false);
229 | }
230 |
231 | for (const el of elements) {
232 | el.addEventListener('scroll', scrollEvent);
233 | }
234 | }
235 |
236 | export const addTooltipListeners = function (pGanttChart, pObj1, pObj2, callback) {
237 | let isShowingTooltip = false;
238 |
239 | addListener('mouseover', function (e) {
240 | if (isShowingTooltip || !callback) {
241 | showToolTip(pGanttChart, e, pObj2, null, pGanttChart.getTimer());
242 | } else if (callback) {
243 | isShowingTooltip = true;
244 | const promise = callback();
245 | showToolTip(pGanttChart, e, pObj2, null, pGanttChart.getTimer());
246 | if (promise && promise.then) {
247 | promise.then(() => {
248 | if (pGanttChart.vTool.vToolCont.getAttribute('showing') === pObj2.id &&
249 | pGanttChart.vTool.style.visibility === 'visible') {
250 | showToolTip(pGanttChart, e, pObj2, null, pGanttChart.getTimer());
251 | }
252 | });
253 | }
254 | }
255 | }, pObj1);
256 |
257 | addListener('mouseout', function (e) {
258 | const outTo = e.relatedTarget;
259 | if (isParentElementOrSelf(outTo, pObj1) || (pGanttChart.vTool && isParentElementOrSelf(outTo, pGanttChart.vTool))) {
260 | // not actually out
261 | } else {
262 | isShowingTooltip = false;
263 | }
264 |
265 | delayedHide(pGanttChart, pGanttChart.vTool, pGanttChart.getTimer());
266 | }, pObj1);
267 | };
268 |
269 | export const addThisRowListeners = function (pGanttChart, pObj1, pObj2) {
270 | addListener('mouseover', function () { pGanttChart.mouseOver(pObj1, pObj2); }, pObj1);
271 | addListener('mouseover', function () { pGanttChart.mouseOver(pObj1, pObj2); }, pObj2);
272 | addListener('mouseout', function () { pGanttChart.mouseOut(pObj1, pObj2); }, pObj1);
273 | addListener('mouseout', function () { pGanttChart.mouseOut(pObj1, pObj2); }, pObj2);
274 | };
275 |
276 | export const updateGridHeaderWidth = function (pGanttChart) {
277 | const head = pGanttChart.getChartHead();
278 | const body = pGanttChart.getChartBody();
279 | if (!head || !body) return;
280 | const isScrollVisible = body.scrollHeight > body.clientHeight;
281 | if (isScrollVisible) {
282 | head.style.width = `calc(100% - ${getScrollbarWidth()}px)`;
283 | } else {
284 | head.style.width = '100%';
285 | }
286 | }
287 |
288 | export const addFolderListeners = function (pGanttChart, pObj, pID) {
289 | addListener('click', function () {
290 | folder(pID, pGanttChart);
291 | updateGridHeaderWidth(pGanttChart);
292 | }, pObj);
293 | };
294 |
295 | export const addFormatListeners = function (pGanttChart, pFormat, pObj) {
296 | addListener('click', function () { changeFormat(pFormat, pGanttChart); }, pObj);
297 | };
298 |
299 | export const addScrollListeners = function (pGanttChart) {
300 | addListener('resize', function () { pGanttChart.getChartHead().scrollLeft = pGanttChart.getChartBody().scrollLeft; }, window);
301 | addListener('resize', function () {
302 | pGanttChart.getListBody().scrollTop = pGanttChart.getChartBody().scrollTop;
303 | }, window);
304 | };
305 |
306 | export const addListenerClickCell = function (vTmpCell, vEvents, task, column) {
307 | addListener('click', function (e) {
308 | if (e.target.classList.contains('gfoldercollapse') === false &&
309 | vEvents[column] && typeof vEvents[column] === 'function') {
310 | vEvents[column](task, e, vTmpCell, column);
311 | }
312 | }, vTmpCell);
313 | }
314 |
315 | export const addListenerInputCell = function (vTmpCell, vEventsChange, callback, tasks, index, column, draw = null, event = 'blur') {
316 | const task = tasks[index];
317 | if (vTmpCell.children[0] && vTmpCell.children[0].children && vTmpCell.children[0].children[0]) {
318 | const tagName = vTmpCell.children[0].children[0].tagName;
319 | const selectInputOrButton = tagName === 'SELECT' || tagName === 'INPUT' || tagName === 'BUTTON';
320 | if (selectInputOrButton) {
321 | addListener(event, function (e) {
322 | if (callback) {
323 | callback(task, e);
324 | }
325 | if (vEventsChange[column] && typeof vEventsChange[column] === 'function') {
326 | const q = vEventsChange[column](tasks, task, e, vTmpCell, vColumnsNames[column]);
327 | if (q && q.then) {
328 | q.then(e => draw());
329 | } else {
330 | draw();
331 | }
332 | } else {
333 | draw();
334 | }
335 | }, vTmpCell.children[0].children[0]);
336 | }
337 | }
338 | }
339 |
340 | export const addListenerDependencies = function (vLineOptions) {
341 | const elements = document.querySelectorAll('.gtaskbarcontainer');
342 | for (let i = 0; i < elements.length; i++) {
343 | const taskDiv = elements[i];
344 | taskDiv.addEventListener('mouseover', e => {
345 | toggleDependencies(e, vLineOptions);
346 | });
347 | taskDiv.addEventListener('mouseout', e => {
348 | toggleDependencies(e, vLineOptions);
349 | });
350 | }
351 | }
352 |
353 | const toggleDependencies = function (e, vLineOptions) {
354 | const target: any = e.currentTarget;
355 | const ids = target.getAttribute('id').split('_');
356 | let style = vLineOptions && vLineOptions.borderStyleHover !== undefined ? vLineOptions.hoverStyle : 'groove';
357 | if (e.type === 'mouseout') {
358 | style = '';
359 | }
360 | if (ids.length > 1) {
361 | const frameZones = Array.from(document.querySelectorAll(`.gDepId${ids[1]}`));
362 | frameZones.forEach((c: any) => {
363 | c.style.borderStyle = style;
364 | });
365 | // document.querySelectorAll(`.gDepId${ids[1]}`).forEach((c: any) => {
366 | // c.style.borderStyle = style;
367 | // });
368 | }
369 | }
370 |
371 | const vColumnsNames = {
372 | taskname: 'pName',
373 | res: 'pRes',
374 | dur: '',
375 | comp: 'pComp',
376 | start: 'pStart',
377 | end: 'pEnd',
378 | planstart: 'pPlanStart',
379 | planend: 'pPlanEnd',
380 | link: 'pLink',
381 | cost: 'pCost',
382 | mile: 'pMile',
383 | group: 'pGroup',
384 | parent: 'pParent',
385 | open: 'pOpen',
386 | depend: 'pDepend',
387 | caption: 'pCaption',
388 | note: 'pNotes'
389 | }
390 |
--------------------------------------------------------------------------------