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 com.google.gwt.core.client.GWT;
33 import com.google.gwt.event.dom.client.ClickEvent;
34 import com.google.gwt.event.dom.client.ClickHandler;
35 import com.google.gwt.event.logical.shared.ValueChangeEvent;
36 import com.google.gwt.event.logical.shared.ValueChangeHandler;
37 import com.google.gwt.user.client.ui.Widget;
38 import org.kuali.student.common.ui.client.application.Application;
39 import org.kuali.student.common.ui.client.configurable.mvc.FieldDescriptor;
40 import org.kuali.student.common.ui.client.configurable.mvc.SectionTitle;
41 import org.kuali.student.common.ui.client.configurable.mvc.binding.HasDataValueBinding;
42 import org.kuali.student.common.ui.client.configurable.mvc.binding.ListOfStringBinding;
43 import org.kuali.student.common.ui.client.configurable.mvc.binding.ModelWidgetBinding;
44 import org.kuali.student.common.ui.client.configurable.mvc.binding.ModelWidgetBindingSupport;
45 import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.CompositeConditionOperator;
46 import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityConfiguration;
47 import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityFieldConfiguration;
48 import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.MultiplicityFieldWidgetInitializer;
49 import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.SwapCompositeCondition;
50 import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.SwapCompositeConditionFieldConfig;
51 import org.kuali.student.common.ui.client.configurable.mvc.multiplicity.SwapCondition;
52 import org.kuali.student.common.ui.client.configurable.mvc.sections.BaseSection;
53 import org.kuali.student.common.ui.client.configurable.mvc.sections.CollapsableSection;
54 import org.kuali.student.common.ui.client.configurable.mvc.sections.GroupSection;
55 import org.kuali.student.common.ui.client.configurable.mvc.sections.MultiplicitySection;
56 import org.kuali.student.common.ui.client.configurable.mvc.sections.Section;
57 import org.kuali.student.common.ui.client.configurable.mvc.sections.SwapEventHandler;
58 import org.kuali.student.common.ui.client.configurable.mvc.sections.SwapSection;
59 import org.kuali.student.common.ui.client.configurable.mvc.sections.VerticalSection;
60 import org.kuali.student.common.ui.client.configurable.mvc.views.SectionView;
61 import org.kuali.student.common.ui.client.configurable.mvc.views.VerticalSectionView;
62 import org.kuali.student.common.ui.client.mvc.Controller;
63 import org.kuali.student.common.ui.client.mvc.DataModel;
64 import org.kuali.student.common.ui.client.mvc.DataModelDefinition;
65 import org.kuali.student.common.ui.client.mvc.HasDataValue;
66 import org.kuali.student.common.ui.client.mvc.View;
67 import org.kuali.student.common.ui.client.widgets.KSButton;
68 import org.kuali.student.common.ui.client.widgets.KSButtonAbstract.ButtonStyle;
69 import org.kuali.student.common.ui.client.widgets.KSCharCount;
70 import org.kuali.student.common.ui.client.widgets.KSCheckBox;
71 import org.kuali.student.common.ui.client.widgets.KSDropDown;
72 import org.kuali.student.common.ui.client.widgets.ListOfStringWidget;
73 import org.kuali.student.common.ui.client.widgets.field.layout.element.MessageKeyInfo;
74 import org.kuali.student.common.ui.client.widgets.field.layout.element.SpanPanel;
75 import org.kuali.student.common.ui.client.widgets.field.layout.layouts.FieldLayoutComponent;
76 import org.kuali.student.common.ui.client.widgets.list.KSLabelList;
77 import org.kuali.student.common.ui.client.widgets.list.KSSelectItemWidgetAbstract;
78 import org.kuali.student.common.ui.client.widgets.list.KSSelectedList;
79 import org.kuali.student.common.ui.client.widgets.list.impl.SimpleListItems;
80 import org.kuali.student.common.ui.client.widgets.search.KSPicker;
81 import org.kuali.student.core.comments.ui.client.widgets.commenttool.CommentTool;
82 import org.kuali.student.core.comments.ui.client.widgets.decisiontool.DecisionPanel;
83 import org.kuali.student.core.document.ui.client.widgets.documenttool.DocumentTool;
84 import org.kuali.student.core.workflow.ui.client.views.CollaboratorSectionView;
85 import org.kuali.student.lum.common.client.lo.LOBuilder;
86 import org.kuali.student.lum.common.client.lo.LOBuilderBinding;
87 import org.kuali.student.lum.common.client.lo.LOPicker;
88 import org.kuali.student.lum.common.client.lo.OutlineNode;
89 import org.kuali.student.lum.common.client.lu.LUUIConstants;
90 import org.kuali.student.lum.lu.assembly.data.client.constants.base.RichTextInfoConstants;
91 import org.kuali.student.lum.lu.assembly.data.client.constants.orch.CreditCourseActivityConstants;
92 import org.kuali.student.lum.lu.assembly.data.client.constants.orch.CreditCourseConstants;
93 import org.kuali.student.lum.lu.assembly.data.client.constants.orch.CreditCourseJointsConstants;
94 import org.kuali.student.lum.lu.ui.course.client.controllers.CourseProposalController;
95 import org.kuali.student.lum.lu.ui.course.client.requirements.CourseRequirementsViewController;
96 import org.kuali.student.r1.common.assembly.data.Data;
97 import org.kuali.student.r1.common.assembly.data.Data.Value;
98 import org.kuali.student.r1.common.assembly.data.Metadata;
99 import org.kuali.student.r1.common.assembly.data.QueryPath;
100 import org.kuali.student.r1.core.statement.dto.StatementTypeInfo;
101 import org.kuali.student.r2.common.dto.DtoConstants;
102
103 import java.util.ArrayList;
104 import java.util.Arrays;
105 import java.util.HashMap;
106 import java.util.List;
107 import java.util.Map;
108
109
110
111
112
113
114
115 public class CourseProposalConfigurer extends AbstractCourseConfigurer {
116
117 protected boolean WITH_DIVIDER = true;
118 protected boolean NO_DIVIDER = false;
119
120 public static final String PROPOSAL_PATH = "proposal";
121 public static final String PROPOSAL_TITLE_PATH = "proposal/name";
122 public static final String COURSE_TITLE_PATH = "/courseTitle";
123
124
125 protected DocumentTool documentTool;
126 protected CourseSummaryConfigurer summaryConfigurer;
127
128 protected List<StatementTypeInfo> stmtTypes;
129
130 public static final String COURSE = "";
131
132 public enum CourseSections {
133 CLU_BEGIN, PEOPLE_PERMISSONS, SUMMARY, AUTHORS_RATIONALE, GOVERNANCE, COURSE_LOGISTICS, COURSE_INFO, LEARNING_OBJECTIVES,
134 COURSE_REQUISITES, ACTIVE_DATES, FINANCIALS, ATTACHMENTS, COMMENTS, DECISIONS, DOCUMENTS,
135 PROGRAM_INFO, ASSEMBLER_TEST, WF_APPROVE_DIALOG
136 }
137
138 public void setStatementTypes(List<StatementTypeInfo> stmtTypes) {
139 this.stmtTypes = stmtTypes;
140 }
141
142
143
144
145
146
147
148 public void configure(final CourseProposalController layout) {
149 type = "course";
150 state = DtoConstants.STATE_DRAFT;
151 groupName = LUUIConstants.COURSE_GROUP_NAME;
152
153 if (modelDefinition.getMetadata().isCanEdit()) {
154 addCluStartSection(layout);
155 String sections = getLabel(LUUIConstants.COURSE_SECTIONS);
156
157
158
159
160 layout.addMenu(sections);
161
162
163
164 layout.addMenuItem(sections, (SectionView) generateCourseInfoSection(initSectionView(CourseSections.COURSE_INFO, LUUIConstants.INFORMATION_LABEL_KEY)));
165 layout.addMenuItem(sections, (SectionView) generateGovernanceSection(initSectionView(CourseSections.GOVERNANCE, LUUIConstants.GOVERNANCE_LABEL_KEY)));
166 layout.addMenuItem(sections, (SectionView) generateCourseLogisticsSection(initSectionView(CourseSections.COURSE_LOGISTICS, LUUIConstants.LOGISTICS_LABEL_KEY)));
167 layout.addMenuItem(sections, generateLearningObjectivesSection());
168
169
170 layout.addMenuItem(sections, generateCourseRequisitesSection(layout, true));
171
172
173 layout.addMenuItem(sections, (SectionView) generateActiveDatesSection(initSectionView(CourseSections.ACTIVE_DATES, LUUIConstants.ACTIVE_DATES_LABEL_KEY)));
174 layout.addMenuItem(sections, (SectionView) generateFinancialsSection(initSectionView(CourseSections.FINANCIALS, LUUIConstants.FINANCIALS_LABEL_KEY)));
175
176
177 layout.addMenuItem(sections, new CollaboratorSectionView(CourseSections.PEOPLE_PERMISSONS, getLabel(LUUIConstants.SECTION_AUTHORS_AND_COLLABORATORS), COURSE_PROPOSAL_MODEL));
178
179
180 documentTool = new DocumentTool(LUUIConstants.REF_DOC_RELATION_PROPOSAL_TYPE, CourseSections.DOCUMENTS, getLabel(LUUIConstants.TOOL_DOCUMENTS_LABEL_KEY));
181 documentTool.setModelDefinition((DataModelDefinition) modelDefinition);
182 layout.addMenuItem(sections, documentTool);
183
184
185 summaryConfigurer = GWT.create(CourseSummaryConfigurer.class);
186 summaryConfigurer.init(type, state, groupName, (DataModelDefinition) modelDefinition, stmtTypes, (Controller) layout, COURSE_PROPOSAL_MODEL);
187 layout.addSpecialMenuItem(summaryConfigurer.generateProposalSummarySection(true), "Review and Submit");
188
189
190 List<Enum<?>> excludedViews = new ArrayList<Enum<?>>();
191 excludedViews.add(CourseSections.DOCUMENTS);
192 excludedViews.add(CourseSections.COURSE_REQUISITES);
193 layout.addCommonButton(LUUIConstants.COURSE_SECTIONS, layout.getSaveButton(), excludedViews);
194 layout.addCommonButton(LUUIConstants.COURSE_SECTIONS, layout.getCancelButton(CourseSections.SUMMARY), excludedViews);
195
196
197
198 layout.addButtonForView(CourseSections.DOCUMENTS, getContinueButton(layout));
199 } else {
200 summaryConfigurer = GWT.create(CourseSummaryConfigurer.class);
201 summaryConfigurer.init(type, state, groupName, (DataModelDefinition) modelDefinition, stmtTypes, (Controller) layout, COURSE_PROPOSAL_MODEL);
202 layout.removeMenuNavigation();
203 layout.addView(summaryConfigurer.generateProposalSummarySection(false));
204 }
205 layout.showPrint(true);
206 layout.setDefaultView(CourseSections.SUMMARY);
207 layout.addContentWidget(layout.getWfUtilities().getProposalStatusLabel());
208 final CommentTool commentTool = new CommentTool(CourseSections.COMMENTS, getLabel(LUUIConstants.TOOL_COMMENTS_LABEL_KEY), "kuali.comment.type.generalRemarks", "Proposal Comments");
209 commentTool.setController(layout);
210
211 layout.addContentWidget(new KSButton("Comments", ButtonStyle.DEFAULT_ANCHOR, new ClickHandler() {
212
213 @Override
214 public void onClick(ClickEvent event) {
215 commentTool.show();
216 }
217 }));
218
219
220 final DecisionPanel decisionPanel = new DecisionPanel(CourseSections.DECISIONS, getLabel(LUUIConstants.TOOL_DECISION_LABEL_KEY), "kuali.comment.type.generalRemarks");
221 layout.addView(decisionPanel);
222 layout.addContentWidget(new KSButton("Decisions", ButtonStyle.DEFAULT_ANCHOR, new ClickHandler() {
223
224 @Override
225 public void onClick(ClickEvent event) {
226 decisionPanel.show();
227 }
228 }));
229
230 }
231
232 protected KSButton getContinueButton(final CourseProposalController layout) {
233 return new KSButton("Continue", new ClickHandler() {
234 public void onClick(ClickEvent event) {
235 layout.showNextViewOnMenu();
236 }
237 });
238 }
239
240 public void addCluStartSection(CourseProposalController layout) {
241 VerticalSectionView section = initSectionView(CourseSections.CLU_BEGIN, LUUIConstants.START_LABEL_KEY);
242 section.setController(layout);
243 addField(section, PROPOSAL_TITLE_PATH, generateMessageInfo(LUUIConstants.PROPOSAL_TITLE_LABEL_KEY));
244 addField(section, COURSE + "/" + COURSE_TITLE, generateMessageInfo(LUUIConstants.COURSE_TITLE_LABEL_KEY)); layout.addStartViewPopup(section);
245 layout.getStartPopup().setMaxHeight(600);
246 }
247
248 protected View generateCourseRequisitesSection(Controller layout, boolean showSaveButtons) {
249 return new CourseRequirementsViewController(layout, getLabel(LUUIConstants.REQUISITES_LABEL_KEY), CourseSections.COURSE_REQUISITES, false, showSaveButtons);
250 }
251
252 protected Section generateActiveDatesSection(Section section) {
253
254 FieldDescriptor fd = addField(section, PROPOSAL_PATH + "/" + PREV_START_TERM, generateMessageInfo(LUUIConstants.PROPOSAL_PREV_START_TERM));
255 fd.getFieldWidget().setVisible(false);
256 fd.hideLabel();
257
258 addField(section, COURSE + "/" + START_TERM, generateMessageInfo(LUUIConstants.START_TERM_LABEL_KEY));
259
260 addField(section, COURSE + "/" + PILOT_COURSE, generateMessageInfo(LUUIConstants.PILOT_COURSE_LABEL_KEY), new KSCheckBox(getLabel(LUUIConstants.PILOT_COURSE_TEXT_LABEL_KEY))).setIgnoreShowRequired(true);
261 addField(section, COURSE + "/" + END_TERM, generateMessageInfo(LUUIConstants.END_TERM_LABEL_KEY)).setIgnoreShowRequired(true);
262
263 return section;
264 }
265
266 protected VerticalSection generateActiveDateEndSection() {
267 VerticalSection endDate = initSection(getH3Title(LUUIConstants.END_DATE_LABEL_KEY), WITH_DIVIDER);
268 addField(endDate, COURSE + "/" + EXPIRATION_DATE, generateMessageInfo(LUUIConstants.EXPIRATION_DATE_LABEL_KEY));
269 return endDate;
270 }
271
272 protected VerticalSection generateActiveDateStartSection() {
273 VerticalSection startDate = initSection(getH3Title(LUUIConstants.START_DATE_LABEL_KEY), WITH_DIVIDER);
274 addField(startDate, COURSE + "/" + CreditCourseConstants.EFFECTIVE_DATE, generateMessageInfo(LUUIConstants.EFFECTIVE_DATE_LABEL_KEY));
275 return startDate;
276 }
277
278 protected Section generateGovernanceSection(Section section) {
279 addField(section, COURSE + "/" + CAMPUS_LOCATIONS, generateMessageInfo(LUUIConstants.CAMPUS_LOCATION_LABEL_KEY));
280 addField(section, COURSE + "/" + CURRICULUM_OVERSIGHT_ORGS_, generateMessageInfo(LUUIConstants.ACADEMIC_SUBJECT_ORGS_KEY));
281 addField(section, COURSE + "/" + ADMIN_ORGS, generateMessageInfo(LUUIConstants.ADMIN_ORG_LABEL_KEY));
282
283 return section;
284 }
285
286 public Section generateCourseInfoSection(Section section) {
287 addField(section, PROPOSAL_TITLE_PATH, generateMessageInfo(LUUIConstants.PROPOSAL_TITLE_LABEL_KEY));
288 addField(section, COURSE + "/" + COURSE_TITLE, generateMessageInfo(LUUIConstants.COURSE_TITLE_LABEL_KEY));
289 addField(section, COURSE + "/" + TRANSCRIPT_TITLE, generateMessageInfo(LUUIConstants.SHORT_TITLE_LABEL_KEY), new KSCharCount(getMetaData(COURSE + "/" + TRANSCRIPT_TITLE)));
290 section.addSection(generateCourseNumberSection());
291 FieldDescriptor instructorsFd = addField(section, COURSE + "/" + INSTRUCTORS, generateMessageInfo(LUUIConstants.INSTRUCTORS_LABEL_KEY));
292 instructorsFd.setWidgetBinding(new KeyListModelWigetBinding("personId"));
293
294 section.addSection(generateDescriptionRationaleSection());
295
296 return section;
297 }
298
299
300 protected GroupSection generateCourseNumberSection() {
301
302
303 GroupSection courseNumber = new GroupSection(getH4Title(""));
304 courseNumber.addStyleName(LUUIConstants.STYLE_SECTION);
305 courseNumber.addStyleName(LUUIConstants.STYLE_SECTION_DIVIDER);
306 addField(courseNumber, COURSE + "/" + SUBJECT_AREA, generateMessageInfo(LUUIConstants.SUBJECT_CODE_LABEL_KEY));
307 addField(courseNumber, COURSE + "/" + COURSE_NUMBER_SUFFIX, generateMessageInfo(LUUIConstants.COURSE_NUMBER_LABEL_KEY));
308
309
310
311 courseNumber.addSection(generateCrossListed_Ver_Joint_Section());
312
313 return courseNumber;
314 }
315
316 protected CollapsableSection generateCrossListed_Ver_Joint_Section() {
317 CollapsableSection result = new CollapsableSection(getLabel(LUUIConstants.CL_V_J_LABEL_KEY));
318
319
320
321
322 SpanPanel crslabelpan = new SpanPanel();
323 crslabelpan.setStyleName("ks-multiplicity-section-label");
324 crslabelpan.setHTML("Cross Listed Courses");
325 crslabelpan.setVisible(true);
326 result.addWidget(crslabelpan);
327 addMultiplicityFields(result, COURSE + QueryPath.getPathSeparator() + CROSS_LISTINGS,
328 LUUIConstants.ADD_CROSS_LISTED_LABEL_KEY,
329 LUUIConstants.CROSS_LISTED_ITEM_LABEL_KEY,
330 Arrays.asList(
331 new MultiplicityFieldConfig(
332 SUBJECT_AREA,
333 LUUIConstants.SUBJECT_CODE_LABEL_KEY, null, null, true),
334 new MultiplicityFieldConfig(
335 COURSE_NUMBER_SUFFIX,
336 LUUIConstants.COURSE_NUMBER_LABEL_KEY, null, null, true)),
337 null,
338 null, 0);
339 SpanPanel jntlabelpan = new SpanPanel();
340 jntlabelpan.setStyleName("ks-multiplicity-section-label");
341 jntlabelpan.setHTML("Jointly Offered Courses");
342 jntlabelpan.setVisible(true);
343 result.addWidget(jntlabelpan);
344 addMultiplicityFields(result, COURSE + QueryPath.getPathSeparator() + JOINTS,
345 LUUIConstants.ADD_EXISTING_LABEL_KEY,
346 LUUIConstants.JOINT_OFFER_ITEM_LABEL_KEY,
347 Arrays.asList(
348 new MultiplicityFieldConfig(
349 CreditCourseJointsConstants.COURSE_ID,
350 LUUIConstants.COURSE_NUMBER_OR_TITLE_LABEL_KEY, null, null, true)),
351 null,
352 null, 0);
353 SpanPanel vsnlabelpan = new SpanPanel();
354 vsnlabelpan.setStyleName("ks-multiplicity-section-label");
355 vsnlabelpan.setHTML("Version Codes");
356 vsnlabelpan.setVisible(true);
357 result.addWidget(vsnlabelpan);
358 addMultiplicityFields(result, COURSE + QueryPath.getPathSeparator() + VERSIONS,
359 LUUIConstants.ADD_VERSION_CODE_LABEL_KEY,
360 LUUIConstants.VERSION_CODE_LABEL_KEY,
361 Arrays.asList(
362 new MultiplicityFieldConfig(
363 "variationCode",
364 LUUIConstants.VERSION_CODE_LABEL_KEY, null, null, true),
365 new MultiplicityFieldConfig(
366 "variationTitle",
367 LUUIConstants.TITLE_LABEL_KEY, null, null, true)
368 ),
369 null,
370 null, 0);
371 if (this.getClass().getName().contains("CourseProposalConfigurer"))
372 result.getLayout().setVisible(true);
373 else {
374 result.getLayout().setVisible(false);
375 }
376 return result;
377 }
378
379 protected void addFeeMultiplicityFields(Section section,
380 String path, String addItemlabelMessageKey,
381 String itemLabelMessageKey, List<MultiplicityFieldConfig> fieldConfigs,
382 Map<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>> swappableFieldsDefinition,
383 List<String> deletionParentKeys) {
384 MultiplicityConfiguration config = setupMultiplicityConfig(
385 MultiplicityConfiguration.MultiplicityType.GROUP,
386 MultiplicityConfiguration.StyleType.TOP_LEVEL_GROUP,
387 path, addItemlabelMessageKey, itemLabelMessageKey,
388 fieldConfigs, swappableFieldsDefinition, deletionParentKeys);
389 MultiplicitySection ms = null;
390 ms = new MultiplicitySection(config, swappableFieldsDefinition, deletionParentKeys);
391 section.addSection(ms);
392
393 }
394
395 protected MultiplicityConfiguration setupMultiplicityConfig(
396 MultiplicityConfiguration.MultiplicityType multiplicityType,
397 MultiplicityConfiguration.StyleType styleType,
398 String path, String addItemlabelMessageKey,
399 String itemLabelMessageKey, List<MultiplicityFieldConfig> fieldConfigs,
400 Map<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>> swappableFieldsDefinition,
401 List<String> deletionParentKeys) {
402 QueryPath parentPath = QueryPath.concat(path);
403 MultiplicityConfiguration config = GWT.create(MultiplicityConfiguration.class);
404 config.init(multiplicityType, styleType, getMetaData(parentPath.toString()));
405 config.setAddItemLabel(getLabel(addItemlabelMessageKey));
406 config.setItemLabel(getLabel(itemLabelMessageKey));
407 config.setUpdateable(true);
408
409 FieldDescriptor parentFd = buildMultiplicityParentFieldDescriptor(path, getLabel(itemLabelMessageKey), null);
410 config.setParent(parentFd);
411
412 if (fieldConfigs != null) {
413 for (MultiplicityFieldConfig fieldConfig : fieldConfigs) {
414 MultiplicityFieldConfiguration fc = buildMultiplicityFD(fieldConfig.getFieldKey(),
415 fieldConfig.getLabelKey(), parentPath.toString());
416 config.addFieldConfiguration(fc);
417 if (fieldConfig.isNextLine()) {
418 config.nextLine();
419 }
420 }
421 }
422 return config;
423 }
424
425 protected MultiplicitySection addMultiplicityFields(Section section,
426 String path, String addItemlabelMessageKey,
427 String itemLabelMessageKey, List<MultiplicityFieldConfig> fieldConfigs,
428 Map<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>> swappableFieldsDefinition,
429 List<String> deletionParentKeys, int defaultItemsCreated) {
430 MultiplicityConfiguration config = setupMultiplicityConfig(
431 MultiplicityConfiguration.MultiplicityType.GROUP,
432 MultiplicityConfiguration.StyleType.TOP_LEVEL_GROUP,
433 path, addItemlabelMessageKey, itemLabelMessageKey,
434 fieldConfigs, swappableFieldsDefinition, deletionParentKeys);
435 config.setDefaultItemsCreated(defaultItemsCreated);
436 MultiplicitySection ms = null;
437 ms = new MultiplicitySection(config, swappableFieldsDefinition, deletionParentKeys);
438 section.addSection(ms);
439 return ms;
440 }
441
442 protected Metadata getMetaData(String fieldKey) {
443 return modelDefinition.getMetadata(QueryPath.concat(fieldKey));
444 }
445
446 protected MultiplicityFieldConfiguration buildMultiplicityFD(
447 String fieldKey, String labelKey, String parentPath) {
448
449 QueryPath fieldPath = QueryPath.concat(parentPath, QueryPath.getWildCard(), fieldKey);
450 Metadata meta = modelDefinition.getMetadata(fieldPath);
451
452 MultiplicityFieldConfiguration fd = new MultiplicityFieldConfiguration(
453 fieldPath.toString(), generateMessageInfo(labelKey), meta, null);
454
455
456 return fd;
457
458 }
459
460 protected FieldDescriptor buildMultiplicityParentFieldDescriptor(String fieldKey, String messageKey, String parentPath) {
461 QueryPath path = QueryPath.concat(parentPath, fieldKey);
462 Metadata meta = modelDefinition.getMetadata(path);
463
464 FieldDescriptor fd = new FieldDescriptor(path.toString(), generateMessageInfo(messageKey), meta);
465 fd.hideLabel();
466 return fd;
467 }
468
469 protected VerticalSection generateCourseInfoShortTitleSection() {
470 VerticalSection shortTitle = initSection(getH3Title(LUUIConstants.SHORT_TITLE_LABEL_KEY), WITH_DIVIDER);
471 addField(shortTitle, COURSE + "/" + TRANSCRIPT_TITLE, null);
472 return shortTitle;
473 }
474
475 protected VerticalSection generateLongTitleSection() {
476 VerticalSection longTitle = initSection(getH3Title(LUUIConstants.TITLE_LABEL_KEY), WITH_DIVIDER);
477 addField(longTitle, COURSE + "/" + COURSE_TITLE, null);
478 return longTitle;
479 }
480
481 protected VerticalSection generateDescriptionRationaleSection() {
482 SectionTitle title = getH4Title(LUUIConstants.PROPOSAL_TITLE_SECTION_LABEL_KEY);
483 VerticalSection description = initSection(title, !WITH_DIVIDER);
484 title.setStyleName("cluProposalTitleSection");
485
486
487 addField(description, COURSE + "/" + DESCRIPTION + "/" + RichTextInfoConstants.PLAIN, generateMessageInfo(LUUIConstants.DESCRIPTION_LABEL_KEY));
488 addField(description, PROPOSAL_PATH + "/" + PROPOSED_RATIONALE + "/" + RichTextInfoConstants.PLAIN, generateMessageInfo(LUUIConstants.PROPOSAL_RATIONALE_LABEL_KEY),
489 new KSCharCount(modelDefinition.getMetadata(QueryPath.parse(PROPOSAL_PATH + "/" + PROPOSED_RATIONALE + "/" + RichTextInfoConstants.PLAIN))));
490
491 return description;
492 }
493
494 public Section generateCourseLogisticsSection(Section section) {
495 if (section instanceof SectionView) {
496 ((SectionView) section).setInstructions(getLabel(LUUIConstants.LOGISTICS_LABEL_KEY + "-instruct") + "<br><br>");
497 }
498
499 section.addSection(generateSchedulingSection());
500 section.addSection(generateDurationSection());
501 section.addSection(generateLearningResultsSection());
502 section.addSection(generateCourseFormatsSection());
503
504 return section;
505 }
506
507 protected Section generateLearningResultsSection() {
508 VerticalSection learningResults = initSection(getH3Title(LUUIConstants.LEARNING_RESULTS_LABEL_KEY), WITH_DIVIDER);
509 learningResults.setInstructions(getLabel(LUUIConstants.LEARNING_RESULTS_LABEL_KEY + "-instruct") + "<br><br><br>");
510
511 learningResults.addSection(generateGradesAssessmentsSection());
512 learningResults.addSection(generateStudentRegistrationOptionsSection());
513 learningResults.addSection(generateFinalExamSection());
514 learningResults.addSection(generateOutcomesSection());
515
516 return learningResults;
517 }
518
519 protected Section generateOutcomesSection() {
520
521 String path = COURSE + QueryPath.getPathSeparator() + CREDIT_OPTIONS;
522 QueryPath creditTypeFullPath = QueryPath.concat(path, QueryPath.getWildCard(), CreditCourseConstants.TYPE);
523 QueryPath creditOptionFixedFullPath = QueryPath.concat(path, QueryPath.getWildCard(), CREDIT_OPTION_FIXED_CREDITS);
524 QueryPath creditOptionMinFullPath = QueryPath.concat(path, QueryPath.getWildCard(), CREDIT_OPTION_MIN_CREDITS);
525 QueryPath creditOptionMaxFullPath = QueryPath.concat(path, QueryPath.getWildCard(), CREDIT_OPTION_MAX_CREDITS);
526
527 QueryPath creditResultValuesFullPath = QueryPath.concat(path, QueryPath.getWildCard(), "resultValueKeys");
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.result.values.group.type.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.result.values.group.type.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.result.values.group.type.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
570 @Override
571 public Widget getNewWidget() {
572 return new ListOfStringWidget("Add Item");
573 }
574 };
575
576 swappableFieldsDefinition.put(multipleCreditCondition,
577 Arrays.asList(
578 new SwapCompositeConditionFieldConfig(
579 new MultiplicityFieldConfiguration(
580 creditResultValuesFullPath.toString(),
581 generateMessageInfo(LUUIConstants.CREDIT_OPTION_FIXED_CREDITS_LABEL_KEY),
582 modelDefinition.getMetadata(creditResultValuesFullPath),
583 multipleCreditInitializer),
584 null
585 )
586 )
587 );
588 swappableFieldsDefinition.put(variableCreditCondition,
589 Arrays.asList(
590 new SwapCompositeConditionFieldConfig(
591 new MultiplicityFieldConfiguration(
592 creditOptionMinFullPath.toString(),
593 generateMessageInfo(LUUIConstants.CREDIT_OPTION_MIN_CREDITS_LABEL_KEY),
594 modelDefinition.getMetadata(creditOptionMinFullPath),
595 null),
596 null
597 ),
598 new SwapCompositeConditionFieldConfig(
599 new MultiplicityFieldConfiguration(
600 creditOptionMaxFullPath.toString(),
601 generateMessageInfo(LUUIConstants.CREDIT_OPTION_MAX_CREDITS_LABEL_KEY),
602 modelDefinition.getMetadata(creditOptionMaxFullPath),
603 null),
604 null
605 )
606 )
607 );
608
609 MultiplicitySection ms = addMultiplicityFields(
610 courseOutcomes,
611 path,
612 LUUIConstants.LEARNING_RESULT_OUTCOME_LABEL_KEY,
613 LUUIConstants.LEARNING_RESULT_OUTCOME_LABEL_KEY,
614 Arrays.asList(
615 new MultiplicityFieldConfig(
616 CreditCourseConstants.TYPE,
617 LUUIConstants.LEARNING_RESULT_OUTCOME_TYPE_LABEL_KEY,
618 null, null, true)
619 ), swappableFieldsDefinition, null, 1);
620
621 courseOutcomes.setRequired(ms.getConfig().getParentFd().getFieldElement().getRequiredPanel());
622 return courseOutcomes;
623
624 }
625
626 protected Section generateStudentRegistrationOptionsSection() {
627 VerticalSection studentRegistrationOptionsSection = initSection(getH3Title(LUUIConstants.LEARNING_RESULTS_STUDENT_REGISTRATION_LABEL_KEY), WITH_DIVIDER);
628
629 addField(studentRegistrationOptionsSection, COURSE + "/" + AUDIT, generateMessageInfo(LUUIConstants.LEARNING_RESULT_AUDIT_LABEL_KEY), new KSCheckBox(getLabel(LUUIConstants.LEARNING_RESULT_AUDIT_TEXT_LABEL_KEY)));
630 addField(studentRegistrationOptionsSection, COURSE + "/" + PASS_FAIL, generateMessageInfo(LUUIConstants.LEARNING_RESULT_PASS_FAIL_LABEL_KEY), new KSCheckBox(getLabel(LUUIConstants.LEARNING_RESULT_PASS_FAIL_TEXT_LABEL_KEY)));
631
632 return studentRegistrationOptionsSection;
633 }
634
635 protected Section generateGradesAssessmentsSection() {
636 VerticalSection gradesAssessments = initSection(getH3Title(LUUIConstants.LEARNING_RESULTS_GRADES_ASSESSMENTS_LABEL_KEY), WITH_DIVIDER);
637
638 addField(gradesAssessments, COURSE + "/" + GRADING_OPTIONS, generateMessageInfo(LUUIConstants.LEARNING_RESULT_ASSESSMENT_SCALE_LABEL_KEY));
639
640 return gradesAssessments;
641 }
642
643 protected VerticalSection generateCourseFormatsSection() {
644
645 VerticalSection courseFormats = initSection(getH3Title(LUUIConstants.FORMATS_LABEL_KEY), WITH_DIVIDER);
646 courseFormats.setHelp(getLabel(LUUIConstants.FORMATS_LABEL_KEY + "-help"));
647 courseFormats.setInstructions(getLabel(LUUIConstants.FORMATS_LABEL_KEY + "-instruct"));
648 MultiplicityConfiguration courseFormatConfig = setupMultiplicityConfig(
649 MultiplicityConfiguration.MultiplicityType.GROUP,
650 MultiplicityConfiguration.StyleType.TOP_LEVEL_GROUP,
651 COURSE + "/" + FORMATS, LUUIConstants.COURSE_ADD_FORMAT_LABEL_KEY,
652 LUUIConstants.FORMAT_LABEL_KEY,
653 null, null, null);
654 courseFormatConfig.setDefaultItemsCreated(1);
655 MultiplicityConfiguration activitiesConfig = setupMultiplicityConfig(
656 MultiplicityConfiguration.MultiplicityType.GROUP,
657 MultiplicityConfiguration.StyleType.SUB_LEVEL_GROUP,
658 COURSE + "/" + FORMATS + "/*/" + ACTIVITIES,
659 LUUIConstants.ADD_ACTIVITY_LABEL_KEY,
660 LUUIConstants.ACTIVITY_LITERAL_LABEL_KEY,
661 Arrays.asList(
662 new MultiplicityFieldConfig(
663 TYPE_KEY,
664 LUUIConstants.ACTIVITY_TYPE_LABEL_KEY,
665 null,
666 null,
667 true),
668 new MultiplicityFieldConfig(
669 CONTACT_HOURS + "/" + "unitQuantity",
670 LUUIConstants.CONTACT_HOURS_LABEL_KEY,
671 null,
672 null,
673 false),
674 new MultiplicityFieldConfig(
675 CONTACT_HOURS + "/" + "unitTypeKey",
676 LUUIConstants.CONTACT_HOURS_FREQUENCY_LABEL_KEY,
677 null,
678 null,
679 true),
680 new MultiplicityFieldConfig(
681 CreditCourseActivityConstants.DURATION + "/" + "atpDurationTypeKey",
682 LUUIConstants.COURSE_FORMATS_DURATION_TYPE_LABEL_KEY,
683 null,
684 null,
685 false),
686 new MultiplicityFieldConfig(
687 CreditCourseActivityConstants.DURATION + "/" + "timeQuantity",
688 LUUIConstants.DURATION_QUANTITY_LABEL_KEY,
689 null,
690 null,
691 true),
692 new MultiplicityFieldConfig(
693 DEFAULT_ENROLLMENT_ESTIMATE,
694 LUUIConstants.CLASS_SIZE_LABEL_KEY,
695 null,
696 null,
697 true)
698 ), null, null);
699 activitiesConfig.setDefaultItemsCreated(1);
700 courseFormatConfig.setNestedConfig(activitiesConfig);
701
702
703 MultiplicitySection ms = null;
704 ms = new MultiplicitySection(courseFormatConfig,
705 null, null);
706 courseFormats.addSection(ms);
707 courseFormats.setRequired(courseFormatConfig.getParentFd().getFieldElement().getRequiredPanel());
708 return courseFormats;
709 }
710
711 protected VerticalSection generateSchedulingSection() {
712 VerticalSection scheduling = initSection(getH3Title(LUUIConstants.SCHEDULING_LABEL_KEY), WITH_DIVIDER);
713 addField(scheduling, COURSE + "/" + TERMS_OFFERED, generateMessageInfo(LUUIConstants.TERMS_OFFERED_LABEL_KEY));
714 return scheduling;
715 }
716
717 protected VerticalSection generateDurationSection() {
718 VerticalSection duration = initSection(getH3Title(LUUIConstants.DURATION_LITERAL_LABEL_KEY), WITH_DIVIDER);
719 duration.setInstructions(getLabel(LUUIConstants.DURATION_LITERAL_LABEL_KEY + "-instruct"));
720 GroupSection duration_group = new GroupSection();
721 addField(duration_group, COURSE + "/" + CreditCourseConstants.DURATION + "/" + "atpDurationTypeKey", generateMessageInfo(LUUIConstants.DURATION_TYPE_LABEL_KEY));
722 addField(duration_group, COURSE + "/" + CreditCourseConstants.DURATION + "/" + "timeQuantity", generateMessageInfo(LUUIConstants.DURATION_QUANTITY_LABEL_KEY));
723
724 duration.addSection(duration_group);
725 return duration;
726 }
727
728 protected VerticalSection generateFinalExamSection() {
729 VerticalSection finalExam = initSection(getH3Title(LUUIConstants.FINAL_EXAM_LABEL_KEY), WITH_DIVIDER);
730 GroupSection finalExam_group = new GroupSection();
731 GroupSection finalExamRationale_group = new GroupSection();
732 GroupSection finalExamRationale_group2 = new GroupSection();
733
734 FieldDescriptor field = addField(finalExam_group, COURSE + "/" + CreditCourseConstants.FINAL_EXAM, generateMessageInfo(LUUIConstants.FINAL_EXAM_STATUS_LABEL_KEY));
735
736 if (field.isVisible()) {
737 KSSelectItemWidgetAbstract picker = (KSSelectItemWidgetAbstract) (((KSPicker) field.getFieldWidget()).getInputWidget());
738 final FieldDescriptor rationaleField = addField(finalExamRationale_group, COURSE + "/" + CreditCourseConstants.FINAL_EXAM_RATIONALE, generateMessageInfo(LUUIConstants.FINAL_EXAM_RATIONALE_LABEL_KEY));
739 rationaleField.setIgnoreShowRequired(true);
740 final FieldDescriptor rationaleField2 = addField(finalExamRationale_group2, COURSE + "/" + CreditCourseConstants.FINAL_EXAM_RATIONALE, generateMessageInfo(LUUIConstants.FINAL_EXAM_RATIONALE_LABEL_KEY));
741 rationaleField2.setIgnoreShowRequired(true);
742
743
744 final String altKey = "ALT";
745 final String noneKey = "None";
746 SwapSection swapSection = new SwapSection(picker);
747 swapSection.addSection(finalExamRationale_group, altKey);
748 swapSection.addSection(finalExamRationale_group2, noneKey);
749 swapSection.setSwapEventHandler(new SwapEventHandler() {
750
751 @Override
752 public void onShowSwappableSection(String key, Section section) {
753 progressiveEnableAndRequireSection(true, section);
754 }
755
756 @Override
757 public void onRemoveSwappableSection(String key, Section section) {
758 progressiveEnableAndRequireSection(false, section);
759 }
760 });
761 finalExam.addSection(finalExam_group);
762
763 finalExam.addSection(swapSection);
764 return finalExam;
765 } else {
766 return new VerticalSection();
767 }
768
769 }
770
771 protected void progressiveEnableAndRequireSection(boolean enableAndRequire, Section section) {
772 if (section != null) {
773 List<FieldDescriptor> fields = section.getFields();
774 if ((fields != null) && (fields.size() > 0)) {
775 BaseSection.progressiveEnableAndRequireFields(enableAndRequire, fields.toArray(new FieldDescriptor[fields.size()]));
776 }
777 }
778 }
779
780 protected VerticalSection generateInstructorsSection() {
781 VerticalSection instructors = initSection(getH3Title(LUUIConstants.INSTRUCTOR_LABEL_KEY), WITH_DIVIDER);
782 addField(instructors, COURSE + "/" + PRIMARY_INSTRUCTOR + "/personId");
783 return instructors;
784 }
785
786 protected SectionView generateLearningObjectivesSection() {
787 VerticalSectionView section = initSectionView(CourseSections.LEARNING_OBJECTIVES, LUUIConstants.LEARNING_OBJECTIVES_LABEL_KEY);
788 section.setInstructions(getLabel(LUUIConstants.LEARNING_OBJECTIVES_LABEL_KEY + "-instruct", QueryPath.concat(COURSE, COURSE_SPECIFIC_LOS, "*", "loInfo", "descr", "plain").toString()));
789 section.addSection(generateLearningObjectivesNestedSection());
790 return section;
791 }
792
793 protected VerticalSection generateLearningObjectivesNestedSection() {
794 final VerticalSection los = initSection(null, NO_DIVIDER);
795
796 QueryPath path = QueryPath.concat(COURSE, COURSE_SPECIFIC_LOS, "*", "loInfo", "descr");
797 Metadata meta = modelDefinition.getMetadata(path);
798
799 LOBuilder loBuilder = new LOBuilder(type, state, groupName, "kuali.loRepository.key.singleUse", COURSE_SPECIFIC_LOS, meta);
800 final FieldDescriptor fd = addField(los, CreditCourseConstants.COURSE_SPECIFIC_LOS, null, loBuilder, COURSE);
801
802 loBuilder.addValueChangeHandler(new ValueChangeHandler<List<OutlineNode<LOPicker>>>() {
803 @Override
804 public void onValueChange(ValueChangeEvent<List<OutlineNode<LOPicker>>> event) {
805 los.setIsDirty(true);
806 }
807 });
808
809
810
811 fd.setWidgetBinding(LOBuilderBinding.INSTANCE);
812
813 los.addStyleName("KS-LUM-Section-Divider");
814 return los;
815 }
816
817 public class PersonList extends KSDropDown {
818 final SimpleListItems people = new SimpleListItems();
819
820 public PersonList() {
821 final PersonList us = this;
822 final String userId = Application.getApplicationContext().getSecurityContext().getUserId();
823
824
825 people.addItem(userId, userId);
826 us.setListItems(people);
827 us.selectItem(userId);
828 us.setBlankFirstItem(false);
829 this.setEnabled(false);
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
859 @Override
860 public boolean isMultipleSelect() {
861 return true;
862 }
863 }
864
865 public class ProposerPersonList extends KSLabelList {
866 public ProposerPersonList() {
867 SimpleListItems list = new SimpleListItems();
868
869 super.setListItems(list);
870 }
871 }
872
873 protected VerticalSection generateShortTitleSection() {
874 VerticalSection shortTitle = initSection(getH3Title(LUUIConstants.SHORT_TITLE_LABEL_KEY), WITH_DIVIDER);
875 addField(shortTitle, "cluInfo/officialIdentifier/shortName", null);
876 return shortTitle;
877 }
878
879 protected VerticalSectionView initSectionView(Enum<?> viewEnum, String labelKey) {
880 VerticalSectionView section = new VerticalSectionView(viewEnum, getLabel(labelKey), this.getModelId());
881 section.addStyleName(LUUIConstants.STYLE_SECTION);
882 return section;
883 }
884
885
886 protected VerticalSection initSection(SectionTitle title, boolean withDivider) {
887 VerticalSection section;
888 if (title != null) {
889 section = new VerticalSection(title);
890 } else {
891 section = new VerticalSection();
892 }
893 section.addStyleName(LUUIConstants.STYLE_SECTION);
894 if (withDivider)
895 section.addStyleName(LUUIConstants.STYLE_SECTION_DIVIDER);
896 return section;
897 }
898
899 @Override
900 public MessageKeyInfo generateMessageInfo(String labelKey) {
901 return new MessageKeyInfo(groupName, type, state, labelKey);
902 }
903
904
905 protected Section generateFinancialsSection(Section section) {
906
907 VerticalSection justiFee = initSection(getH3Title(LUUIConstants.COURSE_FEE_TITLE), WITH_DIVIDER);
908 SpanPanel courseFeeInstruction = new SpanPanel();
909 courseFeeInstruction.setStyleName("ks-form-module-elements-instruction");
910 courseFeeInstruction.setHTML(getLabel(LUUIConstants.COURSE_FEE_TITLE + FieldLayoutComponent.INSTRUCT_MESSAGE_KEY));
911 courseFeeInstruction.setVisible(true);
912 justiFee.addWidget(courseFeeInstruction);
913
914
915
916 addField(justiFee, COURSE + "/" + "feeJustification" + "/" + RichTextInfoConstants.PLAIN, generateMessageInfo(LUUIConstants.JUSTIFICATION_FEE));
917 section.addSection(justiFee);
918 Map<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>> swappableFieldsDefinition =
919 new HashMap<SwapCompositeCondition, List<SwapCompositeConditionFieldConfig>>();
920
921
922
923
924
925
926
927 QueryPath feesPath = QueryPath.concat(COURSE, FEES);
928 QueryPath rateTypeFieldPath = QueryPath.concat(feesPath.toString(), QueryPath.getWildCard(), "rateType");
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
1303
1304
1305
1306
1307
1308
1309
1310