gantt_rails 0.0.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,666 @@
1
+ /*
2
+ Copyright (c) 2012-2014 Open Lab
3
+ Written by Roberto Bicchierai and Silvia Chelazzi http://roberto.open-lab.com
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ */
23
+ function GridEditor(master) {
24
+ this.master = master; // is the a GantEditor instance
25
+ this.gridified = $.gridify($.JST.createFromTemplate({}, "TASKSEDITHEAD"));
26
+ this.element = this.gridified.find(".gdfTable").eq(1);
27
+ }
28
+
29
+
30
+ GridEditor.prototype.fillEmptyLines = function () {
31
+ var factory = new TaskFactory();
32
+
33
+ //console.debug("GridEditor.fillEmptyLines");
34
+ var rowsToAdd = 30 - this.element.find(".taskEditRow").size();
35
+
36
+ //fill with empty lines
37
+ for (var i = 0; i < rowsToAdd; i++) {
38
+ var emptyRow = $.JST.createFromTemplate({}, "TASKEMPTYROW");
39
+ //click on empty row create a task and fill above
40
+ var master = this.master;
41
+ emptyRow.click(function (ev) {
42
+ var emptyRow = $(this);
43
+ //add on the first empty row only
44
+ if (!master.canWrite || emptyRow.prevAll(".emptyRow").size() > 0)
45
+ return
46
+
47
+ master.beginTransaction();
48
+ var lastTask;
49
+ var start = new Date().getTime();
50
+ var level = 0;
51
+ if (master.tasks[0]) {
52
+ start = master.tasks[0].start;
53
+ level = master.tasks[0].level + 1;
54
+ }
55
+
56
+ //fill all empty previouses
57
+ emptyRow.prevAll(".emptyRow").andSelf().each(function () {
58
+ var ch = factory.build("tmp_fk" + new Date().getTime(), "", "", level, start, 1);
59
+ var task = master.addTask(ch);
60
+ lastTask = ch;
61
+ });
62
+ master.endTransaction();
63
+ lastTask.rowElement.click();
64
+ lastTask.rowElement.find("[name=name]").focus()//focus to "name" input
65
+ .blur(function () { //if name not inserted -> undo -> remove just added lines
66
+ var imp = $(this);
67
+ if (imp.val() == "") {
68
+ lastTask.name="Task "+(lastTask.getRow()+1);
69
+ imp.val(lastTask.name);
70
+ }
71
+ });
72
+ });
73
+ this.element.append(emptyRow);
74
+ }
75
+ };
76
+
77
+
78
+ GridEditor.prototype.addTask = function (task, row, hideIfParentCollapsed) {
79
+ //console.debug("GridEditor.addTask",task,row);
80
+ //var prof = new Profiler("editorAddTaskHtml");
81
+
82
+ //remove extisting row
83
+ this.element.find("[taskId=" + task.id + "]").remove();
84
+
85
+ var taskRow = $.JST.createFromTemplate(task, "TASKROW");
86
+ //save row element on task
87
+ task.rowElement = taskRow;
88
+
89
+ this.bindRowEvents(task, taskRow);
90
+
91
+ if (typeof(row) != "number") {
92
+ var emptyRow = this.element.find(".emptyRow:first"); //tries to fill an empty row
93
+ if (emptyRow.size() > 0)
94
+ emptyRow.replaceWith(taskRow);
95
+ else
96
+ this.element.append(taskRow);
97
+ } else {
98
+ var tr = this.element.find("tr.taskEditRow").eq(row);
99
+ if (tr.size() > 0) {
100
+ tr.before(taskRow);
101
+ } else {
102
+ this.element.append(taskRow);
103
+ }
104
+
105
+ }
106
+ // this.element.find(".taskRowIndex").each(function (i, el) {
107
+ // $(el).html(i + 1);
108
+ // });
109
+ //prof.stop();
110
+
111
+
112
+ //[expand]
113
+ if(hideIfParentCollapsed)
114
+ {
115
+ if(task.collapsed) taskRow.find(".exp-controller").removeClass('exp');
116
+ var collapsedDescendant = this.master.getCollapsedDescendant();
117
+ if(collapsedDescendant.indexOf(task) >= 0) taskRow.hide();
118
+ }
119
+
120
+
121
+ return taskRow;
122
+ };
123
+
124
+ GridEditor.prototype.refreshExpandStatus = function(task){
125
+ if(!task) return;
126
+ //[expand]
127
+ var child = task.getChildren();
128
+ if(child.length > 0 && task.rowElement.has(".expcoll").length == 0)
129
+ {
130
+ task.rowElement.find(".exp-controller").addClass('expcoll exp');
131
+ }
132
+ else if(child.length == 0 && task.rowElement.has(".expcoll").length > 0)
133
+ {
134
+ task.rowElement.find(".exp-controller").removeClass('expcoll exp');
135
+ }
136
+
137
+ var par = task.getParent();
138
+ if(par && par.rowElement.has(".expcoll").length == 0)
139
+ {
140
+ par.rowElement.find(".exp-controller").addClass('expcoll exp');
141
+ }
142
+
143
+ }
144
+
145
+ GridEditor.prototype.refreshTaskRow = function (task) {
146
+ //console.debug("refreshTaskRow")
147
+ //var profiler = new Profiler("editorRefreshTaskRow");
148
+ var row = task.rowElement;
149
+
150
+ row.find(".taskRowIndex").html(task.getRow() + 1);
151
+ row.find(".indentCell").css("padding-left", task.level * 10 +18 );
152
+ row.find("[name=name]").val(task.name);
153
+ row.find("[name=code]").val(task.code);
154
+ row.find("[status]").attr("status", task.status);
155
+
156
+ row.find("[name=duration]").val(task.duration);
157
+ row.find("[name=start]").val(new Date(task.start).format()).updateOldValue(); // called on dates only because for other field is called on focus event
158
+ row.find("[name=end]").val(new Date(task.end).format()).updateOldValue();
159
+ row.find("[name=depends]").val(task.depends);
160
+ row.find(".taskAssigs").html(task.getAssigsString());
161
+
162
+ //profiler.stop();
163
+ };
164
+
165
+ GridEditor.prototype.redraw = function () {
166
+ for (var i = 0; i < this.master.tasks.length; i++) {
167
+ this.refreshTaskRow(this.master.tasks[i]);
168
+ }
169
+ };
170
+
171
+ GridEditor.prototype.reset = function () {
172
+ this.element.find("[taskid]").remove();
173
+ };
174
+
175
+
176
+ GridEditor.prototype.bindRowEvents = function (task, taskRow) {
177
+ var self = this;
178
+ //console.debug("bindRowEvents",this,this.master,this.master.canWrite, task.canWrite);
179
+ if (this.master.canWrite && task.canWrite ) {
180
+ self.bindRowInputEvents(task, taskRow);
181
+
182
+ } else { //cannot write: disable input
183
+ taskRow.find("input").attr("readonly", true);
184
+ }
185
+
186
+ self.bindRowExpandEvents(task, taskRow);
187
+
188
+ taskRow.find(".edit").click(function () {self.openFullEditor(task, taskRow)});
189
+ };
190
+
191
+
192
+ GridEditor.prototype.bindRowExpandEvents = function (task, taskRow) {
193
+ var self = this;
194
+ //expand collapse
195
+ taskRow.find(".exp-controller").click(function(){
196
+ //expand?
197
+ var el=$(this);
198
+ var taskId=el.closest("[taskid]").attr("taskid");
199
+ var task=self.master.getTask(taskId);
200
+ var descs=task.getDescendant();
201
+ el.toggleClass('exp');
202
+ task.collapsed = !el.is(".exp");
203
+ var collapsedDescendant = self.master.getCollapsedDescendant();
204
+
205
+ if (el.is(".exp")){
206
+ for (var i=0;i<descs.length;i++)
207
+ {
208
+ var childTask = descs[i];
209
+ if(collapsedDescendant.indexOf(childTask) >= 0) continue;
210
+ childTask.rowElement.show();
211
+ }
212
+
213
+ } else {
214
+ for (var i=0;i<descs.length;i++)
215
+ descs[i].rowElement.hide();
216
+ }
217
+ self.master.gantt.refreshGantt();
218
+
219
+ });
220
+ }
221
+
222
+ GridEditor.prototype.bindRowInputEvents = function (task, taskRow) {
223
+ var self = this;
224
+
225
+ //bind dateField on dates
226
+ taskRow.find(".date").each(function () {
227
+ var el = $(this);
228
+
229
+ //start is readonly in case of deps
230
+ if (task.depends && el.attr("name") == "start") {
231
+ el.attr("readonly", "true");
232
+ } else {
233
+ el.removeAttr("readonly");
234
+ }
235
+
236
+ el.click(function () {
237
+ var inp = $(this);
238
+ inp.dateField({
239
+ inputField:el
240
+ });
241
+ });
242
+
243
+ el.blur(function (date) {
244
+ var inp = $(this);
245
+ if (inp.isValueChanged()) {
246
+ if (!Date.isValid(inp.val())) {
247
+ alert(GanttMaster.messages["INVALID_DATE_FORMAT"]);
248
+ inp.val(inp.getOldValue());
249
+
250
+ } else {
251
+ var date = Date.parseString(inp.val());
252
+ var row = inp.closest("tr");
253
+ var taskId = row.attr("taskId");
254
+ var task = self.master.getTask(taskId);
255
+ var lstart = task.start;
256
+ var lend = task.end;
257
+
258
+ if (inp.attr("name") == "start") {
259
+ lstart = date.getTime();
260
+ if (lstart >= lend) {
261
+ var end_as_date = new Date(lstart);
262
+ lend = end_as_date.add('d', task.duration).getTime();
263
+ }
264
+
265
+ //update task from editor
266
+ self.master.beginTransaction();
267
+ self.master.moveTask(task, lstart);
268
+ self.master.endTransaction();
269
+
270
+ } else {
271
+ lend = date.getTime();
272
+ if (lstart >= lend) {
273
+ lend=lstart;
274
+ }
275
+ lend=lend+3600000*20; // this 20 hours are mandatory to reach the correct day end (snap to grid)
276
+
277
+ //update task from editor
278
+ self.master.beginTransaction();
279
+ self.master.changeTaskDates(task, lstart, lend);
280
+ self.master.endTransaction();
281
+ }
282
+
283
+
284
+ inp.updateOldValue(); //in order to avoid multiple call if nothing changed
285
+ }
286
+ }
287
+ });
288
+ });
289
+
290
+
291
+ //binding on blur for task update (date exluded as click on calendar blur and then focus, so will always return false, its called refreshing the task row)
292
+ taskRow.find("input:not(.date)").focus(function () {
293
+ $(this).updateOldValue();
294
+
295
+ }).blur(function () {
296
+ var el = $(this);
297
+ if (el.isValueChanged()) {
298
+ var row = el.closest("tr");
299
+ var taskId = row.attr("taskId");
300
+
301
+ var task = self.master.getTask(taskId);
302
+
303
+ //update task from editor
304
+ var field = el.attr("name");
305
+
306
+ self.master.beginTransaction();
307
+
308
+ if (field == "depends") {
309
+ var oldDeps = task.depends;
310
+ task.depends = el.val();
311
+
312
+ //start is readonly in case of deps
313
+ if (task.depends) {
314
+ row.find("[name=start]").attr("readonly", "true");
315
+ } else {
316
+ row.find("[name=start]").removeAttr("readonly");
317
+ }
318
+
319
+
320
+ // update links
321
+ var linkOK = self.master.updateLinks(task);
322
+ if (linkOK) {
323
+ //synchronize status from superiors states
324
+ var sups = task.getSuperiors();
325
+ for (var i = 0; i < sups.length; i++) {
326
+ if (!sups[i].from.synchronizeStatus())
327
+ break;
328
+ }
329
+ self.master.changeTaskDates(task, task.start, task.end); // fake change to force date recomputation from dependencies
330
+ }
331
+
332
+ } else if (field == "duration") {
333
+ var dur = task.duration;
334
+ dur = parseInt(el.val()) || 1;
335
+ el.val(dur);
336
+ var newEnd = computeEndByDuration(task.start, dur);
337
+ self.master.changeTaskDates(task, task.start, newEnd);
338
+
339
+ } else if (field == "name" && el.val() == "") { // remove unfilled task
340
+ task.deleteTask();
341
+
342
+ } else {
343
+ task[field] = el.val();
344
+ }
345
+ self.master.endTransaction();
346
+ }
347
+ });
348
+
349
+ //cursor key movement
350
+ taskRow.find("input").keydown(function (event) {
351
+ var theCell = $(this);
352
+ var theTd = theCell.parent();
353
+ var theRow = theTd.parent();
354
+ var col = theTd.prevAll("td").size();
355
+
356
+ var ret = true;
357
+ switch (event.keyCode) {
358
+
359
+ case 37: //left arrow
360
+ if(this.selectionEnd==0)
361
+ theTd.prev().find("input").focus();
362
+ break;
363
+ case 39: //right arrow
364
+ if(this.selectionEnd==this.value.length)
365
+ theTd.next().find("input").focus();
366
+ break;
367
+
368
+ case 38: //up arrow
369
+ var prevRow = theRow.prev();
370
+ var td = prevRow.find("td").eq(col);
371
+ var inp = td.find("input");
372
+
373
+ if (inp.size()>0)
374
+ inp.focus();
375
+ break;
376
+ case 40: //down arrow
377
+ var nextRow = theRow.next();
378
+ var td = nextRow.find("td").eq(col);
379
+ var inp = td.find("input");
380
+ if (inp.size()>0)
381
+ inp.focus();
382
+ else
383
+ nextRow.click(); //create a new row
384
+ break;
385
+ case 36: //home
386
+ break;
387
+ case 35: //end
388
+ break;
389
+
390
+ case 9: //tab
391
+ case 13: //enter
392
+ break;
393
+ }
394
+ return ret;
395
+
396
+ }).focus(function () {
397
+ $(this).closest("tr").click();
398
+ });
399
+
400
+
401
+ //change status
402
+ taskRow.find(".taskStatus").click(function () {
403
+ var el = $(this);
404
+ var tr = el.closest("[taskid]");
405
+ var taskId = tr.attr("taskid");
406
+ var task = self.master.getTask(taskId);
407
+
408
+ var changer = $.JST.createFromTemplate({}, "CHANGE_STATUS");
409
+ changer.find("[status=" + task.status + "]").addClass("selected");
410
+ changer.find(".taskStatus").click(function (e) {
411
+ e.stopPropagation();
412
+ var newStatus = $(this).attr("status");
413
+ changer.remove();
414
+ self.master.beginTransaction();
415
+ task.changeStatus(newStatus);
416
+ self.master.endTransaction();
417
+ el.attr("status", task.status);
418
+ });
419
+ el.oneTime(3000, "hideChanger", function () {
420
+ changer.remove();
421
+ });
422
+ el.after(changer);
423
+ });
424
+
425
+
426
+
427
+ //bind row selection
428
+ taskRow.click(function () {
429
+ var row = $(this);
430
+ //var isSel = row.hasClass("rowSelected");
431
+ row.closest("table").find(".rowSelected").removeClass("rowSelected");
432
+ row.addClass("rowSelected");
433
+
434
+ //set current task
435
+ self.master.currentTask = self.master.getTask(row.attr("taskId"));
436
+
437
+ //move highlighter
438
+ self.master.gantt.synchHighlight();
439
+
440
+ //if offscreen scroll to element
441
+ var top = row.position().top;
442
+ if (top > self.element.parent().height()) {
443
+ row.offsetParent().scrollTop(top - self.element.parent().height() + 100);
444
+ } else if (top<40){
445
+ row.offsetParent().scrollTop(row.offsetParent().scrollTop()-40+top);
446
+ }
447
+ });
448
+
449
+ };
450
+
451
+
452
+ GridEditor.prototype.openFullEditor = function (task, taskRow) {
453
+
454
+ var self = this;
455
+
456
+ //task editor in popup
457
+ var taskId = taskRow.attr("taskId");
458
+ //console.debug(task);
459
+
460
+ //make task editor
461
+ var taskEditor = $.JST.createFromTemplate({}, "TASK_EDITOR");
462
+
463
+ taskEditor.find("#name").val(task.name);
464
+ taskEditor.find("#description").val(task.description);
465
+ taskEditor.find("#code").val(task.code);
466
+ taskEditor.find("#progress").val(task.progress ? parseFloat(task.progress) : 0);
467
+ taskEditor.find("#status").attr("status", task.status);
468
+
469
+ if (task.startIsMilestone)
470
+ taskEditor.find("#startIsMilestone").attr("checked", true);
471
+ if (task.endIsMilestone)
472
+ taskEditor.find("#endIsMilestone").attr("checked", true);
473
+
474
+ taskEditor.find("#duration").val(task.duration);
475
+ var startDate = taskEditor.find("#start");
476
+ startDate.val(new Date(task.start).format());
477
+ //start is readonly in case of deps
478
+ if (task.depends) {
479
+ startDate.attr("readonly", "true");
480
+ } else {
481
+ startDate.removeAttr("readonly");
482
+ }
483
+
484
+ taskEditor.find("#end").val(new Date(task.end).format());
485
+
486
+ //taskEditor.find("[name=depends]").val(task.depends);
487
+
488
+ //make assignments table
489
+ var assigsTable = taskEditor.find("#assigsTable");
490
+ assigsTable.find("[assigId]").remove();
491
+ // loop on already assigned resources
492
+ for (var i = 0; i < task.assigs.length; i++) {
493
+ var assig = task.assigs[i];
494
+ var assigRow = $.JST.createFromTemplate({task:task, assig:assig}, "ASSIGNMENT_ROW");
495
+ assigsTable.append(assigRow);
496
+ }
497
+
498
+ //define start end callbacks
499
+ function startChangeCallback(date) {
500
+ var dur = parseInt(taskEditor.find("#duration").val());
501
+ date.clearTime();
502
+ taskEditor.find("#end").val(new Date(computeEndByDuration(date.getTime(), dur)).format());
503
+ }
504
+
505
+ function endChangeCallback(end) {
506
+ var start = Date.parseString(taskEditor.find("#start").val());
507
+ end.setHours(23, 59, 59, 999);
508
+
509
+ if (end.getTime() < start.getTime()) {
510
+ var dur = parseInt(taskEditor.find("#duration").val());
511
+ start = incrementDateByWorkingDays(end.getTime(), -dur);
512
+ taskEditor.find("#start").val(new Date(computeStart(start)).format());
513
+ } else {
514
+ taskEditor.find("#duration").val(recomputeDuration(start.getTime(), end.getTime()));
515
+ }
516
+ }
517
+
518
+
519
+ if (!self.master.canWrite || !task.canWrite) {
520
+ taskEditor.find("input,textarea").attr("readOnly", true);
521
+ taskEditor.find("input:checkbox,select").attr("disabled", true);
522
+ taskEditor.find("#saveButton").remove();
523
+
524
+ } else {
525
+
526
+ //bind dateField on dates
527
+ taskEditor.find("#start").click(function () {
528
+ $(this).dateField({
529
+ inputField:$(this),
530
+ callback: startChangeCallback
531
+ });
532
+ }).blur(function () {
533
+ var inp = $(this);
534
+ if (!Date.isValid(inp.val())) {
535
+ alert(GanttMaster.messages["INVALID_DATE_FORMAT"]);
536
+ inp.val(inp.getOldValue());
537
+ } else {
538
+ startChangeCallback(Date.parseString(inp.val()))
539
+ }
540
+ });
541
+
542
+ //bind dateField on dates
543
+ taskEditor.find("#end").click(function () {
544
+ $(this).dateField({
545
+ inputField:$(this),
546
+ callback: endChangeCallback
547
+ });
548
+ }).blur(function () {
549
+ var inp = $(this);
550
+ if (!Date.isValid(inp.val())) {
551
+ alert(GanttMaster.messages["INVALID_DATE_FORMAT"]);
552
+ inp.val(inp.getOldValue());
553
+ } else {
554
+ endChangeCallback(Date.parseString(inp.val()))
555
+ }
556
+ });
557
+
558
+ //bind blur on duration
559
+ taskEditor.find("#duration").change(function () {
560
+ var start = Date.parseString(taskEditor.find("#start").val());
561
+ var el = $(this);
562
+ var dur = parseInt(el.val());
563
+ dur = dur <= 0 ? 1 : dur;
564
+ el.val(dur);
565
+ taskEditor.find("#end").val(new Date(computeEndByDuration(start.getTime(), dur)).format());
566
+ });
567
+
568
+
569
+ //bind add assignment
570
+ taskEditor.find("#addAssig").click(function () {
571
+ var assigsTable = taskEditor.find("#assigsTable");
572
+ var assigRow = $.JST.createFromTemplate({task:task, assig:{id:"tmp_" + new Date().getTime()}}, "ASSIGNMENT_ROW");
573
+ assigsTable.append(assigRow);
574
+ $("#bwinPopupd").scrollTop(10000);
575
+ });
576
+
577
+ taskEditor.find("#status").click(function () {
578
+ var tskStatusChooser = $(this);
579
+ var changer = $.JST.createFromTemplate({}, "CHANGE_STATUS");
580
+ changer.find("[status=" + task.status + "]").addClass("selected");
581
+ changer.find(".taskStatus").click(function (e) {
582
+ e.stopPropagation();
583
+ tskStatusChooser.attr("status", $(this).attr("status"));
584
+ changer.remove();
585
+ });
586
+ tskStatusChooser.oneTime(3000, "hideChanger", function () {
587
+ changer.remove();
588
+ });
589
+ tskStatusChooser.after(changer);
590
+ });
591
+
592
+
593
+ //save task
594
+ taskEditor.find("#saveButton").click(function () {
595
+ var task = self.master.getTask(taskId); // get task again because in case of rollback old task is lost
596
+
597
+ self.master.beginTransaction();
598
+ task.name = taskEditor.find("#name").val();
599
+ task.description = taskEditor.find("#description").val();
600
+ task.code = taskEditor.find("#code").val();
601
+ task.progress = parseFloat(taskEditor.find("#progress").val());
602
+ task.duration = parseInt(taskEditor.find("#duration").val());
603
+ task.startIsMilestone = taskEditor.find("#startIsMilestone").is(":checked");
604
+ task.endIsMilestone = taskEditor.find("#endIsMilestone").is(":checked");
605
+
606
+ //set assignments
607
+ taskEditor.find("tr[assigId]").each(function () {
608
+ var trAss = $(this);
609
+ var assId = trAss.attr("assigId");
610
+ var resId = trAss.find("[name=resourceId]").val();
611
+ var roleId = trAss.find("[name=roleId]").val();
612
+ var effort = millisFromString(trAss.find("[name=effort]").val());
613
+
614
+
615
+ //check if an existing assig has been deleted and re-created with the same values
616
+ var found = false;
617
+ for (var i = 0; i < task.assigs.length; i++) {
618
+ var ass = task.assigs[i];
619
+
620
+ if (assId == ass.id) {
621
+ ass.effort = effort;
622
+ ass.roleId = roleId;
623
+ ass.resourceId = resId;
624
+ ass.touched = true;
625
+ found = true;
626
+ break;
627
+
628
+ } else if (roleId == ass.roleId && resId == ass.resourceId) {
629
+ ass.effort = effort;
630
+ ass.touched = true;
631
+ found = true;
632
+ break;
633
+
634
+ }
635
+ }
636
+
637
+ if (!found) { //insert
638
+ var ass = task.createAssignment("tmp_" + new Date().getTime(), resId, roleId, effort);
639
+ ass.touched = true;
640
+ }
641
+
642
+ });
643
+
644
+ //remove untouched assigs
645
+ task.assigs = task.assigs.filter(function (ass) {
646
+ var ret = ass.touched;
647
+ delete ass.touched;
648
+ return ret;
649
+ });
650
+
651
+ //change dates
652
+ task.setPeriod(Date.parseString(taskEditor.find("#start").val()).getTime(), Date.parseString(taskEditor.find("#end").val()).getTime() + (3600000 * 24));
653
+
654
+ //change status
655
+ task.changeStatus(taskEditor.find("#status").attr("status"));
656
+
657
+ if (self.master.endTransaction()) {
658
+ $("#__blackpopup__").trigger("close");
659
+ }
660
+
661
+ });
662
+ }
663
+
664
+ var ndo = createBlackPage(800, 500).append(taskEditor);
665
+
666
+ };