Skip to content

Commit 7f853c6

Browse files
committed
Rework Crockford Objects as JS/TS classes
1 parent cdf1586 commit 7f853c6

File tree

15 files changed

+1127
-1190
lines changed

15 files changed

+1127
-1190
lines changed

frontend/src/assets/sass/backlogs/_taskboard.sass

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -192,7 +192,7 @@
192192
* - .board
193193
*
194194
* Also use by the Column Width preference to determine the unit width of the
195-
* swimlanes. See RB.Taskboard.initialize()
195+
* swimlanes. See Taskboard constructor()
196196
197197
/* status labels */
198198

frontend/src/stimulus/controllers/dynamic/backlogs/backlog.ts

Lines changed: 138 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
// See COPYRIGHT and LICENSE files for more details.
2727
//++
2828

29+
import { Burndown } from './burndown';
30+
import { EditableSprint } from './sprint';
31+
import { EditableStory, EditableStoryType } from './story';
32+
2933
/******************************************
3034
BACKLOG
3135
A backlog is a visual representation of
@@ -37,146 +41,137 @@
3741
sheet (or in Redmine Backlogs!) to
3842
visualize the sprint.
3943
******************************************/
40-
41-
// @ts-expect-error TS(2304): Cannot find name 'RB'.
42-
RB.Backlog = (function ($) {
43-
// @ts-expect-error TS(2304): Cannot find name 'RB'.
44-
return RB.Object.create({
45-
46-
initialize(el:any) {
47-
this.$ = $(el);
48-
this.el = el;
49-
50-
// Associate this object with the element for later retrieval
51-
this.$.data('this', this);
52-
53-
// Make the list sortable
54-
this.getList().sortable({
55-
connectWith: '.stories',
56-
dropOnEmpty: true,
57-
start: this.dragStart,
58-
stop: this.dragStop,
59-
update: this.dragComplete,
60-
receive: this.dragChanged,
61-
remove: this.dragChanged,
62-
containment: $('#backlogs_container'),
63-
cancel: 'input, textarea, button, select, option, .prevent_drag',
64-
scroll: true,
65-
helper(event:any, ui:any) {
66-
const $clone = $(ui).clone();
67-
$clone.css('position', 'absolute');
68-
return $clone.get(0);
69-
},
70-
});
71-
72-
// Observe menu items
73-
this.$.find('.add_new_story').click(this.handleNewStoryClick);
74-
75-
if (this.isSprintBacklog()) {
76-
// @ts-expect-error TS(2304): Cannot find name 'RB'.
77-
RB.Factory.initialize(RB.Sprint, this.getSprint());
78-
// @ts-expect-error TS(2304): Cannot find name 'RB'.
79-
this.burndown = RB.Factory.initialize(RB.Burndown, this.$.find('.show_burndown_chart'));
80-
this.burndown.setSprintId(this.getSprint().data('this').getID());
81-
}
82-
83-
// Initialize each item in the backlog
84-
this.getStories().each(function (this:any, index:any) {
85-
// 'this' refers to an element with class="story"
86-
// @ts-expect-error TS(2304): Cannot find name 'RB'.
87-
RB.Factory.initialize(RB.Story, this);
88-
});
89-
90-
if (this.isSprintBacklog()) {
91-
this.refresh();
92-
}
93-
},
94-
95-
dragChanged(e:any, ui:any) {
96-
$(this).parents('.backlog').data('this').refresh();
97-
},
98-
99-
dragComplete(e:any, ui:any) {
100-
const isDropTarget = (ui.sender === null || ui.sender === undefined);
101-
102-
// jQuery triggers dragComplete of source and target.
103-
// Thus we have to check here. Otherwise, the story
104-
// would be saved twice.
105-
if (isDropTarget) {
106-
ui.item.data('this').saveDragResult();
107-
}
108-
},
109-
110-
dragStart(e:any, ui:any) {
111-
ui.item.addClass('dragging');
112-
},
113-
114-
dragStop(e:any, ui:any) {
115-
ui.item.removeClass('dragging');
116-
},
117-
118-
getSprint() {
119-
return $(this.el).find('.model.sprint').first();
120-
},
121-
122-
getStories() {
123-
return this.getList().children('.story');
124-
},
125-
126-
getList() {
127-
return this.$.children('.stories').first();
128-
},
129-
130-
handleNewStoryClick(e:any) {
131-
const toggler = $(this).parents('.header').find('.toggler');
132-
if (toggler.hasClass('closed')) {
133-
toggler.click();
134-
}
135-
e.preventDefault();
136-
$(this).parents('.backlog').data('this').newStory();
137-
},
138-
139-
// return true if backlog has an element with class="sprint"
140-
isSprintBacklog() {
141-
return $(this.el).find('.sprint').length === 1;
142-
},
143-
144-
newStory() {
145-
let story;
146-
let o;
147-
148-
story = $('#story_template').children().first().clone();
149-
this.getList().prepend(story);
150-
151-
// @ts-expect-error TS(2304): Cannot find name 'RB'.
152-
o = RB.Factory.initialize(RB.Story, story[0]);
153-
o.edit();
154-
155-
story.find('.editor').first().focus();
156-
},
157-
158-
refresh() {
159-
this.recalcVelocity();
160-
this.recalcOddity();
161-
},
162-
163-
recalcVelocity() {
164-
let total:any;
165-
166-
if (!this.isSprintBacklog()) {
167-
return;
168-
}
169-
170-
total = 0;
171-
this.getStories().each(function (this:any, index:any) {
172-
total += $(this).data('this').getPoints();
173-
});
174-
this.$.children('.header').children('.velocity').text(total);
175-
},
176-
177-
recalcOddity() {
178-
this.$.find('.story:even').removeClass('odd').addClass('even');
179-
this.$.find('.story:odd').removeClass('even').addClass('odd');
180-
},
181-
});
182-
}(jQuery));
44+
export class Backlog {
45+
private $:JQuery;
46+
private el:HTMLElement;
47+
burndown:Burndown;
48+
49+
constructor(el:HTMLElement) {
50+
this.$ = $(el);
51+
this.el = el;
52+
53+
// Associate this object with the element for later retrieval
54+
this.$.data('this', this);
55+
56+
// Make the list sortable
57+
this.getList().sortable({
58+
connectWith: '.stories',
59+
dropOnEmpty: true,
60+
start: this.dragStart,
61+
stop: this.dragStop,
62+
update: this.dragComplete,
63+
receive: this.dragChanged,
64+
remove: this.dragChanged,
65+
containment: $('#backlogs_container'),
66+
cancel: 'input, textarea, button, select, option, .prevent_drag',
67+
scroll: true,
68+
helper(ui) {
69+
const $clone = $(ui).clone();
70+
$clone.css('position', 'absolute');
71+
return $clone.get(0) as unknown as Element;
72+
},
73+
});
74+
75+
// Observe menu items
76+
this.$.find('.add_new_story').click(this.handleNewStoryClick);
77+
78+
if (this.isSprintBacklog()) {
79+
new EditableSprint(this.getSprint()[0]);
80+
this.burndown = new Burndown(this.$.find('.show_burndown_chart')[0]);
81+
this.burndown.setSprintId(this.getSprint().data('this').getID());
82+
}
83+
84+
// Initialize each item in the backlog
85+
this.getStories().each((index, element) => {
86+
// refers to an element with class="story"
87+
new EditableStory(element);
88+
});
89+
90+
if (this.isSprintBacklog()) {
91+
this.refresh();
92+
}
93+
}
94+
95+
dragChanged(e:JQueryEventObject, ui:JQueryUI.SortableUIParams) {
96+
$(this).parents('.backlog').data('this').refresh();
97+
}
98+
99+
dragComplete(e:JQueryEventObject, ui:JQueryUI.SortableUIParams) {
100+
const isDropTarget = (ui.sender === null || ui.sender === undefined);
101+
102+
// jQuery triggers dragComplete of source and target.
103+
// Thus we have to check here. Otherwise, the story
104+
// would be saved twice.
105+
if (isDropTarget) {
106+
ui.item.data('this').saveDragResult();
107+
}
108+
}
109+
110+
dragStart(e:JQueryEventObject, ui:JQueryUI.SortableUIParams) {
111+
ui.item.addClass('dragging');
112+
}
113+
114+
dragStop(e:JQueryEventObject, ui:JQueryUI.SortableUIParams) {
115+
ui.item.removeClass('dragging');
116+
}
117+
118+
getSprint() {
119+
return $(this.el).find('.model.sprint').first();
120+
}
121+
122+
getStories() {
123+
return this.getList().children('.story');
124+
}
125+
126+
getList() {
127+
return this.$.children('.stories').first();
128+
}
129+
130+
handleNewStoryClick(e:JQuery.Event) {
131+
const toggler = $(this).parents('.header').find('.toggler');
132+
if (toggler.hasClass('closed')) {
133+
toggler.click();
134+
}
135+
e.preventDefault();
136+
this.newStory();
137+
}
138+
139+
// return true if backlog has an element with class="sprint"
140+
isSprintBacklog() {
141+
return $(this.el).find('.sprint').length === 1;
142+
}
143+
144+
newStory() {
145+
const story = $('#story_template').children().first().clone();
146+
this.getList().prepend(story);
147+
148+
const o = new EditableStory(story[0]);
149+
o.edit();
150+
151+
story.find('.editor').first().focus();
152+
}
153+
154+
refresh() {
155+
this.recalcVelocity();
156+
this.recalcOddity();
157+
}
158+
159+
recalcVelocity() {
160+
let total:any;
161+
162+
if (!this.isSprintBacklog()) {
163+
return;
164+
}
165+
166+
total = 0;
167+
this.getStories().each((index, element) => {
168+
total += ($(element).data('this') as EditableStoryType).getPoints();
169+
});
170+
this.$.children('.header').children('.velocity').text(total);
171+
}
172+
173+
recalcOddity() {
174+
this.$.find('.story:even').removeClass('odd').addClass('even');
175+
this.$.find('.story:odd').removeClass('even').addClass('odd');
176+
}
177+
}

