1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 package org.kuali.student.lum.lu.ui.course.client.configuration;
31
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.HashMap;
35 import java.util.List;
36 import java.util.Map;
37
38 import org.kuali.student.common.assembly.data.Data;
39 import org.kuali.student.common.assembly.data.Data.Value;
40 import org.kuali.student.common.assembly.data.Metadata;
41 import org.kuali.student.common.assembly.data.QueryPath;
42 import org.kuali.student.common.dto.DtoConstants;
43 import org.kuali.student.common.ui.client.application.Application;
44 import org.kuali.student.common.ui.client.configurable.mvc.FieldDescriptor;
45 import org.kuali.student.common.ui.client.configurable.mvc.SectionTitle;
46 import org.kuali.student.common.ui.client.configurable.mvc.binding.HasDataValueBinding;
47 import org.kuali.student.common.ui.client.configurable.mvc.binding.ListOfStringBinding;
48 import org.kuali.student.common.ui.client.configurable.mvc.binding.ModelWidgetBinding;
49 import org.kuali.student.common.ui.client.configurable.mvc.binding.ModelWidgetBindingSupport;
50 import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.CompositeConditionOperator;
51 import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityConfiguration;
52 import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityFieldConfiguration;
53 import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityFieldWidgetInitializer;
54 import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.SwapCompositeCondition;
55 import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.SwapCompositeConditionFieldConfig;
56 import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.SwapCondition;
57 import org.kuali.student.common.ui.client.configurable.mvc.sections.BaseSection;
58 import org.kuali.student.common.ui.client.configurable.mvc.sections.CollapsableSection;
59 import org.kuali.student.common.ui.client.configurable.mvc.sections.GroupSection;
60 import org.kuali.student.common.ui.client.configurable.mvc.sections.MultiplicitySection;
61 import org.kuali.student.common.ui.client.configurable.mvc.sections.Section;
62 import org.kuali.student.common.ui.client.configurable.mvc.sections.SwapEventHandler;
63 import org.kuali.student.common.ui.client.configurable.mvc.sections.SwapSection;
64 import org.kuali.student.common.ui.client.configurable.mvc.sections.VerticalSection;
65 import org.kuali.student.common.ui.client.configurable.mvc.views.SectionView;
66 import org.kuali.student.common.ui.client.configurable.mvc.views.VerticalSectionView;
67 import org.kuali.student.common.ui.client.mvc.Controller;
68 import org.kuali.student.common.ui.client.mvc.DataModel;
69 import org.kuali.student.common.ui.client.mvc.DataModelDefinition;
70 import org.kuali.student.common.ui.client.mvc.HasDataValue;
71 import org.kuali.student.common.ui.client.mvc.View;
72 import org.kuali.student.common.ui.client.widgets.KSButton;
73 import org.kuali.student.common.ui.client.widgets.KSButtonAbstract.ButtonStyle;
74 import org.kuali.student.common.ui.client.widgets.KSCharCount;
75 import org.kuali.student.common.ui.client.widgets.KSCheckBox;
76 import org.kuali.student.common.ui.client.widgets.KSDropDown;
77 import org.kuali.student.common.ui.client.widgets.ListOfStringWidget;
78 import org.kuali.student.common.ui.client.widgets.field.layout.element.MessageKeyInfo;
79 import org.kuali.student.common.ui.client.widgets.field.layout.element.SpanPanel;
80 import org.kuali.student.common.ui.client.widgets.field.layout.layouts.FieldLayoutComponent;
81 import org.kuali.student.common.ui.client.widgets.list.KSLabelList;
82 import org.kuali.student.common.ui.client.widgets.list.KSSelectItemWidgetAbstract;
83 import org.kuali.student.common.ui.client.widgets.list.KSSelectedList;
84 import org.kuali.student.common.ui.client.widgets.list.impl.SimpleListItems;
85 import org.kuali.student.common.ui.client.widgets.search.KSPicker;
86 import org.kuali.student.core.comments.ui.client.widgets.commenttool.CommentTool;
87 import org.kuali.student.core.comments.ui.client.widgets.decisiontool.DecisionPanel;
88 import org.kuali.student.core.document.ui.client.widgets.documenttool.DocumentTool;
89 import org.kuali.student.core.statement.dto.StatementTypeInfo;
90 import org.kuali.student.core.workflow.ui.client.views.CollaboratorSectionView;
91 import org.kuali.student.lum.common.client.lo.LOBuilder;
92 import org.kuali.student.lum.common.client.lo.LOBuilderBinding;
93 import org.kuali.student.lum.common.client.lo.LOPicker;
94 import org.kuali.student.lum.common.client.lo.OutlineNode;
95 import org.kuali.student.lum.common.client.lu.LUUIConstants;
96 import org.kuali.student.lum.lu.assembly.data.client.constants.base.RichTextInfoConstants;
97 import org.kuali.student.lum.lu.assembly.data.client.constants.orch.CreditCourseActivityConstants;
98 import org.kuali.student.lum.lu.assembly.data.client.constants.orch.CreditCourseConstants;
99 import org.kuali.student.lum.lu.assembly.data.client.constants.orch.CreditCourseJointsConstants;
100 import org.kuali.student.lum.lu.ui.course.client.controllers.CourseProposalController;
101 import org.kuali.student.lum.lu.ui.course.client.requirements.CourseRequirementsViewController;
102
103 import com.google.gwt.core.client.GWT;
104 import com.google.gwt.event.dom.client.ClickEvent;
105 import com.google.gwt.event.dom.client.ClickHandler;
106 import com.google.gwt.event.logical.shared.ValueChangeEvent;
107 import com.google.gwt.event.logical.shared.ValueChangeHandler;
108 import com.google.gwt.user.client.ui.Widget;
109
110
111
112
113
114
115
116 public class CourseProposalConfigurer extends AbstractCourseConfigurer {
117
118 protected boolean WITH_DIVIDER = true;
119 protected boolean NO_DIVIDER = false;
120
121 public static final String PROPOSAL_PATH = "proposal";
122 public static final String PROPOSAL_TITLE_PATH = "proposal/name";
123 public static final String COURSE_TITLE_PATH = "/courseTitle";
124
125
126 protected DocumentTool documentTool;
127 protected CourseSummaryConfigurer summaryConfigurer;
128
129 protected List<StatementTypeInfo> stmtTypes;
130
131 public static final String COURSE = "";
132
133 public enum CourseSections {
134 CLU_BEGIN, PEOPLE_PERMISSONS, SUMMARY, AUTHORS_RATIONALE, GOVERNANCE, COURSE_LOGISTICS, COURSE_INFO, LEARNING_OBJECTIVES,
135 COURSE_REQUISITES, ACTIVE_DATES, FINANCIALS, ATTACHMENTS, COMMENTS, DECISIONS, DOCUMENTS,
136 PROGRAM_INFO, ASSEMBLER_TEST, WF_APPROVE_DIALOG
137 }
138
139 public void setStatementTypes(List<StatementTypeInfo> stmtTypes) {
140 this.stmtTypes = stmtTypes;
141 }
142
143
144
145
146
147
148
149 public void configure(final CourseProposalController layout) {
150 type = "course";
151 state = DtoConstants.STATE_DRAFT;
152 groupName = LUUIConstants.COURSE_GROUP_NAME;
153
154 if (modelDefinition.getMetadata().isCanEdit()) {
155 addCluStartSection(layout);
156 String sections = getLabel(LUUIConstants.COURSE_SECTIONS);
157
158
159
160
161 layout.addMenu(sections);
162
163
164
165 layout.addMenuItem(sections, (SectionView)generateCourseInfoSection(initSectionView(CourseSections.COURSE_INFO, LUUIConstants.INFORMATION_LABEL_KEY)));
166 layout.addMenuItem(sections, (SectionView)generateGovernanceSection(initSectionView(CourseSections.GOVERNANCE, LUUIConstants.GOVERNANCE_LABEL_KEY)));
167 layout.addMenuItem(sections, (SectionView)generateCourseLogisticsSection(initSectionView(CourseSections.COURSE_LOGISTICS, LUUIConstants.LOGISTICS_LABEL_KEY)));
168 layout.addMenuItem(sections, generateLearningObjectivesSection());
169
170
171 layout.addMenuItem(sections, generateCourseRequisitesSection(layout,true));
172
173
174 layout.addMenuItem(sections, (SectionView)generateActiveDatesSection(initSectionView(CourseSections.ACTIVE_DATES, LUUIConstants.ACTIVE_DATES_LABEL_KEY)));
175 layout.addMenuItem(sections, (SectionView)generateFinancialsSection(initSectionView(CourseSections.FINANCIALS, LUUIConstants.FINANCIALS_LABEL_KEY)));
176
177
178 layout.addMenuItem(sections, new CollaboratorSectionView(CourseSections.PEOPLE_PERMISSONS, getLabel(LUUIConstants.SECTION_AUTHORS_AND_COLLABORATORS), COURSE_PROPOSAL_MODEL));
179
180
181 documentTool = new DocumentTool(LUUIConstants.REF_DOC_RELATION_PROPOSAL_TYPE,CourseSections.DOCUMENTS, getLabel(LUUIConstants.TOOL_DOCUMENTS_LABEL_KEY));
182 documentTool.setModelDefinition((DataModelDefinition)modelDefinition);
183 layout.addMenuItem(sections, documentTool);
184
185
186 summaryConfigurer = GWT.create(CourseSummaryConfigurer.class);
187 summaryConfigurer.init(type, state, groupName,(DataModelDefinition)modelDefinition, stmtTypes, (Controller)layout, COURSE_PROPOSAL_MODEL);
188 layout.addSpecialMenuItem(summaryConfigurer.generateProposalSummarySection(true), "Review and Submit");
189
190
191 List<Enum<?>> excludedViews = new ArrayList<Enum<?>>();
192 excludedViews.add(CourseSections.DOCUMENTS);
193 excludedViews.add(CourseSections.COURSE_REQUISITES);
194 layout.addCommonButton(LUUIConstants.COURSE_SECTIONS, layout.getSaveButton(), excludedViews);
195 layout.addCommonButton(LUUIConstants.COURSE_SECTIONS, layout.getCancelButton(CourseSections.SUMMARY), excludedViews);
196
197
198
199 layout.addButtonForView(CourseSections.DOCUMENTS, getContinueButton(layout));
200 }
201 else{
202 CourseSummaryConfigurer summaryConfigurer = GWT.create(CourseSummaryConfigurer.class);
203 summaryConfigurer.init(type, state, groupName, (DataModelDefinition)modelDefinition, stmtTypes, (Controller)layout, COURSE_PROPOSAL_MODEL);
204 layout.removeMenuNavigation();
205 layout.addView(summaryConfigurer.generateProposalSummarySection(false));
206 }
207 layout.showPrint(true);
208 layout.setDefaultView(CourseSections.SUMMARY);
209 layout.addContentWidget(layout.getWfUtilities().getProposalStatusLabel());
210 final CommentTool commentTool = new CommentTool(CourseSections.COMMENTS, getLabel(LUUIConstants.TOOL_COMMENTS_LABEL_KEY), "kuali.comment.type.generalRemarks", "Proposal Comments");
211 commentTool.setController(layout);
212
213 layout.addContentWidget(new KSButton("Comments", ButtonStyle.DEFAULT_ANCHOR, new ClickHandler() {
214
215 @Override
216 public void onClick(ClickEvent event) {
217 commentTool.show();
218 }
219 }));
220
221
222 final DecisionPanel decisionPanel = new DecisionPanel(CourseSections.DECISIONS, getLabel(LUUIConstants.TOOL_DECISION_LABEL_KEY), "kuali.comment.type.generalRemarks");
223 layout.addView(decisionPanel);
224 layout.addContentWidget(new KSButton("Decisions", ButtonStyle.DEFAULT_ANCHOR, new ClickHandler() {
225
226 @Override
227 public void onClick(ClickEvent event) {
228 decisionPanel.show();
229 }
230 }));
231
232 }
233
234 protected KSButton getContinueButton(final CourseProposalController layout){
235 return new KSButton("Continue", new ClickHandler(){
236 public void onClick(ClickEvent event) {
237 layout.showNextViewOnMenu();
238 }
239 });
240 }
241
242 public void addCluStartSection(CourseProposalController layout) {
243 VerticalSectionView section = initSectionView(CourseSections.CLU_BEGIN, LUUIConstants.START_LABEL_KEY);
244 section.setController(layout);
245 addField(section, PROPOSAL_TITLE_PATH, generateMessageInfo(LUUIConstants.PROPOSAL_TITLE_LABEL_KEY));
246 addField(section, COURSE + "/" + COURSE_TITLE, generateMessageInfo(LUUIConstants.COURSE_TITLE_LABEL_KEY));
247
248
249 layout.addStartViewPopup(section);
250 layout.getStartPopup().setMaxHeight(600);
251 }
252
253 protected View generateCourseRequisitesSection(Controller layout, boolean showSaveButtons) {
254 return new CourseRequirementsViewController(layout, getLabel(LUUIConstants.REQUISITES_LABEL_KEY), CourseSections.COURSE_REQUISITES, false, showSaveButtons);
255 }
256
257 protected Section generateActiveDatesSection(Section section) {
258
259 FieldDescriptor fd = addField(section, PROPOSAL_PATH + "/" + PREV_START_TERM, generateMessageInfo(LUUIConstants.PROPOSAL_PREV_START_TERM));
260 fd.getFieldWidget().setVisible(false);
261 fd.hideLabel();
262
263 addField(section, COURSE + "/" + START_TERM, generateMessageInfo(LUUIConstants.START_TERM_LABEL_KEY));
264
265 addField(section, COURSE + "/" + PILOT_COURSE, generateMessageInfo(LUUIConstants.PILOT_COURSE_LABEL_KEY), new KSCheckBox(getLabel(LUUIConstants.PILOT_COURSE_TEXT_LABEL_KEY))).setIgnoreShowRequired(true);
266 addField(section, COURSE + "/" + END_TERM, generateMessageInfo(LUUIConstants.END_TERM_LABEL_KEY)).setIgnoreShowRequired(true);
267
268 return section;
269 }
270
271 protected VerticalSection generateActiveDateEndSection() {
272 VerticalSection endDate = initSection(getH3Title(LUUIConstants.END_DATE_LABEL_KEY), WITH_DIVIDER);
273 addField(endDate, COURSE + "/" + EXPIRATION_DATE, generateMessageInfo(LUUIConstants.EXPIRATION_DATE_LABEL_KEY));
274 return endDate;
275 }
276
277 protected VerticalSection generateActiveDateStartSection() {
278 VerticalSection startDate = initSection(getH3Title(LUUIConstants.START_DATE_LABEL_KEY), WITH_DIVIDER);
279 addField(startDate, COURSE + "/" + CreditCourseConstants.EFFECTIVE_DATE, generateMessageInfo(LUUIConstants.EFFECTIVE_DATE_LABEL_KEY));
280 return startDate;
281 }
282
283 protected Section generateGovernanceSection(Section section) {
284 addField(section, COURSE + "/" + CAMPUS_LOCATIONS, generateMessageInfo(LUUIConstants.CAMPUS_LOCATION_LABEL_KEY));
285 addField(section, COURSE + "/" + CURRICULUM_OVERSIGHT_ORGS_, generateMessageInfo(LUUIConstants.ACADEMIC_SUBJECT_ORGS_KEY));
286 addField(section, COURSE + "/" + ADMIN_ORGS, generateMessageInfo(LUUIConstants.ADMIN_ORG_LABEL_KEY));
287
288 return section;
289 }
290
291 public Section generateCourseInfoSection(Section section) {
292 addField(section, PROPOSAL_TITLE_PATH, generateMessageInfo(LUUIConstants.PROPOSAL_TITLE_LABEL_KEY));
293 addField(section, COURSE + "/" + COURSE_TITLE, generateMessageInfo(LUUIConstants.COURSE_TITLE_LABEL_KEY));
294 addField(section, COURSE + "/" + TRANSCRIPT_TITLE, generateMessageInfo(LUUIConstants.SHORT_TITLE_LABEL_KEY), new KSCharCount(getMetaData(COURSE + "/" + TRANSCRIPT_TITLE)));
295 section.addSection(generateCourseNumberSection());
296 FieldDescriptor instructorsFd = addField(section, COURSE + "/" + INSTRUCTORS, generateMessageInfo(LUUIConstants.INSTRUCTORS_LABEL_KEY));
297 instructorsFd.setWidgetBinding(new KeyListModelWigetBinding("personId"));
298
299 section.addSection(generateDescriptionRationaleSection());
300
301 return section;
302 }
303
304
305 protected GroupSection generateCourseNumberSection() {
306
307
308 GroupSection courseNumber = new GroupSection(getH4Title(""));
309 courseNumber.addStyleName(LUUIConstants.STYLE_SECTION);
310 courseNumber.addStyleName(LUUIConstants.STYLE_SECTION_DIVIDER);
311 addField(courseNumber, COURSE + "/" + SUBJECT_AREA, generateMessageInfo(LUUIConstants.SUBJECT_CODE_LABEL_KEY));
312 addField(courseNumber, COURSE + "/" + COURSE_NUMBER_SUFFIX, generateMessageInfo(LUUIConstants.COURSE_NUMBER_LABEL_KEY));
313
314
315
316 courseNumber.addSection(generateCrossListed_Ver_Joint_Section());
317
318 return courseNumber;
319 }
320
321 protected CollapsableSection generateCrossListed_Ver_Joint_Section() {
322 CollapsableSection result = new CollapsableSection(getLabel(LUUIConstants.CL_V_J_LABEL_KEY));
323
324
325
326
327 SpanPanel crslabelpan = new SpanPanel();
328 crslabelpan.setStyleName("ks-multiplicity-section-label");
329 crslabelpan.setHTML("Cross Listed Courses");
330 crslabelpan.setVisible(true);
331 result.addWidget(crslabelpan);
332 addMultiplicityFields(result, COURSE + QueryPath.getPathSeparator() + CROSS_LISTINGS,
333 LUUIConstants.ADD_CROSS_LISTED_LABEL_KEY,
334 LUUIConstants.CROSS_LISTED_ITEM_LABEL_KEY,
335 Arrays.asList(
336 new MultiplicityFieldConfig(
337 SUBJECT_AREA,
338 LUUIConstants.SUBJECT_CODE_LABEL_KEY, null, null, true),
339 new MultiplicityFieldConfig(
340 COURSE_NUMBER_SUFFIX,
341 LUUIConstants.COURSE_NUMBER_LABEL_KEY, null, null, true)),
342 null,
343 null,0);
344 SpanPanel jntlabelpan = new SpanPanel();
345 jntlabelpan.setStyleName("ks-multiplicity-section-label");
346 jntlabelpan.setHTML("Jointly Offered Courses");
347 jntlabelpan.setVisible(true);
348 result.addWidget(jntlabelpan);
349 addMultiplicityFields(result, COURSE + QueryPath.getPathSeparator() + JOINTS,
350 LUUIConstants.ADD_EXISTING_LABEL_KEY,
351 LUUIConstants.JOINT_OFFER_ITEM_LABEL_KEY,
352 Arrays.asList(
353 new MultiplicityFieldConfig(
354 CreditCourseJointsConstants.COURSE_ID,
355 LUUIConstants.COURSE_NUMBER_OR_TITLE_LABEL_KEY, null, null, true)),
356 null,
357 null,0);
358 SpanPanel vsnlabelpan = new SpanPanel();
359 vsnlabelpan.setStyleName("ks-multiplicity-section-label");
360 vsnlabelpan.setHTML("Version Codes");
361 vsnlabelpan.setVisible(true);
362 result.addWidget(vsnlabelpan);
363 addMultiplicityFields(result, COURSE + QueryPath.getPathSeparator() + VERSIONS,
364 LUUIConstants.ADD_VERSION_CODE_LABEL_KEY,
365 LUUIConstants.VERSION_CODE_LABEL_KEY,
366 Arrays.asList(
367 new MultiplicityFieldConfig(
368 "variationCode",
369 LUUIConstants.VERSION_CODE_LABEL_KEY, null, null, true),
370 new MultiplicityFieldConfig(
371 "variationTitle",
372 LUUIConstants.TITLE_LABEL_KEY, null, null, true)
373 ),
374 null,
375 null,0);
376 result.getLayout().setVisible(false);
377 return result;
378 }
379
380 protected void addFeeMultiplicityFields(Section section,
381 String path, String addItemlabelMessageKey,
382 String itemLabelMessageKey, List<MultiplicityFieldConfig> fieldConfigs,
383 Map<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>> swappableFieldsDefinition,
384 List<String> deletionParentKeys) {
385 MultiplicityConfiguration config = setupMultiplicityConfig(
386 MultiplicityConfiguration.MultiplicityType.GROUP,
387 MultiplicityConfiguration.StyleType.TOP_LEVEL_GROUP,
388 path, addItemlabelMessageKey, itemLabelMessageKey,
389 fieldConfigs, swappableFieldsDefinition, deletionParentKeys);
390 MultiplicitySection ms = null;
391 ms = new MultiplicitySection(config, swappableFieldsDefinition, deletionParentKeys);
392 section.addSection(ms);
393
394 }
395
396 protected MultiplicityConfiguration setupMultiplicityConfig(
397 MultiplicityConfiguration.MultiplicityType multiplicityType,
398 MultiplicityConfiguration.StyleType styleType,
399 String path, String addItemlabelMessageKey,
400 String itemLabelMessageKey, List<MultiplicityFieldConfig> fieldConfigs,
401 Map<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>> swappableFieldsDefinition,
402 List<String> deletionParentKeys) {
403 QueryPath parentPath = QueryPath.concat(path);
404 MultiplicityConfiguration config = new MultiplicityConfiguration(multiplicityType,
405 styleType, getMetaData(parentPath.toString()));
406 config.setAddItemLabel(getLabel(addItemlabelMessageKey));
407 config.setItemLabel(getLabel(itemLabelMessageKey));
408 config.setUpdateable(true);
409
410 FieldDescriptor parentFd = buildMultiplicityParentFieldDescriptor(path, getLabel(itemLabelMessageKey), null);
411 config.setParent(parentFd);
412
413 if (fieldConfigs != null) {
414 for (MultiplicityFieldConfig fieldConfig : fieldConfigs) {
415 MultiplicityFieldConfiguration fc = buildMultiplicityFD(fieldConfig.getFieldKey(),
416 fieldConfig.getLabelKey(), parentPath.toString());
417 config.addFieldConfiguration(fc);
418 if (fieldConfig.isNextLine()) {
419 config.nextLine();
420 }
421 }
422 }
423 return config;
424 }
425
426 protected MultiplicitySection addMultiplicityFields(Section section,
427 String path, String addItemlabelMessageKey,
428 String itemLabelMessageKey, List<MultiplicityFieldConfig> fieldConfigs,
429 Map<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>> swappableFieldsDefinition,
430 List<String> deletionParentKeys,int defaultItemsCreated) {
431 MultiplicityConfiguration config = setupMultiplicityConfig(
432 MultiplicityConfiguration.MultiplicityType.GROUP,
433 MultiplicityConfiguration.StyleType.TOP_LEVEL_GROUP,
434 path, addItemlabelMessageKey, itemLabelMessageKey,
435 fieldConfigs, swappableFieldsDefinition, deletionParentKeys);
436 config.setDefaultItemsCreated(defaultItemsCreated);
437 MultiplicitySection ms = null;
438 ms = new MultiplicitySection(config, swappableFieldsDefinition, deletionParentKeys);
439 section.addSection(ms);
440 return ms;
441 }
442
443 protected Metadata getMetaData(String fieldKey) {
444 return modelDefinition.getMetadata(QueryPath.concat(fieldKey));
445 }
446
447 protected MultiplicityFieldConfiguration buildMultiplicityFD(
448 String fieldKey, String labelKey, String parentPath) {
449
450 QueryPath fieldPath = QueryPath.concat(parentPath, QueryPath.getWildCard(), fieldKey);
451 Metadata meta = modelDefinition.getMetadata(fieldPath);
452
453 MultiplicityFieldConfiguration fd = new MultiplicityFieldConfiguration(
454 fieldPath.toString(), generateMessageInfo(labelKey), meta, null);
455
456
457 return fd;
458
459 }
460
461 protected FieldDescriptor buildMultiplicityParentFieldDescriptor(String fieldKey, String messageKey, String parentPath) {
462 QueryPath path = QueryPath.concat(parentPath, fieldKey);
463 Metadata meta = modelDefinition.getMetadata(path);
464
465 FieldDescriptor fd = new FieldDescriptor(path.toString(), generateMessageInfo(messageKey), meta);
466 fd.hideLabel();
467 return fd;
468 }
469
470 protected VerticalSection generateCourseInfoShortTitleSection() {
471 VerticalSection shortTitle = initSection(getH3Title(LUUIConstants.SHORT_TITLE_LABEL_KEY), WITH_DIVIDER);
472 addField(shortTitle, COURSE + "/" + TRANSCRIPT_TITLE, null);
473 return shortTitle;
474 }
475
476 protected VerticalSection generateLongTitleSection() {
477 VerticalSection longTitle = initSection(getH3Title(LUUIConstants.TITLE_LABEL_KEY), WITH_DIVIDER);
478 addField(longTitle, COURSE + "/" + COURSE_TITLE, null);
479 return longTitle;
480 }
481
482 protected VerticalSection generateDescriptionRationaleSection() {
483 SectionTitle title = getH4Title(LUUIConstants.PROPOSAL_TITLE_SECTION_LABEL_KEY);
484 VerticalSection description = initSection(title, !WITH_DIVIDER);
485 title.setStyleName("cluProposalTitleSection");
486
487
488 addField(description, COURSE + "/" + DESCRIPTION + "/" + RichTextInfoConstants.PLAIN, generateMessageInfo(LUUIConstants.DESCRIPTION_LABEL_KEY));
489 addField(description, "proposal/rationale", generateMessageInfo(LUUIConstants.PROPOSAL_RATIONALE_LABEL_KEY),
490 new KSCharCount(modelDefinition.getMetadata(QueryPath.parse("proposal/rationale"))));
491
492 return description;
493 }
494
495 public Section generateCourseLogisticsSection(Section section) {
496 if (section instanceof SectionView){
497 ((SectionView)section).setInstructions(getLabel(LUUIConstants.LOGISTICS_LABEL_KEY + "-instruct") + "<br><br>");
498 }
499
500 section.addSection(generateSchedulingSection());
501 section.addSection(generateDurationSection());
502 section.addSection(generateLearningResultsSection());
503 section.addSection(generateCourseFormatsSection());
504
505 return section;
506 }
507
508 protected Section generateLearningResultsSection() {
509 VerticalSection learningResults = initSection(getH3Title(LUUIConstants.LEARNING_RESULTS_LABEL_KEY), WITH_DIVIDER);
510 learningResults.setInstructions(getLabel(LUUIConstants.LEARNING_RESULTS_LABEL_KEY + "-instruct") + "<br><br><br>");
511
512 learningResults.addSection(generateGradesAssessmentsSection());
513 learningResults.addSection(generateStudentRegistrationOptionsSection());
514 learningResults.addSection(generateFinalExamSection());
515 learningResults.addSection(generateOutcomesSection());
516
517 return learningResults;
518 }
519
520 protected Section generateOutcomesSection() {
521
522 String path = COURSE + QueryPath.getPathSeparator() + CREDIT_OPTIONS;
523 QueryPath creditTypeFullPath = QueryPath.concat(path, QueryPath.getWildCard(), CreditCourseConstants.TYPE);
524 QueryPath creditOptionFixedFullPath = QueryPath.concat(path, QueryPath.getWildCard(), CREDIT_OPTION_FIXED_CREDITS);
525 QueryPath creditOptionMinFullPath = QueryPath.concat(path, QueryPath.getWildCard(), CREDIT_OPTION_MIN_CREDITS);
526 QueryPath creditOptionMaxFullPath = QueryPath.concat(path, QueryPath.getWildCard(), CREDIT_OPTION_MAX_CREDITS);
527 QueryPath creditResultValuesFullPath = QueryPath.concat(path, QueryPath.getWildCard(), "resultValues");
528
529 VerticalSection courseOutcomes = initSection(getH3Title(LUUIConstants.LEARNING_RESULT_OUTCOME_LABEL_KEY), WITH_DIVIDER);
530 Map<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>> swappableFieldsDefinition =
531 new HashMap<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>>();
532 SwapCompositeCondition fixedCreditCondition = new SwapCompositeCondition(
533 CompositeConditionOperator.AND);
534 fixedCreditCondition.getChildrenConditions().add(
535 makeCondition(creditTypeFullPath, LUUIConstants.LEARNING_RESULT_OUTCOME_TYPE_LABEL_KEY, "kuali.resultComponentType.credit.degree.fixed")
536 );
537 fixedCreditCondition.setConditionId("1");
538 SwapCompositeCondition multipleCreditCondition = new SwapCompositeCondition(
539 CompositeConditionOperator.AND);
540 multipleCreditCondition.getChildrenConditions().add(
541 makeCondition(creditTypeFullPath, LUUIConstants.LEARNING_RESULT_OUTCOME_TYPE_LABEL_KEY, "kuali.resultComponentType.credit.degree.multiple")
542 );
543 multipleCreditCondition.setConditionId("2");
544 SwapCompositeCondition variableCreditCondition = new SwapCompositeCondition(
545 CompositeConditionOperator.AND);
546 variableCreditCondition.getChildrenConditions().add(
547 makeCondition(creditTypeFullPath, LUUIConstants.LEARNING_RESULT_OUTCOME_TYPE_LABEL_KEY, "kuali.resultComponentType.credit.degree.range")
548 );
549 variableCreditCondition.setConditionId("3");
550
551 swappableFieldsDefinition.put(fixedCreditCondition,
552 Arrays.asList(
553 new SwapCompositeConditionFieldConfig(
554 new MultiplicityFieldConfiguration(
555 creditOptionFixedFullPath.toString(),
556 generateMessageInfo(LUUIConstants.CREDIT_OPTION_FIXED_CREDITS_LABEL_KEY),
557 modelDefinition.getMetadata(creditOptionFixedFullPath),
558 null),
559 null
560 )
561 )
562 );
563 MultiplicityFieldWidgetInitializer multipleCreditInitializer =
564 new MultiplicityFieldWidgetInitializer() {
565 @Override
566 public ModelWidgetBinding<?> getModelWidgetBindingInstance() {
567 return new ListOfStringBinding();
568 }
569 @Override
570 public Widget getNewWidget() {
571 return new ListOfStringWidget("Add Item");
572 }
573 };
574
575 swappableFieldsDefinition.put(multipleCreditCondition,
576 Arrays.asList(
577 new SwapCompositeConditionFieldConfig(
578 new MultiplicityFieldConfiguration(
579 creditResultValuesFullPath.toString(),
580 generateMessageInfo(LUUIConstants.CREDIT_OPTION_FIXED_CREDITS_LABEL_KEY),
581 modelDefinition.getMetadata(creditResultValuesFullPath),
582 multipleCreditInitializer),
583 null
584 )
585 )
586 );
587 swappableFieldsDefinition.put(variableCreditCondition,
588 Arrays.asList(
589 new SwapCompositeConditionFieldConfig(
590 new MultiplicityFieldConfiguration(
591 creditOptionMinFullPath.toString(),
592 generateMessageInfo(LUUIConstants.CREDIT_OPTION_MIN_CREDITS_LABEL_KEY),
593 modelDefinition.getMetadata(creditOptionMinFullPath),
594 null),
595 null
596 ),
597 new SwapCompositeConditionFieldConfig(
598 new MultiplicityFieldConfiguration(
599 creditOptionMaxFullPath.toString(),
600 generateMessageInfo(LUUIConstants.CREDIT_OPTION_MAX_CREDITS_LABEL_KEY),
601 modelDefinition.getMetadata(creditOptionMaxFullPath),
602 null),
603 null
604 )
605 )
606 );
607
608 MultiplicitySection ms = addMultiplicityFields(
609 courseOutcomes,
610 path,
611 LUUIConstants.LEARNING_RESULT_OUTCOME_LABEL_KEY,
612 LUUIConstants.LEARNING_RESULT_OUTCOME_LABEL_KEY,
613 Arrays.asList(
614 new MultiplicityFieldConfig(
615 CreditCourseConstants.TYPE,
616 LUUIConstants.LEARNING_RESULT_OUTCOME_TYPE_LABEL_KEY,
617 null, null, true)
618 ), swappableFieldsDefinition, null,1);
619
620 courseOutcomes.setRequired(ms.getConfig().getParentFd().getFieldElement().getRequiredPanel());
621 return courseOutcomes;
622
623 }
624
625 protected Section generateStudentRegistrationOptionsSection() {
626 VerticalSection studentRegistrationOptionsSection = initSection(getH3Title(LUUIConstants.LEARNING_RESULTS_STUDENT_REGISTRATION_LABEL_KEY), WITH_DIVIDER);
627
628 addField(studentRegistrationOptionsSection, COURSE + "/" + AUDIT, generateMessageInfo(LUUIConstants.LEARNING_RESULT_AUDIT_LABEL_KEY), new KSCheckBox(getLabel(LUUIConstants.LEARNING_RESULT_AUDIT_TEXT_LABEL_KEY)));
629 addField(studentRegistrationOptionsSection, COURSE + "/" + PASS_FAIL, generateMessageInfo(LUUIConstants.LEARNING_RESULT_PASS_FAIL_LABEL_KEY), new KSCheckBox(getLabel(LUUIConstants.LEARNING_RESULT_PASS_FAIL_TEXT_LABEL_KEY)));
630
631 return studentRegistrationOptionsSection;
632 }
633
634 protected Section generateGradesAssessmentsSection() {
635 VerticalSection gradesAssessments = initSection(getH3Title(LUUIConstants.LEARNING_RESULTS_GRADES_ASSESSMENTS_LABEL_KEY), WITH_DIVIDER);
636
637 addField(gradesAssessments, COURSE + "/" + GRADING_OPTIONS, generateMessageInfo(LUUIConstants.LEARNING_RESULT_ASSESSMENT_SCALE_LABEL_KEY));
638
639 return gradesAssessments;
640 }
641
642 protected VerticalSection generateCourseFormatsSection() {
643
644 VerticalSection courseFormats = initSection(getH3Title(LUUIConstants.FORMATS_LABEL_KEY), WITH_DIVIDER);
645 courseFormats.setHelp(getLabel(LUUIConstants.FORMATS_LABEL_KEY + "-help"));
646 courseFormats.setInstructions(getLabel(LUUIConstants.FORMATS_LABEL_KEY + "-instruct"));
647 MultiplicityConfiguration courseFormatConfig = setupMultiplicityConfig(
648 MultiplicityConfiguration.MultiplicityType.GROUP,
649 MultiplicityConfiguration.StyleType.TOP_LEVEL_GROUP,
650 COURSE + "/" + FORMATS, LUUIConstants.COURSE_ADD_FORMAT_LABEL_KEY,
651 LUUIConstants.FORMAT_LABEL_KEY,
652 null, null, null);
653 courseFormatConfig.setDefaultItemsCreated(1);
654 MultiplicityConfiguration activitiesConfig = setupMultiplicityConfig(
655 MultiplicityConfiguration.MultiplicityType.GROUP,
656 MultiplicityConfiguration.StyleType.SUB_LEVEL_GROUP,
657 COURSE + "/" + FORMATS + "/*/" + ACTIVITIES,
658 LUUIConstants.ADD_ACTIVITY_LABEL_KEY,
659 LUUIConstants.ACTIVITY_LITERAL_LABEL_KEY,
660 Arrays.asList(
661 new MultiplicityFieldConfig(
662 ACTIVITY_TYPE,
663 LUUIConstants.ACTIVITY_TYPE_LABEL_KEY,
664 null,
665 null,
666 true),
667 new MultiplicityFieldConfig(
668 CONTACT_HOURS + "/" + "unitQuantity",
669 LUUIConstants.CONTACT_HOURS_LABEL_KEY,
670 null,
671 null,
672 false),
673 new MultiplicityFieldConfig(
674 CONTACT_HOURS + "/" + "unitType",
675 LUUIConstants.CONTACT_HOURS_FREQUENCY_LABEL_KEY,
676 null,
677 null,
678 true),
679 new MultiplicityFieldConfig(
680 CreditCourseActivityConstants.DURATION + "/" + "atpDurationTypeKey",
681 LUUIConstants.COURSE_FORMATS_DURATION_TYPE_LABEL_KEY,
682 null,
683 null,
684 false),
685 new MultiplicityFieldConfig(
686 CreditCourseActivityConstants.DURATION + "/" + "timeQuantity",
687 LUUIConstants.DURATION_QUANTITY_LABEL_KEY,
688 null,
689 null,
690 true),
691 new MultiplicityFieldConfig(
692 DEFAULT_ENROLLMENT_ESTIMATE,
693 LUUIConstants.CLASS_SIZE_LABEL_KEY,
694 null,
695 null,
696 true)
697 ), null, null);
698 activitiesConfig.setDefaultItemsCreated(1);
699 courseFormatConfig.setNestedConfig(activitiesConfig);
700
701
702 MultiplicitySection ms = null;
703 ms = new MultiplicitySection(courseFormatConfig,
704 null, null);
705 courseFormats.addSection(ms);
706 courseFormats.setRequired(courseFormatConfig.getParentFd().getFieldElement().getRequiredPanel());
707 return courseFormats;
708 }
709
710 protected VerticalSection generateSchedulingSection() {
711 VerticalSection scheduling = initSection(getH3Title(LUUIConstants.SCHEDULING_LABEL_KEY), WITH_DIVIDER);
712 addField(scheduling, COURSE + "/" + TERMS_OFFERED, generateMessageInfo(LUUIConstants.TERMS_OFFERED_LABEL_KEY));
713 return scheduling;
714 }
715
716 protected VerticalSection generateDurationSection() {
717 VerticalSection duration = initSection(getH3Title(LUUIConstants.DURATION_LITERAL_LABEL_KEY), WITH_DIVIDER);
718 duration.setInstructions(getLabel(LUUIConstants.DURATION_LITERAL_LABEL_KEY + "-instruct"));
719 GroupSection duration_group = new GroupSection();
720 addField(duration_group, COURSE + "/" + CreditCourseConstants.DURATION + "/" + "atpDurationTypeKey", generateMessageInfo(LUUIConstants.DURATION_TYPE_LABEL_KEY));
721 addField(duration_group, COURSE + "/" + CreditCourseConstants.DURATION + "/" + "timeQuantity", generateMessageInfo(LUUIConstants.DURATION_QUANTITY_LABEL_KEY));
722
723 duration.addSection(duration_group);
724 return duration;
725 }
726
727 protected VerticalSection generateFinalExamSection() {
728 VerticalSection finalExam = initSection(getH3Title(LUUIConstants.FINAL_EXAM_LABEL_KEY), WITH_DIVIDER);
729 GroupSection finalExam_group = new GroupSection();
730 GroupSection finalExamRationale_group = new GroupSection();
731 GroupSection finalExamRationale_group2 = new GroupSection();
732
733 FieldDescriptor field = addField(finalExam_group, COURSE + "/" + CreditCourseConstants.FINAL_EXAM, generateMessageInfo(LUUIConstants.FINAL_EXAM_STATUS_LABEL_KEY));
734
735 if (field.isVisible()){
736 KSSelectItemWidgetAbstract picker = (KSSelectItemWidgetAbstract) (((KSPicker) field.getFieldWidget()).getInputWidget());
737 final FieldDescriptor rationaleField = addField(finalExamRationale_group, COURSE + "/" + CreditCourseConstants.FINAL_EXAM_RATIONALE, generateMessageInfo(LUUIConstants.FINAL_EXAM_RATIONALE_LABEL_KEY));
738 rationaleField.setIgnoreShowRequired(true);
739 final FieldDescriptor rationaleField2 = addField(finalExamRationale_group2, COURSE + "/" + CreditCourseConstants.FINAL_EXAM_RATIONALE, generateMessageInfo(LUUIConstants.FINAL_EXAM_RATIONALE_LABEL_KEY));
740 rationaleField2.setIgnoreShowRequired(true);
741
742
743 final String altKey = "ALT";
744 final String noneKey = "None";
745 SwapSection swapSection = new SwapSection(picker);
746 swapSection.addSection(finalExamRationale_group, altKey);
747 swapSection.addSection(finalExamRationale_group2, noneKey);
748 swapSection.setSwapEventHandler(new SwapEventHandler() {
749
750 @Override
751 public void onShowSwappableSection(String key, Section section) {
752 progressiveEnableAndRequireSection(true, section);
753 }
754
755 @Override
756 public void onRemoveSwappableSection(String key, Section section) {
757 progressiveEnableAndRequireSection(false, section);
758 }
759 });
760 finalExam.addSection(finalExam_group);
761
762 finalExam.addSection(swapSection);
763 return finalExam;
764 } else {
765 return new VerticalSection();
766 }
767
768 }
769
770 protected void progressiveEnableAndRequireSection(boolean enableAndRequire, Section section){
771 if (section != null){
772 List<FieldDescriptor> fields = section.getFields();
773 if ((fields != null) && (fields.size() > 0)){
774 BaseSection.progressiveEnableAndRequireFields(enableAndRequire, fields.toArray(new FieldDescriptor[fields.size()]));
775 }
776 }
777 }
778
779 protected VerticalSection generateInstructorsSection() {
780 VerticalSection instructors = initSection(getH3Title(LUUIConstants.INSTRUCTOR_LABEL_KEY), WITH_DIVIDER);
781 addField(instructors, COURSE + "/" + PRIMARY_INSTRUCTOR + "/personId");
782 return instructors;
783 }
784
785 protected SectionView generateLearningObjectivesSection() {
786 VerticalSectionView section = initSectionView(CourseSections.LEARNING_OBJECTIVES, LUUIConstants.LEARNING_OBJECTIVES_LABEL_KEY);
787 section.setInstructions(getLabel(LUUIConstants.LEARNING_OBJECTIVES_LABEL_KEY + "-instruct", QueryPath.concat(COURSE, COURSE_SPECIFIC_LOS, "*", "loInfo", "desc", "plain").toString()));
788 section.addSection(generateLearningObjectivesNestedSection());
789 return section;
790 }
791
792 protected VerticalSection generateLearningObjectivesNestedSection() {
793 final VerticalSection los = initSection(null, NO_DIVIDER);
794
795 QueryPath path = QueryPath.concat(COURSE, COURSE_SPECIFIC_LOS, "*", "loInfo", "desc");
796 Metadata meta = modelDefinition.getMetadata(path);
797
798 LOBuilder loBuilder = new LOBuilder(type, state, groupName, "kuali.loRepository.key.singleUse", COURSE_SPECIFIC_LOS, meta);
799 final FieldDescriptor fd = addField(los, CreditCourseConstants.COURSE_SPECIFIC_LOS, null,loBuilder, COURSE);
800
801 loBuilder.addValueChangeHandler(new ValueChangeHandler<List<OutlineNode<LOPicker>>>(){
802 @Override
803 public void onValueChange(ValueChangeEvent<List<OutlineNode<LOPicker>>> event) {
804 los.setIsDirty(true);
805 }
806 });
807
808
809
810 fd.setWidgetBinding(LOBuilderBinding.INSTANCE);
811
812 los.addStyleName("KS-LUM-Section-Divider");
813 return los;
814 }
815
816 public class PersonList extends KSDropDown {
817 final SimpleListItems people = new SimpleListItems();
818
819 public PersonList() {
820 final PersonList us = this;
821 final String userId = Application.getApplicationContext().getSecurityContext().getUserId();
822
823
824 people.addItem(userId, userId);
825 us.setListItems(people);
826 us.selectItem(userId);
827 us.setBlankFirstItem(false);
828 this.setEnabled(false);
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856 }
857
858 @Override
859 public boolean isMultipleSelect() {
860 return true;
861 }
862 }
863
864 public class ProposerPersonList extends KSLabelList {
865 public ProposerPersonList() {
866 SimpleListItems list = new SimpleListItems();
867
868 super.setListItems(list);
869 }
870 }
871
872 protected VerticalSection generateShortTitleSection() {
873 VerticalSection shortTitle = initSection(getH3Title(LUUIConstants.SHORT_TITLE_LABEL_KEY), WITH_DIVIDER);
874 addField(shortTitle, "cluInfo/officialIdentifier/shortName", null);
875 return shortTitle;
876 }
877
878 protected VerticalSectionView initSectionView(Enum<?> viewEnum, String labelKey) {
879 VerticalSectionView section = new VerticalSectionView(viewEnum, getLabel(labelKey), this.getModelId());
880 section.addStyleName(LUUIConstants.STYLE_SECTION);
881 return section;
882 }
883
884
885 protected VerticalSection initSection(SectionTitle title, boolean withDivider) {
886 VerticalSection section;
887 if (title != null) {
888 section = new VerticalSection(title);
889 } else {
890 section = new VerticalSection();
891 }
892 section.addStyleName(LUUIConstants.STYLE_SECTION);
893 if (withDivider)
894 section.addStyleName(LUUIConstants.STYLE_SECTION_DIVIDER);
895 return section;
896 }
897
898 @Override
899 public MessageKeyInfo generateMessageInfo(String labelKey) {
900 return new MessageKeyInfo(groupName, type, state, labelKey);
901 }
902
903
904 protected Section generateFinancialsSection(Section section) {
905
906 VerticalSection justiFee = initSection(getH3Title(LUUIConstants.COURSE_FEE_TITLE), WITH_DIVIDER);
907 SpanPanel courseFeeInstruction = new SpanPanel();
908 courseFeeInstruction.setStyleName("ks-form-module-elements-instruction");
909 courseFeeInstruction.setHTML(getLabel(LUUIConstants.COURSE_FEE_TITLE + FieldLayoutComponent.INSTRUCT_MESSAGE_KEY));
910 courseFeeInstruction.setVisible(true);
911 justiFee.addWidget(courseFeeInstruction);
912
913
914
915 addField(justiFee, COURSE + "/" + "feeJustification" + "/" + RichTextInfoConstants.PLAIN, generateMessageInfo(LUUIConstants.JUSTIFICATION_FEE));
916 section.addSection(justiFee);
917 Map<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>> swappableFieldsDefinition =
918 new HashMap<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>>();
919
920
921
922
923
924
925
926 QueryPath feesPath = QueryPath.concat(COURSE, FEES);
927 QueryPath rateTypeFieldPath = QueryPath.concat(feesPath.toString(), QueryPath.getWildCard(), "rateType");
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302