frontend/src/stimulus/controllers/dynamic/backlogs/burndown.ts

Lines changed: 32 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -26,42 +26,43 @@
2626
// See COPYRIGHT and LICENSE files for more details.
2727
//++
2828

29-
// @ts-expect-error TS(2304): Cannot find name 'RB'.
30-
RB.Burndown = (function ($) {
31-
// @ts-expect-error TS(2304): Cannot find name 'RB'.
32-
return RB.Object.create({
29+
import { RBGlobal } from './common';
3330

34-
initialize(el:any) {
35-
this.$ = $(el);
36-
this.el = el;
31+
declare const RB:RBGlobal;
3732

38-
// Associate this object with the element for later retrieval
39-
this.$.data('this', this);
33+
export class Burndown {
34+
$:JQuery;
35+
el:HTMLElement;
36+
sprintId:number;
4037

41-
// Observe menu items
42-
this.$.click(this.show);
43-
},
38+
constructor(el:HTMLElement) {
39+
this.$ = $(el);
40+
this.el = el;
4441

45-
setSprintId(sprintId:any) {
46-
this.sprintId = sprintId;
47-
},
42+
// Associate this object with the element for later retrieval
43+
this.$.data('this', this);
4844

49-
getSprintId() {
50-
return this.sprintId;
51-
},
45+
// Observe menu items
46+
this.$.click(this.show);
47+
}
5248

53-
show(e:any) {
54-
e.preventDefault();
49+
setSprintId(sprintId:number) {
50+
this.sprintId = sprintId;
51+
}
5552

56-
if ($('#charts').length === 0) {
57-
$('<div id="charts"></div>').appendTo('body');
58-
}
59-
// @ts-expect-error TS(2304): Cannot find name 'RB'.
60-
$('#charts').html(`<div class='loading'>${RB.i18n.generating_graph}</div>`);
53+
getSprintId() {
54+
return this.sprintId;
55+
}
6156

62-
// @ts-expect-error TS(2304): Cannot find name 'RB'.
63-
const url = RB.urlFor('show_burndown_chart', { sprint_id: $(this).data('this').sprintId, project_id: RB.constants.project_id });
64-
window.open(url);
65-
},
66-
});
67-
}(jQuery));
57+
show(e:JQuery.Event) {
58+
e.preventDefault();
59+
60+
if ($('#charts').length === 0) {
61+
$('<div id="charts"></div>').appendTo('body');
62+
}
63+
$('#charts').html(`<div class='loading'>${RB.i18n.generating_graph}</div>`);
64+
65+
const url = RB.urlFor('show_burndown_chart', { sprint_id: $(this).data('this').sprintId, project_id: RB.constants.project_id });
66+
window.open(url);
67+
}
68+
}

0 commit comments

Comments
 (0)