diff --git a/.checkstyle b/.checkstyle new file mode 100644 index 00000000..b14994a4 --- /dev/null +++ b/.checkstyle @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/.checkstyle.xml b/.checkstyle.xml new file mode 100644 index 00000000..df2864b8 --- /dev/null +++ b/.checkstyle.xml @@ -0,0 +1,182 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/.classpath b/.classpath new file mode 100644 index 00000000..7c0ee169 --- /dev/null +++ b/.classpath @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/.gitignore b/.gitignore index 562b66d5..fd5077b1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,14 @@ .idea/ -out/ \ No newline at end of file +out/ +bin/ +.gradle/ +build/ +gradle-app.setting +reports/ +lib/jfxtras-labs-8.0-r5.jar +*.dat +import_report.txt +*.ics +errorlog.txt +.*.sw* +config.properties diff --git a/.project b/.project new file mode 100644 index 00000000..3d51443d --- /dev/null +++ b/.project @@ -0,0 +1,35 @@ + + + RaiderPlanner.git + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.buildship.core.gradleprojectbuilder + + + + + net.sf.eclipsecs.core.CheckstyleBuilder + + + + + edu.umd.cs.findbugs.plugin.eclipse.findbugsBuilder + + + + + + org.eclipse.jdt.core.javanature + net.sf.eclipsecs.core.CheckstyleNature + edu.umd.cs.findbugs.plugin.eclipse.findbugsNature + org.eclipse.buildship.core.gradleprojectnature + + diff --git a/.settings/org.eclipse.buildship.core.prefs b/.settings/org.eclipse.buildship.core.prefs new file mode 100644 index 00000000..e8895216 --- /dev/null +++ b/.settings/org.eclipse.buildship.core.prefs @@ -0,0 +1,2 @@ +connection.project.dir= +eclipse.preferences.version=1 diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 00000000..99f26c02 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000..b0a237b9 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,316 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 +org.eclipse.jdt.core.formatter.align_fields_grouping_blank_lines=2147483647 +org.eclipse.jdt.core.formatter.align_type_members_on_columns=false +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_enum_constant=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_assignment=0 +org.eclipse.jdt.core.formatter.alignment_for_binary_expression=16 +org.eclipse.jdt.core.formatter.alignment_for_compact_if=16 +org.eclipse.jdt.core.formatter.alignment_for_conditional_expression=80 +org.eclipse.jdt.core.formatter.alignment_for_enum_constants=0 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_array_initializer=16 +org.eclipse.jdt.core.formatter.alignment_for_expressions_in_for_loop_header=0 +org.eclipse.jdt.core.formatter.alignment_for_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +org.eclipse.jdt.core.formatter.alignment_for_parameterized_type_references=0 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_parameters_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_resources_in_try=80 +org.eclipse.jdt.core.formatter.alignment_for_selector_in_method_invocation=16 +org.eclipse.jdt.core.formatter.alignment_for_superclass_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_superinterfaces_in_type_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_throws_clause_in_method_declaration=16 +org.eclipse.jdt.core.formatter.alignment_for_type_arguments=0 +org.eclipse.jdt.core.formatter.alignment_for_type_parameters=0 +org.eclipse.jdt.core.formatter.alignment_for_union_type_in_multicatch=16 +org.eclipse.jdt.core.formatter.blank_lines_after_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_after_package=1 +org.eclipse.jdt.core.formatter.blank_lines_before_field=0 +org.eclipse.jdt.core.formatter.blank_lines_before_first_class_body_declaration=0 +org.eclipse.jdt.core.formatter.blank_lines_before_imports=1 +org.eclipse.jdt.core.formatter.blank_lines_before_member_type=1 +org.eclipse.jdt.core.formatter.blank_lines_before_method=1 +org.eclipse.jdt.core.formatter.blank_lines_before_new_chunk=1 +org.eclipse.jdt.core.formatter.blank_lines_before_package=0 +org.eclipse.jdt.core.formatter.blank_lines_between_import_groups=1 +org.eclipse.jdt.core.formatter.blank_lines_between_type_declarations=1 +org.eclipse.jdt.core.formatter.brace_position_for_annotation_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_anonymous_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_array_initializer=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_block_in_case=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_constructor_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_constant=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_enum_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_lambda_body=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_method_declaration=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_switch=end_of_line +org.eclipse.jdt.core.formatter.brace_position_for_type_declaration=end_of_line +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_block_comment=false +org.eclipse.jdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment=false +org.eclipse.jdt.core.formatter.comment.count_line_length_from_starting_position=true +org.eclipse.jdt.core.formatter.comment.format_block_comments=true +org.eclipse.jdt.core.formatter.comment.format_header=false +org.eclipse.jdt.core.formatter.comment.format_html=true +org.eclipse.jdt.core.formatter.comment.format_javadoc_comments=true +org.eclipse.jdt.core.formatter.comment.format_line_comments=true +org.eclipse.jdt.core.formatter.comment.format_source_code=true +org.eclipse.jdt.core.formatter.comment.indent_parameter_description=true +org.eclipse.jdt.core.formatter.comment.indent_root_tags=true +org.eclipse.jdt.core.formatter.comment.insert_new_line_before_root_tags=insert +org.eclipse.jdt.core.formatter.comment.insert_new_line_for_parameter=insert +org.eclipse.jdt.core.formatter.comment.line_length=100 +org.eclipse.jdt.core.formatter.comment.new_lines_at_block_boundaries=true +org.eclipse.jdt.core.formatter.comment.new_lines_at_javadoc_boundaries=true +org.eclipse.jdt.core.formatter.comment.preserve_white_space_between_code_and_line_comments=false +org.eclipse.jdt.core.formatter.compact_else_if=true +org.eclipse.jdt.core.formatter.continuation_indentation=2 +org.eclipse.jdt.core.formatter.continuation_indentation_for_array_initializer=2 +org.eclipse.jdt.core.formatter.disabling_tag=@formatter\:off +org.eclipse.jdt.core.formatter.enabling_tag=@formatter\:on +org.eclipse.jdt.core.formatter.format_guardian_clause_on_one_line=false +org.eclipse.jdt.core.formatter.format_line_comment_starting_on_first_column=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header=true +org.eclipse.jdt.core.formatter.indent_body_declarations_compare_to_type_header=true +org.eclipse.jdt.core.formatter.indent_breaks_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_empty_lines=false +org.eclipse.jdt.core.formatter.indent_statements_compare_to_block=true +org.eclipse.jdt.core.formatter.indent_statements_compare_to_body=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_cases=true +org.eclipse.jdt.core.formatter.indent_switchstatements_compare_to_switch=false +org.eclipse.jdt.core.formatter.indentation.size=4 +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_field=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_local_variable=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_method=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_package=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_parameter=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_annotation_on_type=insert +org.eclipse.jdt.core.formatter.insert_new_line_after_label=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_after_type_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_at_end_of_file_if_missing=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_catch_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_else_in_if_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_finally_in_try_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_before_while_in_do_statement=do not insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_annotation_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_block=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_method_body=insert +org.eclipse.jdt.core.formatter.insert_new_line_in_empty_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_after_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_after_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_at_in_annotation_type_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_after_closing_paren_in_cast=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_case=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_colon_in_labeled_statement=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_allocation_expression=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_annotation=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_enum_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_increments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_for_inits=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_declaration_throws=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_superinterfaces=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_arguments=insert +org.eclipse.jdt.core.formatter.insert_space_after_comma_in_type_parameters=insert +org.eclipse.jdt.core.formatter.insert_space_after_ellipsis=insert +org.eclipse.jdt.core.formatter.insert_space_after_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_opening_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_after_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_after_semicolon_in_try_resources=insert +org.eclipse.jdt.core.formatter.insert_space_after_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_and_in_type_parameter=insert +org.eclipse.jdt.core.formatter.insert_space_before_assignment_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_at_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_binary_operator=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_cast=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_catch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_if=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_switch=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_synchronized=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_try=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_closing_paren_in_while=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_assert=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_case=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_default=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_colon_in_labeled_statement=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_enum_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_increments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_for_inits=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_declaration_throws=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_superinterfaces=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_comma_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_ellipsis=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_lambda_arrow=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_array_initializer=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_block=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_constant=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_method_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_brace_in_type_declaration=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_catch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_for=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_if=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_switch=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_synchronized=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_try=insert +org.eclipse.jdt.core.formatter.insert_space_before_opening_paren_in_while=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_return=insert +org.eclipse.jdt.core.formatter.insert_space_before_parenthesized_expression_in_throw=insert +org.eclipse.jdt.core.formatter.insert_space_before_postfix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_prefix_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_conditional=insert +org.eclipse.jdt.core.formatter.insert_space_before_question_in_wildcard=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_for=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_semicolon_in_try_resources=do not insert +org.eclipse.jdt.core.formatter.insert_space_before_unary_operator=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_brackets_in_array_type_reference=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_braces_in_array_initializer=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_enum_constant=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_declaration=do not insert +org.eclipse.jdt.core.formatter.insert_space_between_empty_parens_in_method_invocation=do not insert +org.eclipse.jdt.core.formatter.join_lines_in_comments=true +org.eclipse.jdt.core.formatter.join_wrapped_lines=true +org.eclipse.jdt.core.formatter.keep_else_statement_on_same_line=false +org.eclipse.jdt.core.formatter.keep_empty_array_initializer_on_one_line=false +org.eclipse.jdt.core.formatter.keep_imple_if_on_one_line=false +org.eclipse.jdt.core.formatter.keep_then_statement_on_same_line=false +org.eclipse.jdt.core.formatter.lineSplit=100 +org.eclipse.jdt.core.formatter.never_indent_block_comments_on_first_column=false +org.eclipse.jdt.core.formatter.never_indent_line_comments_on_first_column=false +org.eclipse.jdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body=0 +org.eclipse.jdt.core.formatter.number_of_empty_lines_to_preserve=1 +org.eclipse.jdt.core.formatter.parentheses_positions_in_annotation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_catch_clause=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_enum_constant_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_for_statment=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_if_while_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_lambda_declaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_delcaration=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_method_invocation=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_switch_statement=common_lines +org.eclipse.jdt.core.formatter.parentheses_positions_in_try_clause=common_lines +org.eclipse.jdt.core.formatter.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=tab +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=false +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_assignment_operator=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_conditional_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true +org.eclipse.jdt.core.javaFormatter=org.eclipse.jdt.core.defaultJavaFormatter diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 00000000..6d4f26b8 --- /dev/null +++ b/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,63 @@ +cleanup.add_default_serial_version_id=true +cleanup.add_generated_serial_version_id=false +cleanup.add_missing_annotations=true +cleanup.add_missing_deprecated_annotations=true +cleanup.add_missing_methods=false +cleanup.add_missing_nls_tags=false +cleanup.add_missing_override_annotations=true +cleanup.add_missing_override_annotations_interface_methods=true +cleanup.add_serial_version_id=false +cleanup.always_use_blocks=true +cleanup.always_use_parentheses_in_expressions=false +cleanup.always_use_this_for_non_static_field_access=false +cleanup.always_use_this_for_non_static_method_access=false +cleanup.convert_functional_interfaces=false +cleanup.convert_to_enhanced_for_loop=false +cleanup.correct_indentation=false +cleanup.format_source_code=false +cleanup.format_source_code_changes_only=false +cleanup.insert_inferred_type_arguments=false +cleanup.make_local_variable_final=true +cleanup.make_parameters_final=false +cleanup.make_private_fields_final=true +cleanup.make_type_abstract_if_missing_method=false +cleanup.make_variable_declarations_final=false +cleanup.never_use_blocks=false +cleanup.never_use_parentheses_in_expressions=true +cleanup.organize_imports=true +cleanup.qualify_static_field_accesses_with_declaring_class=false +cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +cleanup.qualify_static_member_accesses_with_declaring_class=true +cleanup.qualify_static_method_accesses_with_declaring_class=false +cleanup.remove_private_constructors=true +cleanup.remove_redundant_type_arguments=false +cleanup.remove_trailing_whitespaces=false +cleanup.remove_trailing_whitespaces_all=true +cleanup.remove_trailing_whitespaces_ignore_empty=false +cleanup.remove_unnecessary_casts=true +cleanup.remove_unnecessary_nls_tags=true +cleanup.remove_unused_imports=true +cleanup.remove_unused_local_variables=false +cleanup.remove_unused_private_fields=true +cleanup.remove_unused_private_members=false +cleanup.remove_unused_private_methods=true +cleanup.remove_unused_private_types=true +cleanup.sort_members=false +cleanup.sort_members_all=false +cleanup.use_anonymous_class_creation=false +cleanup.use_blocks=true +cleanup.use_blocks_only_for_return_and_throw=false +cleanup.use_lambda=true +cleanup.use_parentheses_in_expressions=false +cleanup.use_this_for_non_static_field_access=false +cleanup.use_this_for_non_static_field_access_only_if_necessary=true +cleanup.use_this_for_non_static_method_access=false +cleanup.use_this_for_non_static_method_access_only_if_necessary=true +cleanup_profile=_eclipse-cs RaiderPlanner.git +cleanup_settings_version=2 +eclipse.preferences.version=1 +formatter_profile=_eclipse-cs RaiderPlanner.git +formatter_settings_version=13 +org.eclipse.jdt.ui.javadoc=true +org.eclipse.jdt.ui.text.custom_code_templates= diff --git a/.travis.yml b/.travis.yml index bf693a4c..dc7f064b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,3 +10,10 @@ addons: packages: - oracle-java8-installer +notifications: + slack: + secure: BxGzrZ41FnMPNi0D4ABLLc+YnmgtC4bizhK3my+TkbF/E/skZU08akuTxaneXgRIbT0wgD6OqgGEAQ/s/mIh94gLXPEaTuPEKr18fJKM14FyfFFy6eiRgJQhegxjtRexuNAfVk06y0jp7e8s1Nsnq2jVr7aBpJyoH95feSyuc0UTkFPL6LvdSJD1UwAGryM5mEIT7AZeOCnDX3I/aRr4y2k6A9Cu3hvpj8evuYWV03X5kPzn/9/9hiGkpeUnLWVewPmirGKCLAG21ND+ceERLnXSHrkvb/OA6x4ZWbvo22BlhCBeFrOfoT5pQQom8hEBGYId6eQq1acdnaiGSNdATxQG3a+LfQP1ia/HyOsj0GOq5I8Y8RwJ6GjTGvQcbsfcuf7Qc8+036yBYUMHyn27u/Vu+KX9JscAfcGeUv1uG330f0bSl0bJB9BagJFD/qRh7P4VpV22sNn/c1FBS4DSvl7l55bOa2fGPXzF99oDw41CYXmPXoC8fNPl5hnQZz/z5si5QHsA1/Fks5I4eDpl7UZ2UBBleAVOmP8mV3M43+Pfb5QRJvuvFHCjrHl8Sdg3ph15rbeG4Z33MANPqiuUyICeGU/i11q0vhBROwZ5E75Qz0/AdAmLoWOVr3Yv/YtuEXmbYfObNcxpDaPld/eDeNJRgDaWwF6/JpAtXp9R4FM= +branches: + except: + - "/^.*-dev/" + diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..c722245f --- /dev/null +++ b/AUTHORS @@ -0,0 +1,53 @@ +RaiderPlanner was forked from an application called PearPlanner. The original +PearPlanner authors are (alphabetically by last name): + +Bijan Ghasemi Afshar +Ben Dickson +Zilvinas Ceikauskas +Andrew Odintsov + +Since the project was forked for the CEG3120 course at Wright State University, +the following individuals have contributed (alphabetically by last name): + +Fahad Alnashwan +Matthew Barrett +Gage Berghoff +Aashish Bharadwaj +Alena Brand +Nathaniel C Crossman +Gabriel Dodds +Randy Forte +Aaron Hammer +Dominick Hatton +Peter Haviland +Rachel Hill +Andy Hulett +Ryan Koeller +James Languirand +Eric Levine +Amanda McCollum +Ian Mahaffy +Don Miller +Cole Morgan +Rafael Nunez +Michael Plymire +Cameron Roudebush +Roberto C. Sánchez +Eric Sweet +Nguyen Vo +Amila Dias +Katherine Lemmert +Nathan Schroer +Hayden Richter +Joshua Shaw +Ian Smith +Jacob Smith +Clayton D. Terrill +Spencer Tracy +Sara Walsh +Jesse Williams +Alexander Voultos +Sara Walsh +Isaiah Winfrey +Joseph Yancey +Adam Cone \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 00000000..e5fc99cd --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,163 @@ +# How to Contribute to RaiderPlanner + +**READ THIS DOCUMENT COMPLETELY BEFORE YOU START WORKING ON THE PROJECT** + +This document describes the process and guidelines for making contributions to the [RaiderPlanner project](https://github.com/rsanchez-wsu/RaiderPlanner). + +Note that [RaiderPlanner](https://rsanchez-wsu.github.io/RaiderPlanner) is an academic project which is part of the course CEG 3120, Introduction to the Design of Information Technology Systems, at Wright State University. As a result, this document includes aspects related to submitting pull requests for course credit. + +You are welcome to participate and contribute even if you are not a student in CEG 3120. Simply ignore the part toward the end about course credit for pull requests, grading, and submitting links to Pilot. + +## Before Your First Pull Request + +There are a few items which **must** be completed **before** you can participate in the RaiderPlanner project. Here is an itemized list (a bit more on each item follows the list): + + * Install and configure the [development toolchain](TOOLCHAIN.md) + * Join the [RaiderPlanner Slack team](https://raiderplanner.slack.com) + * If you do not already have a GitHub account, [sign up](https://github.com/join) for one + * Read the help articles on [setting up Git and GitHub](https://help.github.com/categories/setup/) + * Fork the [RaiderPlanner repository](https://github.com/rsanchez-wsu/RaiderPlanner); [instructions](https://help.github.com/articles/fork-a-repo) + * Read [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/) + +The development toolchain lists required and recommended tools for RaiderPlanner development. The two primary collaborative tools used for RaiderPlanner development are Slack (for IM/chat) and GitHub (for managing code, issues, documentation, etc.). You will need to register accounts for both if you have not done so already. After registering a GitHub account, ensure that you read the documentation on configuring Git and GitHub. + +Project contributions are managed by pull requests. Since you will not have permissions to make changes directly to the code in the main project repository, you will need to create a fork in GitHub. You will make your proposed changes in your fork and submit your proposed changes via a pull request. The process is described in greater detail in the next section. + +It is extraordinarily important that you read the guidelines for writing a Git commit message. Additionally, commit messages must include references to other issues and/or closures as appropriate. [This article](https://help.github.com/articles/autolinked-references-and-urls/#issues-and-pull-requests) and [this article](https://help.github.com/articles/closing-issues-using-keywords/) discuss how to refer to and how to close an issue with a commit message. Every commit message must have one or more issue references and/or closures. + +If you submit a pull request with non-conforming commit mesages, your pull request may be rejected. + +Additional [GitHub help](https://help.github.com/) is available on many different topics. + +## For Every Pull Request + +It is vitally important that you ensure that any contributions you make to the RaiderPlanner project conform to the guidelines established here. Failure to conform these guidelines may result in your contribution being rejected. + +If you are a CEG 3120 student and your submission is being made for course credit, rejection of your submission *will prevent you from receiving points* for the assignment. The next section describes the process for obtaining course project credit for your pull request. + + * Ensure that you have cloned your fork of the RaiderPlanner repository + * Ensure that [Git is setup to sync the original RaiderPlanner repository](https://help.github.com/articles/fork-a-repo/#keep-your-fork-synced) + * Make sure you have [synchronized your fork](https://help.github.com/articles/syncing-a-fork/) so you have the latest changes + * Select [an issue](https://github.com/rsanchez-wsu/RaiderPlanner/issues) that your pull request will address + * If you would like to work on something for which there is not yet an issue written, write a new issue + * Issues need to be well written + * The write up of the issue must be clear and describe both the current state as well as the desired end state + * The issue must include enough detail that another developer in the project could pick up the issue as written and complete the task with minimal additional information + * If you are unsure, ask for help with writing a new issue + * "Claim" the issue + * GitHub issues can only be assigned to collaborators who have push access to the main project + * Since you will likely not have such access, simply make a comment on the issue indicating that you are working the issue + * Ensure that any issues you work are present in the main RaiderPlanner project repository and not in your own fork + * Research the issue and possible resolutions + * Solicit input from the instructor and/or other developers via comments on the GitHub issue + * Identify related issues in the project which may impact your issue or which may be impacted by your issue + * Connect with developers working on related issues to minimize conflicts between the work being performed + * It is important that you not simply dive into working on something without first fully understanding the issue and also developing a sound approach for a solution + * Do your work + * Create a new branch on which to perform your work + * Doing your work on a branch is very important because GitHub only allows one pull request per branch + * Using a branch for your work allows you to submit a pull request and then make a new branch to begin work on something else even before your previous pull request is merged + * Name your branch in a sensible way (e.g., *issue_123*, *chat_network_module*, etc.) + * Limit changes to the specific issue(s) which you are working + * If you encounter style problems or other bugs in lines which you are chainging, fix the problem you encounter + * If you encounter style problems or other bugs outside of the lines which you are changing, make those changes in a separate commit + * Do not make extraneous or unrelated changes in a commit + * If you are making changes to the GUI, follow the guidelines for [modifying the GUI](https://github.com/rsanchez-wsu/RaiderPlanner/wiki/Modifying-the-GUI) + * Additionally, you must provide additional supporting information in the GitHub issue for your GUI change + * Provide one or more annotated screenshots or video captures of the particular GUI feature/behavior *before* the change (the annotation can be detailed descriptive text and/or visual markers added to the image/video) + * Provide one or more annotated screenshots or video captures of the particular GUI feature/behavior *after* the change + * Ensure that the change(s) that you make improves the user experience and does not change/break unrelated functionality + * Major structural/architectural changes to the project may require significant discussion + * Discussions may take place via GitHub issues, Slack, or in person + * All design decisions must be documented in the relevant GitHub issue(s) + * Supporting documentation (e.g., UML diagrams, use case descriptions, etc.) will be attached to the relevant GitHub issue(s) + * If in doubt, ask for guidance + * Test your work + * There are two primary mechanisms for testing your work: + * The project build's *check* target, invoked with the command `./gradlew clean check` or `gradlew.bat clean check` + * The project build's *run* target, invoked with the command `./gradlew run` or `gradlew.bat run` + * Execute the *check* target and confirm that the build completes successfully with the output `BUILD SUCCESSFUL` + * Additionally check that your changes did not introduce any new bugs (as reported by FindBugs in its build report) or new style violations (as reported by CheckStyle in its build report) + * Review the JUnit build report and confirm that test output is as expected + * Execute the *run* target and confirm that the application behaves as expected + * Verify that any changes you made produce expected results in the application + * Ensure that your changes did not break another part of the application + * Be sure to attempt invalid actions and supply invalid inputs to ensure proper error handling + * Review your changes + * Before you commit your changes, make sure to review them + * The Eclipse *Team Synchronization* and *Git Staging* views provide the ability to view changes before you commit + * You can also use the commands described in section 2.2 of *Pro Git* + * **Make sure that only changes you expect are in your workspace** + * Carefully examine the differences you are preparing to commit and confirm that each change is expected + * In particular, ensure that none of the following are inadvertently included in a commit + * Log files + * Temporary files + * Line ending flips (the `git status` and `git diff` commands should warn you if this is happening, but it is your responsibility to ensure that line ending changes are not made unnecessarily) + * Changes to the project metadata files (e.g., `.project`, `.classpath`, etc.) + * If this is your first contribution to the project, add your name and email to the AUTHORS file (note that names are listed alphabetically by surname) + * Ensure that you add the current year and your name to the copyright header in any file to which you made significant changes + * Confirm that you are on the branch you created for the specific issue you are working + * Each commit should represent a discrete work unit, issue, task, etc. + * Commits which contain very large changes should be coordinated in advance, or your pull request may be rejected + * Commit your changes + * After reviewing your changes stage the files that you will include in the commit + * Confirm that the staged changes include only those changes which you expect and specifically intend to be a part of the commit you are preparing + * Write a commit message that conforms to the guidelines in [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/) + * Ensure that your commit message includes references to and/or closures of related issues as appropriate + * Push your changes + * Once you have made a commit in your local working copy, you will need to push it to your fork + * GitHub will see the new commit and automatically present you the option to create a pull request + * If you are not done with your work, continue working and committing/pushing additional changes as needed + * Submit your pull request + * If in doubt, consult the many available resources on pull requests: + * [This article](https://help.github.com/articles/about-pull-requests/) provides an overview of pull requests + * [This article](https://help.github.com/articles/creating-a-pull-request/) describes how to create a pull request + * [This collection help articles](https://help.github.com/categories/collaborating-with-issues-and-pull-requests/) describes all the different aspects of working with pull requests + * Ensure that the pull request subject line and write up capture the complete scope of work done in the pull request + * The pull request write up should conform to the guidelines for writing a Git commit message (but it will summarize all of the commits in the pull request) + * The pull request write up needs to include references to the issues affected by the pull request + * Review/monitor your pull request + * View the status page for your pull request and make sure that the diff looks like what you expect (e.g., if you made changes on 200 lines but the pull request page says that there are changes on 5,000 lines then something went wrong) + * Monitor the Travis CI [build status](https://travis-ci.org/rsanchez-wsu/RaiderPlanner/pull_requests) + * The page for your pull request will also provide a direct link to the build for your pull request + * If the build does not pass (i.e., green status) because of an error (i.e., yellow status) or failure (i.e., red status), determine the cause, fix it, and push one or more new commits to fix the problem + * Confirm that your pull request can be merged without conflict + * If your pull request status page indicates that there are merge conflicts, you will need to synchronize your fork and resolve the conflicts + * As you resolve any conflicts with your pull request, be sure to properly preserve and/or integrate conflicting changes + * **DO NOT** simply overwrite conflicting changes with your own changes + * The instructor or another project collaborator may request that you make changes/updates to your pull request before it can be merged + +## Course Project Credit For Your Pull Request + +If you are a student in CEG 3120 then you will be required to submit three project assignments during the course of the academic term. The three project assignments will almost certainly take the form of substantial pull requests to the RaiderPlanner project. If you have an idea for a project assignment that does not take the form of a GitHub pull request, discuss your idea with the instructor so that suitable criteria may be agreed upon. + +When crafting a pull request to be submitted as a project assignment, there are several important considerations: + + * The work performed should reflect a level effort commensurate with a project-level assignment in an upper division university class + * There is no minimum line count, number of issues resolved, or other quantifiable criteria + * Software development at times involves considerable effort which yields only small visible changes in the software product + * If you are concerned that your pull request may not encompass an adequate amount of effort or work, address your concern to the instructor immediately; the time to address your concern to the instructor is well ahead of the deadline to ensure you have enough time to make necessary adjustments + * If the instructor has questions regarding your work on a pull request, you may be asked to provide additional supporting documentation via verbal conversation, Slack, email, GitHub issue comment, wiki page, etc. + * The assignment deadline is the deadline by which the pull request link must be submitted to the Pilot dropbox for the project assignment + * When the link is submitted, the pull request must be *complete* + * A *complete* pull request is one which meets all of the guidelines established in the previous section + * Note that the guidelines for a good commit message apply to a pull request message/comment as well + * A pull request which is not complete (e.g., improper commit messages, extraneous changes, missing issue references, failing build, etc.) may be rejected + * If a pull request is rejected, you will need to fix whatever is wrong and submit a new pull request + * A link to the new pull request will need to be submitted to the Pilot dropbox + * If the new link is submitted after the assignment deadline, points may be deducted for being late + * If an old pull request is closed and a new pull request submitted in its place, you **must** reference the old pull request in the comment of the new pull request + * Pull requests are processed in the order submitted + * If an earlier pull request is merged and subsequently creates a conflict with your pull request, you will be asked to resolve the conflict + * If you must resolve conflicts arising from a merge of another pull request, you will not be penalized for being late, assuming that your pull request was otherwise *complete* prior to the deadline + * You will not be penalized for being late if your pull request is merged after the deadline + * Project assignment points will be awarded in Pilot once a pull request is merged + * The URL to the pull request which is merged must match the URL submitted to the Pilot dropbox + * If a pull request ends up not being merged, then points will not be awarded + * The instructor may, at his sole discretion, waive the merge requirement + * A pull request may be rejected in this case because it is too disruptive, not consistent with project goals, or for other reasons + * If the merge requirement is waived, points will be awarded as for any other pull request + * All other criteria for a *complete* pull request will still apply, as will the assignment deadline + * Disputes over points awarded should be addressed directly to the instructor + * Waiting for the last minute is a nearly certain way to ensure that you will run into problems and end up losing points + diff --git a/DeveloperInstructions/ImplementSettings.txt b/DeveloperInstructions/ImplementSettings.txt new file mode 100644 index 00000000..ff578451 --- /dev/null +++ b/DeveloperInstructions/ImplementSettings.txt @@ -0,0 +1,41 @@ +To implement functionality for a setting: +1. Add controls within the SettingsController. + For reference: https://docs.oracle.com/javafx/2/api/javafx/scene/control/package-summary.html +2. Create event handler methods within SettingsController. These should call the methods in Settings.java. +3. Create Global Parameters with default values in the Settings.java. These represent the values that will be saved within the config.properties. +4. In the createConfig(), add property values using a string as the key and then the value assigned to it. + Example: + this.prop.setProperty("keyString", this.globalVariable); +5. In the loadSettings(), assign the global variables the config.properties values. + Example: + this.globalVariable = prop.getProperty("keyString"); +6. In the saveSettings(), add property values using a string as the key and then the value assigned to it. + Example: + this.prop.setProperty("keyString", this.globalVariable); +7. Create a set function to assign a global variable. + Example: + public void setGlobalVariable(String globalVariableTemp) { + this.globalVariable = globalVariableTemp; + } +8. Create a get function to assign a global variable. + Example: + public String getGlobalVariable() { + return this.globalVariable; + } + +To save settings using SettingsController: +1. Call set function: + Example: + settings.setGlobalVariable(control.getText()); +2. Make sure an event calls saveSettings(): + Example: + settings.saveSettings(); + +To access settings from any controller: +1. Add import: + import edu.wright.cs.raiderplanner.model.Settings; +2. Create object reference globally: + public static Settings settings = new Settings() +3. Call get functions. + Example: + settings.getGlobalVariable() \ No newline at end of file diff --git a/Final Documents/User Manual RaiderPlanner.pdf b/Final Documents/User Manual RaiderPlanner.pdf new file mode 100644 index 00000000..ab38ae73 Binary files /dev/null and b/Final Documents/User Manual RaiderPlanner.pdf differ diff --git a/README.md b/README.md index 88270912..d7a43beb 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,14 @@ -# PearPlanner +# RaiderPlanner #### The best study planner since the Gantt Diagram +Please have a look at the [project website](https://rsanchez-wsu.github.io/RaiderPlanner) for information about RaiderPlanner. +If you would like to contribute to RaiderPlanner, then please read about configuring the [development toolchain](TOOLCHAIN.md) and then read the [contributor guidelines](CONTRIBUTING.md). | Branch | Build status | | ------------- | ------------- | -| origin/master: | [![Build Status](https://travis-ci.org/Alienturnedhuman/PearPlanner.svg?branch=master)](https://travis-ci.org/Alienturnedhuman/PearPlanner) | -| origin/Alienturnedhuman: | [![Build Status2](https://travis-ci.org/Alienturnedhuman/PearPlanner.svg?branch=Alienturnedhuman)](https://travis-ci.org/Alienturnedhuman/PearPlanner) | -| origin/aBranch: | [![Build Status3](https://travis-ci.org/Alienturnedhuman/PearPlanner.svg?branch=aBranch)](https://travis-ci.org/Alienturnedhuman/PearPlanner) | -| origin/zBranch: | [![Build Status4](https://travis-ci.org/Alienturnedhuman/PearPlanner.svg?branch=zBranch)](https://travis-ci.org/Alienturnedhuman/PearPlanner) | -| origin/Bijan: | [![Build Status4](https://travis-ci.org/Alienturnedhuman/PearPlanner.svg?branch=Bijan)](https://travis-ci.org/Alienturnedhuman/PearPlanner) | +| rsanchez-wsu/master: | [![Build Status](https://api.travis-ci.org/rsanchez-wsu/RaiderPlanner.svg?branch=master)](https://travis-ci.org/rsanchez-wsu/RaiderPlanner) | + diff --git a/PearPlanner.iml b/RaiderPlanner.iml similarity index 94% rename from PearPlanner.iml rename to RaiderPlanner.iml index 6386de9d..d0585549 100644 --- a/PearPlanner.iml +++ b/RaiderPlanner.iml @@ -9,7 +9,6 @@ - diff --git a/Script.sql b/Script.sql new file mode 100644 index 00000000..76c899da --- /dev/null +++ b/Script.sql @@ -0,0 +1,10 @@ + +CREATE TABLE ExceptionDB ( + ExID INTEGER NOT NULL, + Message VARCHAR(2000) NOT NULL, + Type VARCHAR(200) NOT NULL, + Date DATE NOT NULL, + Time TIME NOT NULL, + PRIMARY KEY (ExID) + +); \ No newline at end of file diff --git a/StudyPlanner.dat b/StudyPlanner.dat deleted file mode 100644 index dd1312dd..00000000 Binary files a/StudyPlanner.dat and /dev/null differ diff --git a/TOOLCHAIN.md b/TOOLCHAIN.md new file mode 100644 index 00000000..273a467a --- /dev/null +++ b/TOOLCHAIN.md @@ -0,0 +1,116 @@ +# RaiderPlanner Development Toolchain + +This document outlines the recommended toolchain for developers wishing to contribute to the RaiderPlanner project. + +The recommendations are based on the tools used by the instructor. You are welcome to use whatever tools you prefer, but you will be on your own in terms of help and support for problems with alternative tools. + +The basic tools consist of a JDK, the Eclipse IDE, and the Git version control system. + +## JDK + +If you are using Linux, then you likely have OpenJDK already installed. If not, then install it with this command or something similar: + +``` +sudo apt-get install openjdk-8-jdk +``` + +If you are using Windows or Mac and your system does not have Java installed on it. You will need to [download an installer package](http://www.oracle.com/technetwork/java/javase/downloads/index.html) from Oracle. + +Whether you install via a package manager on Linux or via an Oracle installer on Windows or Mac, make sure that you install a **Java development kit (JDK)**. A Java runtime environment (JRE) will not provide all the necessary tools. + +After you have installed the JDK, you should be able to run the command `javac -version` and see the version of the specific JDK you installed: + +``` +user@debian:~$ javac -version +javac 1.8.0_151 +``` + +or + +``` +C:\>javac -version +javac 1.8.0_151 +``` + +If you see an error message, then you either did not install the correct package, or your system configuration requires adjustment: + +``` +user@debian:~$ javac -version +bash: javac: command not found +``` + +or + +``` +C:\>javac -version +Can't recognize 'javac -version' as an internal or external command, or batch script. +``` + +## IDE + +> NOTE: The instructor of the course uses Eclipse. Students have mostly used Eclipse, though students in the past have used NetBeans or IntelliJ IDEA if they were more comfortable with those IDEs. You are welcome to use any IDE that you like, but if you encouter problems the instructor will not be able to assist you in resolving them. + +Download and install the latest version of the [Eclipse IDE](https://www.eclipse.org). + +Additionally, using the `Help -> Install New Software...` menu option and/or `Help -> Eclipse Marketplace...`, install at a minimum the following plugins: + + * Gradle BuildShip + * Data Tools Platform SDK/Data Tools Platform Extender SDK + * CheckStyle + * FindBugs + +Eclipse is a rather complex piece of software, as is any IDE. If you are not familiar with it then it is recommended that you read these articles: + + * [Eclipse IDE - Tutorial](http://www.vogella.com/tutorials/Eclipse/article.html) + * [Eclipse Shortcuts - Tutorial](http://www.vogella.com/tutorials/EclipseShortcuts/article.html) + +If you intend to work on any database/JDBC aspects of the project, then this article will be helpful: + + * [Managing databases with Eclipse and the Database Tools - Tutorial](http://www.vogella.com/tutorials/EclipseDataToolsPlatform/article.html) + +## Git + +While it is possible to perform most Git tasks through the GUI provided by your IDE, there are occasions when it is necessary to use the Git command line tool in order to perform a task more efficiently or to resolve a problem. It is best to install and configure Git prior to beginning project work. + +If you are using Linux, then you likely have Git already installed. If not, then install it with this command or something similar: + +``` +sudo apt-get install git +``` + +If you are using Windows or Mac your system most likely does not have Git installed on it. You will need to [download an installer package for Windows](http://git-scm.com/download/win) or [download an installer package for Mac](http://git-scm.com/download/mac). Note that chapter 1 of the [Pro Git Book](https://git-scm.com/book/en/v2) covers installing and setting up Git. + +After you have installed Git, you should be able to run the following command: + +``` +user@debian:~$ git --version +git version 2.11.0 +``` + +or + +``` +C:\>git --version +git version 2.11.0 +``` + +If you see an error message, then you either did not install the correct package, or your system configuration requires adjustment: + +``` +user@debian:~$ git --version +bash: git: command not found +``` + +or + +``` +C:\>git --version +Can't recognize 'git --version' as an internal or external command, or batch script. +``` + +If you are unfamiliar with Git, then it is strongly recommended that you read the [Pro Git Book](https://git-scm.com/book/en/v2), chapters 1-3, and 5-6. (Incidentally, this is assigned reading for the first week of class.) + +Additionally, the GitHub help site provides several informative articles on [setting up Git](https://help.github.com/categories/setup/). + +**WARNING**: Make sure that you have properly configured your system to [deal with line endings](https://help.github.com/articles/dealing-with-line-endings/). Not every developer on the project works from the same platform, so you must understand this issue and ensure your system is configured properly. Failure to properly configure your system may result in your contributions being rejected. + diff --git a/Test/Controller/DataControllerTest.java b/Test/Controller/DataControllerTest.java deleted file mode 100644 index f757f3c3..00000000 --- a/Test/Controller/DataControllerTest.java +++ /dev/null @@ -1,38 +0,0 @@ -package Controller; - -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Created by bijan on 08/05/2017. - */ -public class DataControllerTest { - - @Before - public void setUp() throws Exception - { - } - - @Test - public void existingSettingsFile() throws Exception - { - } - - @Test - public void getNodes() throws Exception - { - } - - @Test - public void validNodeList() throws Exception - { - } - - @Test - public void loadHubFile() throws Exception - { - } - -} \ No newline at end of file diff --git a/Test/Controller/MainControllerTest.java b/Test/Controller/MainControllerTest.java deleted file mode 100644 index 9d0fbe48..00000000 --- a/Test/Controller/MainControllerTest.java +++ /dev/null @@ -1,28 +0,0 @@ -package Controller; - -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Created by bijan on 08/05/2017. - */ -public class MainControllerTest { - - @Before - public void setUp() throws Exception - { - } - - @Test - public void initialise() throws Exception - { - } - - @Test - public void reportError() throws Exception - { - } - -} \ No newline at end of file diff --git a/Test/Controller/StudyPlannerControllerTest.java b/Test/Controller/StudyPlannerControllerTest.java deleted file mode 100644 index ea6fcd5c..00000000 --- a/Test/Controller/StudyPlannerControllerTest.java +++ /dev/null @@ -1,63 +0,0 @@ -package Controller; - -import Model.Account; -import Model.Person; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Created by bijan on 08/05/2017. - */ -public class StudyPlannerControllerTest { - - @Before - public void setUp() throws Exception - { - Account a = new Account(new Person("Mr","Adrew",true),"100125464"); - StudyPlannerController studyPlannerController = new StudyPlannerController(a); - } - - @Test - public void getStudyProfiles() throws Exception - { - } - - @Test - public void fileValidation() throws Exception - { - } - - @Test - public void containsStudyProfile() throws Exception - { - } - - @Test - public void getCurrentVersion() throws Exception - { - } - - @Test - public void createStudyProfile() throws Exception - { - } - - @Test - public void updateStudyProfile() throws Exception - { - } - - @Test - public void getListOfTasks() throws Exception - { - - } - - @Test - public void newActivity() throws Exception - { - } - -} \ No newline at end of file diff --git a/Test/Model/AccountTest.java b/Test/Model/AccountTest.java deleted file mode 100644 index dcd800d6..00000000 --- a/Test/Model/AccountTest.java +++ /dev/null @@ -1,59 +0,0 @@ -package Model; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.util.ArrayList; - -import static org.junit.Assert.*; - -/** - * Created by bijan on 04/05/2017. - */ -public class AccountTest { - - Person person; - Account account; - - @Before - public void setUp() throws Exception - { - person = new Person("Mr","Andrew Odintsov", true); - account = new Account(person, "10012721-UG"); - } - - @Test - public void getStudentDetails() throws Exception - { - assertEquals(person, account.getStudentDetails()); - } - - @Test - public void getStudentNumber() throws Exception - { - assertEquals("10012721-UG", account.getStudentNumber()); - } - - @Test - public void setStudentDetails() throws Exception - { - Person person2 = new Person("Dr","Zilvinas Ceikauskas", true, "zil.Cei@gmail.com"); - account.setStudentDetails(person2); - assertEquals(person2, account.getStudentDetails()); - } - - @Test - public void setStudentNumber() throws Exception - { - account.setStudentNumber("99222213-UG"); - assertEquals("99222213-UG", account.getStudentNumber()); - } - - @After - public void tearDown() throws Exception - { - person = null; - account = null; - } -} \ No newline at end of file diff --git a/Test/Model/AssignmentTest.java b/Test/Model/AssignmentTest.java deleted file mode 100644 index 73545768..00000000 --- a/Test/Model/AssignmentTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package Model; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Created by bijan on 12/05/2017. - */ -public class AssignmentTest { - - Assignment assignment; - Person person1; - Person person2; - - @Before - public void setUp() throws Exception - { - person1 = new Person("Dr.", "Mark Fisher", true); - person2 = new Person("Dr.", "Steven Laycock", true); - assignment = new Assignment(30, person1, person1, person2, 100); - } - - @After - public void tearDown() throws Exception - { - person1 = null; - person2 = null; - assignment = null; - } - - @Test - public void toStringTest() throws Exception - { - String expected = "Assignment '"+assignment.getName()+"'"; - assertEquals(expected, assignment.toString()); - } - - @Test - public void toStringTest2() throws Exception - { - // Testing with a true verbose - StringBuilder r = new StringBuilder(); - r.append(assignment); - r.append("\n"); - r.append("Total marks: "+100); - r.append("\n"); - r.append("Total weighting: "+30); - - r.append("\n"); - r.append("Set By: "+person1.toString()); - r.append("\n"); - r.append("Marked By: "+person1.toString()); - r.append("\n"); - r.append("Reviewed By: "+person2.toString()); - - assertEquals(r.toString(), assignment.toString(true)); - - - // Testing with a false verbose - assertEquals(assignment.toString(), assignment.toString(false)); - } - - - -} \ No newline at end of file diff --git a/Test/Model/EventTest.java b/Test/Model/EventTest.java deleted file mode 100644 index ea3e88bc..00000000 --- a/Test/Model/EventTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package Model; - -import Controller.MainController; -import javafx.scene.input.KeyCode; -import javafx.scene.input.MouseButton; -import javafx.stage.Stage; -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.testfx.api.FxToolkit; -import org.testfx.framework.junit.ApplicationTest; - - -import java.util.GregorianCalendar; - -import static org.junit.Assert.*; - -/** - * Created by bijan on 12/05/2017. - */ - - -public class EventTest extends ApplicationTest -{ - - @Override - public void start(Stage primaryStage) throws Exception - { - MainController.isNumeric("23"); - } - - Event event; - - @Before - public void setUp() throws Exception - { - event = new Event("09/04/2017T15:00:00Z"); - } - - @Test - public void validDateString() throws Exception - { - - // Testing a valid date format - assertEquals(true, Event.validDateString("02/05/2017T05:02:09Z")); - - // Testing an invalid date format - assertEquals(false, Event.validDateString("02/05/217T0:02:09Z")); - - } - - @Test - public void toStringTest() throws Exception - { - - GregorianCalendar expectedDate = new GregorianCalendar(2017, 3, 9, 15, 0, 0); - assertEquals(expectedDate.getTime().toString(), event.toString()); - } - - @Test - public void setDate() throws Exception - { - event.setDate("04/10/2017T09:08:13Z"); - GregorianCalendar expectedDate = new GregorianCalendar(2017, 9, 4, 9, 8, 13); - assertEquals(expectedDate.getTime().toString(), event.toString()); - } - -} \ No newline at end of file diff --git a/Test/Model/HubFileTest.java b/Test/Model/HubFileTest.java deleted file mode 100644 index dae8311d..00000000 --- a/Test/Model/HubFileTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package Model; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Created by bijan on 06/05/2017. - */ -public class HubFileTest { - - @Test - public void getModules() throws Exception - { - } - - @Test - public void getExtensions() throws Exception - { - } - - @Test - public void getUpdates() throws Exception - { - } - -} \ No newline at end of file diff --git a/Test/Model/ModelEntityTest.java b/Test/Model/ModelEntityTest.java deleted file mode 100644 index fa8ce740..00000000 --- a/Test/Model/ModelEntityTest.java +++ /dev/null @@ -1,110 +0,0 @@ -package Model; - -import Controller.MainController; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; - -import java.util.ArrayList; -import java.util.GregorianCalendar; - -import static org.junit.Assert.*; - -/** - * Created by bijan on 08/05/2017. - */ -@Ignore -public class ModelEntityTest { - - private ModelEntity modelEntity; - private GregorianCalendar gregorianCalendar; - private String[] detailsArray= {"detail"}; - private MultilineString multilineString; - private Note note; - private ArrayList notes; - - @Before - public void setUp() throws Exception - { - gregorianCalendar = new GregorianCalendar(2017, 06, 02, 3, - 31, 30); - multilineString = new MultilineString("This is some note for testing purposes"); - note = new Note("Note1", gregorianCalendar, multilineString); - notes = new ArrayList<>(); - modelEntity = new ModelEntity("name1", detailsArray, notes); - } - - @After - public void tearDown() throws Exception - { - modelEntity = null; - } - - - @Test - public void getDetails() throws Exception - { - assertEquals("detail", modelEntity.getDetails().getAsString()); - } - - @Test - public void setName1() throws Exception - { - modelEntity.setName("Andrew"); - assertEquals("Andrew", modelEntity.getName()); - } - - @Test - public void setDetails() throws Exception - { - // Testing setDetails with String argument - modelEntity.setDetails("Some details to be added"); - assertEquals("Some details to be added", modelEntity.getDetails().getAsString()); - } - - @Test - public void setDetails1() throws Exception - { - // Testing setDetails with String array argument - String[] detailArray = {"Some details to be added", "more details to be added"}; - modelEntity.setDetails(detailArray); - assertArrayEquals(detailArray, modelEntity.getDetails().getAsArray()); - } - - @Test - public void setDetails2() throws Exception - { - // Testing setDetails with String ArrayList argument - ArrayList detailArrrayList = new ArrayList<>(); - detailArrrayList.add("New details to be added "); - detailArrrayList.add("And some more details to be added "); - modelEntity.setDetails(detailArrrayList); - assertArrayEquals(detailArrrayList.toArray(), modelEntity.getDetails().getAsArray()); - } - - @Test - public void setDetails3() throws Exception - { - // Testing setDetails with multiline String argument - MultilineString multilineString = new MultilineString("New details to be added "); - modelEntity.setDetails(multilineString); - assertArrayEquals(multilineString.getAsArray(), modelEntity.getDetails().getAsArray()); - } - - @Test - public void addProperties() throws Exception - { - modelEntity.addProperties("name2", new MultilineString("Added details")); - assertEquals("name2", modelEntity.getName()); - assertEquals("Added details", modelEntity.getDetails().getAsString()); - } - - @Test - public void addProperties1() throws Exception - { - modelEntity.addProperties("name3", "Added more details"); - assertEquals("name3", modelEntity.getName()); - assertEquals("Added more details", modelEntity.getDetails().getAsString()); - } -} \ No newline at end of file diff --git a/Test/Model/ModuleTest.java b/Test/Model/ModuleTest.java deleted file mode 100644 index f5c7db14..00000000 --- a/Test/Model/ModuleTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package Model; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Created by bijan on 06/05/2017. - */ -public class ModuleTest { - - @Test - public void replace() throws Exception - { - } - - @Test - public void addAssignment() throws Exception - { - } - - @Test - public void removeAssignment() throws Exception - { - } - - @Test - public void setOrganiser() throws Exception - { - } - - @Test - public void setModuleCode() throws Exception - { - } - - @Test - public void addTimetableEvent() throws Exception - { - } - - @Test - public void removeTimetableEvent() throws Exception - { - } - -} \ No newline at end of file diff --git a/Test/Model/MultilineStringTest.java b/Test/Model/MultilineStringTest.java deleted file mode 100644 index b3bb8358..00000000 --- a/Test/Model/MultilineStringTest.java +++ /dev/null @@ -1,115 +0,0 @@ -package Model; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import java.util.ArrayList; - -import static org.junit.Assert.*; - -/** - * Created by bijan on 06/05/2017. - */ -public class MultilineStringTest { - - private MultilineString multilineStringEmpty, multilineStringNormal, multilineStringArray; - - @Before - public void setUp() throws Exception - { - multilineStringEmpty = new MultilineString(); - - String text = "Much did had call new drew that kept.\n" + - "Limits expect wonder law she. Now has you views woman noisy match money rooms.\n" + - "To up remark it eldest length oh passed. Off because yet mistake feeling has men."; - multilineStringNormal = new MultilineString(text); - - String[] textArray = {"Much did had call new drew that kept.", - "Limits expect wonder law she. Now has you views woman noisy match money rooms.", - "To up remark it eldest length oh passed. Off because yet mistake feeling has men."}; - multilineStringArray = new MultilineString(textArray); - } - - @Test - public void getLines() throws Exception - { - // Checking an empty MultilineString - assertEquals(0, multilineStringEmpty.getLines()); - - // Checking a MultilineString with 3 lines - assertEquals(3, multilineStringNormal.getLines()); - - // Checking a MultilineString with 3 lines (Created by an array of Strings) - assertEquals(3, multilineStringArray.getLines()); - } - - @Test - public void getAsArrayList() throws Exception - { - // Checking an empty MultilineString - ArrayList empty = new ArrayList<>(); - assertEquals(empty, multilineStringEmpty.getAsArrayList()); - - // Checking a MultilineString with 3 lines - ArrayList text = new ArrayList<>(); - text.add("Much did had call new drew that kept."); - text.add("Limits expect wonder law she. Now has you views woman noisy match money rooms."); - text.add("To up remark it eldest length oh passed. Off because yet mistake feeling has men."); - for (int i=0; i personName = new ArrayList<>(); - private Person person1, person2; - - @Before - public void setUp() throws Exception - { - personName.add("Andrew"); - person1 = new Person("Mr", personName, "Odintsov", true, "Andrew.odi@apple.com"); - person2 = new Person("Mr","Zilvinas Ceikauskas", true, "Zill.cei@apple.com"); - } - - @Test - public void getFullName() throws Exception - { - // Testing with seperate given names and family name passed to the constructor - String expectedFullName = "Mr Andrew Odintsov"; - assertEquals(expectedFullName, person1.getFullName()); - - person1 = new Person("Mr", personName, "Odintsov", false, "Andrew.odi@apple.com"); - expectedFullName = "Mr Odintsov Andrew"; - assertEquals(expectedFullName, person1.getFullName()); - - personName.add("Ben"); - person1 = new Person("Mr", personName, "Odintsov", true, "Andrew.odi@apple.com"); - expectedFullName = "Mr Andrew Ben Odintsov"; - assertEquals(expectedFullName, person1.getFullName()); - - person1 = new Person("Mr", personName, "Odintsov", false, "Andrew.odi@apple.com"); - expectedFullName = "Mr Odintsov Andrew Ben"; - assertEquals(expectedFullName, person1.getFullName()); - - // Testing with full name passed to the constructor - expectedFullName = "Mr Zilvinas Ceikauskas"; - assertEquals(expectedFullName, person2.getFullName()); - - person2 = new Person("Mr","Ceikauskas Zilvinas", false, "Zill.cei@apple.com"); - expectedFullName = "Mr Ceikauskas Zilvinas"; - assertEquals(expectedFullName, person2.getFullName()); - } - - @Test - public void getFamilyName() throws Exception - { - // Testing with seperate given names and family name passed to the constructor - String expectedFamilyName = "Odintsov"; - assertEquals(expectedFamilyName, person1.getFamilyName()); - - // Testing with full name passed to the constructor - expectedFamilyName = "Ceikauskas"; - assertEquals(expectedFamilyName, person2.getFamilyName()); - - } - - @Test - public void getEmail() throws Exception - { - // Testing with seperate given names and family name passed to the constructor - String expectedFullName = "Andrew.odi@apple.com"; - assertEquals(expectedFullName, person1.getEmail()); - - // Testing with full name passed to the constructor - expectedFullName = "Zill.cei@apple.com"; - assertEquals(expectedFullName, person2.getEmail()); - } - - @Test - public void getGivenNames() throws Exception - { - // Testing with seperate given names and family name passed to the constructor - assertEquals(personName, person1.getGivenNames()); - - personName.add("Ben"); - person1 = new Person("Mr", personName, "Odintsov", true, "Andrew.odi@apple.com"); - assertEquals(personName, person1.getGivenNames()); - - // Testing with full name passed to the constructor - ArrayList person2GivenNames = new ArrayList<>(); - person2GivenNames.add("Zilvinas"); - assertEquals(person2GivenNames, person2.getGivenNames()); - - person2 = new Person("Mr","Zilvinas Ben Ceikauskas", true, "Zill.cei@apple.com"); - person2GivenNames.add("Ben"); - assertEquals(person2GivenNames, person2.getGivenNames()); - } - - @Test - public void getFamilyNameLast() throws Exception - { - // Testing with TRUE value for familyNameLast - assertTrue(person1.getFamilyNameLast()); - - // Testing with FALSE value for familyNameLast - person2 = new Person("Mr","Zilvinas Ceikauskas", false, "Zill.cei@apple.com"); - assertFalse(person2.getFamilyNameLast()); - } - - @Test - public void getSalutation() throws Exception - { - String expectedSalutation = "Mr"; - assertEquals(expectedSalutation, person1.getSalutation()); - - expectedSalutation = "Mr"; - assertEquals(expectedSalutation, person2.getSalutation()); - } - - @Test - public void hasSalutation() throws Exception - { - // Testing with salutation - assertEquals(true, person1.hasSalutation()); - - // Testing without salutation - person2 = new Person("","Zilvinas Ceikauskas", true, "Zill.cei@apple.com"); - assertEquals(false, person2.hasSalutation()); - } - - @Test - public void getPreferredName() throws Exception - { - // Testing with one name - assertEquals("Andrew", person1.getPreferredName()); - assertEquals("Zilvinas", person2.getPreferredName()); - - // Testing with multiple names - personName.add("Ben"); - person1 = new Person("Mr", personName, "Odintsov", true, "Andrew.odi@apple.com"); - assertEquals(personName.get(0), person1.getPreferredName()); - - person2 = new Person("Mr","Zilvinas Ben Ceikauskas", true, "Zill.cei@apple.com"); - assertEquals("Zilvinas", person2.getPreferredName()); - - // Testing with no names - personName.clear(); - person1 = new Person("Mr", personName, "Odintsov", true, "Andrew.odi@apple.com"); - assertEquals("Odintsov", person1.getPreferredName()); - - person2 = new Person("Mr","Ceikauskas", true, "Zill.cei@apple.com"); - assertEquals("Ceikauskas", person2.getPreferredName()); - - // Testing with Preferred Name set - person1.setPreferredName("Andy"); - assertEquals("Andy", person1.getPreferredName()); - - person2.setPreferredName("Zil"); - assertEquals("Zil", person2.getPreferredName()); - } - - @Test - public void setFamilyName() throws Exception - { - person1.setFamilyName("Williams"); - assertEquals("Williams", person1.getFamilyName()); - - person2.setFamilyName("Dickson"); - assertEquals("Dickson", person2.getFamilyName()); - } - - @Test - public void setGivenNames() throws Exception - { - person1.setGivenNames("Zil Ceikauskas"); - ArrayList names = new ArrayList<>(); - names.add("Zil"); - names.add("Ceikauskas"); - for(int i=0; i<2; i++){ - assertEquals(names.get(i), person1.getGivenNames().get(i)); - } - - person2.setGivenNames("Andrew Odintsov"); - names.clear(); - names.add("Andrew"); - names.add("Odintsov"); - for(int i=0; i<2; i++){ - assertEquals(names.get(i), person2.getGivenNames().get(i)); - } - } - - @Test - public void setName() throws Exception - { - person1.setName("Zil Ceikauskas", true); - assertEquals("Zil", person1.getPreferredName()); - assertEquals("Ceikauskas", person1.getFamilyName()); - - person1.setName("Ceikauskas Zil", false); - assertEquals("Zil", person1.getPreferredName()); - assertEquals("Ceikauskas", person1.getFamilyName()); - } - - @Test - public void setSalutation() throws Exception - { - person1.setSalutation("Dr"); - assertEquals("Dr", person1.getSalutation()); - - person2.setSalutation("Ms"); - assertEquals("Ms", person2.getSalutation()); - } - - @Test - public void validSalutation() throws Exception - { - // Testing a valid salutation - boolean result = Person.validSalutation("Mr"); - assertEquals(true, result); - - // Testing an invalid salutation - result = Person.validSalutation("7483mfd"); - assertEquals(false, result); - - result = Person.validSalutation("Mr mr"); - assertEquals(false, result); - } - - @Test - public void validName() throws Exception - { - // Testing a valid name - boolean result = Person.validName("Andrew Odintsov"); - assertEquals(true, result); - - // Testing an invalid name - result = Person.validName("Andrew35 Odintsov"); - assertEquals(false, result); - } - - @Test - public void validEmail() throws Exception - { - // Testing valid emails - // Valid emails from: https://blogs.msdn.microsoft.com/testing123/2009/02/06/email-address-test-cases/ - String[] validEmails = {"email@domain.com", "firstname.lastname@domain.com", "email@subdomain.domain.com", - "firstname+lastname@domain.com", "email@123.123.123.123", "email@[123.123.123.123]", "“email”@domain.com", - "1234567890@domain.com", "email@domain-one.com", "_______@domain.com", "email@domain.name", - "email@domain.co.jp", "firstname-lastname@domain.com"}; - - for (String validEmail : validEmails) { - assertTrue(Person.validEmail(validEmail)); - } - - // Testing invalid emails - // Invalid emails from: https://blogs.msdn.microsoft.com/testing123/2009/02/06/email-address-test-cases/ - String[] invalidEmails = {"plainaddress", "#@%^%#$@#$@#.com", "@domain.com", "Joe Smith /", - "email.domain.com", "email@domain@domain.com", ".email@domain.com", "email.@domain.com", - "email..email@domain.com", "email@domain.com (Joe Smith)", "email@domain..com"}; - - for (String invalidEmail : invalidEmails) { - assertFalse(Person.validEmail(invalidEmail)); - } - - } - - @After - public void tearDown() throws Exception - { - personName = null; - person1 = null; - person2 = null; - } -} \ No newline at end of file diff --git a/Test/Model/RequirementTest.java b/Test/Model/RequirementTest.java deleted file mode 100644 index 917898b9..00000000 --- a/Test/Model/RequirementTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package Model; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Created by bijan on 06/05/2017. - */ -public class RequirementTest { - - @Test - public void isComplete() throws Exception - { - } - - @Test - public void requirementProgress() throws Exception - { - } - -} \ No newline at end of file diff --git a/Test/Model/StudyPlannerTest.java b/Test/Model/StudyPlannerTest.java deleted file mode 100644 index 22085ef9..00000000 --- a/Test/Model/StudyPlannerTest.java +++ /dev/null @@ -1,27 +0,0 @@ -package Model; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Created by bijan on 06/05/2017. - */ -public class StudyPlannerTest { - - @Test - public void getListOfStudyProfiles() throws Exception - { - } - - @Test - public void loadFile() throws Exception - { - } - - @Test - public void processHubFile() throws Exception - { - } - -} \ No newline at end of file diff --git a/Test/Model/StudyProfileTest.java b/Test/Model/StudyProfileTest.java deleted file mode 100644 index 55580817..00000000 --- a/Test/Model/StudyProfileTest.java +++ /dev/null @@ -1,22 +0,0 @@ -package Model; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Created by bijan on 06/05/2017. - */ -public class StudyProfileTest { - - @Test - public void milestonesCompleted() throws Exception - { - } - - @Test - public void milestonesProgress() throws Exception - { - } - -} \ No newline at end of file diff --git a/Test/Model/TaskTest.java b/Test/Model/TaskTest.java deleted file mode 100644 index 8a2c8021..00000000 --- a/Test/Model/TaskTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package Model; - -import org.junit.Test; - -import static org.junit.Assert.*; - -/** - * Created by bijan on 06/05/2017. - */ -public class TaskTest { - - @Test - public void dependenciesComplete() throws Exception - { - } - - @Test - public void hasDependencies() throws Exception - { - } - - @Test - public void isComplete() throws Exception - { - } - - @Test - public void canCheckComplete() throws Exception - { - } - -} \ No newline at end of file diff --git a/Test/Model/VersionControlEntityTest.java b/Test/Model/VersionControlEntityTest.java deleted file mode 100644 index e5396cfc..00000000 --- a/Test/Model/VersionControlEntityTest.java +++ /dev/null @@ -1,92 +0,0 @@ -package Model; - -import Controller.MainController; -import javafx.stage.Stage; -import org.junit.After; -import org.junit.Before; -import org.junit.Ignore; -import org.junit.Test; -import org.testfx.framework.junit.ApplicationTest; - -import static org.junit.Assert.*; - -/** - * Created by bijan on 06/05/2017. - */ -@Ignore -public class VersionControlEntityTest extends ApplicationTest -{ - - VersionControlEntity versionControlEntity = new VersionControlEntity(); - - @Before - public void setUp() throws Exception - { - versionControlEntity = new VersionControlEntity(); - } - - - @Override - public void start(Stage stage) throws Exception - { - MainController.initialise(); - MainController.isNumeric("23"); - } - - @Test - public void findAndUpdate() throws Exception - { - assertFalse(VersionControlEntity.findAndUpdate(versionControlEntity)); - - versionControlEntity.setUID("99856-ID"); - assertTrue(VersionControlEntity.findAndUpdate(versionControlEntity)); - } - - @Test - public void inLibrary() throws Exception - { - versionControlEntity.setUID("1234-ID"); - assertTrue(VersionControlEntity.inLibrary("1234-ID")); - - assertFalse(VersionControlEntity.inLibrary("10132-ID")); - versionControlEntity.setUID("10132-ID"); - assertTrue(VersionControlEntity.inLibrary("10132-ID")); - } - - @Test - public void getVersion() throws Exception - { - assertEquals(0, versionControlEntity.getVersion()); - } - - @Test - public void getUID() throws Exception - { - assertEquals(null, versionControlEntity.getUID()); - } - - @Test - public void setUID() throws Exception - { - // Testing setUID with one argument - versionControlEntity.setUID("1234-ID"); - assertEquals("1234-ID", versionControlEntity.getUID()); - - // Testing the duplication - versionControlEntity.setUID("1234-ID"); - - // Testing setUID with two argument - assertEquals(true, versionControlEntity.setUID("95657-ID",1)); - assertEquals("95657-ID", versionControlEntity.getUID()); - assertEquals(1, versionControlEntity.getVersion()); - - // Testing the duplication -// assertFalse(versionControlEntity.setUID("5678-ID", 2)); - } - - @After - public void tearDown() throws Exception - { - versionControlEntity = null; - } -} \ No newline at end of file diff --git a/Test/View/CreateAccountUITest.java b/Test/View/CreateAccountUITest.java deleted file mode 100644 index 7f39c0c7..00000000 --- a/Test/View/CreateAccountUITest.java +++ /dev/null @@ -1,62 +0,0 @@ -package View; - - -import javafx.scene.control.Button; -import javafx.scene.control.TextField; -import org.junit.Test; - - -import static org.testfx.api.FxAssert.verifyThat; -import static org.testfx.matcher.base.NodeMatchers.hasText; -import static org.testfx.matcher.base.NodeMatchers.isDisabled; -import static org.testfx.matcher.base.NodeMatchers.isEnabled; - - -/** - * PearPlanner - * Created by Team BRONZE on 08/05/2017 - */ -public class CreateAccountUITest extends FXBase { - - @Test - public void testTextField () - { - TextField t = find("#salutation"); - TextField t1 = find("#full_name"); - TextField t3 = find("#account_no"); - TextField t4 = find("#email"); - Button b1 = find("#submit"); - - clickOn(t).write("Mr"); - clickOn(t1).write("Andrei Odintsov"); - clickOn(t3).write("100125464"); - clickOn(t4).write("a1covker@gmail.com"); - - - verifyThat(b1, isEnabled()); - - } - - - @Test - public void testTextFieldRegex () - { - TextField t = find("#salutation"); - TextField t1 = find("#full_name"); - TextField t3 = find("#account_no"); - TextField t4 = find("#email"); - Button b1 = find("#submit"); - - - clickOn(t).write("Mr"); - clickOn(t1).write("Andrei Odintsov"); - clickOn(t3).write("100125464"); - clickOn(t4).write("somethingnotemail"); - - - verifyThat(b1, isDisabled()); - - - } - -} \ No newline at end of file diff --git a/Test/View/FXBase.java b/Test/View/FXBase.java deleted file mode 100644 index e59beb4f..00000000 --- a/Test/View/FXBase.java +++ /dev/null @@ -1,61 +0,0 @@ -package View; - - - -import Controller.AccountController; -import javafx.application.Platform; -import javafx.fxml.FXMLLoader; -import javafx.scene.Node; -import javafx.scene.Parent; -import javafx.scene.Scene; -import javafx.scene.input.KeyCode; -import javafx.scene.input.MouseButton; -import javafx.stage.Stage; -import org.junit.After; -import org.testfx.api.FxToolkit; -import org.testfx.framework.junit.ApplicationTest; - -import java.util.concurrent.TimeoutException; - -/** - * PearPlanner - * Created by Team BRONZE on 08/05/2017 - */ -public abstract class FXBase extends ApplicationTest { - - - - @Override - public void start(Stage stage) throws Exception - { - - AccountController accountControl = new AccountController(); - FXMLLoader loader = new FXMLLoader(getClass().getResource("CreateAccount.fxml")); - loader.setController(accountControl); - Parent root = loader.load(); - - stage.setOnCloseRequest(e -> { - Platform.exit(); - System.exit(0); - }); - - Scene scene = new Scene(root, 550, 232); - - stage.setScene(scene); - - stage.show(); - } - - @After - public void afterEachTest() throws TimeoutException - { - FxToolkit.hideStage(); - release(new KeyCode[]{}); - release(new MouseButton[]{}); - } - - - public T find (final String query) { - return (T) lookup(query).queryAll().iterator().next(); - } -} diff --git a/build.gradle b/build.gradle new file mode 100644 index 00000000..47a94043 --- /dev/null +++ b/build.gradle @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2017 - Matthew Barrett, Randy Forte, Brandon Hulbert, + * Donald Miller, Nathaniel C Crossman + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +import org.gradle.plugins.ide.eclipse.model.Container + +/** + * The application plugin facilitates creating an executable JVM application. + */ +buildscript { + repositories { + maven { + url 'https://plugins.gradle.org/m2/' + } + mavenLocal() + mavenCentral() + jcenter() + } + dependencies { + classpath 'org.junit.platform:junit-platform-gradle-plugin:1.0.1' + classpath 'gradle.plugin.edu.sc.seis.gradle:launch4j:2.4.4' + classpath "gradle.plugin.com.github.spotbugs:spotbugs-gradle-plugin:1.6.3" + } +} + +apply plugin: 'java' +apply plugin: 'eclipse' +apply plugin: 'application' +apply plugin: 'checkstyle' +apply plugin: 'com.github.spotbugs' +apply plugin: 'edu.sc.seis.launch4j' +apply plugin: 'org.junit.platform.gradle.plugin' + + +compileJava { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 +} + +if (JavaVersion.current() != JavaVersion.VERSION_1_8) { + throw new GradleException("This project requires Java 8, but it's running on " + JavaVersion.current()) +} + +/** + * This line specifies where the main.java file is found. + */ +mainClassName = 'edu.wright.cs.raiderplanner.view.Main' + +/** + * This statement declares where Gradle is to find directories + * for source code, external resources, and test classes + */ +sourceSets { + main { + java { + srcDirs = ['src'] + } + resources { + srcDirs = ['src'] + } + } + + test { + java { + srcDirs = ["test"] + } + } +} + +/** + * The mavenCentral() function allows Gradle to access Maven's + * central repositories. I also identify two external repositories + * for dependencies not found in Maven Central. + * NOTE: The dependency "testfx-core-4.0.0-20150226.214553-8-sources.jar" + * is pulled from the sonatype URL. + */ +repositories { + mavenCentral() + jcenter() + maven { + url "https://github.com/screamingfrog/Maven/blob/master/uk/co/screamingfrog/AppleJavaExtensions/1.6/" + url "https://oss.sonatype.org/content/repositories/snapshots/org/testfx/testfx-core/4.0.0-SNAPSHOT/" + url "https://jitpack.io" + } +} + +/** + * Gradle grabs dependencies from the above repositories. + */ +dependencies { + compile 'net.sf.biweekly:biweekly:0.6.1' + compile 'commons-validator:commons-validator:1.6' + compile 'com.google.guava:guava:21.0' + compile 'org.apache.commons:commons-lang3:3.7' + compile 'org.hamcrest:hamcrest-all:1.3' + compile 'org.hamcrest:hamcrest-core:1.3' + compile 'org.hamcrest:hamcrest-generator:1.3' + compile 'org.hamcrest:hamcrest-integration:1.3' + compile 'org.hamcrest:hamcrest-junit:2.0.0.0' + compile 'org.hamcrest:hamcrest-library:1.3' + compile 'com.jfoenix:jfoenix:1.0.0' + compile 'org.jfxtras:jfxtras-agenda:8.0-r5' + compile 'org.jfxtras:jfxtras-common:8.0-r5' + compile 'org.jfxtras:jfxtras-controls:8.0-r5' + compile 'org.testfx:testfx-core:4.0.6-alpha' + compile 'org.testfx:testfx-junit:4.0.6-alpha' + compile 'org.testfx:testfx-junit5:4.0.6-alpha' + compile 'org.testfx:testfx-legacy:4.0.6-alpha' + compile 'com.github.mangstadt:vinnie:2.0.1' + compile 'org.junit.jupiter:junit-jupiter-api:5.0.1' + compile 'org.junit.jupiter:junit-jupiter-engine:5.0.1' + compile 'org.junit.platform:junit-platform-runner:1.0.1' + compile 'org.junit.vintage:junit-vintage-engine:4.12.1' + implementation 'com.github.PlusHaze:TrayNotification:5393c3a54f' + compile fileTree(dir: 'src', include: ['*.jar']) + compile group: 'org.apache.derby', name: 'derby', version: '10.14.1.0' + runtime group: 'org.apache.derby', name: 'derby', version: '10.14.1.0' +} + +/** + * Ensure the build fails if any unit test fails. + */ +test { + ignoreFailures = false +} + +/** + * Tells Gradle to assemble a JAR archive. + */ +jar { + manifest.attributes "Main-Class": mainClassName + baseName = project.name + from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } } +} + +/** + * Create executable from jar + */ +launch4j { + outputDir = 'libs' + outfile = 'raiderplanner.exe' + icon = '/../../icon.ico' + mainClassName = mainClassName + jar = "raiderplanner.jar" + productName = 'Raider Planner' + fileDescription = 'The best study planner since the Gantt Diagram' +} + +junitPlatform { + platformVersion '1.0.0' + reportsDir file('build/test-results/junit-platform') // this is the default +} + +checkstyle { + configFile = '.checkstyle.xml' as File + maxWarnings = 0 +} + +tasks.withType(com.github.spotbugs.SpotBugsTask) { + reports { + xml.enabled false + html.enabled true + // html.stylesheet resources.text.fromFile( 'config/xsl/findbugs-custom.xsl' ) + } + ignoreFailures = true +} +/** + * Keep Gradle from rewriting the generic default JRE container configuration + */ +eclipse.classpath.file.whenMerged { classpath -> + classpath.entries.removeAll { entry -> entry.kind == 'con' } + classpath.entries.add(new Container('org.eclipse.jdt.launching.JRE_CONTAINER')) +} diff --git a/build.properties b/build.properties deleted file mode 100644 index 38d1fcf1..00000000 --- a/build.properties +++ /dev/null @@ -1,2 +0,0 @@ -path.variable.kotlin_bundled=/Applications/IntelliJ IDEA.app/Contents/plugins/Kotlin/kotlinc -path.variable.maven_repository=/Users/markel33/.m2/repository \ No newline at end of file diff --git a/build.xml b/build.xml deleted file mode 100644 index 8e6759ab..00000000 --- a/build.xml +++ /dev/null @@ -1,226 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/docs/PHOTO-CREDITS.md b/docs/PHOTO-CREDITS.md new file mode 100644 index 00000000..820dfe41 --- /dev/null +++ b/docs/PHOTO-CREDITS.md @@ -0,0 +1,56 @@ +# PHOTO CREDITS AND COPYRIGHTS: + +RaiderPlanner/docs/assets/img/calendar.png - ICONSHOP, https://freeiconshop.com/icon/calendar-icon-outline-filled/ + +RaiderPlanner/docs/assets/img/reminder.png - Incenticon, http://www.myiconfinder.com/icon/timer-alarm-bell-business-clockcounter-event-history-hour-minute-plan-reminder-schedule-stopwatch-time-time-management-timertiming-wait-lap-lap-time-waiting-watch-history/936 + +RaiderPlanner/docs/assets/img/gantt.png - ConceptDraw, http://www.conceptdraw.com/examples/icon-project-management + +RaiderPlanner/docs/assets/img/pearplanner.jpg - screen shot of RaiderPlanner program, taken by James Languirand + +RaiderPlanner/docs/assets/img/pearplannerThumb.jpg - screen shot of RaiderPlanner program, taken by James Languirand + +RaiderPlanner/docs/assets/img/classPicture.jpg - taken by Kat + +RaiderPlanner/docs/assets/img/classPictureThumb.jpg - taken by Kat + +RaiderPlanner/docs/assets/img/movingforward.jpeg - photographer unknown, retrieved from http://businessessays.net/operationmanagement/project-planning-techniques-cpa-pert-and-gantt-charts/ + + +RaiderPlanner/docs/assets/img/movingforwardThumb.jpeg - photographer unknown, retrieved from http://businessessays.net/operationmanagement/project-planning-techniques-cpa-pert-and-gantt-charts/ + +RaiderPlanner/docs/assets/img/code.jpg - screen shot of RaiderPlanner source code, taken by James Languirand + +RaiderPlanner/docs/assets/img/codeThumb.jpg - screen shot of RaiderPlanner source code, taken by James Languirand + +RaiderPlanner/docs/assets/img/download.jpg - Kliponius, https://openclipart.org/detail/201887/download-button-green + +RaiderPlanner/docs/assets/img/new.png - created by Yousif sobhi + +RaiderPlanner/docs/assets/img/favnew.png - created by Yousif Sobhi + + + +# Bootstrap copyright and licenes +Copyright (c) 2011-2018 Twitter, Inc. + +Copyright (c) 2011-2018 The Bootstrap Authors + +https://github.com/twbs/bootstrap/blob/master/LICENSE + +# Images from unsplach +All other images are taken from the open source unsplach. + +unsplash License Link: https://unsplash.com/license + +RaiderPlanner/docs/assets/img/home-bg.jpg - created by JESHOOTS.COM, https://unsplash.com/photos/pUAM5hPaCRI + +RaiderPlanner/docs/assets/img/about/1.jpg - created by rawpixel, https://unsplash.com/photos/1GepXKtYUK8 + +RaiderPlanner/docs/assets/img/about/2.jpg - created by José Martín Ramírez C, https://unsplash.com/photos/Gauk-pFdvKk + +RaiderPlanner/docs/assets/img/about/3.jpg - created by Aleksi Tappura, https://unsplash.com/photos/PjH_BkzjxTA + +RaiderPlanner/docs/assets/img/about/4.jpg - created by Benjamin Voros, https://unsplash.com/photos/phIFdC6lA4E + +RaiderPlanner/docs/assets/img/Help.jpg - created by Russ Ward, https://unsplash.com/photos/bqzLehtF8XE diff --git a/docs/about.html b/docs/about.html new file mode 100644 index 00000000..e69de29b diff --git a/docs/assets/bootstrap/css/bootstrap.min.css b/docs/assets/bootstrap/css/bootstrap.min.css new file mode 100644 index 00000000..4ae18152 --- /dev/null +++ b/docs/assets/bootstrap/css/bootstrap.min.css @@ -0,0 +1,12 @@ +/* + * Start Bootstrap - Agency 4.1.1 (https://startbootstrap.com/template-overviews/agency) + * Copyright 2013-2018, Start Bootstrap + * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap-agency/blob/master/LICENSE) + */ +@import url("https://fonts.googleapis.com/css?family=Montserrat:400,700");@import url("https://fonts.googleapis.com/css?family=Kaushan+Script");@import url("https://fonts.googleapis.com/css?family=Droid+Serif:400,700,400italic,700italic");@import url("https://fonts.googleapis.com/css?family=Roboto+Slab:400,100,300,700"); +/*! + * Bootstrap v4.1.3 (https://getbootstrap.com/) + * Copyright 2011-2018 The Bootstrap Authors + * Copyright 2011-2018 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */:root{--blue:#007bff;--indigo:#6610f2;--purple:#6f42c1;--pink:#e83e8c;--red:#dc3545;--orange:#fd7e14;--yellow:#ffc107;--green:#28a745;--teal:#20c997;--cyan:#17a2b8;--white:#fff;--gray:#868e96;--gray-dark:#343a40;--primary:#fed136;--secondary:#868e96;--success:#28a745;--info:#17a2b8;--warning:#ffc107;--danger:#dc3545;--light:#f8f9fa;--dark:#343a40;--breakpoint-xs:0;--breakpoint-sm:576px;--breakpoint-md:768px;--breakpoint-lg:992px;--breakpoint-xl:1200px;--font-family-sans-serif:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-family-monospace:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace}*,:after,:before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:rgba(0,0,0,0)}@-ms-viewport{width:device-width}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{font-style:normal;line-height:inherit}address,dl,ol,ul{margin-bottom:1rem}dl,ol,ul{margin-top:0}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#e6b301;text-decoration:underline}a:not([href]):not([tabindex]),a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{border-style:none}img,svg{vertical-align:middle}svg{overflow:hidden}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#868e96;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{margin-bottom:.5rem;font-family:inherit;font-weight:500;line-height:1.2;color:inherit}.h1,h1{font-size:2.5rem}.h2,h2{font-size:2rem}.h3,h3{font-size:1.75rem}.h4,h4{font-size:1.5rem}.h5,h5{font-size:1.25rem}.h6,h6{font-size:1rem}.lead{font-size:1.25rem;font-weight:300}.display-1{font-size:6rem}.display-1,.display-2{font-weight:300;line-height:1.2}.display-2{font-size:5.5rem}.display-3{font-size:4.5rem}.display-3,.display-4{font-weight:300;line-height:1.2}.display-4{font-size:3.5rem}hr{margin-top:1rem;margin-bottom:1rem;border:0;border-top:1px solid rgba(0,0,0,.1)}.small,small{font-size:80%;font-weight:400}.mark,mark{padding:.2em;background-color:#fcf8e3}.list-inline,.list-unstyled{padding-left:0;list-style:none}.list-inline-item{display:inline-block}.list-inline-item:not(:last-child){margin-right:.5rem}.initialism{font-size:90%;text-transform:uppercase}.blockquote{margin-bottom:1rem;font-size:1.25rem}.blockquote-footer{display:block;font-size:80%;color:#868e96}.blockquote-footer:before{content:"\2014 \00A0"}.img-fluid,.img-thumbnail{max-width:100%;height:auto}.img-thumbnail{padding:.25rem;background-color:#fff;border:1px solid #dee2e6;border-radius:.25rem}.figure{display:inline-block}.figure-img{margin-bottom:.5rem;line-height:1}.figure-caption{font-size:90%;color:#868e96}code{font-size:87.5%;color:#e83e8c;word-break:break-word}a>code{color:inherit}kbd{padding:.2rem .4rem;font-size:87.5%;color:#fff;background-color:#212529;border-radius:.2rem}kbd kbd{padding:0;font-size:100%;font-weight:700}pre{display:block;font-size:87.5%;color:#212529}pre code{font-size:inherit;color:inherit;word-break:normal}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:576px){.container{max-width:540px}}@media (min-width:768px){.container{max-width:720px}}@media (min-width:992px){.container{max-width:960px}}@media (min-width:1200px){.container{max-width:1140px}}.container-fluid{width:100%;padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-15px;margin-left:-15px}.no-gutters{margin-right:0;margin-left:0}.no-gutters>.col,.no-gutters>[class*=col-]{padding-right:0;padding-left:0}.col,.col-1,.col-2,.col-3,.col-4,.col-5,.col-6,.col-7,.col-8,.col-9,.col-10,.col-11,.col-12,.col-auto,.col-lg,.col-lg-1,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-auto,.col-md,.col-md-1,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-md-10,.col-md-11,.col-md-12,.col-md-auto,.col-sm,.col-sm-1,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-auto,.col-xl,.col-xl-1,.col-xl-2,.col-xl-3,.col-xl-4,.col-xl-5,.col-xl-6,.col-xl-7,.col-xl-8,.col-xl-9,.col-xl-10,.col-xl-11,.col-xl-12,.col-xl-auto{position:relative;width:100%;min-height:1px;padding-right:15px;padding-left:15px}.col{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-1{-ms-flex:0 0 8.33333%;flex:0 0 8.33333%;max-width:8.33333%}.col-2{-ms-flex:0 0 16.66667%;flex:0 0 16.66667%;max-width:16.66667%}.col-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-4{-ms-flex:0 0 33.33333%;flex:0 0 33.33333%;max-width:33.33333%}.col-5{-ms-flex:0 0 41.66667%;flex:0 0 41.66667%;max-width:41.66667%}.col-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-7{-ms-flex:0 0 58.33333%;flex:0 0 58.33333%;max-width:58.33333%}.col-8{-ms-flex:0 0 66.66667%;flex:0 0 66.66667%;max-width:66.66667%}.col-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-10{-ms-flex:0 0 83.33333%;flex:0 0 83.33333%;max-width:83.33333%}.col-11{-ms-flex:0 0 91.66667%;flex:0 0 91.66667%;max-width:91.66667%}.col-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-first{-ms-flex-order:-1;order:-1}.order-last{-ms-flex-order:13;order:13}.order-0{-ms-flex-order:0;order:0}.order-1{-ms-flex-order:1;order:1}.order-2{-ms-flex-order:2;order:2}.order-3{-ms-flex-order:3;order:3}.order-4{-ms-flex-order:4;order:4}.order-5{-ms-flex-order:5;order:5}.order-6{-ms-flex-order:6;order:6}.order-7{-ms-flex-order:7;order:7}.order-8{-ms-flex-order:8;order:8}.order-9{-ms-flex-order:9;order:9}.order-10{-ms-flex-order:10;order:10}.order-11{-ms-flex-order:11;order:11}.order-12{-ms-flex-order:12;order:12}.offset-1{margin-left:8.33333%}.offset-2{margin-left:16.66667%}.offset-3{margin-left:25%}.offset-4{margin-left:33.33333%}.offset-5{margin-left:41.66667%}.offset-6{margin-left:50%}.offset-7{margin-left:58.33333%}.offset-8{margin-left:66.66667%}.offset-9{margin-left:75%}.offset-10{margin-left:83.33333%}.offset-11{margin-left:91.66667%}@media (min-width:576px){.col-sm{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-sm-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-sm-1{-ms-flex:0 0 8.33333%;flex:0 0 8.33333%;max-width:8.33333%}.col-sm-2{-ms-flex:0 0 16.66667%;flex:0 0 16.66667%;max-width:16.66667%}.col-sm-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-sm-4{-ms-flex:0 0 33.33333%;flex:0 0 33.33333%;max-width:33.33333%}.col-sm-5{-ms-flex:0 0 41.66667%;flex:0 0 41.66667%;max-width:41.66667%}.col-sm-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-sm-7{-ms-flex:0 0 58.33333%;flex:0 0 58.33333%;max-width:58.33333%}.col-sm-8{-ms-flex:0 0 66.66667%;flex:0 0 66.66667%;max-width:66.66667%}.col-sm-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-sm-10{-ms-flex:0 0 83.33333%;flex:0 0 83.33333%;max-width:83.33333%}.col-sm-11{-ms-flex:0 0 91.66667%;flex:0 0 91.66667%;max-width:91.66667%}.col-sm-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-sm-first{-ms-flex-order:-1;order:-1}.order-sm-last{-ms-flex-order:13;order:13}.order-sm-0{-ms-flex-order:0;order:0}.order-sm-1{-ms-flex-order:1;order:1}.order-sm-2{-ms-flex-order:2;order:2}.order-sm-3{-ms-flex-order:3;order:3}.order-sm-4{-ms-flex-order:4;order:4}.order-sm-5{-ms-flex-order:5;order:5}.order-sm-6{-ms-flex-order:6;order:6}.order-sm-7{-ms-flex-order:7;order:7}.order-sm-8{-ms-flex-order:8;order:8}.order-sm-9{-ms-flex-order:9;order:9}.order-sm-10{-ms-flex-order:10;order:10}.order-sm-11{-ms-flex-order:11;order:11}.order-sm-12{-ms-flex-order:12;order:12}.offset-sm-0{margin-left:0}.offset-sm-1{margin-left:8.33333%}.offset-sm-2{margin-left:16.66667%}.offset-sm-3{margin-left:25%}.offset-sm-4{margin-left:33.33333%}.offset-sm-5{margin-left:41.66667%}.offset-sm-6{margin-left:50%}.offset-sm-7{margin-left:58.33333%}.offset-sm-8{margin-left:66.66667%}.offset-sm-9{margin-left:75%}.offset-sm-10{margin-left:83.33333%}.offset-sm-11{margin-left:91.66667%}}@media (min-width:768px){.col-md{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-md-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-md-1{-ms-flex:0 0 8.33333%;flex:0 0 8.33333%;max-width:8.33333%}.col-md-2{-ms-flex:0 0 16.66667%;flex:0 0 16.66667%;max-width:16.66667%}.col-md-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-md-4{-ms-flex:0 0 33.33333%;flex:0 0 33.33333%;max-width:33.33333%}.col-md-5{-ms-flex:0 0 41.66667%;flex:0 0 41.66667%;max-width:41.66667%}.col-md-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-md-7{-ms-flex:0 0 58.33333%;flex:0 0 58.33333%;max-width:58.33333%}.col-md-8{-ms-flex:0 0 66.66667%;flex:0 0 66.66667%;max-width:66.66667%}.col-md-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-md-10{-ms-flex:0 0 83.33333%;flex:0 0 83.33333%;max-width:83.33333%}.col-md-11{-ms-flex:0 0 91.66667%;flex:0 0 91.66667%;max-width:91.66667%}.col-md-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-md-first{-ms-flex-order:-1;order:-1}.order-md-last{-ms-flex-order:13;order:13}.order-md-0{-ms-flex-order:0;order:0}.order-md-1{-ms-flex-order:1;order:1}.order-md-2{-ms-flex-order:2;order:2}.order-md-3{-ms-flex-order:3;order:3}.order-md-4{-ms-flex-order:4;order:4}.order-md-5{-ms-flex-order:5;order:5}.order-md-6{-ms-flex-order:6;order:6}.order-md-7{-ms-flex-order:7;order:7}.order-md-8{-ms-flex-order:8;order:8}.order-md-9{-ms-flex-order:9;order:9}.order-md-10{-ms-flex-order:10;order:10}.order-md-11{-ms-flex-order:11;order:11}.order-md-12{-ms-flex-order:12;order:12}.offset-md-0{margin-left:0}.offset-md-1{margin-left:8.33333%}.offset-md-2{margin-left:16.66667%}.offset-md-3{margin-left:25%}.offset-md-4{margin-left:33.33333%}.offset-md-5{margin-left:41.66667%}.offset-md-6{margin-left:50%}.offset-md-7{margin-left:58.33333%}.offset-md-8{margin-left:66.66667%}.offset-md-9{margin-left:75%}.offset-md-10{margin-left:83.33333%}.offset-md-11{margin-left:91.66667%}}@media (min-width:992px){.col-lg{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-lg-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-lg-1{-ms-flex:0 0 8.33333%;flex:0 0 8.33333%;max-width:8.33333%}.col-lg-2{-ms-flex:0 0 16.66667%;flex:0 0 16.66667%;max-width:16.66667%}.col-lg-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-lg-4{-ms-flex:0 0 33.33333%;flex:0 0 33.33333%;max-width:33.33333%}.col-lg-5{-ms-flex:0 0 41.66667%;flex:0 0 41.66667%;max-width:41.66667%}.col-lg-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-lg-7{-ms-flex:0 0 58.33333%;flex:0 0 58.33333%;max-width:58.33333%}.col-lg-8{-ms-flex:0 0 66.66667%;flex:0 0 66.66667%;max-width:66.66667%}.col-lg-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-lg-10{-ms-flex:0 0 83.33333%;flex:0 0 83.33333%;max-width:83.33333%}.col-lg-11{-ms-flex:0 0 91.66667%;flex:0 0 91.66667%;max-width:91.66667%}.col-lg-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-lg-first{-ms-flex-order:-1;order:-1}.order-lg-last{-ms-flex-order:13;order:13}.order-lg-0{-ms-flex-order:0;order:0}.order-lg-1{-ms-flex-order:1;order:1}.order-lg-2{-ms-flex-order:2;order:2}.order-lg-3{-ms-flex-order:3;order:3}.order-lg-4{-ms-flex-order:4;order:4}.order-lg-5{-ms-flex-order:5;order:5}.order-lg-6{-ms-flex-order:6;order:6}.order-lg-7{-ms-flex-order:7;order:7}.order-lg-8{-ms-flex-order:8;order:8}.order-lg-9{-ms-flex-order:9;order:9}.order-lg-10{-ms-flex-order:10;order:10}.order-lg-11{-ms-flex-order:11;order:11}.order-lg-12{-ms-flex-order:12;order:12}.offset-lg-0{margin-left:0}.offset-lg-1{margin-left:8.33333%}.offset-lg-2{margin-left:16.66667%}.offset-lg-3{margin-left:25%}.offset-lg-4{margin-left:33.33333%}.offset-lg-5{margin-left:41.66667%}.offset-lg-6{margin-left:50%}.offset-lg-7{margin-left:58.33333%}.offset-lg-8{margin-left:66.66667%}.offset-lg-9{margin-left:75%}.offset-lg-10{margin-left:83.33333%}.offset-lg-11{margin-left:91.66667%}}@media (min-width:1200px){.col-xl{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;max-width:100%}.col-xl-auto{-ms-flex:0 0 auto;flex:0 0 auto;width:auto;max-width:none}.col-xl-1{-ms-flex:0 0 8.33333%;flex:0 0 8.33333%;max-width:8.33333%}.col-xl-2{-ms-flex:0 0 16.66667%;flex:0 0 16.66667%;max-width:16.66667%}.col-xl-3{-ms-flex:0 0 25%;flex:0 0 25%;max-width:25%}.col-xl-4{-ms-flex:0 0 33.33333%;flex:0 0 33.33333%;max-width:33.33333%}.col-xl-5{-ms-flex:0 0 41.66667%;flex:0 0 41.66667%;max-width:41.66667%}.col-xl-6{-ms-flex:0 0 50%;flex:0 0 50%;max-width:50%}.col-xl-7{-ms-flex:0 0 58.33333%;flex:0 0 58.33333%;max-width:58.33333%}.col-xl-8{-ms-flex:0 0 66.66667%;flex:0 0 66.66667%;max-width:66.66667%}.col-xl-9{-ms-flex:0 0 75%;flex:0 0 75%;max-width:75%}.col-xl-10{-ms-flex:0 0 83.33333%;flex:0 0 83.33333%;max-width:83.33333%}.col-xl-11{-ms-flex:0 0 91.66667%;flex:0 0 91.66667%;max-width:91.66667%}.col-xl-12{-ms-flex:0 0 100%;flex:0 0 100%;max-width:100%}.order-xl-first{-ms-flex-order:-1;order:-1}.order-xl-last{-ms-flex-order:13;order:13}.order-xl-0{-ms-flex-order:0;order:0}.order-xl-1{-ms-flex-order:1;order:1}.order-xl-2{-ms-flex-order:2;order:2}.order-xl-3{-ms-flex-order:3;order:3}.order-xl-4{-ms-flex-order:4;order:4}.order-xl-5{-ms-flex-order:5;order:5}.order-xl-6{-ms-flex-order:6;order:6}.order-xl-7{-ms-flex-order:7;order:7}.order-xl-8{-ms-flex-order:8;order:8}.order-xl-9{-ms-flex-order:9;order:9}.order-xl-10{-ms-flex-order:10;order:10}.order-xl-11{-ms-flex-order:11;order:11}.order-xl-12{-ms-flex-order:12;order:12}.offset-xl-0{margin-left:0}.offset-xl-1{margin-left:8.33333%}.offset-xl-2{margin-left:16.66667%}.offset-xl-3{margin-left:25%}.offset-xl-4{margin-left:33.33333%}.offset-xl-5{margin-left:41.66667%}.offset-xl-6{margin-left:50%}.offset-xl-7{margin-left:58.33333%}.offset-xl-8{margin-left:66.66667%}.offset-xl-9{margin-left:75%}.offset-xl-10{margin-left:83.33333%}.offset-xl-11{margin-left:91.66667%}}.table{width:100%;margin-bottom:1rem;background-color:transparent}.table td,.table th{padding:.75rem;vertical-align:top;border-top:1px solid #dee2e6}.table thead th{vertical-align:bottom;border-bottom:2px solid #dee2e6}.table tbody+tbody{border-top:2px solid #dee2e6}.table .table{background-color:#fff}.table-sm td,.table-sm th{padding:.3rem}.table-bordered,.table-bordered td,.table-bordered th{border:1px solid #dee2e6}.table-bordered thead td,.table-bordered thead th{border-bottom-width:2px}.table-borderless tbody+tbody,.table-borderless td,.table-borderless th,.table-borderless thead th{border:0}.table-striped tbody tr:nth-of-type(odd){background-color:rgba(0,0,0,.05)}.table-hover tbody tr:hover{background-color:rgba(0,0,0,.075)}.table-primary,.table-primary>td,.table-primary>th{background-color:#fff2c7}.table-hover .table-primary:hover,.table-hover .table-primary:hover>td,.table-hover .table-primary:hover>th{background-color:#ffecae}.table-secondary,.table-secondary>td,.table-secondary>th{background-color:#dddfe2}.table-hover .table-secondary:hover,.table-hover .table-secondary:hover>td,.table-hover .table-secondary:hover>th{background-color:#cfd2d6}.table-success,.table-success>td,.table-success>th{background-color:#c3e6cb}.table-hover .table-success:hover,.table-hover .table-success:hover>td,.table-hover .table-success:hover>th{background-color:#b1dfbb}.table-info,.table-info>td,.table-info>th{background-color:#bee5eb}.table-hover .table-info:hover,.table-hover .table-info:hover>td,.table-hover .table-info:hover>th{background-color:#abdde5}.table-warning,.table-warning>td,.table-warning>th{background-color:#ffeeba}.table-hover .table-warning:hover,.table-hover .table-warning:hover>td,.table-hover .table-warning:hover>th{background-color:#ffe8a1}.table-danger,.table-danger>td,.table-danger>th{background-color:#f5c6cb}.table-hover .table-danger:hover,.table-hover .table-danger:hover>td,.table-hover .table-danger:hover>th{background-color:#f1b0b7}.table-light,.table-light>td,.table-light>th{background-color:#fdfdfe}.table-hover .table-light:hover,.table-hover .table-light:hover>td,.table-hover .table-light:hover>th{background-color:#ececf6}.table-dark,.table-dark>td,.table-dark>th{background-color:#c6c8ca}.table-hover .table-dark:hover,.table-hover .table-dark:hover>td,.table-hover .table-dark:hover>th{background-color:#b9bbbe}.table-active,.table-active>td,.table-active>th,.table-hover .table-active:hover,.table-hover .table-active:hover>td,.table-hover .table-active:hover>th{background-color:rgba(0,0,0,.075)}.table .thead-dark th{color:#fff;background-color:#212529;border-color:#32383e}.table .thead-light th{color:#495057;background-color:#e9ecef;border-color:#dee2e6}.table-dark{color:#fff;background-color:#212529}.table-dark td,.table-dark th,.table-dark thead th{border-color:#32383e}.table-dark.table-bordered{border:0}.table-dark.table-striped tbody tr:nth-of-type(odd){background-color:hsla(0,0%,100%,.05)}.table-dark.table-hover tbody tr:hover{background-color:hsla(0,0%,100%,.075)}@media (max-width:575.98px){.table-responsive-sm{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-sm>.table-bordered{border:0}}@media (max-width:767.98px){.table-responsive-md{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-md>.table-bordered{border:0}}@media (max-width:991.98px){.table-responsive-lg{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-lg>.table-bordered{border:0}}@media (max-width:1199.98px){.table-responsive-xl{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive-xl>.table-bordered{border:0}}.table-responsive{display:block;width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;-ms-overflow-style:-ms-autohiding-scrollbar}.table-responsive>.table-bordered{border:0}.form-control{display:block;width:100%;height:calc(2.25rem + 2px);padding:.375rem .75rem;font-size:1rem;line-height:1.5;color:#495057;background-color:#fff;background-clip:padding-box;border:1px solid #ced4da;border-radius:.25rem;transition:border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.form-control{transition:none}}.form-control::-ms-expand{background-color:transparent;border:0}.form-control:focus{color:#495057;background-color:#fff;border-color:#ffeeb5;outline:0;box-shadow:0 0 0 .2rem rgba(254,209,54,.25)}.form-control::-webkit-input-placeholder{color:#868e96;opacity:1}.form-control::-moz-placeholder{color:#868e96;opacity:1}.form-control:-ms-input-placeholder,.form-control::-ms-input-placeholder{color:#868e96;opacity:1}.form-control::placeholder{color:#868e96;opacity:1}.form-control:disabled,.form-control[readonly]{background-color:#e9ecef;opacity:1}select.form-control:focus::-ms-value{color:#495057;background-color:#fff}.form-control-file,.form-control-range{display:block;width:100%}.col-form-label{padding-top:calc(.375rem + 1px);padding-bottom:calc(.375rem + 1px);margin-bottom:0;font-size:inherit;line-height:1.5}.col-form-label-lg{padding-top:calc(.5rem + 1px);padding-bottom:calc(.5rem + 1px);font-size:1.25rem;line-height:1.5}.col-form-label-sm{padding-top:calc(.25rem + 1px);padding-bottom:calc(.25rem + 1px);font-size:.875rem;line-height:1.5}.form-control-plaintext{display:block;width:100%;padding-top:.375rem;padding-bottom:.375rem;margin-bottom:0;line-height:1.5;color:#212529;background-color:transparent;border:solid transparent;border-width:1px 0}.form-control-plaintext.form-control-lg,.form-control-plaintext.form-control-sm{padding-right:0;padding-left:0}.form-control-sm{height:calc(1.8125rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.form-control-lg{height:calc(2.875rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}select.form-control[multiple],select.form-control[size],textarea.form-control{height:auto}.form-group{margin-bottom:1rem}.form-text{display:block;margin-top:.25rem}.form-row{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;margin-right:-5px;margin-left:-5px}.form-row>.col,.form-row>[class*=col-]{padding-right:5px;padding-left:5px}.form-check{position:relative;display:block;padding-left:1.25rem}.form-check-input{position:absolute;margin-top:.3rem;margin-left:-1.25rem}.form-check-input:disabled~.form-check-label{color:#868e96}.form-check-label{margin-bottom:0}.form-check-inline{display:-ms-inline-flexbox;display:inline-flex;-ms-flex-align:center;align-items:center;padding-left:0;margin-right:.75rem}.form-check-inline .form-check-input{position:static;margin-top:0;margin-right:.3125rem;margin-left:0}.valid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#28a745}.valid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(40,167,69,.9);border-radius:.25rem}.custom-select.is-valid,.form-control.is-valid,.was-validated .custom-select:valid,.was-validated .form-control:valid{border-color:#28a745}.custom-select.is-valid:focus,.form-control.is-valid:focus,.was-validated .custom-select:valid:focus,.was-validated .form-control:valid:focus{border-color:#28a745;box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.custom-select.is-valid~.valid-feedback,.custom-select.is-valid~.valid-tooltip,.form-control-file.is-valid~.valid-feedback,.form-control-file.is-valid~.valid-tooltip,.form-control.is-valid~.valid-feedback,.form-control.is-valid~.valid-tooltip,.was-validated .custom-select:valid~.valid-feedback,.was-validated .custom-select:valid~.valid-tooltip,.was-validated .form-control-file:valid~.valid-feedback,.was-validated .form-control-file:valid~.valid-tooltip,.was-validated .form-control:valid~.valid-feedback,.was-validated .form-control:valid~.valid-tooltip{display:block}.form-check-input.is-valid~.form-check-label,.was-validated .form-check-input:valid~.form-check-label{color:#28a745}.form-check-input.is-valid~.valid-feedback,.form-check-input.is-valid~.valid-tooltip,.was-validated .form-check-input:valid~.valid-feedback,.was-validated .form-check-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid~.custom-control-label,.was-validated .custom-control-input:valid~.custom-control-label{color:#28a745}.custom-control-input.is-valid~.custom-control-label:before,.was-validated .custom-control-input:valid~.custom-control-label:before{background-color:#71dd8a}.custom-control-input.is-valid~.valid-feedback,.custom-control-input.is-valid~.valid-tooltip,.was-validated .custom-control-input:valid~.valid-feedback,.was-validated .custom-control-input:valid~.valid-tooltip{display:block}.custom-control-input.is-valid:checked~.custom-control-label:before,.was-validated .custom-control-input:valid:checked~.custom-control-label:before{background-color:#34ce57}.custom-control-input.is-valid:focus~.custom-control-label:before,.was-validated .custom-control-input:valid:focus~.custom-control-label:before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(40,167,69,.25)}.custom-file-input.is-valid~.custom-file-label,.was-validated .custom-file-input:valid~.custom-file-label{border-color:#28a745}.custom-file-input.is-valid~.custom-file-label:after,.was-validated .custom-file-input:valid~.custom-file-label:after{border-color:inherit}.custom-file-input.is-valid~.valid-feedback,.custom-file-input.is-valid~.valid-tooltip,.was-validated .custom-file-input:valid~.valid-feedback,.was-validated .custom-file-input:valid~.valid-tooltip{display:block}.custom-file-input.is-valid:focus~.custom-file-label,.was-validated .custom-file-input:valid:focus~.custom-file-label{box-shadow:0 0 0 .2rem rgba(40,167,69,.25)}.invalid-feedback{display:none;width:100%;margin-top:.25rem;font-size:80%;color:#dc3545}.invalid-tooltip{position:absolute;top:100%;z-index:5;display:none;max-width:100%;padding:.25rem .5rem;margin-top:.1rem;font-size:.875rem;line-height:1.5;color:#fff;background-color:rgba(220,53,69,.9);border-radius:.25rem}.custom-select.is-invalid,.form-control.is-invalid,.was-validated .custom-select:invalid,.was-validated .form-control:invalid{border-color:#dc3545}.custom-select.is-invalid:focus,.form-control.is-invalid:focus,.was-validated .custom-select:invalid:focus,.was-validated .form-control:invalid:focus{border-color:#dc3545;box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.custom-select.is-invalid~.invalid-feedback,.custom-select.is-invalid~.invalid-tooltip,.form-control-file.is-invalid~.invalid-feedback,.form-control-file.is-invalid~.invalid-tooltip,.form-control.is-invalid~.invalid-feedback,.form-control.is-invalid~.invalid-tooltip,.was-validated .custom-select:invalid~.invalid-feedback,.was-validated .custom-select:invalid~.invalid-tooltip,.was-validated .form-control-file:invalid~.invalid-feedback,.was-validated .form-control-file:invalid~.invalid-tooltip,.was-validated .form-control:invalid~.invalid-feedback,.was-validated .form-control:invalid~.invalid-tooltip{display:block}.form-check-input.is-invalid~.form-check-label,.was-validated .form-check-input:invalid~.form-check-label{color:#dc3545}.form-check-input.is-invalid~.invalid-feedback,.form-check-input.is-invalid~.invalid-tooltip,.was-validated .form-check-input:invalid~.invalid-feedback,.was-validated .form-check-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid~.custom-control-label,.was-validated .custom-control-input:invalid~.custom-control-label{color:#dc3545}.custom-control-input.is-invalid~.custom-control-label:before,.was-validated .custom-control-input:invalid~.custom-control-label:before{background-color:#efa2a9}.custom-control-input.is-invalid~.invalid-feedback,.custom-control-input.is-invalid~.invalid-tooltip,.was-validated .custom-control-input:invalid~.invalid-feedback,.was-validated .custom-control-input:invalid~.invalid-tooltip{display:block}.custom-control-input.is-invalid:checked~.custom-control-label:before,.was-validated .custom-control-input:invalid:checked~.custom-control-label:before{background-color:#e4606d}.custom-control-input.is-invalid:focus~.custom-control-label:before,.was-validated .custom-control-input:invalid:focus~.custom-control-label:before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(220,53,69,.25)}.custom-file-input.is-invalid~.custom-file-label,.was-validated .custom-file-input:invalid~.custom-file-label{border-color:#dc3545}.custom-file-input.is-invalid~.custom-file-label:after,.was-validated .custom-file-input:invalid~.custom-file-label:after{border-color:inherit}.custom-file-input.is-invalid~.invalid-feedback,.custom-file-input.is-invalid~.invalid-tooltip,.was-validated .custom-file-input:invalid~.invalid-feedback,.was-validated .custom-file-input:invalid~.invalid-tooltip{display:block}.custom-file-input.is-invalid:focus~.custom-file-label,.was-validated .custom-file-input:invalid:focus~.custom-file-label{box-shadow:0 0 0 .2rem rgba(220,53,69,.25)}.form-inline{display:-ms-flexbox;display:flex;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center;align-items:center}.form-inline .form-check{width:100%}@media (min-width:576px){.form-inline label{-ms-flex-align:center;-ms-flex-pack:center;justify-content:center}.form-inline .form-group,.form-inline label{display:-ms-flexbox;display:flex;align-items:center;margin-bottom:0}.form-inline .form-group{-ms-flex:0 0 auto;flex:0 0 auto;-ms-flex-flow:row wrap;flex-flow:row wrap;-ms-flex-align:center}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-plaintext{display:inline-block}.form-inline .custom-select,.form-inline .input-group{width:auto}.form-inline .form-check{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:auto;padding-left:0}.form-inline .form-check-input{position:relative;margin-top:0;margin-right:.25rem;margin-left:0}.form-inline .custom-control{-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center}.form-inline .custom-control-label{margin-bottom:0}}.btn{display:inline-block;font-weight:400;text-align:center;white-space:nowrap;vertical-align:middle;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border:1px solid transparent;padding:.375rem .75rem;font-size:1rem;line-height:1.5;border-radius:.25rem;transition:color .15s ease-in-out,background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.btn{transition:none}}.btn:focus,.btn:hover{text-decoration:none}.btn.focus,.btn:focus{outline:0;box-shadow:0 0 0 .2rem rgba(254,209,54,.25)}.btn.disabled,.btn:disabled{opacity:.65}.btn:not(:disabled):not(.disabled){cursor:pointer}a.btn.disabled,fieldset:disabled a.btn{pointer-events:none}.btn-primary{color:#212529}.btn-primary:hover{color:#212529;background-color:#fec810;border-color:#fec503}.btn-primary.focus,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(254,209,54,.5)}.btn-primary.disabled,.btn-primary:disabled{color:#212529;background-color:#fed136;border-color:#fed136}.btn-primary:not(:disabled):not(.disabled).active,.btn-primary:not(:disabled):not(.disabled):active,.show>.btn-primary.dropdown-toggle{color:#212529;background-color:#fec503;border-color:#f3bd01}.btn-primary:not(:disabled):not(.disabled).active:focus,.btn-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(254,209,54,.5)}.btn-secondary{color:#fff;background-color:#868e96;border-color:#868e96}.btn-secondary:hover{color:#fff;background-color:#727b84;border-color:#6c757d}.btn-secondary.focus,.btn-secondary:focus{box-shadow:0 0 0 .2rem hsla(210,7%,56%,.5)}.btn-secondary.disabled,.btn-secondary:disabled{color:#fff;background-color:#868e96;border-color:#868e96}.btn-secondary:not(:disabled):not(.disabled).active,.btn-secondary:not(:disabled):not(.disabled):active,.show>.btn-secondary.dropdown-toggle{color:#fff;background-color:#6c757d;border-color:#666e76}.btn-secondary:not(:disabled):not(.disabled).active:focus,.btn-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem hsla(210,7%,56%,.5)}.btn-success{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:hover{color:#fff;background-color:#218838;border-color:#1e7e34}.btn-success.focus,.btn-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-success.disabled,.btn-success:disabled{color:#fff;background-color:#28a745;border-color:#28a745}.btn-success:not(:disabled):not(.disabled).active,.btn-success:not(:disabled):not(.disabled):active,.show>.btn-success.dropdown-toggle{color:#fff;background-color:#1e7e34;border-color:#1c7430}.btn-success:not(:disabled):not(.disabled).active:focus,.btn-success:not(:disabled):not(.disabled):active:focus,.show>.btn-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-info{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:hover{color:#fff;background-color:#138496;border-color:#117a8b}.btn-info.focus,.btn-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-info.disabled,.btn-info:disabled{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-info:not(:disabled):not(.disabled).active,.btn-info:not(:disabled):not(.disabled):active,.show>.btn-info.dropdown-toggle{color:#fff;background-color:#117a8b;border-color:#10707f}.btn-info:not(:disabled):not(.disabled).active:focus,.btn-info:not(:disabled):not(.disabled):active:focus,.show>.btn-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-warning{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:hover{color:#212529;background-color:#e0a800;border-color:#d39e00}.btn-warning.focus,.btn-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-warning.disabled,.btn-warning:disabled{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-warning:not(:disabled):not(.disabled).active,.btn-warning:not(:disabled):not(.disabled):active,.show>.btn-warning.dropdown-toggle{color:#212529;background-color:#d39e00;border-color:#c69500}.btn-warning:not(:disabled):not(.disabled).active:focus,.btn-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-danger{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:hover{color:#fff;background-color:#c82333;border-color:#bd2130}.btn-danger.focus,.btn-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-danger.disabled,.btn-danger:disabled{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-danger:not(:disabled):not(.disabled).active,.btn-danger:not(:disabled):not(.disabled):active,.show>.btn-danger.dropdown-toggle{color:#fff;background-color:#bd2130;border-color:#b21f2d}.btn-danger:not(:disabled):not(.disabled).active:focus,.btn-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-light{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:hover{color:#212529;background-color:#e2e6ea;border-color:#dae0e5}.btn-light.focus,.btn-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-light.disabled,.btn-light:disabled{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-light:not(:disabled):not(.disabled).active,.btn-light:not(:disabled):not(.disabled):active,.show>.btn-light.dropdown-toggle{color:#212529;background-color:#dae0e5;border-color:#d3d9df}.btn-light:not(:disabled):not(.disabled).active:focus,.btn-light:not(:disabled):not(.disabled):active:focus,.show>.btn-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-dark{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:hover{color:#fff;background-color:#23272b;border-color:#1d2124}.btn-dark.focus,.btn-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-dark.disabled,.btn-dark:disabled{color:#fff;background-color:#343a40;border-color:#343a40}.btn-dark:not(:disabled):not(.disabled).active,.btn-dark:not(:disabled):not(.disabled):active,.show>.btn-dark.dropdown-toggle{color:#fff;background-color:#1d2124;border-color:#171a1d}.btn-dark:not(:disabled):not(.disabled).active:focus,.btn-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-primary{color:#fed136;background-color:transparent;background-image:none;border-color:#fed136}.btn-outline-primary:hover{color:#212529;background-color:#fed136;border-color:#fed136}.btn-outline-primary.focus,.btn-outline-primary:focus{box-shadow:0 0 0 .2rem rgba(254,209,54,.5)}.btn-outline-primary.disabled,.btn-outline-primary:disabled{color:#fed136;background-color:transparent}.btn-outline-primary:not(:disabled):not(.disabled).active,.btn-outline-primary:not(:disabled):not(.disabled):active,.show>.btn-outline-primary.dropdown-toggle{color:#212529;background-color:#fed136;border-color:#fed136}.btn-outline-primary:not(:disabled):not(.disabled).active:focus,.btn-outline-primary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-primary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(254,209,54,.5)}.btn-outline-secondary{color:#868e96;background-color:transparent;background-image:none;border-color:#868e96}.btn-outline-secondary:hover{color:#fff;background-color:#868e96;border-color:#868e96}.btn-outline-secondary.focus,.btn-outline-secondary:focus{box-shadow:0 0 0 .2rem hsla(210,7%,56%,.5)}.btn-outline-secondary.disabled,.btn-outline-secondary:disabled{color:#868e96;background-color:transparent}.btn-outline-secondary:not(:disabled):not(.disabled).active,.btn-outline-secondary:not(:disabled):not(.disabled):active,.show>.btn-outline-secondary.dropdown-toggle{color:#fff;background-color:#868e96;border-color:#868e96}.btn-outline-secondary:not(:disabled):not(.disabled).active:focus,.btn-outline-secondary:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-secondary.dropdown-toggle:focus{box-shadow:0 0 0 .2rem hsla(210,7%,56%,.5)}.btn-outline-success{color:#28a745;background-color:transparent;background-image:none;border-color:#28a745}.btn-outline-success:hover{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success.focus,.btn-outline-success:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-success.disabled,.btn-outline-success:disabled{color:#28a745;background-color:transparent}.btn-outline-success:not(:disabled):not(.disabled).active,.btn-outline-success:not(:disabled):not(.disabled):active,.show>.btn-outline-success.dropdown-toggle{color:#fff;background-color:#28a745;border-color:#28a745}.btn-outline-success:not(:disabled):not(.disabled).active:focus,.btn-outline-success:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-success.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(40,167,69,.5)}.btn-outline-info{color:#17a2b8;background-color:transparent;background-image:none;border-color:#17a2b8}.btn-outline-info:hover{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info.focus,.btn-outline-info:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-info.disabled,.btn-outline-info:disabled{color:#17a2b8;background-color:transparent}.btn-outline-info:not(:disabled):not(.disabled).active,.btn-outline-info:not(:disabled):not(.disabled):active,.show>.btn-outline-info.dropdown-toggle{color:#fff;background-color:#17a2b8;border-color:#17a2b8}.btn-outline-info:not(:disabled):not(.disabled).active:focus,.btn-outline-info:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-info.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(23,162,184,.5)}.btn-outline-warning{color:#ffc107;background-color:transparent;background-image:none;border-color:#ffc107}.btn-outline-warning:hover{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning.focus,.btn-outline-warning:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-warning.disabled,.btn-outline-warning:disabled{color:#ffc107;background-color:transparent}.btn-outline-warning:not(:disabled):not(.disabled).active,.btn-outline-warning:not(:disabled):not(.disabled):active,.show>.btn-outline-warning.dropdown-toggle{color:#212529;background-color:#ffc107;border-color:#ffc107}.btn-outline-warning:not(:disabled):not(.disabled).active:focus,.btn-outline-warning:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-warning.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(255,193,7,.5)}.btn-outline-danger{color:#dc3545;background-color:transparent;background-image:none;border-color:#dc3545}.btn-outline-danger:hover{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger.focus,.btn-outline-danger:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-danger.disabled,.btn-outline-danger:disabled{color:#dc3545;background-color:transparent}.btn-outline-danger:not(:disabled):not(.disabled).active,.btn-outline-danger:not(:disabled):not(.disabled):active,.show>.btn-outline-danger.dropdown-toggle{color:#fff;background-color:#dc3545;border-color:#dc3545}.btn-outline-danger:not(:disabled):not(.disabled).active:focus,.btn-outline-danger:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-danger.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(220,53,69,.5)}.btn-outline-light{color:#f8f9fa;background-color:transparent;background-image:none;border-color:#f8f9fa}.btn-outline-light:hover{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light.focus,.btn-outline-light:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-light.disabled,.btn-outline-light:disabled{color:#f8f9fa;background-color:transparent}.btn-outline-light:not(:disabled):not(.disabled).active,.btn-outline-light:not(:disabled):not(.disabled):active,.show>.btn-outline-light.dropdown-toggle{color:#212529;background-color:#f8f9fa;border-color:#f8f9fa}.btn-outline-light:not(:disabled):not(.disabled).active:focus,.btn-outline-light:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-light.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(248,249,250,.5)}.btn-outline-dark{color:#343a40;background-color:transparent;background-image:none;border-color:#343a40}.btn-outline-dark:hover{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark.focus,.btn-outline-dark:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-outline-dark.disabled,.btn-outline-dark:disabled{color:#343a40;background-color:transparent}.btn-outline-dark:not(:disabled):not(.disabled).active,.btn-outline-dark:not(:disabled):not(.disabled):active,.show>.btn-outline-dark.dropdown-toggle{color:#fff;background-color:#343a40;border-color:#343a40}.btn-outline-dark:not(:disabled):not(.disabled).active:focus,.btn-outline-dark:not(:disabled):not(.disabled):active:focus,.show>.btn-outline-dark.dropdown-toggle:focus{box-shadow:0 0 0 .2rem rgba(52,58,64,.5)}.btn-link{font-weight:400;color:#fed136;background-color:transparent}.btn-link:hover{color:#e6b301;background-color:transparent}.btn-link.focus,.btn-link:focus,.btn-link:hover{text-decoration:underline;border-color:transparent}.btn-link.focus,.btn-link:focus{box-shadow:none}.btn-link.disabled,.btn-link:disabled{color:#868e96;pointer-events:none}.btn-group-lg>.btn,.btn-lg{padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.btn-group-sm>.btn,.btn-sm{padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:.5rem}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{transition:opacity .15s linear}@media screen and (prefers-reduced-motion:reduce){.fade{transition:none}}.fade:not(.show){opacity:0}.collapse:not(.show){display:none}.collapsing{position:relative;height:0;overflow:hidden;transition:height .35s ease}@media screen and (prefers-reduced-motion:reduce){.collapsing{transition:none}}.dropdown,.dropleft,.dropright,.dropup{position:relative}.dropdown-toggle:after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid;border-right:.3em solid transparent;border-bottom:0;border-left:.3em solid transparent}.dropdown-toggle:empty:after{margin-left:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:10rem;padding:.5rem 0;margin:.125rem 0 0;font-size:1rem;color:#212529;text-align:left;list-style:none;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.15);border-radius:.25rem}.dropdown-menu-right{right:0;left:auto}.dropup .dropdown-menu{top:auto;bottom:100%;margin-top:0;margin-bottom:.125rem}.dropup .dropdown-toggle:after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:0;border-right:.3em solid transparent;border-bottom:.3em solid;border-left:.3em solid transparent}.dropup .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-menu{top:0;right:auto;left:100%;margin-top:0;margin-left:.125rem}.dropright .dropdown-toggle:after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:0;border-bottom:.3em solid transparent;border-left:.3em solid}.dropright .dropdown-toggle:empty:after{margin-left:0}.dropright .dropdown-toggle:after{vertical-align:0}.dropleft .dropdown-menu{top:0;right:100%;left:auto;margin-top:0;margin-right:.125rem}.dropleft .dropdown-toggle:after{display:inline-block;width:0;height:0;margin-left:.255em;vertical-align:.255em;content:"";display:none}.dropleft .dropdown-toggle:before{display:inline-block;width:0;height:0;margin-right:.255em;vertical-align:.255em;content:"";border-top:.3em solid transparent;border-right:.3em solid;border-bottom:.3em solid transparent}.dropleft .dropdown-toggle:empty:after{margin-left:0}.dropleft .dropdown-toggle:before{vertical-align:0}.dropdown-menu[x-placement^=bottom],.dropdown-menu[x-placement^=left],.dropdown-menu[x-placement^=right],.dropdown-menu[x-placement^=top]{right:auto;bottom:auto}.dropdown-divider{height:0;margin:.5rem 0;overflow:hidden;border-top:1px solid #e9ecef}.dropdown-item{display:block;width:100%;padding:.25rem 1.5rem;clear:both;font-weight:400;color:#212529;text-align:inherit;white-space:nowrap;background-color:transparent;border:0}.dropdown-item:focus,.dropdown-item:hover{color:#16181b;text-decoration:none;background-color:#f8f9fa}.dropdown-item.active,.dropdown-item:active{color:#fff;text-decoration:none;background-color:#fed136}.dropdown-item.disabled,.dropdown-item:disabled{color:#868e96;background-color:transparent}.dropdown-menu.show{display:block}.dropdown-header{display:block;padding:.5rem 1.5rem;margin-bottom:0;font-size:.875rem;color:#868e96;white-space:nowrap}.dropdown-item-text{display:block;padding:.25rem 1.5rem;color:#212529}.btn-group,.btn-group-vertical{position:relative;display:-ms-inline-flexbox;display:inline-flex;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;-ms-flex:0 1 auto;flex:0 1 auto}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:1}.btn-group-vertical .btn+.btn,.btn-group-vertical .btn+.btn-group,.btn-group-vertical .btn-group+.btn,.btn-group-vertical .btn-group+.btn-group,.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-pack:start;justify-content:flex-start}.btn-toolbar .input-group{width:auto}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn-group:not(:last-child)>.btn,.btn-group>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:not(:first-child)>.btn,.btn-group>.btn:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.dropdown-toggle-split{padding-right:.5625rem;padding-left:.5625rem}.dropdown-toggle-split:after,.dropright .dropdown-toggle-split:after,.dropup .dropdown-toggle-split:after{margin-left:0}.dropleft .dropdown-toggle-split:before{margin-right:0}.btn-group-sm>.btn+.dropdown-toggle-split,.btn-sm+.dropdown-toggle-split{padding-right:.375rem;padding-left:.375rem}.btn-group-lg>.btn+.dropdown-toggle-split,.btn-lg+.dropdown-toggle-split{padding-right:.75rem;padding-left:.75rem}.btn-group-vertical{-ms-flex-direction:column;flex-direction:column;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:center;justify-content:center}.btn-group-vertical .btn,.btn-group-vertical .btn-group{width:100%}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn-group:not(:last-child)>.btn,.btn-group-vertical>.btn:not(:last-child):not(.dropdown-toggle){border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:not(:first-child)>.btn,.btn-group-vertical>.btn:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.btn-group-toggle>.btn,.btn-group-toggle>.btn-group>.btn{margin-bottom:0}.btn-group-toggle>.btn-group>.btn input[type=checkbox],.btn-group-toggle>.btn-group>.btn input[type=radio],.btn-group-toggle>.btn input[type=checkbox],.btn-group-toggle>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:stretch;align-items:stretch;width:100%}.input-group>.custom-file,.input-group>.custom-select,.input-group>.form-control{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;width:1%;margin-bottom:0}.input-group>.custom-file+.custom-file,.input-group>.custom-file+.custom-select,.input-group>.custom-file+.form-control,.input-group>.custom-select+.custom-file,.input-group>.custom-select+.custom-select,.input-group>.custom-select+.form-control,.input-group>.form-control+.custom-file,.input-group>.form-control+.custom-select,.input-group>.form-control+.form-control{margin-left:-1px}.input-group>.custom-file .custom-file-input:focus~.custom-file-label,.input-group>.custom-select:focus,.input-group>.form-control:focus{z-index:3}.input-group>.custom-file .custom-file-input:focus{z-index:4}.input-group>.custom-select:not(:last-child),.input-group>.form-control:not(:last-child){border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-select:not(:first-child),.input-group>.form-control:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.input-group>.custom-file{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center}.input-group>.custom-file:not(:last-child) .custom-file-label,.input-group>.custom-file:not(:last-child) .custom-file-label:after{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.custom-file:not(:first-child) .custom-file-label{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-append,.input-group-prepend{display:-ms-flexbox;display:flex}.input-group-append .btn,.input-group-prepend .btn{position:relative;z-index:2}.input-group-append .btn+.btn,.input-group-append .btn+.input-group-text,.input-group-append .input-group-text+.btn,.input-group-append .input-group-text+.input-group-text,.input-group-prepend .btn+.btn,.input-group-prepend .btn+.input-group-text,.input-group-prepend .input-group-text+.btn,.input-group-prepend .input-group-text+.input-group-text{margin-left:-1px}.input-group-prepend{margin-right:-1px}.input-group-append{margin-left:-1px}.input-group-text{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;padding:.375rem .75rem;margin-bottom:0;font-size:1rem;font-weight:400;line-height:1.5;color:#495057;text-align:center;white-space:nowrap;background-color:#e9ecef;border:1px solid #ced4da;border-radius:.25rem}.input-group-text input[type=checkbox],.input-group-text input[type=radio]{margin-top:0}.input-group-lg>.form-control,.input-group-lg>.input-group-append>.btn,.input-group-lg>.input-group-append>.input-group-text,.input-group-lg>.input-group-prepend>.btn,.input-group-lg>.input-group-prepend>.input-group-text{height:calc(2.875rem + 2px);padding:.5rem 1rem;font-size:1.25rem;line-height:1.5;border-radius:.3rem}.input-group-sm>.form-control,.input-group-sm>.input-group-append>.btn,.input-group-sm>.input-group-append>.input-group-text,.input-group-sm>.input-group-prepend>.btn,.input-group-sm>.input-group-prepend>.input-group-text{height:calc(1.8125rem + 2px);padding:.25rem .5rem;font-size:.875rem;line-height:1.5;border-radius:.2rem}.input-group>.input-group-append:last-child>.btn:not(:last-child):not(.dropdown-toggle),.input-group>.input-group-append:last-child>.input-group-text:not(:last-child),.input-group>.input-group-append:not(:last-child)>.btn,.input-group>.input-group-append:not(:last-child)>.input-group-text,.input-group>.input-group-prepend>.btn,.input-group>.input-group-prepend>.input-group-text{border-top-right-radius:0;border-bottom-right-radius:0}.input-group>.input-group-append>.btn,.input-group>.input-group-append>.input-group-text,.input-group>.input-group-prepend:first-child>.btn:not(:first-child),.input-group>.input-group-prepend:first-child>.input-group-text:not(:first-child),.input-group>.input-group-prepend:not(:first-child)>.btn,.input-group>.input-group-prepend:not(:first-child)>.input-group-text{border-top-left-radius:0;border-bottom-left-radius:0}.custom-control{position:relative;display:block;min-height:1.5rem;padding-left:1.5rem}.custom-control-inline{display:-ms-inline-flexbox;display:inline-flex;margin-right:1rem}.custom-control-input{position:absolute;z-index:-1;opacity:0}.custom-control-input:checked~.custom-control-label:before{color:#fff;background-color:#fed136}.custom-control-input:focus~.custom-control-label:before{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(254,209,54,.25)}.custom-control-input:active~.custom-control-label:before{color:#fff;background-color:#fffae8}.custom-control-input:disabled~.custom-control-label{color:#868e96}.custom-control-input:disabled~.custom-control-label:before{background-color:#e9ecef}.custom-control-label{position:relative;margin-bottom:0}.custom-control-label:before{pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-color:#dee2e6}.custom-control-label:after,.custom-control-label:before{position:absolute;top:.25rem;left:-1.5rem;display:block;width:1rem;height:1rem;content:""}.custom-control-label:after{background-repeat:no-repeat;background-position:50%;background-size:50% 50%}.custom-checkbox .custom-control-label:before{border-radius:.25rem}.custom-checkbox .custom-control-input:checked~.custom-control-label:before{background-color:#fed136}.custom-checkbox .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3E%3Cpath fill='%23fff' d='M6.564.75l-3.59 3.612-1.538-1.55L0 4.26 2.974 7.25 8 2.193z'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:before{background-color:#fed136}.custom-checkbox .custom-control-input:indeterminate~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 4'%3E%3Cpath stroke='%23fff' d='M0 2h4'/%3E%3C/svg%3E")}.custom-checkbox .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(254,209,54,.5)}.custom-checkbox .custom-control-input:disabled:indeterminate~.custom-control-label:before{background-color:rgba(254,209,54,.5)}.custom-radio .custom-control-label:before{border-radius:50%}.custom-radio .custom-control-input:checked~.custom-control-label:before{background-color:#fed136}.custom-radio .custom-control-input:checked~.custom-control-label:after{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3E%3Ccircle r='3' fill='%23fff'/%3E%3C/svg%3E")}.custom-radio .custom-control-input:disabled:checked~.custom-control-label:before{background-color:rgba(254,209,54,.5)}.custom-select{display:inline-block;width:100%;height:calc(2.25rem + 2px);padding:.375rem 1.75rem .375rem .75rem;line-height:1.5;color:#495057;vertical-align:middle;background:#fff url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 4 5'%3E%3Cpath fill='%23343a40' d='M2 0L0 2h4zm0 5L0 3h4z'/%3E%3C/svg%3E") no-repeat right .75rem center;background-size:8px 10px;border:1px solid #ced4da;border-radius:.25rem;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-select:focus{border-color:#ffeeb5;outline:0;box-shadow:0 0 0 .2rem rgba(255,238,181,.5)}.custom-select:focus::-ms-value{color:#495057;background-color:#fff}.custom-select[multiple],.custom-select[size]:not([size="1"]){height:auto;padding-right:.75rem;background-image:none}.custom-select:disabled{color:#868e96;background-color:#e9ecef}.custom-select::-ms-expand{opacity:0}.custom-select-sm{height:calc(1.8125rem + 2px);font-size:75%}.custom-select-lg,.custom-select-sm{padding-top:.375rem;padding-bottom:.375rem}.custom-select-lg{height:calc(2.875rem + 2px);font-size:125%}.custom-file{display:inline-block;margin-bottom:0}.custom-file,.custom-file-input{position:relative;width:100%;height:calc(2.25rem + 2px)}.custom-file-input{z-index:2;margin:0;opacity:0}.custom-file-input:focus~.custom-file-label{border-color:#ffeeb5;box-shadow:0 0 0 .2rem rgba(254,209,54,.25)}.custom-file-input:focus~.custom-file-label:after{border-color:#ffeeb5}.custom-file-input:disabled~.custom-file-label{background-color:#e9ecef}.custom-file-input:lang(en)~.custom-file-label:after{content:"Browse"}.custom-file-label{left:0;z-index:1;height:calc(2.25rem + 2px);background-color:#fff;border:1px solid #ced4da;border-radius:.25rem}.custom-file-label,.custom-file-label:after{position:absolute;top:0;right:0;padding:.375rem .75rem;line-height:1.5;color:#495057}.custom-file-label:after{bottom:0;z-index:3;display:block;height:2.25rem;content:"Browse";background-color:#e9ecef;border-left:1px solid #ced4da;border-radius:0 .25rem .25rem 0}.custom-range{width:100%;padding-left:0;background-color:transparent;-webkit-appearance:none;-moz-appearance:none;appearance:none}.custom-range:focus{outline:none}.custom-range:focus::-webkit-slider-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(254,209,54,.25)}.custom-range:focus::-moz-range-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(254,209,54,.25)}.custom-range:focus::-ms-thumb{box-shadow:0 0 0 1px #fff,0 0 0 .2rem rgba(254,209,54,.25)}.custom-range::-moz-focus-outer{border:0}.custom-range::-webkit-slider-thumb{width:1rem;height:1rem;margin-top:-.25rem;background-color:#fed136;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-webkit-appearance:none;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-webkit-slider-thumb{transition:none}}.custom-range::-webkit-slider-thumb:active{background-color:#fffae8}.custom-range::-webkit-slider-runnable-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-moz-range-thumb{width:1rem;height:1rem;background-color:#fed136;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;-moz-appearance:none;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-moz-range-thumb{transition:none}}.custom-range::-moz-range-thumb:active{background-color:#fffae8}.custom-range::-moz-range-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:#dee2e6;border-color:transparent;border-radius:1rem}.custom-range::-ms-thumb{width:1rem;height:1rem;margin-top:0;margin-right:.2rem;margin-left:.2rem;background-color:#fed136;border:0;border-radius:1rem;transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out;appearance:none}@media screen and (prefers-reduced-motion:reduce){.custom-range::-ms-thumb{transition:none}}.custom-range::-ms-thumb:active{background-color:#fffae8}.custom-range::-ms-track{width:100%;height:.5rem;color:transparent;cursor:pointer;background-color:transparent;border-color:transparent;border-width:.5rem}.custom-range::-ms-fill-lower,.custom-range::-ms-fill-upper{background-color:#dee2e6;border-radius:1rem}.custom-range::-ms-fill-upper{margin-right:15px}.custom-control-label:before,.custom-file-label,.custom-select{transition:background-color .15s ease-in-out,border-color .15s ease-in-out,box-shadow .15s ease-in-out}@media screen and (prefers-reduced-motion:reduce){.custom-control-label:before,.custom-file-label,.custom-select{transition:none}}.nav{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding-left:0;margin-bottom:0;list-style:none}.nav-link{display:block;padding:.5rem 1rem}.nav-link:focus,.nav-link:hover{text-decoration:none}.nav-link.disabled{color:#868e96}.nav-tabs{border-bottom:1px solid #dee2e6}.nav-tabs .nav-item{margin-bottom:-1px}.nav-tabs .nav-link{border:1px solid transparent;border-top-left-radius:.25rem;border-top-right-radius:.25rem}.nav-tabs .nav-link:focus,.nav-tabs .nav-link:hover{border-color:#e9ecef #e9ecef #dee2e6}.nav-tabs .nav-link.disabled{color:#868e96;background-color:transparent;border-color:transparent}.nav-tabs .nav-item.show .nav-link,.nav-tabs .nav-link.active{color:#495057;background-color:#fff;border-color:#dee2e6 #dee2e6 #fff}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.nav-pills .nav-link{border-radius:.25rem}.nav-pills .nav-link.active,.nav-pills .show>.nav-link{color:#fff;background-color:#fed136}.nav-fill .nav-item{-ms-flex:1 1 auto;flex:1 1 auto;text-align:center}.nav-justified .nav-item{-ms-flex-preferred-size:0;flex-basis:0;-ms-flex-positive:1;flex-grow:1;text-align:center}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.navbar{position:relative;padding:.5rem 1rem}.navbar,.navbar>.container,.navbar>.container-fluid{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;-ms-flex-align:center;align-items:center;-ms-flex-pack:justify;justify-content:space-between}.navbar-brand{display:inline-block;padding-top:.3125rem;padding-bottom:.3125rem;margin-right:1rem;font-size:1.25rem;line-height:inherit;white-space:nowrap}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-nav{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0;list-style:none}.navbar-nav .nav-link{padding-right:0;padding-left:0}.navbar-nav .dropdown-menu{position:static;float:none}.navbar-text{display:inline-block;padding-top:.5rem;padding-bottom:.5rem}.navbar-collapse{-ms-flex-preferred-size:100%;flex-basis:100%;-ms-flex-positive:1;flex-grow:1;-ms-flex-align:center;align-items:center}.navbar-toggler{padding:.25rem .75rem;font-size:1.25rem;line-height:1;background-color:transparent;border:1px solid transparent;border-radius:.25rem}.navbar-toggler:focus,.navbar-toggler:hover{text-decoration:none}.navbar-toggler:not(:disabled):not(.disabled){cursor:pointer}.navbar-toggler-icon{display:inline-block;width:1.5em;height:1.5em;vertical-align:middle;content:"";background:no-repeat 50%;background-size:100% 100%}@media (max-width:575.98px){.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:576px){.navbar-expand-sm{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-sm .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-sm .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-sm .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-sm>.container,.navbar-expand-sm>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-sm .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-sm .navbar-toggler{display:none}}@media (max-width:767.98px){.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:768px){.navbar-expand-md{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-md .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-md .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-md .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-md>.container,.navbar-expand-md>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-md .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-md .navbar-toggler{display:none}}@media (max-width:991.98px){.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:992px){.navbar-expand-lg{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-lg .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-lg .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-lg .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-lg>.container,.navbar-expand-lg>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-lg .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-lg .navbar-toggler{display:none}}@media (max-width:1199.98px){.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{padding-right:0;padding-left:0}}@media (min-width:1200px){.navbar-expand-xl{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand-xl .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand-xl .navbar-nav .dropdown-menu{position:absolute}.navbar-expand-xl .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand-xl>.container,.navbar-expand-xl>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand-xl .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand-xl .navbar-toggler{display:none}}.navbar-expand{-ms-flex-flow:row nowrap;flex-flow:row nowrap;-ms-flex-pack:start;justify-content:flex-start}.navbar-expand>.container,.navbar-expand>.container-fluid{padding-right:0;padding-left:0}.navbar-expand .navbar-nav{-ms-flex-direction:row;flex-direction:row}.navbar-expand .navbar-nav .dropdown-menu{position:absolute}.navbar-expand .navbar-nav .nav-link{padding-right:.5rem;padding-left:.5rem}.navbar-expand>.container,.navbar-expand>.container-fluid{-ms-flex-wrap:nowrap;flex-wrap:nowrap}.navbar-expand .navbar-collapse{display:-ms-flexbox!important;display:flex!important;-ms-flex-preferred-size:auto;flex-basis:auto}.navbar-expand .navbar-toggler{display:none}.navbar-light .navbar-brand,.navbar-light .navbar-brand:focus,.navbar-light .navbar-brand:hover{color:rgba(0,0,0,.9)}.navbar-light .navbar-nav .nav-link{color:rgba(0,0,0,.5)}.navbar-light .navbar-nav .nav-link:focus,.navbar-light .navbar-nav .nav-link:hover{color:rgba(0,0,0,.7)}.navbar-light .navbar-nav .nav-link.disabled{color:rgba(0,0,0,.3)}.navbar-light .navbar-nav .active>.nav-link,.navbar-light .navbar-nav .nav-link.active,.navbar-light .navbar-nav .nav-link.show,.navbar-light .navbar-nav .show>.nav-link{color:rgba(0,0,0,.9)}.navbar-light .navbar-toggler{color:rgba(0,0,0,.5);border-color:rgba(0,0,0,.1)}.navbar-light .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(0, 0, 0, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-light .navbar-text{color:rgba(0,0,0,.5)}.navbar-light .navbar-text a,.navbar-light .navbar-text a:focus,.navbar-light .navbar-text a:hover{color:rgba(0,0,0,.9)}.navbar-dark .navbar-brand,.navbar-dark .navbar-brand:focus,.navbar-dark .navbar-brand:hover{color:#fff}.navbar-dark .navbar-nav .nav-link{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-nav .nav-link:focus,.navbar-dark .navbar-nav .nav-link:hover{color:hsla(0,0%,100%,.75)}.navbar-dark .navbar-nav .nav-link.disabled{color:hsla(0,0%,100%,.25)}.navbar-dark .navbar-nav .active>.nav-link,.navbar-dark .navbar-nav .nav-link.active,.navbar-dark .navbar-nav .nav-link.show,.navbar-dark .navbar-nav .show>.nav-link{color:#fff}.navbar-dark .navbar-toggler{color:hsla(0,0%,100%,.5);border-color:hsla(0,0%,100%,.1)}.navbar-dark .navbar-toggler-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg viewBox='0 0 30 30' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath stroke='rgba(255, 255, 255, 0.5)' stroke-width='2' stroke-linecap='round' stroke-miterlimit='10' d='M4 7h22M4 15h22M4 23h22'/%3E%3C/svg%3E")}.navbar-dark .navbar-text{color:hsla(0,0%,100%,.5)}.navbar-dark .navbar-text a,.navbar-dark .navbar-text a:focus,.navbar-dark .navbar-text a:hover{color:#fff}.card{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;min-width:0;word-wrap:break-word;background-color:#fff;background-clip:border-box;border:1px solid rgba(0,0,0,.125);border-radius:.25rem}.card>hr{margin-right:0;margin-left:0}.card>.list-group:first-child .list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-body{-ms-flex:1 1 auto;flex:1 1 auto;padding:1.25rem}.card-title{margin-bottom:.75rem}.card-subtitle{margin-top:-.375rem}.card-subtitle,.card-text:last-child{margin-bottom:0}.card-link:hover{text-decoration:none}.card-link+.card-link{margin-left:1.25rem}.card-header{padding:.75rem 1.25rem;margin-bottom:0;background-color:rgba(0,0,0,.03);border-bottom:1px solid rgba(0,0,0,.125)}.card-header:first-child{border-radius:calc(.25rem - 1px) calc(.25rem - 1px) 0 0}.card-header+.list-group .list-group-item:first-child{border-top:0}.card-footer{padding:.75rem 1.25rem;background-color:rgba(0,0,0,.03);border-top:1px solid rgba(0,0,0,.125)}.card-footer:last-child{border-radius:0 0 calc(.25rem - 1px) calc(.25rem - 1px)}.card-header-tabs{margin-bottom:-.75rem;border-bottom:0}.card-header-pills,.card-header-tabs{margin-right:-.625rem;margin-left:-.625rem}.card-img-overlay{position:absolute;top:0;right:0;bottom:0;left:0;padding:1.25rem}.card-img{width:100%;border-radius:calc(.25rem - 1px)}.card-img-top{width:100%;border-top-left-radius:calc(.25rem - 1px);border-top-right-radius:calc(.25rem - 1px)}.card-img-bottom{width:100%;border-bottom-right-radius:calc(.25rem - 1px);border-bottom-left-radius:calc(.25rem - 1px)}.card-deck{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-deck .card{margin-bottom:15px}@media (min-width:576px){.card-deck{-ms-flex-flow:row wrap;flex-flow:row wrap;margin-right:-15px;margin-left:-15px}.card-deck .card{display:-ms-flexbox;display:flex;-ms-flex:1 0 0%;flex:1 0 0%;-ms-flex-direction:column;flex-direction:column;margin-right:15px;margin-bottom:0;margin-left:15px}}.card-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column}.card-group>.card{margin-bottom:15px}@media (min-width:576px){.card-group{-ms-flex-flow:row wrap;flex-flow:row wrap}.card-group>.card{-ms-flex:1 0 0%;flex:1 0 0%;margin-bottom:0}.card-group>.card+.card{margin-left:0;border-left:0}.card-group>.card:first-child{border-top-right-radius:0;border-bottom-right-radius:0}.card-group>.card:first-child .card-header,.card-group>.card:first-child .card-img-top{border-top-right-radius:0}.card-group>.card:first-child .card-footer,.card-group>.card:first-child .card-img-bottom{border-bottom-right-radius:0}.card-group>.card:last-child{border-top-left-radius:0;border-bottom-left-radius:0}.card-group>.card:last-child .card-header,.card-group>.card:last-child .card-img-top{border-top-left-radius:0}.card-group>.card:last-child .card-footer,.card-group>.card:last-child .card-img-bottom{border-bottom-left-radius:0}.card-group>.card:only-child{border-radius:.25rem}.card-group>.card:only-child .card-header,.card-group>.card:only-child .card-img-top{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.card-group>.card:only-child .card-footer,.card-group>.card:only-child .card-img-bottom{border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.card-group>.card:not(:first-child):not(:last-child):not(:only-child),.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-footer,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-header,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-bottom,.card-group>.card:not(:first-child):not(:last-child):not(:only-child) .card-img-top{border-radius:0}}.card-columns .card{margin-bottom:.75rem}@media (min-width:576px){.card-columns{-webkit-column-count:3;-moz-column-count:3;column-count:3;-webkit-column-gap:1.25rem;-moz-column-gap:1.25rem;column-gap:1.25rem;orphans:1;widows:1}.card-columns .card{display:inline-block;width:100%}}.accordion .card:not(:first-of-type):not(:last-of-type){border-bottom:0;border-radius:0}.accordion .card:not(:first-of-type) .card-header:first-child{border-radius:0}.accordion .card:first-of-type{border-bottom:0;border-bottom-right-radius:0;border-bottom-left-radius:0}.accordion .card:last-of-type{border-top-left-radius:0;border-top-right-radius:0}.breadcrumb{display:-ms-flexbox;display:flex;-ms-flex-wrap:wrap;flex-wrap:wrap;padding:.75rem 1rem;margin-bottom:1rem;list-style:none;background-color:#e9ecef;border-radius:.25rem}.breadcrumb-item+.breadcrumb-item{padding-left:.5rem}.breadcrumb-item+.breadcrumb-item:before{display:inline-block;padding-right:.5rem;color:#868e96;content:"/"}.breadcrumb-item+.breadcrumb-item:hover:before{text-decoration:underline;text-decoration:none}.breadcrumb-item.active{color:#868e96}.pagination{display:-ms-flexbox;display:flex;padding-left:0;list-style:none;border-radius:.25rem}.page-link{position:relative;display:block;padding:.5rem .75rem;margin-left:-1px;line-height:1.25;color:#fed136;background-color:#fff;border:1px solid #dee2e6}.page-link:hover{z-index:2;color:#e6b301;text-decoration:none;background-color:#e9ecef;border-color:#dee2e6}.page-link:focus{z-index:2;outline:0;box-shadow:0 0 0 .2rem rgba(254,209,54,.25)}.page-link:not(:disabled):not(.disabled){cursor:pointer}.page-item:first-child .page-link{margin-left:0;border-top-left-radius:.25rem;border-bottom-left-radius:.25rem}.page-item:last-child .page-link{border-top-right-radius:.25rem;border-bottom-right-radius:.25rem}.page-item.active .page-link{z-index:1;color:#fff;background-color:#fed136;border-color:#fed136}.page-item.disabled .page-link{color:#868e96;pointer-events:none;cursor:auto;background-color:#fff;border-color:#dee2e6}.pagination-lg .page-link{padding:.75rem 1.5rem;font-size:1.25rem;line-height:1.5}.pagination-lg .page-item:first-child .page-link{border-top-left-radius:.3rem;border-bottom-left-radius:.3rem}.pagination-lg .page-item:last-child .page-link{border-top-right-radius:.3rem;border-bottom-right-radius:.3rem}.pagination-sm .page-link{padding:.25rem .5rem;font-size:.875rem;line-height:1.5}.pagination-sm .page-item:first-child .page-link{border-top-left-radius:.2rem;border-bottom-left-radius:.2rem}.pagination-sm .page-item:last-child .page-link{border-top-right-radius:.2rem;border-bottom-right-radius:.2rem}.badge{display:inline-block;padding:.25em .4em;font-size:75%;font-weight:700;line-height:1;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25rem}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.badge-pill{padding-right:.6em;padding-left:.6em;border-radius:10rem}.badge-primary{color:#212529;background-color:#fed136}.badge-primary[href]:focus,.badge-primary[href]:hover{color:#212529;text-decoration:none;background-color:#fec503}.badge-secondary{color:#fff;background-color:#868e96}.badge-secondary[href]:focus,.badge-secondary[href]:hover{color:#fff;text-decoration:none;background-color:#6c757d}.badge-success{color:#fff;background-color:#28a745}.badge-success[href]:focus,.badge-success[href]:hover{color:#fff;text-decoration:none;background-color:#1e7e34}.badge-info{color:#fff;background-color:#17a2b8}.badge-info[href]:focus,.badge-info[href]:hover{color:#fff;text-decoration:none;background-color:#117a8b}.badge-warning{color:#212529;background-color:#ffc107}.badge-warning[href]:focus,.badge-warning[href]:hover{color:#212529;text-decoration:none;background-color:#d39e00}.badge-danger{color:#fff;background-color:#dc3545}.badge-danger[href]:focus,.badge-danger[href]:hover{color:#fff;text-decoration:none;background-color:#bd2130}.badge-light{color:#212529;background-color:#f8f9fa}.badge-light[href]:focus,.badge-light[href]:hover{color:#212529;text-decoration:none;background-color:#dae0e5}.badge-dark{color:#fff;background-color:#343a40}.badge-dark[href]:focus,.badge-dark[href]:hover{color:#fff;text-decoration:none;background-color:#1d2124}.jumbotron{padding:2rem 1rem;margin-bottom:2rem;background-color:#e9ecef;border-radius:.3rem}@media (min-width:576px){.jumbotron{padding:4rem 2rem}}.jumbotron-fluid{padding-right:0;padding-left:0;border-radius:0}.alert{position:relative;padding:.75rem 1.25rem;margin-bottom:1rem;border:1px solid transparent;border-radius:.25rem}.alert-heading{color:inherit}.alert-link{font-weight:700}.alert-dismissible{padding-right:4rem}.alert-dismissible .close{position:absolute;top:0;right:0;padding:.75rem 1.25rem;color:inherit}.alert-primary{color:#846d1c;background-color:#fff6d7;border-color:#fff2c7}.alert-primary hr{border-top-color:#ffecae}.alert-primary .alert-link{color:#5a4a13}.alert-secondary{color:#464a4e;background-color:#e7e8ea;border-color:#dddfe2}.alert-secondary hr{border-top-color:#cfd2d6}.alert-secondary .alert-link{color:#2e3133}.alert-success{color:#155724;background-color:#d4edda;border-color:#c3e6cb}.alert-success hr{border-top-color:#b1dfbb}.alert-success .alert-link{color:#0b2e13}.alert-info{color:#0c5460;background-color:#d1ecf1;border-color:#bee5eb}.alert-info hr{border-top-color:#abdde5}.alert-info .alert-link{color:#062c33}.alert-warning{color:#856404;background-color:#fff3cd;border-color:#ffeeba}.alert-warning hr{border-top-color:#ffe8a1}.alert-warning .alert-link{color:#533f03}.alert-danger{color:#721c24;background-color:#f8d7da;border-color:#f5c6cb}.alert-danger hr{border-top-color:#f1b0b7}.alert-danger .alert-link{color:#491217}.alert-light{color:#818182;background-color:#fefefe;border-color:#fdfdfe}.alert-light hr{border-top-color:#ececf6}.alert-light .alert-link{color:#686868}.alert-dark{color:#1b1e21;background-color:#d6d8d9;border-color:#c6c8ca}.alert-dark hr{border-top-color:#b9bbbe}.alert-dark .alert-link{color:#040505}@-webkit-keyframes a{0%{background-position:1rem 0}to{background-position:0 0}}@keyframes a{0%{background-position:1rem 0}to{background-position:0 0}}.progress{height:1rem;overflow:hidden;font-size:.75rem;background-color:#e9ecef;border-radius:.25rem}.progress,.progress-bar{display:-ms-flexbox;display:flex}.progress-bar{-ms-flex-direction:column;flex-direction:column;-ms-flex-pack:center;justify-content:center;color:#fff;text-align:center;white-space:nowrap;background-color:#fed136;transition:width .6s ease}@media screen and (prefers-reduced-motion:reduce){.progress-bar{transition:none}}.progress-bar-striped{background-image:linear-gradient(45deg,hsla(0,0%,100%,.15) 25%,transparent 0,transparent 50%,hsla(0,0%,100%,.15) 0,hsla(0,0%,100%,.15) 75%,transparent 0,transparent);background-size:1rem 1rem}.progress-bar-animated{-webkit-animation:a 1s linear infinite;animation:a 1s linear infinite}.media{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start}.media-body{-ms-flex:1;flex:1}.list-group{display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;padding-left:0;margin-bottom:0}.list-group-item-action{width:100%;color:#495057;text-align:inherit}.list-group-item-action:focus,.list-group-item-action:hover{color:#495057;text-decoration:none;background-color:#f8f9fa}.list-group-item-action:active{color:#212529;background-color:#e9ecef}.list-group-item{position:relative;display:block;padding:.75rem 1.25rem;margin-bottom:-1px;background-color:#fff;border:1px solid rgba(0,0,0,.125)}.list-group-item:first-child{border-top-left-radius:.25rem;border-top-right-radius:.25rem}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:.25rem;border-bottom-left-radius:.25rem}.list-group-item:focus,.list-group-item:hover{z-index:1;text-decoration:none}.list-group-item.disabled,.list-group-item:disabled{color:#868e96;background-color:#fff}.list-group-item.active{z-index:2;color:#fff;background-color:#fed136;border-color:#fed136}.list-group-flush .list-group-item{border-right:0;border-left:0;border-radius:0}.list-group-flush:first-child .list-group-item:first-child{border-top:0}.list-group-flush:last-child .list-group-item:last-child{border-bottom:0}.list-group-item-primary{color:#846d1c;background-color:#fff2c7}.list-group-item-primary.list-group-item-action:focus,.list-group-item-primary.list-group-item-action:hover{color:#846d1c;background-color:#ffecae}.list-group-item-primary.list-group-item-action.active{color:#fff;background-color:#846d1c;border-color:#846d1c}.list-group-item-secondary{color:#464a4e;background-color:#dddfe2}.list-group-item-secondary.list-group-item-action:focus,.list-group-item-secondary.list-group-item-action:hover{color:#464a4e;background-color:#cfd2d6}.list-group-item-secondary.list-group-item-action.active{color:#fff;background-color:#464a4e;border-color:#464a4e}.list-group-item-success{color:#155724;background-color:#c3e6cb}.list-group-item-success.list-group-item-action:focus,.list-group-item-success.list-group-item-action:hover{color:#155724;background-color:#b1dfbb}.list-group-item-success.list-group-item-action.active{color:#fff;background-color:#155724;border-color:#155724}.list-group-item-info{color:#0c5460;background-color:#bee5eb}.list-group-item-info.list-group-item-action:focus,.list-group-item-info.list-group-item-action:hover{color:#0c5460;background-color:#abdde5}.list-group-item-info.list-group-item-action.active{color:#fff;background-color:#0c5460;border-color:#0c5460}.list-group-item-warning{color:#856404;background-color:#ffeeba}.list-group-item-warning.list-group-item-action:focus,.list-group-item-warning.list-group-item-action:hover{color:#856404;background-color:#ffe8a1}.list-group-item-warning.list-group-item-action.active{color:#fff;background-color:#856404;border-color:#856404}.list-group-item-danger{color:#721c24;background-color:#f5c6cb}.list-group-item-danger.list-group-item-action:focus,.list-group-item-danger.list-group-item-action:hover{color:#721c24;background-color:#f1b0b7}.list-group-item-danger.list-group-item-action.active{color:#fff;background-color:#721c24;border-color:#721c24}.list-group-item-light{color:#818182;background-color:#fdfdfe}.list-group-item-light.list-group-item-action:focus,.list-group-item-light.list-group-item-action:hover{color:#818182;background-color:#ececf6}.list-group-item-light.list-group-item-action.active{color:#fff;background-color:#818182;border-color:#818182}.list-group-item-dark{color:#1b1e21;background-color:#c6c8ca}.list-group-item-dark.list-group-item-action:focus,.list-group-item-dark.list-group-item-action:hover{color:#1b1e21;background-color:#b9bbbe}.list-group-item-dark.list-group-item-action.active{color:#fff;background-color:#1b1e21;border-color:#1b1e21}.close{float:right;font-size:1.5rem;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;opacity:.5}.close:not(:disabled):not(.disabled){cursor:pointer}.close:not(:disabled):not(.disabled):focus,.close:not(:disabled):not(.disabled):hover{color:#000;text-decoration:none;opacity:.75}button.close{padding:0;background-color:transparent;border:0;-webkit-appearance:none}.modal-open{overflow:hidden}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;outline:0}.modal-dialog{position:relative;width:auto;margin:.5rem;pointer-events:none}.modal.fade .modal-dialog{transition:-webkit-transform .3s ease-out;transition:transform .3s ease-out;transition:transform .3s ease-out,-webkit-transform .3s ease-out;-webkit-transform:translateY(-25%);transform:translateY(-25%)}@media screen and (prefers-reduced-motion:reduce){.modal.fade .modal-dialog{transition:none}}.modal.show .modal-dialog{-webkit-transform:translate(0);transform:translate(0)}.modal-dialog-centered{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;min-height:calc(100% - 1rem)}.modal-dialog-centered:before{display:block;height:calc(100vh - 1rem);content:""}.modal-content{position:relative;display:-ms-flexbox;display:flex;-ms-flex-direction:column;flex-direction:column;width:100%;pointer-events:auto;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem;outline:0}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{opacity:0}.modal-backdrop.show{opacity:.5}.modal-header{display:-ms-flexbox;display:flex;-ms-flex-align:start;align-items:flex-start;-ms-flex-pack:justify;justify-content:space-between;padding:1rem;border-bottom:1px solid #e9ecef;border-top-left-radius:.3rem;border-top-right-radius:.3rem}.modal-header .close{padding:1rem;margin:-1rem -1rem -1rem auto}.modal-title{margin-bottom:0;line-height:1.5}.modal-body{position:relative;-ms-flex:1 1 auto;flex:1 1 auto;padding:1rem}.modal-footer{display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:end;justify-content:flex-end;padding:1rem;border-top:1px solid #e9ecef}.modal-footer>:not(:first-child){margin-left:.25rem}.modal-footer>:not(:last-child){margin-right:.25rem}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:576px){.modal-dialog{max-width:500px;margin:1.75rem auto}.modal-dialog-centered{min-height:calc(100% - 3.5rem)}.modal-dialog-centered:before{height:calc(100vh - 3.5rem)}.modal-sm{max-width:300px}}@media (min-width:992px){.modal-lg{max-width:800px}}.tooltip{position:absolute;z-index:1070;display:block;margin:0;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;opacity:0}.tooltip.show{opacity:.9}.tooltip .arrow{position:absolute;display:block;width:.8rem;height:.4rem}.tooltip .arrow:before{position:absolute;content:"";border-color:transparent;border-style:solid}.bs-tooltip-auto[x-placement^=top],.bs-tooltip-top{padding:.4rem 0}.bs-tooltip-auto[x-placement^=top] .arrow,.bs-tooltip-top .arrow{bottom:0}.bs-tooltip-auto[x-placement^=top] .arrow:before,.bs-tooltip-top .arrow:before{top:0;border-width:.4rem .4rem 0;border-top-color:#000}.bs-tooltip-auto[x-placement^=right],.bs-tooltip-right{padding:0 .4rem}.bs-tooltip-auto[x-placement^=right] .arrow,.bs-tooltip-right .arrow{left:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=right] .arrow:before,.bs-tooltip-right .arrow:before{right:0;border-width:.4rem .4rem .4rem 0;border-right-color:#000}.bs-tooltip-auto[x-placement^=bottom],.bs-tooltip-bottom{padding:.4rem 0}.bs-tooltip-auto[x-placement^=bottom] .arrow,.bs-tooltip-bottom .arrow{top:0}.bs-tooltip-auto[x-placement^=bottom] .arrow:before,.bs-tooltip-bottom .arrow:before{bottom:0;border-width:0 .4rem .4rem;border-bottom-color:#000}.bs-tooltip-auto[x-placement^=left],.bs-tooltip-left{padding:0 .4rem}.bs-tooltip-auto[x-placement^=left] .arrow,.bs-tooltip-left .arrow{right:0;width:.4rem;height:.8rem}.bs-tooltip-auto[x-placement^=left] .arrow:before,.bs-tooltip-left .arrow:before{left:0;border-width:.4rem 0 .4rem .4rem;border-left-color:#000}.tooltip-inner{max-width:200px;padding:.25rem .5rem;color:#fff;text-align:center;background-color:#000;border-radius:.25rem}.popover{top:0;left:0;z-index:1060;max-width:276px;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,sans-serif;font-style:normal;font-weight:400;line-height:1.5;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;white-space:normal;line-break:auto;font-size:.875rem;word-wrap:break-word;background-color:#fff;background-clip:padding-box;border:1px solid rgba(0,0,0,.2);border-radius:.3rem}.popover,.popover .arrow{position:absolute;display:block}.popover .arrow{width:1rem;height:.5rem;margin:0 .3rem}.popover .arrow:after,.popover .arrow:before{position:absolute;display:block;content:"";border-color:transparent;border-style:solid}.bs-popover-auto[x-placement^=top],.bs-popover-top{margin-bottom:.5rem}.bs-popover-auto[x-placement^=top] .arrow,.bs-popover-top .arrow{bottom:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=top] .arrow:after,.bs-popover-auto[x-placement^=top] .arrow:before,.bs-popover-top .arrow:after,.bs-popover-top .arrow:before{border-width:.5rem .5rem 0}.bs-popover-auto[x-placement^=top] .arrow:before,.bs-popover-top .arrow:before{bottom:0;border-top-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=top] .arrow:after,.bs-popover-top .arrow:after{bottom:1px;border-top-color:#fff}.bs-popover-auto[x-placement^=right],.bs-popover-right{margin-left:.5rem}.bs-popover-auto[x-placement^=right] .arrow,.bs-popover-right .arrow{left:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=right] .arrow:after,.bs-popover-auto[x-placement^=right] .arrow:before,.bs-popover-right .arrow:after,.bs-popover-right .arrow:before{border-width:.5rem .5rem .5rem 0}.bs-popover-auto[x-placement^=right] .arrow:before,.bs-popover-right .arrow:before{left:0;border-right-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=right] .arrow:after,.bs-popover-right .arrow:after{left:1px;border-right-color:#fff}.bs-popover-auto[x-placement^=bottom],.bs-popover-bottom{margin-top:.5rem}.bs-popover-auto[x-placement^=bottom] .arrow,.bs-popover-bottom .arrow{top:calc((.5rem + 1px) * -1)}.bs-popover-auto[x-placement^=bottom] .arrow:after,.bs-popover-auto[x-placement^=bottom] .arrow:before,.bs-popover-bottom .arrow:after,.bs-popover-bottom .arrow:before{border-width:0 .5rem .5rem}.bs-popover-auto[x-placement^=bottom] .arrow:before,.bs-popover-bottom .arrow:before{top:0;border-bottom-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=bottom] .arrow:after,.bs-popover-bottom .arrow:after{top:1px;border-bottom-color:#fff}.bs-popover-auto[x-placement^=bottom] .popover-header:before,.bs-popover-bottom .popover-header:before{position:absolute;top:0;left:50%;display:block;width:1rem;margin-left:-.5rem;content:"";border-bottom:1px solid #f7f7f7}.bs-popover-auto[x-placement^=left],.bs-popover-left{margin-right:.5rem}.bs-popover-auto[x-placement^=left] .arrow,.bs-popover-left .arrow{right:calc((.5rem + 1px) * -1);width:.5rem;height:1rem;margin:.3rem 0}.bs-popover-auto[x-placement^=left] .arrow:after,.bs-popover-auto[x-placement^=left] .arrow:before,.bs-popover-left .arrow:after,.bs-popover-left .arrow:before{border-width:.5rem 0 .5rem .5rem}.bs-popover-auto[x-placement^=left] .arrow:before,.bs-popover-left .arrow:before{right:0;border-left-color:rgba(0,0,0,.25)}.bs-popover-auto[x-placement^=left] .arrow:after,.bs-popover-left .arrow:after{right:1px;border-left-color:#fff}.popover-header{padding:.5rem .75rem;margin-bottom:0;font-size:1rem;color:inherit;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-top-left-radius:calc(.3rem - 1px);border-top-right-radius:calc(.3rem - 1px)}.popover-header:empty{display:none}.popover-body{padding:.5rem .75rem;color:#212529}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-item{position:relative;display:none;-ms-flex-align:center;align-items:center;width:100%;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-item-next,.carousel-item-prev,.carousel-item.active{display:block;transition:-webkit-transform .6s ease;transition:transform .6s ease;transition:transform .6s ease,-webkit-transform .6s ease}@media screen and (prefers-reduced-motion:reduce){.carousel-item-next,.carousel-item-prev,.carousel-item.active{transition:none}}.carousel-item-next,.carousel-item-prev{position:absolute;top:0}.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translateX(0);transform:translateX(0)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.carousel-item-next.carousel-item-left,.carousel-item-prev.carousel-item-right{-webkit-transform:translateZ(0);transform:translateZ(0)}}.active.carousel-item-right,.carousel-item-next{-webkit-transform:translateX(100%);transform:translateX(100%)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.active.carousel-item-right,.carousel-item-next{-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}}.active.carousel-item-left,.carousel-item-prev{-webkit-transform:translateX(-100%);transform:translateX(-100%)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.active.carousel-item-left,.carousel-item-prev{-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}}.carousel-fade .carousel-item{opacity:0;transition-duration:.6s;transition-property:opacity}.carousel-fade .carousel-item-next.carousel-item-left,.carousel-fade .carousel-item-prev.carousel-item-right,.carousel-fade .carousel-item.active{opacity:1}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-right{opacity:0}.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-prev,.carousel-fade .carousel-item-next,.carousel-fade .carousel-item-prev,.carousel-fade .carousel-item.active{-webkit-transform:translateX(0);transform:translateX(0)}@supports ((-webkit-transform-style:preserve-3d) or (transform-style:preserve-3d)){.carousel-fade .active.carousel-item-left,.carousel-fade .active.carousel-item-prev,.carousel-fade .carousel-item-next,.carousel-fade .carousel-item-prev,.carousel-fade .carousel-item.active{-webkit-transform:translateZ(0);transform:translateZ(0)}}.carousel-control-next,.carousel-control-prev{position:absolute;top:0;bottom:0;display:-ms-flexbox;display:flex;-ms-flex-align:center;align-items:center;-ms-flex-pack:center;justify-content:center;width:15%;color:#fff;text-align:center;opacity:.5}.carousel-control-next:focus,.carousel-control-next:hover,.carousel-control-prev:focus,.carousel-control-prev:hover{color:#fff;text-decoration:none;outline:0;opacity:.9}.carousel-control-prev{left:0}.carousel-control-next{right:0}.carousel-control-next-icon,.carousel-control-prev-icon{display:inline-block;width:20px;height:20px;background:transparent no-repeat 50%;background-size:100% 100%}.carousel-control-prev-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M5.25 0l-4 4 4 4 1.5-1.5-2.5-2.5 2.5-2.5-1.5-1.5z'/%3E%3C/svg%3E")}.carousel-control-next-icon{background-image:url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='%23fff' viewBox='0 0 8 8'%3E%3Cpath d='M2.75 0l-1.5 1.5 2.5 2.5-2.5 2.5 1.5 1.5 4-4-4-4z'/%3E%3C/svg%3E")}.carousel-indicators{position:absolute;right:0;bottom:10px;left:0;z-index:15;display:-ms-flexbox;display:flex;-ms-flex-pack:center;justify-content:center;padding-left:0;margin-right:15%;margin-left:15%;list-style:none}.carousel-indicators li{position:relative;-ms-flex:0 1 auto;flex:0 1 auto;width:30px;height:3px;margin-right:3px;margin-left:3px;text-indent:-999px;cursor:pointer;background-color:hsla(0,0%,100%,.5)}.carousel-indicators li:before{top:-10px}.carousel-indicators li:after,.carousel-indicators li:before{position:absolute;left:0;display:inline-block;width:100%;height:10px;content:""}.carousel-indicators li:after{bottom:-10px}.carousel-indicators .active{background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center}.align-baseline{vertical-align:baseline!important}.align-top{vertical-align:top!important}.align-middle{vertical-align:middle!important}.align-bottom{vertical-align:bottom!important}.align-text-bottom{vertical-align:text-bottom!important}.align-text-top{vertical-align:text-top!important}.bg-primary{background-color:#fed136!important}a.bg-primary:focus,a.bg-primary:hover,button.bg-primary:focus,button.bg-primary:hover{background-color:#fec503!important}.bg-secondary{background-color:#868e96!important}a.bg-secondary:focus,a.bg-secondary:hover,button.bg-secondary:focus,button.bg-secondary:hover{background-color:#6c757d!important}.bg-success{background-color:#28a745!important}a.bg-success:focus,a.bg-success:hover,button.bg-success:focus,button.bg-success:hover{background-color:#1e7e34!important}.bg-info{background-color:#17a2b8!important}a.bg-info:focus,a.bg-info:hover,button.bg-info:focus,button.bg-info:hover{background-color:#117a8b!important}.bg-warning{background-color:#ffc107!important}a.bg-warning:focus,a.bg-warning:hover,button.bg-warning:focus,button.bg-warning:hover{background-color:#d39e00!important}.bg-danger{background-color:#dc3545!important}a.bg-danger:focus,a.bg-danger:hover,button.bg-danger:focus,button.bg-danger:hover{background-color:#bd2130!important}.bg-light{background-color:#f8f9fa!important}a.bg-light:focus,a.bg-light:hover,button.bg-light:focus,button.bg-light:hover{background-color:#dae0e5!important}.bg-dark{background-color:#343a40!important}a.bg-dark:focus,a.bg-dark:hover,button.bg-dark:focus,button.bg-dark:hover{background-color:#1d2124!important}.bg-white{background-color:#fff!important}.bg-transparent{background-color:transparent!important}.border{border:1px solid #dee2e6!important}.border-top{border-top:1px solid #dee2e6!important}.border-right{border-right:1px solid #dee2e6!important}.border-bottom{border-bottom:1px solid #dee2e6!important}.border-left{border-left:1px solid #dee2e6!important}.border-0{border:0!important}.border-top-0{border-top:0!important}.border-right-0{border-right:0!important}.border-bottom-0{border-bottom:0!important}.border-left-0{border-left:0!important}.border-primary{border-color:#fed136!important}.border-secondary{border-color:#868e96!important}.border-success{border-color:#28a745!important}.border-info{border-color:#17a2b8!important}.border-warning{border-color:#ffc107!important}.border-danger{border-color:#dc3545!important}.border-light{border-color:#f8f9fa!important}.border-dark{border-color:#343a40!important}.border-white{border-color:#fff!important}.rounded{border-radius:.25rem!important}.rounded-top{border-top-left-radius:.25rem!important}.rounded-right,.rounded-top{border-top-right-radius:.25rem!important}.rounded-bottom,.rounded-right{border-bottom-right-radius:.25rem!important}.rounded-bottom,.rounded-left{border-bottom-left-radius:.25rem!important}.rounded-left{border-top-left-radius:.25rem!important}.rounded-circle{border-radius:50%!important}.rounded-0{border-radius:0!important}.clearfix:after{display:block;clear:both;content:""}.d-none{display:none!important}.d-inline{display:inline!important}.d-inline-block{display:inline-block!important}.d-block{display:block!important}.d-table{display:table!important}.d-table-row{display:table-row!important}.d-table-cell{display:table-cell!important}.d-flex{display:-ms-flexbox!important;display:flex!important}.d-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}@media (min-width:576px){.d-sm-none{display:none!important}.d-sm-inline{display:inline!important}.d-sm-inline-block{display:inline-block!important}.d-sm-block{display:block!important}.d-sm-table{display:table!important}.d-sm-table-row{display:table-row!important}.d-sm-table-cell{display:table-cell!important}.d-sm-flex{display:-ms-flexbox!important;display:flex!important}.d-sm-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:768px){.d-md-none{display:none!important}.d-md-inline{display:inline!important}.d-md-inline-block{display:inline-block!important}.d-md-block{display:block!important}.d-md-table{display:table!important}.d-md-table-row{display:table-row!important}.d-md-table-cell{display:table-cell!important}.d-md-flex{display:-ms-flexbox!important;display:flex!important}.d-md-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:992px){.d-lg-none{display:none!important}.d-lg-inline{display:inline!important}.d-lg-inline-block{display:inline-block!important}.d-lg-block{display:block!important}.d-lg-table{display:table!important}.d-lg-table-row{display:table-row!important}.d-lg-table-cell{display:table-cell!important}.d-lg-flex{display:-ms-flexbox!important;display:flex!important}.d-lg-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media (min-width:1200px){.d-xl-none{display:none!important}.d-xl-inline{display:inline!important}.d-xl-inline-block{display:inline-block!important}.d-xl-block{display:block!important}.d-xl-table{display:table!important}.d-xl-table-row{display:table-row!important}.d-xl-table-cell{display:table-cell!important}.d-xl-flex{display:-ms-flexbox!important;display:flex!important}.d-xl-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}@media print{.d-print-none{display:none!important}.d-print-inline{display:inline!important}.d-print-inline-block{display:inline-block!important}.d-print-block{display:block!important}.d-print-table{display:table!important}.d-print-table-row{display:table-row!important}.d-print-table-cell{display:table-cell!important}.d-print-flex{display:-ms-flexbox!important;display:flex!important}.d-print-inline-flex{display:-ms-inline-flexbox!important;display:inline-flex!important}}.embed-responsive{position:relative;display:block;width:100%;padding:0;overflow:hidden}.embed-responsive:before{display:block;content:""}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-21by9:before{padding-top:42.85714%}.embed-responsive-16by9:before{padding-top:56.25%}.embed-responsive-4by3:before{padding-top:75%}.embed-responsive-1by1:before{padding-top:100%}.flex-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-center{-ms-flex-align:center!important;align-items:center!important}.align-items-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}@media (min-width:576px){.flex-sm-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-sm-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-sm-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-sm-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-sm-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-sm-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-sm-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-sm-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-sm-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-sm-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-sm-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-sm-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-sm-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-sm-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-sm-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-sm-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-sm-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-sm-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-sm-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-sm-center{-ms-flex-align:center!important;align-items:center!important}.align-items-sm-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-sm-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-sm-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-sm-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-sm-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-sm-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-sm-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-sm-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-sm-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-sm-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-sm-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-sm-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-sm-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-sm-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:768px){.flex-md-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-md-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-md-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-md-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-md-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-md-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-md-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-md-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-md-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-md-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-md-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-md-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-md-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-md-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-md-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-md-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-md-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-md-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-md-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-md-center{-ms-flex-align:center!important;align-items:center!important}.align-items-md-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-md-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-md-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-md-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-md-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-md-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-md-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-md-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-md-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-md-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-md-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-md-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-md-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-md-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:992px){.flex-lg-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-lg-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-lg-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-lg-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-lg-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-lg-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-lg-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-lg-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-lg-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-lg-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-lg-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-lg-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-lg-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-lg-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-lg-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-lg-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-lg-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-lg-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-lg-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-lg-center{-ms-flex-align:center!important;align-items:center!important}.align-items-lg-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-lg-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-lg-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-lg-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-lg-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-lg-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-lg-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-lg-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-lg-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-lg-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-lg-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-lg-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-lg-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-lg-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}@media (min-width:1200px){.flex-xl-row{-ms-flex-direction:row!important;flex-direction:row!important}.flex-xl-column{-ms-flex-direction:column!important;flex-direction:column!important}.flex-xl-row-reverse{-ms-flex-direction:row-reverse!important;flex-direction:row-reverse!important}.flex-xl-column-reverse{-ms-flex-direction:column-reverse!important;flex-direction:column-reverse!important}.flex-xl-wrap{-ms-flex-wrap:wrap!important;flex-wrap:wrap!important}.flex-xl-nowrap{-ms-flex-wrap:nowrap!important;flex-wrap:nowrap!important}.flex-xl-wrap-reverse{-ms-flex-wrap:wrap-reverse!important;flex-wrap:wrap-reverse!important}.flex-xl-fill{-ms-flex:1 1 auto!important;flex:1 1 auto!important}.flex-xl-grow-0{-ms-flex-positive:0!important;flex-grow:0!important}.flex-xl-grow-1{-ms-flex-positive:1!important;flex-grow:1!important}.flex-xl-shrink-0{-ms-flex-negative:0!important;flex-shrink:0!important}.flex-xl-shrink-1{-ms-flex-negative:1!important;flex-shrink:1!important}.justify-content-xl-start{-ms-flex-pack:start!important;justify-content:flex-start!important}.justify-content-xl-end{-ms-flex-pack:end!important;justify-content:flex-end!important}.justify-content-xl-center{-ms-flex-pack:center!important;justify-content:center!important}.justify-content-xl-between{-ms-flex-pack:justify!important;justify-content:space-between!important}.justify-content-xl-around{-ms-flex-pack:distribute!important;justify-content:space-around!important}.align-items-xl-start{-ms-flex-align:start!important;align-items:flex-start!important}.align-items-xl-end{-ms-flex-align:end!important;align-items:flex-end!important}.align-items-xl-center{-ms-flex-align:center!important;align-items:center!important}.align-items-xl-baseline{-ms-flex-align:baseline!important;align-items:baseline!important}.align-items-xl-stretch{-ms-flex-align:stretch!important;align-items:stretch!important}.align-content-xl-start{-ms-flex-line-pack:start!important;align-content:flex-start!important}.align-content-xl-end{-ms-flex-line-pack:end!important;align-content:flex-end!important}.align-content-xl-center{-ms-flex-line-pack:center!important;align-content:center!important}.align-content-xl-between{-ms-flex-line-pack:justify!important;align-content:space-between!important}.align-content-xl-around{-ms-flex-line-pack:distribute!important;align-content:space-around!important}.align-content-xl-stretch{-ms-flex-line-pack:stretch!important;align-content:stretch!important}.align-self-xl-auto{-ms-flex-item-align:auto!important;align-self:auto!important}.align-self-xl-start{-ms-flex-item-align:start!important;align-self:flex-start!important}.align-self-xl-end{-ms-flex-item-align:end!important;align-self:flex-end!important}.align-self-xl-center{-ms-flex-item-align:center!important;align-self:center!important}.align-self-xl-baseline{-ms-flex-item-align:baseline!important;align-self:baseline!important}.align-self-xl-stretch{-ms-flex-item-align:stretch!important;align-self:stretch!important}}.float-left{float:left!important}.float-right{float:right!important}.float-none{float:none!important}@media (min-width:576px){.float-sm-left{float:left!important}.float-sm-right{float:right!important}.float-sm-none{float:none!important}}@media (min-width:768px){.float-md-left{float:left!important}.float-md-right{float:right!important}.float-md-none{float:none!important}}@media (min-width:992px){.float-lg-left{float:left!important}.float-lg-right{float:right!important}.float-lg-none{float:none!important}}@media (min-width:1200px){.float-xl-left{float:left!important}.float-xl-right{float:right!important}.float-xl-none{float:none!important}}.position-static{position:static!important}.position-relative{position:relative!important}.position-absolute{position:absolute!important}.position-fixed{position:fixed!important}.position-sticky{position:-webkit-sticky!important;position:sticky!important}.fixed-top{top:0}.fixed-bottom,.fixed-top{position:fixed;right:0;left:0;z-index:1030}.fixed-bottom{bottom:0}@supports ((position:-webkit-sticky) or (position:sticky)){.sticky-top{position:-webkit-sticky;position:sticky;top:0;z-index:1020}}.sr-only{position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal}.shadow-sm{box-shadow:0 .125rem .25rem rgba(0,0,0,.075)!important}.shadow{box-shadow:0 .5rem 1rem rgba(0,0,0,.15)!important}.shadow-lg{box-shadow:0 1rem 3rem rgba(0,0,0,.175)!important}.shadow-none{box-shadow:none!important}.w-25{width:25%!important}.w-50{width:50%!important}.w-75{width:75%!important}.w-100{width:100%!important}.w-auto{width:auto!important}.h-25{height:25%!important}.h-50{height:50%!important}.h-75{height:75%!important}.h-100{height:100%!important}.h-auto{height:auto!important}.mw-100{max-width:100%!important}.mh-100{max-height:100%!important}.m-0{margin:0!important}.mt-0,.my-0{margin-top:0!important}.mr-0,.mx-0{margin-right:0!important}.mb-0,.my-0{margin-bottom:0!important}.ml-0,.mx-0{margin-left:0!important}.m-1{margin:.25rem!important}.mt-1,.my-1{margin-top:.25rem!important}.mr-1,.mx-1{margin-right:.25rem!important}.mb-1,.my-1{margin-bottom:.25rem!important}.ml-1,.mx-1{margin-left:.25rem!important}.m-2{margin:.5rem!important}.mt-2,.my-2{margin-top:.5rem!important}.mr-2,.mx-2{margin-right:.5rem!important}.mb-2,.my-2{margin-bottom:.5rem!important}.ml-2,.mx-2{margin-left:.5rem!important}.m-3{margin:1rem!important}.mt-3,.my-3{margin-top:1rem!important}.mr-3,.mx-3{margin-right:1rem!important}.mb-3,.my-3{margin-bottom:1rem!important}.ml-3,.mx-3{margin-left:1rem!important}.m-4{margin:1.5rem!important}.mt-4,.my-4{margin-top:1.5rem!important}.mr-4,.mx-4{margin-right:1.5rem!important}.mb-4,.my-4{margin-bottom:1.5rem!important}.ml-4,.mx-4{margin-left:1.5rem!important}.m-5{margin:3rem!important}.mt-5,.my-5{margin-top:3rem!important}.mr-5,.mx-5{margin-right:3rem!important}.mb-5,.my-5{margin-bottom:3rem!important}.ml-5,.mx-5{margin-left:3rem!important}.p-0{padding:0!important}.pt-0,.py-0{padding-top:0!important}.pr-0,.px-0{padding-right:0!important}.pb-0,.py-0{padding-bottom:0!important}.pl-0,.px-0{padding-left:0!important}.p-1{padding:.25rem!important}.pt-1,.py-1{padding-top:.25rem!important}.pr-1,.px-1{padding-right:.25rem!important}.pb-1,.py-1{padding-bottom:.25rem!important}.pl-1,.px-1{padding-left:.25rem!important}.p-2{padding:.5rem!important}.pt-2,.py-2{padding-top:.5rem!important}.pr-2,.px-2{padding-right:.5rem!important}.pb-2,.py-2{padding-bottom:.5rem!important}.pl-2,.px-2{padding-left:.5rem!important}.p-3{padding:1rem!important}.pt-3,.py-3{padding-top:1rem!important}.pr-3,.px-3{padding-right:1rem!important}.pb-3,.py-3{padding-bottom:1rem!important}.pl-3,.px-3{padding-left:1rem!important}.p-4{padding:1.5rem!important}.pt-4,.py-4{padding-top:1.5rem!important}.pr-4,.px-4{padding-right:1.5rem!important}.pb-4,.py-4{padding-bottom:1.5rem!important}.pl-4,.px-4{padding-left:1.5rem!important}.p-5{padding:3rem!important}.pt-5,.py-5{padding-top:3rem!important}.pr-5,.px-5{padding-right:3rem!important}.pb-5,.py-5{padding-bottom:3rem!important}.pl-5,.px-5{padding-left:3rem!important}.m-auto{margin:auto!important}.mt-auto,.my-auto{margin-top:auto!important}.mr-auto,.mx-auto{margin-right:auto!important}.mb-auto,.my-auto{margin-bottom:auto!important}.ml-auto,.mx-auto{margin-left:auto!important}@media (min-width:576px){.m-sm-0{margin:0!important}.mt-sm-0,.my-sm-0{margin-top:0!important}.mr-sm-0,.mx-sm-0{margin-right:0!important}.mb-sm-0,.my-sm-0{margin-bottom:0!important}.ml-sm-0,.mx-sm-0{margin-left:0!important}.m-sm-1{margin:.25rem!important}.mt-sm-1,.my-sm-1{margin-top:.25rem!important}.mr-sm-1,.mx-sm-1{margin-right:.25rem!important}.mb-sm-1,.my-sm-1{margin-bottom:.25rem!important}.ml-sm-1,.mx-sm-1{margin-left:.25rem!important}.m-sm-2{margin:.5rem!important}.mt-sm-2,.my-sm-2{margin-top:.5rem!important}.mr-sm-2,.mx-sm-2{margin-right:.5rem!important}.mb-sm-2,.my-sm-2{margin-bottom:.5rem!important}.ml-sm-2,.mx-sm-2{margin-left:.5rem!important}.m-sm-3{margin:1rem!important}.mt-sm-3,.my-sm-3{margin-top:1rem!important}.mr-sm-3,.mx-sm-3{margin-right:1rem!important}.mb-sm-3,.my-sm-3{margin-bottom:1rem!important}.ml-sm-3,.mx-sm-3{margin-left:1rem!important}.m-sm-4{margin:1.5rem!important}.mt-sm-4,.my-sm-4{margin-top:1.5rem!important}.mr-sm-4,.mx-sm-4{margin-right:1.5rem!important}.mb-sm-4,.my-sm-4{margin-bottom:1.5rem!important}.ml-sm-4,.mx-sm-4{margin-left:1.5rem!important}.m-sm-5{margin:3rem!important}.mt-sm-5,.my-sm-5{margin-top:3rem!important}.mr-sm-5,.mx-sm-5{margin-right:3rem!important}.mb-sm-5,.my-sm-5{margin-bottom:3rem!important}.ml-sm-5,.mx-sm-5{margin-left:3rem!important}.p-sm-0{padding:0!important}.pt-sm-0,.py-sm-0{padding-top:0!important}.pr-sm-0,.px-sm-0{padding-right:0!important}.pb-sm-0,.py-sm-0{padding-bottom:0!important}.pl-sm-0,.px-sm-0{padding-left:0!important}.p-sm-1{padding:.25rem!important}.pt-sm-1,.py-sm-1{padding-top:.25rem!important}.pr-sm-1,.px-sm-1{padding-right:.25rem!important}.pb-sm-1,.py-sm-1{padding-bottom:.25rem!important}.pl-sm-1,.px-sm-1{padding-left:.25rem!important}.p-sm-2{padding:.5rem!important}.pt-sm-2,.py-sm-2{padding-top:.5rem!important}.pr-sm-2,.px-sm-2{padding-right:.5rem!important}.pb-sm-2,.py-sm-2{padding-bottom:.5rem!important}.pl-sm-2,.px-sm-2{padding-left:.5rem!important}.p-sm-3{padding:1rem!important}.pt-sm-3,.py-sm-3{padding-top:1rem!important}.pr-sm-3,.px-sm-3{padding-right:1rem!important}.pb-sm-3,.py-sm-3{padding-bottom:1rem!important}.pl-sm-3,.px-sm-3{padding-left:1rem!important}.p-sm-4{padding:1.5rem!important}.pt-sm-4,.py-sm-4{padding-top:1.5rem!important}.pr-sm-4,.px-sm-4{padding-right:1.5rem!important}.pb-sm-4,.py-sm-4{padding-bottom:1.5rem!important}.pl-sm-4,.px-sm-4{padding-left:1.5rem!important}.p-sm-5{padding:3rem!important}.pt-sm-5,.py-sm-5{padding-top:3rem!important}.pr-sm-5,.px-sm-5{padding-right:3rem!important}.pb-sm-5,.py-sm-5{padding-bottom:3rem!important}.pl-sm-5,.px-sm-5{padding-left:3rem!important}.m-sm-auto{margin:auto!important}.mt-sm-auto,.my-sm-auto{margin-top:auto!important}.mr-sm-auto,.mx-sm-auto{margin-right:auto!important}.mb-sm-auto,.my-sm-auto{margin-bottom:auto!important}.ml-sm-auto,.mx-sm-auto{margin-left:auto!important}}@media (min-width:768px){.m-md-0{margin:0!important}.mt-md-0,.my-md-0{margin-top:0!important}.mr-md-0,.mx-md-0{margin-right:0!important}.mb-md-0,.my-md-0{margin-bottom:0!important}.ml-md-0,.mx-md-0{margin-left:0!important}.m-md-1{margin:.25rem!important}.mt-md-1,.my-md-1{margin-top:.25rem!important}.mr-md-1,.mx-md-1{margin-right:.25rem!important}.mb-md-1,.my-md-1{margin-bottom:.25rem!important}.ml-md-1,.mx-md-1{margin-left:.25rem!important}.m-md-2{margin:.5rem!important}.mt-md-2,.my-md-2{margin-top:.5rem!important}.mr-md-2,.mx-md-2{margin-right:.5rem!important}.mb-md-2,.my-md-2{margin-bottom:.5rem!important}.ml-md-2,.mx-md-2{margin-left:.5rem!important}.m-md-3{margin:1rem!important}.mt-md-3,.my-md-3{margin-top:1rem!important}.mr-md-3,.mx-md-3{margin-right:1rem!important}.mb-md-3,.my-md-3{margin-bottom:1rem!important}.ml-md-3,.mx-md-3{margin-left:1rem!important}.m-md-4{margin:1.5rem!important}.mt-md-4,.my-md-4{margin-top:1.5rem!important}.mr-md-4,.mx-md-4{margin-right:1.5rem!important}.mb-md-4,.my-md-4{margin-bottom:1.5rem!important}.ml-md-4,.mx-md-4{margin-left:1.5rem!important}.m-md-5{margin:3rem!important}.mt-md-5,.my-md-5{margin-top:3rem!important}.mr-md-5,.mx-md-5{margin-right:3rem!important}.mb-md-5,.my-md-5{margin-bottom:3rem!important}.ml-md-5,.mx-md-5{margin-left:3rem!important}.p-md-0{padding:0!important}.pt-md-0,.py-md-0{padding-top:0!important}.pr-md-0,.px-md-0{padding-right:0!important}.pb-md-0,.py-md-0{padding-bottom:0!important}.pl-md-0,.px-md-0{padding-left:0!important}.p-md-1{padding:.25rem!important}.pt-md-1,.py-md-1{padding-top:.25rem!important}.pr-md-1,.px-md-1{padding-right:.25rem!important}.pb-md-1,.py-md-1{padding-bottom:.25rem!important}.pl-md-1,.px-md-1{padding-left:.25rem!important}.p-md-2{padding:.5rem!important}.pt-md-2,.py-md-2{padding-top:.5rem!important}.pr-md-2,.px-md-2{padding-right:.5rem!important}.pb-md-2,.py-md-2{padding-bottom:.5rem!important}.pl-md-2,.px-md-2{padding-left:.5rem!important}.p-md-3{padding:1rem!important}.pt-md-3,.py-md-3{padding-top:1rem!important}.pr-md-3,.px-md-3{padding-right:1rem!important}.pb-md-3,.py-md-3{padding-bottom:1rem!important}.pl-md-3,.px-md-3{padding-left:1rem!important}.p-md-4{padding:1.5rem!important}.pt-md-4,.py-md-4{padding-top:1.5rem!important}.pr-md-4,.px-md-4{padding-right:1.5rem!important}.pb-md-4,.py-md-4{padding-bottom:1.5rem!important}.pl-md-4,.px-md-4{padding-left:1.5rem!important}.p-md-5{padding:3rem!important}.pt-md-5,.py-md-5{padding-top:3rem!important}.pr-md-5,.px-md-5{padding-right:3rem!important}.pb-md-5,.py-md-5{padding-bottom:3rem!important}.pl-md-5,.px-md-5{padding-left:3rem!important}.m-md-auto{margin:auto!important}.mt-md-auto,.my-md-auto{margin-top:auto!important}.mr-md-auto,.mx-md-auto{margin-right:auto!important}.mb-md-auto,.my-md-auto{margin-bottom:auto!important}.ml-md-auto,.mx-md-auto{margin-left:auto!important}}@media (min-width:992px){.m-lg-0{margin:0!important}.mt-lg-0,.my-lg-0{margin-top:0!important}.mr-lg-0,.mx-lg-0{margin-right:0!important}.mb-lg-0,.my-lg-0{margin-bottom:0!important}.ml-lg-0,.mx-lg-0{margin-left:0!important}.m-lg-1{margin:.25rem!important}.mt-lg-1,.my-lg-1{margin-top:.25rem!important}.mr-lg-1,.mx-lg-1{margin-right:.25rem!important}.mb-lg-1,.my-lg-1{margin-bottom:.25rem!important}.ml-lg-1,.mx-lg-1{margin-left:.25rem!important}.m-lg-2{margin:.5rem!important}.mt-lg-2,.my-lg-2{margin-top:.5rem!important}.mr-lg-2,.mx-lg-2{margin-right:.5rem!important}.mb-lg-2,.my-lg-2{margin-bottom:.5rem!important}.ml-lg-2,.mx-lg-2{margin-left:.5rem!important}.m-lg-3{margin:1rem!important}.mt-lg-3,.my-lg-3{margin-top:1rem!important}.mr-lg-3,.mx-lg-3{margin-right:1rem!important}.mb-lg-3,.my-lg-3{margin-bottom:1rem!important}.ml-lg-3,.mx-lg-3{margin-left:1rem!important}.m-lg-4{margin:1.5rem!important}.mt-lg-4,.my-lg-4{margin-top:1.5rem!important}.mr-lg-4,.mx-lg-4{margin-right:1.5rem!important}.mb-lg-4,.my-lg-4{margin-bottom:1.5rem!important}.ml-lg-4,.mx-lg-4{margin-left:1.5rem!important}.m-lg-5{margin:3rem!important}.mt-lg-5,.my-lg-5{margin-top:3rem!important}.mr-lg-5,.mx-lg-5{margin-right:3rem!important}.mb-lg-5,.my-lg-5{margin-bottom:3rem!important}.ml-lg-5,.mx-lg-5{margin-left:3rem!important}.p-lg-0{padding:0!important}.pt-lg-0,.py-lg-0{padding-top:0!important}.pr-lg-0,.px-lg-0{padding-right:0!important}.pb-lg-0,.py-lg-0{padding-bottom:0!important}.pl-lg-0,.px-lg-0{padding-left:0!important}.p-lg-1{padding:.25rem!important}.pt-lg-1,.py-lg-1{padding-top:.25rem!important}.pr-lg-1,.px-lg-1{padding-right:.25rem!important}.pb-lg-1,.py-lg-1{padding-bottom:.25rem!important}.pl-lg-1,.px-lg-1{padding-left:.25rem!important}.p-lg-2{padding:.5rem!important}.pt-lg-2,.py-lg-2{padding-top:.5rem!important}.pr-lg-2,.px-lg-2{padding-right:.5rem!important}.pb-lg-2,.py-lg-2{padding-bottom:.5rem!important}.pl-lg-2,.px-lg-2{padding-left:.5rem!important}.p-lg-3{padding:1rem!important}.pt-lg-3,.py-lg-3{padding-top:1rem!important}.pr-lg-3,.px-lg-3{padding-right:1rem!important}.pb-lg-3,.py-lg-3{padding-bottom:1rem!important}.pl-lg-3,.px-lg-3{padding-left:1rem!important}.p-lg-4{padding:1.5rem!important}.pt-lg-4,.py-lg-4{padding-top:1.5rem!important}.pr-lg-4,.px-lg-4{padding-right:1.5rem!important}.pb-lg-4,.py-lg-4{padding-bottom:1.5rem!important}.pl-lg-4,.px-lg-4{padding-left:1.5rem!important}.p-lg-5{padding:3rem!important}.pt-lg-5,.py-lg-5{padding-top:3rem!important}.pr-lg-5,.px-lg-5{padding-right:3rem!important}.pb-lg-5,.py-lg-5{padding-bottom:3rem!important}.pl-lg-5,.px-lg-5{padding-left:3rem!important}.m-lg-auto{margin:auto!important}.mt-lg-auto,.my-lg-auto{margin-top:auto!important}.mr-lg-auto,.mx-lg-auto{margin-right:auto!important}.mb-lg-auto,.my-lg-auto{margin-bottom:auto!important}.ml-lg-auto,.mx-lg-auto{margin-left:auto!important}}@media (min-width:1200px){.m-xl-0{margin:0!important}.mt-xl-0,.my-xl-0{margin-top:0!important}.mr-xl-0,.mx-xl-0{margin-right:0!important}.mb-xl-0,.my-xl-0{margin-bottom:0!important}.ml-xl-0,.mx-xl-0{margin-left:0!important}.m-xl-1{margin:.25rem!important}.mt-xl-1,.my-xl-1{margin-top:.25rem!important}.mr-xl-1,.mx-xl-1{margin-right:.25rem!important}.mb-xl-1,.my-xl-1{margin-bottom:.25rem!important}.ml-xl-1,.mx-xl-1{margin-left:.25rem!important}.m-xl-2{margin:.5rem!important}.mt-xl-2,.my-xl-2{margin-top:.5rem!important}.mr-xl-2,.mx-xl-2{margin-right:.5rem!important}.mb-xl-2,.my-xl-2{margin-bottom:.5rem!important}.ml-xl-2,.mx-xl-2{margin-left:.5rem!important}.m-xl-3{margin:1rem!important}.mt-xl-3,.my-xl-3{margin-top:1rem!important}.mr-xl-3,.mx-xl-3{margin-right:1rem!important}.mb-xl-3,.my-xl-3{margin-bottom:1rem!important}.ml-xl-3,.mx-xl-3{margin-left:1rem!important}.m-xl-4{margin:1.5rem!important}.mt-xl-4,.my-xl-4{margin-top:1.5rem!important}.mr-xl-4,.mx-xl-4{margin-right:1.5rem!important}.mb-xl-4,.my-xl-4{margin-bottom:1.5rem!important}.ml-xl-4,.mx-xl-4{margin-left:1.5rem!important}.m-xl-5{margin:3rem!important}.mt-xl-5,.my-xl-5{margin-top:3rem!important}.mr-xl-5,.mx-xl-5{margin-right:3rem!important}.mb-xl-5,.my-xl-5{margin-bottom:3rem!important}.ml-xl-5,.mx-xl-5{margin-left:3rem!important}.p-xl-0{padding:0!important}.pt-xl-0,.py-xl-0{padding-top:0!important}.pr-xl-0,.px-xl-0{padding-right:0!important}.pb-xl-0,.py-xl-0{padding-bottom:0!important}.pl-xl-0,.px-xl-0{padding-left:0!important}.p-xl-1{padding:.25rem!important}.pt-xl-1,.py-xl-1{padding-top:.25rem!important}.pr-xl-1,.px-xl-1{padding-right:.25rem!important}.pb-xl-1,.py-xl-1{padding-bottom:.25rem!important}.pl-xl-1,.px-xl-1{padding-left:.25rem!important}.p-xl-2{padding:.5rem!important}.pt-xl-2,.py-xl-2{padding-top:.5rem!important}.pr-xl-2,.px-xl-2{padding-right:.5rem!important}.pb-xl-2,.py-xl-2{padding-bottom:.5rem!important}.pl-xl-2,.px-xl-2{padding-left:.5rem!important}.p-xl-3{padding:1rem!important}.pt-xl-3,.py-xl-3{padding-top:1rem!important}.pr-xl-3,.px-xl-3{padding-right:1rem!important}.pb-xl-3,.py-xl-3{padding-bottom:1rem!important}.pl-xl-3,.px-xl-3{padding-left:1rem!important}.p-xl-4{padding:1.5rem!important}.pt-xl-4,.py-xl-4{padding-top:1.5rem!important}.pr-xl-4,.px-xl-4{padding-right:1.5rem!important}.pb-xl-4,.py-xl-4{padding-bottom:1.5rem!important}.pl-xl-4,.px-xl-4{padding-left:1.5rem!important}.p-xl-5{padding:3rem!important}.pt-xl-5,.py-xl-5{padding-top:3rem!important}.pr-xl-5,.px-xl-5{padding-right:3rem!important}.pb-xl-5,.py-xl-5{padding-bottom:3rem!important}.pl-xl-5,.px-xl-5{padding-left:3rem!important}.m-xl-auto{margin:auto!important}.mt-xl-auto,.my-xl-auto{margin-top:auto!important}.mr-xl-auto,.mx-xl-auto{margin-right:auto!important}.mb-xl-auto,.my-xl-auto{margin-bottom:auto!important}.ml-xl-auto,.mx-xl-auto{margin-left:auto!important}}.text-monospace{font-family:SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}.text-justify{text-align:justify!important}.text-nowrap{white-space:nowrap!important}.text-truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.text-left{text-align:left!important}.text-right{text-align:right!important}.text-center{text-align:center!important}@media (min-width:576px){.text-sm-left{text-align:left!important}.text-sm-right{text-align:right!important}.text-sm-center{text-align:center!important}}@media (min-width:768px){.text-md-left{text-align:left!important}.text-md-right{text-align:right!important}.text-md-center{text-align:center!important}}@media (min-width:992px){.text-lg-left{text-align:left!important}.text-lg-right{text-align:right!important}.text-lg-center{text-align:center!important}}@media (min-width:1200px){.text-xl-left{text-align:left!important}.text-xl-right{text-align:right!important}.text-xl-center{text-align:center!important}}.text-lowercase{text-transform:lowercase!important}.text-uppercase{text-transform:uppercase!important}.text-capitalize{text-transform:capitalize!important}.font-weight-light{font-weight:300!important}.font-weight-normal{font-weight:400!important}.font-weight-bold{font-weight:700!important}.font-italic{font-style:italic!important}.text-white{color:#fff!important}a.text-primary:focus,a.text-primary:hover{color:#fec503!important}.text-secondary{color:#868e96!important}a.text-secondary:focus,a.text-secondary:hover{color:#6c757d!important}.text-success{color:#28a745!important}a.text-success:focus,a.text-success:hover{color:#1e7e34!important}.text-info{color:#17a2b8!important}a.text-info:focus,a.text-info:hover{color:#117a8b!important}.text-warning{color:#ffc107!important}a.text-warning:focus,a.text-warning:hover{color:#d39e00!important}.text-danger{color:#dc3545!important}a.text-danger:focus,a.text-danger:hover{color:#bd2130!important}.text-light{color:#f8f9fa!important}a.text-light:focus,a.text-light:hover{color:#dae0e5!important}.text-dark{color:#343a40!important}a.text-dark:focus,a.text-dark:hover{color:#1d2124!important}.text-body{color:#212529!important}.text-muted{color:#868e96!important}.text-black-50{color:rgba(0,0,0,.5)!important}.text-white-50{color:hsla(0,0%,100%,.5)!important}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.visible{visibility:visible!important}.invisible{visibility:hidden!important}@media print{*,:after,:before{text-shadow:none!important;box-shadow:none!important}a:not(.btn){text-decoration:underline}abbr[title]:after{content:" (" attr(title) ")"}pre{white-space:pre-wrap!important}blockquote,pre{border:1px solid #adb5bd;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}@page{size:a3}.container,body{min-width:992px!important}.navbar{display:none}.badge{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #dee2e6!important}.table-dark{color:inherit}.table-dark tbody+tbody,.table-dark td,.table-dark th,.table-dark thead th{border-color:#dee2e6}.table .thead-dark th{color:inherit;border-color:#dee2e6}}#mainNav{background-color:#212529}#mainNav .navbar-toggler{font-size:12px;right:0;padding:13px;text-transform:uppercase;color:#fff;border:0;background-color:#fed136;font-family:Montserrat,Helvetica Neue,Helvetica,Arial,sans-serif}#mainNav .navbar-brand{color:#fed136;font-family:Kaushan Script,Helvetica Neue,Helvetica,Arial,cursive}#mainNav .navbar-brand.active,#mainNav .navbar-brand:active,#mainNav .navbar-brand:focus,#mainNav .navbar-brand:hover{color:#fec503}#mainNav .navbar-nav .nav-item .nav-link{font-size:90%;font-weight:400;padding:.75em 0;letter-spacing:1px;color:#fff;font-family:Montserrat,Helvetica Neue,Helvetica,Arial,sans-serif}#mainNav .navbar-nav .nav-item .nav-link.active,#mainNav .navbar-nav .nav-item .nav-link:hover{color:#fed136}@media (min-width:992px){#mainNav{padding-top:25px;padding-bottom:25px;transition:padding-top .3s,padding-bottom .3s;border:none;background-color:transparent}#mainNav .navbar-brand{font-size:1.75em;transition:all .3s}#mainNav .navbar-nav .nav-item .nav-link{padding:1.1em 1em!important}#mainNav.navbar-shrink{padding-top:0;padding-bottom:0;background-color:#212529}#mainNav.navbar-shrink .navbar-brand{font-size:1.25em;padding:12px 0}}header.masthead{text-align:center;color:#fff;background-repeat:no-repeat;background-attachment:scroll;background-position:50%;background-size:cover}header.masthead .intro-text{padding-top:150px;padding-bottom:100px}header.masthead .intro-text .intro-lead-in{font-size:22px;font-style:italic;line-height:22px;margin-bottom:25px;font-family:Droid Serif,Helvetica Neue,Helvetica,Arial,sans-serif}header.masthead .intro-text .intro-heading{font-size:50px;font-weight:700;line-height:50px;margin-bottom:25px;font-family:Montserrat,Helvetica Neue,Helvetica,Arial,sans-serif}@media (min-width:768px){header.masthead .intro-text{padding-top:300px;padding-bottom:200px}header.masthead .intro-text .intro-lead-in{font-size:40px;font-style:italic;line-height:40px;margin-bottom:25px;font-family:Droid Serif,Helvetica Neue,Helvetica,Arial,sans-serif}header.masthead .intro-text .intro-heading{font-size:75px;font-weight:700;line-height:75px;margin-bottom:50px;font-family:Montserrat,Helvetica Neue,Helvetica,Arial,sans-serif}}.service-heading{margin:15px 0;text-transform:none}#portfolio .portfolio-item{right:0;margin:0 0 15px}#portfolio .portfolio-item .portfolio-link{position:relative;display:block;max-width:400px;margin:0 auto;cursor:pointer}#portfolio .portfolio-item .portfolio-link .portfolio-hover{position:absolute;width:100%;height:100%;transition:all .5s ease;opacity:0;background:rgba(254,209,54,.9)}#portfolio .portfolio-item .portfolio-link .portfolio-hover:hover{opacity:1}#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content{font-size:20px;position:absolute;top:50%;width:100%;height:20px;margin-top:-12px;text-align:center;color:#fff}#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content i{margin-top:-12px}#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content h3,#portfolio .portfolio-item .portfolio-link .portfolio-hover .portfolio-hover-content h4{margin:0}#portfolio .portfolio-item .portfolio-caption{max-width:400px;margin:0 auto;padding:25px;text-align:center;background-color:#fff}#portfolio .portfolio-item .portfolio-caption h4{margin:0;text-transform:none}#portfolio .portfolio-item .portfolio-caption p{font-size:16px;font-style:italic;margin:0;font-family:Droid Serif,Helvetica Neue,Helvetica,Arial,sans-serif}#portfolio *{z-index:2}@media (min-width:767px){#portfolio .portfolio-item{margin:0 0 30px}}.portfolio-modal{padding-right:0!important}.portfolio-modal .modal-dialog{margin:1rem;max-width:100vw}.portfolio-modal .modal-content{padding:100px 0;text-align:center}.portfolio-modal .modal-content h2{font-size:3em;margin-bottom:15px}.portfolio-modal .modal-content p{margin-bottom:30px}.portfolio-modal .modal-content p.item-intro{font-size:16px;font-style:italic;margin:20px 0 30px;font-family:Droid Serif,Helvetica Neue,Helvetica,Arial,sans-serif}.portfolio-modal .modal-content ul.list-inline{margin-top:0;margin-bottom:30px}.portfolio-modal .modal-content img{margin-bottom:30px}.portfolio-modal .modal-content button{cursor:pointer}.portfolio-modal .close-modal{position:absolute;top:25px;right:25px;width:75px;height:75px;cursor:pointer;background-color:transparent}.portfolio-modal .close-modal:hover{opacity:.3}.portfolio-modal .close-modal .lr{z-index:1051;width:1px;height:75px;margin-left:35px;-webkit-transform:rotate(45deg);transform:rotate(45deg);background-color:#212529}.portfolio-modal .close-modal .lr .rl{z-index:1052;width:1px;height:75px;-webkit-transform:rotate(90deg);transform:rotate(90deg);background-color:#212529}.timeline{position:relative;padding:0;list-style:none}.timeline:before{position:absolute;top:0;bottom:0;left:40px;width:2px;margin-left:-1.5px;content:"";background-color:#e9ecef}.timeline>li{position:relative;min-height:50px;margin-bottom:50px}.timeline>li:after,.timeline>li:before{display:table;content:" "}.timeline>li:after{clear:both}.timeline>li .timeline-panel{position:relative;float:right;width:100%;padding:0 20px 0 100px;text-align:left}.timeline>li .timeline-panel:before{right:auto;left:-15px;border-right-width:15px;border-left-width:0}.timeline>li .timeline-panel:after{right:auto;left:-14px;border-right-width:14px;border-left-width:0}.timeline>li .timeline-image{position:absolute;z-index:100;left:0;width:80px;height:80px;margin-left:0;text-align:center;color:#fff;border:7px solid #e9ecef;border-radius:100%;background-color:#fed136}.timeline>li .timeline-image h4{font-size:10px;line-height:14px;margin-top:12px}.timeline>li.timeline-inverted>.timeline-panel{float:right;padding:0 20px 0 100px;text-align:left}.timeline>li.timeline-inverted>.timeline-panel:before{right:auto;left:-15px;border-right-width:15px;border-left-width:0}.timeline>li.timeline-inverted>.timeline-panel:after{right:auto;left:-14px;border-right-width:14px;border-left-width:0}.timeline>li:last-child{margin-bottom:0}.timeline .timeline-heading h4{margin-top:0;color:inherit}.timeline .timeline-heading h4.subheading{text-transform:none}.timeline .timeline-body>p,.timeline .timeline-body>ul{margin-bottom:0}@media (min-width:768px){.timeline:before{left:50%}.timeline>li{min-height:100px;margin-bottom:100px}.timeline>li .timeline-panel{float:left;width:41%;padding:0 20px 20px 30px;text-align:right}.timeline>li .timeline-image{left:50%;width:100px;height:100px;margin-left:-50px}.timeline>li .timeline-image h4{font-size:13px;line-height:18px;margin-top:16px}.timeline>li.timeline-inverted>.timeline-panel{float:right;padding:0 30px 20px 20px;text-align:left}}@media (min-width:992px){.timeline>li{min-height:150px}.timeline>li .timeline-panel{padding:0 20px 20px}.timeline>li .timeline-image{width:150px;height:150px;margin-left:-75px}.timeline>li .timeline-image h4{font-size:18px;line-height:26px;margin-top:30px}.timeline>li.timeline-inverted>.timeline-panel{padding:0 20px 20px}}@media (min-width:1200px){.timeline>li{min-height:170px}.timeline>li .timeline-panel{padding:0 20px 20px 100px}.timeline>li .timeline-image{width:170px;height:170px;margin-left:-85px}.timeline>li .timeline-image h4{margin-top:40px}.timeline>li.timeline-inverted>.timeline-panel{padding:0 100px 20px 20px}}#about ul.list-group li{border:none;background:none}.team-member{margin-bottom:50px;text-align:center}.team-member img{width:225px;height:225px;border:7px solid #fff}.team-member h4{margin-top:25px;margin-bottom:0;text-transform:none}.team-member p{margin-top:0}section#contact{background-color:#212529;background-repeat:no-repeat;background-position:50%}section#contact .section-heading{color:#fff}section#contact .form-group{margin-bottom:25px}section#contact .form-group input,section#contact .form-group textarea{padding:20px}section#contact .form-group input.form-control{height:auto}section#contact .form-group textarea.form-control{height:248px}section#contact .form-control:focus{border-color:#fed136;box-shadow:none}section#contact ::-webkit-input-placeholder{font-weight:700;color:#ced4da;font-family:Montserrat,Helvetica Neue,Helvetica,Arial,sans-serif}section#contact :-moz-placeholder,section#contact ::-moz-placeholder{font-weight:700;color:#ced4da;font-family:Montserrat,Helvetica Neue,Helvetica,Arial,sans-serif}section#contact :-ms-input-placeholder{font-weight:700;color:#ced4da;font-family:Montserrat,Helvetica Neue,Helvetica,Arial,sans-serif}#contact .col-md-6{padding:0 15px}footer{padding:25px 0;text-align:center}footer span.copyright,footer ul.quicklinks{font-size:90%;line-height:40px;text-transform:none;font-family:Montserrat,Helvetica Neue,Helvetica,Arial,sans-serif}footer ul.quicklinks,ul.social-buttons{margin-bottom:0}ul.social-buttons li a{font-size:20px;line-height:40px;display:block;width:40px;height:40px;transition:all .3s;color:#fff;border-radius:100%;outline:none;background-color:#212529}ul.social-buttons li a:active,ul.social-buttons li a:focus,ul.social-buttons li a:hover{background-color:#fed136}body{overflow-x:hidden;font-family:Roboto Slab,Helvetica Neue,Helvetica,Arial,sans-serif}p{line-height:1.75}a{color:#fed136}a:hover{color:#fec503}.text-primary{color:#fed136!important}h1,h2,h3,h4,h5,h6{font-weight:700;font-family:Montserrat,Helvetica Neue,Helvetica,Arial,sans-serif}section{padding:100px 0}section h2.section-heading{font-size:40px;margin-top:0;margin-bottom:15px}section h3.section-subheading{font-size:16px;font-weight:400;font-style:italic;margin-bottom:75px;text-transform:none;font-family:Droid Serif,Helvetica Neue,Helvetica,Arial,sans-serif}@media (min-width:768px){section{padding:150px 0}}.btn{font-family:Montserrat,Helvetica Neue,Helvetica,Arial,sans-serif;font-weight:700}.btn-xl{font-size:18px;padding:20px 40px}.btn-primary{color:#fff;background-color:#fed136;border-color:#fed136}.btn-primary:active,.btn-primary:focus,.btn-primary:hover{background-color:#fec810!important;border-color:#fec810!important;color:#fff}.btn-primary:active,.btn-primary:focus{box-shadow:0 0 0 .2rem rgba(254,209,55,.5)!important}::-moz-selection{background:#fed136;text-shadow:none}::selection{background:#fed136;text-shadow:none}img::selection{background:transparent}img::-moz-selection{background:transparent} \ No newline at end of file diff --git a/docs/assets/bootstrap/js/bootstrap.min.js b/docs/assets/bootstrap/js/bootstrap.min.js new file mode 100644 index 00000000..72a46cf9 --- /dev/null +++ b/docs/assets/bootstrap/js/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v4.1.3 (https://getbootstrap.com/) + * Copyright 2011-2018 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("jquery")):"function"==typeof define&&define.amd?define(["exports","jquery"],t):t(e.bootstrap={},e.jQuery)}(this,function(e,t){"use strict";function i(e,t){for(var n=0;nthis._items.length-1||e<0))if(this._isSliding)k(this._element).one(q.SLID,function(){return t.to(e)});else{if(n===e)return this.pause(),void this.cycle();var i=n=i.clientWidth&&n>=i.clientHeight}),u=0l[e]&&!i.escapeWithReference&&(n=Math.min(u[t],l[e]-("right"===e?u.width:u.height))),Ve({},t,n)}};return c.forEach(function(e){var t=-1!==["left","top"].indexOf(e)?"primary":"secondary";u=ze({},u,f[t](e))}),e.offsets.popper=u,e},priority:["left","right","top","bottom"],padding:5,boundariesElement:"scrollParent"},keepTogether:{order:400,enabled:!0,fn:function(e){var t=e.offsets,n=t.popper,i=t.reference,r=e.placement.split("-")[0],o=Math.floor,s=-1!==["top","bottom"].indexOf(r),a=s?"right":"bottom",l=s?"left":"top",c=s?"width":"height";return n[a]o(i[a])&&(e.offsets.popper[l]=o(i[a])),e}},arrow:{order:500,enabled:!0,fn:function(e,t){var n;if(!pt(e.instance.modifiers,"arrow","keepTogether"))return e;var i=t.element;if("string"==typeof i){if(!(i=e.instance.popper.querySelector(i)))return e}else if(!e.instance.popper.contains(i))return console.warn("WARNING: `arrow.element` must be child of its popper element!"),e;var r=e.placement.split("-")[0],o=e.offsets,s=o.popper,a=o.reference,l=-1!==["left","right"].indexOf(r),c=l?"height":"width",u=l?"Top":"Left",f=u.toLowerCase(),h=l?"left":"top",d=l?"bottom":"right",p=nt(i)[c];a[d]-ps[d]&&(e.offsets.popper[f]+=a[f]+p-s[d]),e.offsets.popper=Ge(e.offsets.popper);var m=a[f]+a[c]/2-p/2,g=Pe(e.instance.popper),_=parseFloat(g["margin"+u],10),v=parseFloat(g["border"+u+"Width"],10),y=m-e.offsets.popper[f]-_-v;return y=Math.max(Math.min(s[c]-p,y),0),e.arrowElement=i,e.offsets.arrow=(Ve(n={},f,Math.round(y)),Ve(n,h,""),n),e},element:"[x-arrow]"},flip:{order:600,enabled:!0,fn:function(p,m){if(at(p.instance.modifiers,"inner"))return p;if(p.flipped&&p.placement===p.originalPlacement)return p;var g=$e(p.instance.popper,p.instance.reference,m.padding,m.boundariesElement,p.positionFixed),_=p.placement.split("-")[0],v=it(_),y=p.placement.split("-")[1]||"",E=[];switch(m.behavior){case vt:E=[_,v];break;case yt:E=_t(_);break;case Et:E=_t(_,!0);break;default:E=m.behavior}return E.forEach(function(e,t){if(_!==e||E.length===t+1)return p;_=p.placement.split("-")[0],v=it(_);var n,i=p.offsets.popper,r=p.offsets.reference,o=Math.floor,s="left"===_&&o(i.right)>o(r.left)||"right"===_&&o(i.left)o(r.top)||"bottom"===_&&o(i.top)o(g.right),c=o(i.top)o(g.bottom),f="left"===_&&a||"right"===_&&l||"top"===_&&c||"bottom"===_&&u,h=-1!==["top","bottom"].indexOf(_),d=!!m.flipVariations&&(h&&"start"===y&&a||h&&"end"===y&&l||!h&&"start"===y&&c||!h&&"end"===y&&u);(s||f||d)&&(p.flipped=!0,(s||f)&&(_=E[t+1]),d&&(y="end"===(n=y)?"start":"start"===n?"end":n),p.placement=_+(y?"-"+y:""),p.offsets.popper=ze({},p.offsets.popper,rt(p.instance.popper,p.offsets.reference,p.placement)),p=st(p.instance.modifiers,p,"flip"))}),p},behavior:"flip",padding:5,boundariesElement:"viewport"},inner:{order:700,enabled:!1,fn:function(e){var t=e.placement,n=t.split("-")[0],i=e.offsets,r=i.popper,o=i.reference,s=-1!==["left","right"].indexOf(n),a=-1===["top","left"].indexOf(n);return r[s?"left":"top"]=o[n]-(a?r[s?"width":"height"]:0),e.placement=it(t),e.offsets.popper=Ge(r),e}},hide:{order:800,enabled:!0,fn:function(e){if(!pt(e.instance.modifiers,"hide","preventOverflow"))return e;var t=e.offsets.reference,n=ot(e.instance.modifiers,function(e){return"preventOverflow"===e.name}).boundaries;if(t.bottomn.right||t.top>n.bottom||t.rightdocument.documentElement.clientHeight;!this._isBodyOverflowing&&e&&(this._element.style.paddingLeft=this._scrollbarWidth+"px"),this._isBodyOverflowing&&!e&&(this._element.style.paddingRight=this._scrollbarWidth+"px")},e._resetAdjustments=function(){this._element.style.paddingLeft="",this._element.style.paddingRight=""},e._checkScrollbar=function(){var e=document.body.getBoundingClientRect();this._isBodyOverflowing=e.left+e.right
',trigger:"hover focus",title:"",delay:0,html:!(An={AUTO:"auto",TOP:"top",RIGHT:"right",BOTTOM:"bottom",LEFT:"left"}),selector:!(Dn={animation:"boolean",template:"string",title:"(string|element|function)",trigger:"string",delay:"(number|object)",html:"boolean",selector:"(string|boolean)",placement:"(string|function)",offset:"(number|string)",container:"(string|element|boolean)",fallbackPlacement:"(string|array)",boundary:"(string|element)"}),placement:"top",offset:0,container:!1,fallbackPlacement:"flip",boundary:"scrollParent"},Nn="out",kn={HIDE:"hide"+wn,HIDDEN:"hidden"+wn,SHOW:(On="show")+wn,SHOWN:"shown"+wn,INSERTED:"inserted"+wn,CLICK:"click"+wn,FOCUSIN:"focusin"+wn,FOCUSOUT:"focusout"+wn,MOUSEENTER:"mouseenter"+wn,MOUSELEAVE:"mouseleave"+wn},xn="fade",Pn="show",Ln=".tooltip-inner",jn=".arrow",Hn="hover",Mn="focus",Fn="click",Wn="manual",Rn=function(){function i(e,t){if("undefined"==typeof Ct)throw new TypeError("Bootstrap tooltips require Popper.js (https://popper.js.org)");this._isEnabled=!0,this._timeout=0,this._hoverState="",this._activeTrigger={},this._popper=null,this.element=e,this.config=this._getConfig(t),this.tip=null,this._setListeners()}var e=i.prototype;return e.enable=function(){this._isEnabled=!0},e.disable=function(){this._isEnabled=!1},e.toggleEnabled=function(){this._isEnabled=!this._isEnabled},e.toggle=function(e){if(this._isEnabled)if(e){var t=this.constructor.DATA_KEY,n=yn(e.currentTarget).data(t);n||(n=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(t,n)),n._activeTrigger.click=!n._activeTrigger.click,n._isWithActiveTrigger()?n._enter(null,n):n._leave(null,n)}else{if(yn(this.getTipElement()).hasClass(Pn))return void this._leave(null,this);this._enter(null,this)}},e.dispose=function(){clearTimeout(this._timeout),yn.removeData(this.element,this.constructor.DATA_KEY),yn(this.element).off(this.constructor.EVENT_KEY),yn(this.element).closest(".modal").off("hide.bs.modal"),this.tip&&yn(this.tip).remove(),this._isEnabled=null,this._timeout=null,this._hoverState=null,(this._activeTrigger=null)!==this._popper&&this._popper.destroy(),this._popper=null,this.element=null,this.config=null,this.tip=null},e.show=function(){var t=this;if("none"===yn(this.element).css("display"))throw new Error("Please use show on visible elements");var e=yn.Event(this.constructor.Event.SHOW);if(this.isWithContent()&&this._isEnabled){yn(this.element).trigger(e);var n=yn.contains(this.element.ownerDocument.documentElement,this.element);if(e.isDefaultPrevented()||!n)return;var i=this.getTipElement(),r=we.getUID(this.constructor.NAME);i.setAttribute("id",r),this.element.setAttribute("aria-describedby",r),this.setContent(),this.config.animation&&yn(i).addClass(xn);var o="function"==typeof this.config.placement?this.config.placement.call(this,i,this.element):this.config.placement,s=this._getAttachment(o);this.addAttachmentClass(s);var a=!1===this.config.container?document.body:yn(document).find(this.config.container);yn(i).data(this.constructor.DATA_KEY,this),yn.contains(this.element.ownerDocument.documentElement,this.tip)||yn(i).appendTo(a),yn(this.element).trigger(this.constructor.Event.INSERTED),this._popper=new Ct(this.element,i,{placement:s,modifiers:{offset:{offset:this.config.offset},flip:{behavior:this.config.fallbackPlacement},arrow:{element:jn},preventOverflow:{boundariesElement:this.config.boundary}},onCreate:function(e){e.originalPlacement!==e.placement&&t._handlePopperPlacementChange(e)},onUpdate:function(e){t._handlePopperPlacementChange(e)}}),yn(i).addClass(Pn),"ontouchstart"in document.documentElement&&yn(document.body).children().on("mouseover",null,yn.noop);var l=function(){t.config.animation&&t._fixTransition();var e=t._hoverState;t._hoverState=null,yn(t.element).trigger(t.constructor.Event.SHOWN),e===Nn&&t._leave(null,t)};if(yn(this.tip).hasClass(xn)){var c=we.getTransitionDurationFromElement(this.tip);yn(this.tip).one(we.TRANSITION_END,l).emulateTransitionEnd(c)}else l()}},e.hide=function(e){var t=this,n=this.getTipElement(),i=yn.Event(this.constructor.Event.HIDE),r=function(){t._hoverState!==On&&n.parentNode&&n.parentNode.removeChild(n),t._cleanTipClass(),t.element.removeAttribute("aria-describedby"),yn(t.element).trigger(t.constructor.Event.HIDDEN),null!==t._popper&&t._popper.destroy(),e&&e()};if(yn(this.element).trigger(i),!i.isDefaultPrevented()){if(yn(n).removeClass(Pn),"ontouchstart"in document.documentElement&&yn(document.body).children().off("mouseover",null,yn.noop),this._activeTrigger[Fn]=!1,this._activeTrigger[Mn]=!1,this._activeTrigger[Hn]=!1,yn(this.tip).hasClass(xn)){var o=we.getTransitionDurationFromElement(n);yn(n).one(we.TRANSITION_END,r).emulateTransitionEnd(o)}else r();this._hoverState=""}},e.update=function(){null!==this._popper&&this._popper.scheduleUpdate()},e.isWithContent=function(){return Boolean(this.getTitle())},e.addAttachmentClass=function(e){yn(this.getTipElement()).addClass(Tn+"-"+e)},e.getTipElement=function(){return this.tip=this.tip||yn(this.config.template)[0],this.tip},e.setContent=function(){var e=this.getTipElement();this.setElementContent(yn(e.querySelectorAll(Ln)),this.getTitle()),yn(e).removeClass(xn+" "+Pn)},e.setElementContent=function(e,t){var n=this.config.html;"object"==typeof t&&(t.nodeType||t.jquery)?n?yn(t).parent().is(e)||e.empty().append(t):e.text(yn(t).text()):e[n?"html":"text"](t)},e.getTitle=function(){var e=this.element.getAttribute("data-original-title");return e||(e="function"==typeof this.config.title?this.config.title.call(this.element):this.config.title),e},e._getAttachment=function(e){return An[e.toUpperCase()]},e._setListeners=function(){var i=this;this.config.trigger.split(" ").forEach(function(e){if("click"===e)yn(i.element).on(i.constructor.Event.CLICK,i.config.selector,function(e){return i.toggle(e)});else if(e!==Wn){var t=e===Hn?i.constructor.Event.MOUSEENTER:i.constructor.Event.FOCUSIN,n=e===Hn?i.constructor.Event.MOUSELEAVE:i.constructor.Event.FOCUSOUT;yn(i.element).on(t,i.config.selector,function(e){return i._enter(e)}).on(n,i.config.selector,function(e){return i._leave(e)})}yn(i.element).closest(".modal").on("hide.bs.modal",function(){return i.hide()})}),this.config.selector?this.config=l({},this.config,{trigger:"manual",selector:""}):this._fixTitle()},e._fixTitle=function(){var e=typeof this.element.getAttribute("data-original-title");(this.element.getAttribute("title")||"string"!==e)&&(this.element.setAttribute("data-original-title",this.element.getAttribute("title")||""),this.element.setAttribute("title",""))},e._enter=function(e,t){var n=this.constructor.DATA_KEY;(t=t||yn(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusin"===e.type?Mn:Hn]=!0),yn(t.getTipElement()).hasClass(Pn)||t._hoverState===On?t._hoverState=On:(clearTimeout(t._timeout),t._hoverState=On,t.config.delay&&t.config.delay.show?t._timeout=setTimeout(function(){t._hoverState===On&&t.show()},t.config.delay.show):t.show())},e._leave=function(e,t){var n=this.constructor.DATA_KEY;(t=t||yn(e.currentTarget).data(n))||(t=new this.constructor(e.currentTarget,this._getDelegateConfig()),yn(e.currentTarget).data(n,t)),e&&(t._activeTrigger["focusout"===e.type?Mn:Hn]=!1),t._isWithActiveTrigger()||(clearTimeout(t._timeout),t._hoverState=Nn,t.config.delay&&t.config.delay.hide?t._timeout=setTimeout(function(){t._hoverState===Nn&&t.hide()},t.config.delay.hide):t.hide())},e._isWithActiveTrigger=function(){for(var e in this._activeTrigger)if(this._activeTrigger[e])return!0;return!1},e._getConfig=function(e){return"number"==typeof(e=l({},this.constructor.Default,yn(this.element).data(),"object"==typeof e&&e?e:{})).delay&&(e.delay={show:e.delay,hide:e.delay}),"number"==typeof e.title&&(e.title=e.title.toString()),"number"==typeof e.content&&(e.content=e.content.toString()),we.typeCheckConfig(En,e,this.constructor.DefaultType),e},e._getDelegateConfig=function(){var e={};if(this.config)for(var t in this.config)this.constructor.Default[t]!==this.config[t]&&(e[t]=this.config[t]);return e},e._cleanTipClass=function(){var e=yn(this.getTipElement()),t=e.attr("class").match(Sn);null!==t&&t.length&&e.removeClass(t.join(""))},e._handlePopperPlacementChange=function(e){var t=e.instance;this.tip=t.popper,this._cleanTipClass(),this.addAttachmentClass(this._getAttachment(e.placement))},e._fixTransition=function(){var e=this.getTipElement(),t=this.config.animation;null===e.getAttribute("x-placement")&&(yn(e).removeClass(xn),this.config.animation=!1,this.hide(),this.show(),this.config.animation=t)},i._jQueryInterface=function(n){return this.each(function(){var e=yn(this).data(bn),t="object"==typeof n&&n;if((e||!/dispose|hide/.test(n))&&(e||(e=new i(this,t),yn(this).data(bn,e)),"string"==typeof n)){if("undefined"==typeof e[n])throw new TypeError('No method named "'+n+'"');e[n]()}})},s(i,null,[{key:"VERSION",get:function(){return"4.1.3"}},{key:"Default",get:function(){return In}},{key:"NAME",get:function(){return En}},{key:"DATA_KEY",get:function(){return bn}},{key:"Event",get:function(){return kn}},{key:"EVENT_KEY",get:function(){return wn}},{key:"DefaultType",get:function(){return Dn}}]),i}(),yn.fn[En]=Rn._jQueryInterface,yn.fn[En].Constructor=Rn,yn.fn[En].noConflict=function(){return yn.fn[En]=Cn,Rn._jQueryInterface},Rn),Qi=(Bn="popover",Kn="."+(qn="bs.popover"),Qn=(Un=t).fn[Bn],Yn="bs-popover",Vn=new RegExp("(^|\\s)"+Yn+"\\S+","g"),zn=l({},Ki.Default,{placement:"right",trigger:"click",content:"",template:''}),Gn=l({},Ki.DefaultType,{content:"(string|element|function)"}),Jn="fade",Xn=".popover-header",$n=".popover-body",ei={HIDE:"hide"+Kn,HIDDEN:"hidden"+Kn,SHOW:(Zn="show")+Kn,SHOWN:"shown"+Kn,INSERTED:"inserted"+Kn,CLICK:"click"+Kn,FOCUSIN:"focusin"+Kn,FOCUSOUT:"focusout"+Kn,MOUSEENTER:"mouseenter"+Kn,MOUSELEAVE:"mouseleave"+Kn},ti=function(e){var t,n;function i(){return e.apply(this,arguments)||this}n=e,(t=i).prototype=Object.create(n.prototype),(t.prototype.constructor=t).__proto__=n;var r=i.prototype;return r.isWithContent=function(){return this.getTitle()||this._getContent()},r.addAttachmentClass=function(e){Un(this.getTipElement()).addClass(Yn+"-"+e)},r.getTipElement=function(){return this.tip=this.tip||Un(this.config.template)[0],this.tip},r.setContent=function(){var e=Un(this.getTipElement());this.setElementContent(e.find(Xn),this.getTitle());var t=this._getContent();"function"==typeof t&&(t=t.call(this.element)),this.setElementContent(e.find($n),t),e.removeClass(Jn+" "+Zn)},r._getContent=function(){return this.element.getAttribute("data-content")||this.config.content},r._cleanTipClass=function(){var e=Un(this.getTipElement()),t=e.attr("class").match(Vn);null!==t&&0=this._offsets[r]&&("undefined"==typeof this._offsets[r+1]||eli{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-remove:before,.fa-close:before,.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before,.fa-bar-chart:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook-f:before,.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-feed:before,.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before,.fa-gratipay:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-pp:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:"\f1e3"}.fa-tty:before{content:"\f1e4"}.fa-binoculars:before{content:"\f1e5"}.fa-plug:before{content:"\f1e6"}.fa-slideshare:before{content:"\f1e7"}.fa-twitch:before{content:"\f1e8"}.fa-yelp:before{content:"\f1e9"}.fa-newspaper-o:before{content:"\f1ea"}.fa-wifi:before{content:"\f1eb"}.fa-calculator:before{content:"\f1ec"}.fa-paypal:before{content:"\f1ed"}.fa-google-wallet:before{content:"\f1ee"}.fa-cc-visa:before{content:"\f1f0"}.fa-cc-mastercard:before{content:"\f1f1"}.fa-cc-discover:before{content:"\f1f2"}.fa-cc-amex:before{content:"\f1f3"}.fa-cc-paypal:before{content:"\f1f4"}.fa-cc-stripe:before{content:"\f1f5"}.fa-bell-slash:before{content:"\f1f6"}.fa-bell-slash-o:before{content:"\f1f7"}.fa-trash:before{content:"\f1f8"}.fa-copyright:before{content:"\f1f9"}.fa-at:before{content:"\f1fa"}.fa-eyedropper:before{content:"\f1fb"}.fa-paint-brush:before{content:"\f1fc"}.fa-birthday-cake:before{content:"\f1fd"}.fa-area-chart:before{content:"\f1fe"}.fa-pie-chart:before{content:"\f200"}.fa-line-chart:before{content:"\f201"}.fa-lastfm:before{content:"\f202"}.fa-lastfm-square:before{content:"\f203"}.fa-toggle-off:before{content:"\f204"}.fa-toggle-on:before{content:"\f205"}.fa-bicycle:before{content:"\f206"}.fa-bus:before{content:"\f207"}.fa-ioxhost:before{content:"\f208"}.fa-angellist:before{content:"\f209"}.fa-cc:before{content:"\f20a"}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:"\f20b"}.fa-meanpath:before{content:"\f20c"}.fa-buysellads:before{content:"\f20d"}.fa-connectdevelop:before{content:"\f20e"}.fa-dashcube:before{content:"\f210"}.fa-forumbee:before{content:"\f211"}.fa-leanpub:before{content:"\f212"}.fa-sellsy:before{content:"\f213"}.fa-shirtsinbulk:before{content:"\f214"}.fa-simplybuilt:before{content:"\f215"}.fa-skyatlas:before{content:"\f216"}.fa-cart-plus:before{content:"\f217"}.fa-cart-arrow-down:before{content:"\f218"}.fa-diamond:before{content:"\f219"}.fa-ship:before{content:"\f21a"}.fa-user-secret:before{content:"\f21b"}.fa-motorcycle:before{content:"\f21c"}.fa-street-view:before{content:"\f21d"}.fa-heartbeat:before{content:"\f21e"}.fa-venus:before{content:"\f221"}.fa-mars:before{content:"\f222"}.fa-mercury:before{content:"\f223"}.fa-intersex:before,.fa-transgender:before{content:"\f224"}.fa-transgender-alt:before{content:"\f225"}.fa-venus-double:before{content:"\f226"}.fa-mars-double:before{content:"\f227"}.fa-venus-mars:before{content:"\f228"}.fa-mars-stroke:before{content:"\f229"}.fa-mars-stroke-v:before{content:"\f22a"}.fa-mars-stroke-h:before{content:"\f22b"}.fa-neuter:before{content:"\f22c"}.fa-genderless:before{content:"\f22d"}.fa-facebook-official:before{content:"\f230"}.fa-pinterest-p:before{content:"\f231"}.fa-whatsapp:before{content:"\f232"}.fa-server:before{content:"\f233"}.fa-user-plus:before{content:"\f234"}.fa-user-times:before{content:"\f235"}.fa-hotel:before,.fa-bed:before{content:"\f236"}.fa-viacoin:before{content:"\f237"}.fa-train:before{content:"\f238"}.fa-subway:before{content:"\f239"}.fa-medium:before{content:"\f23a"}.fa-yc:before,.fa-y-combinator:before{content:"\f23b"}.fa-optin-monster:before{content:"\f23c"}.fa-opencart:before{content:"\f23d"}.fa-expeditedssl:before{content:"\f23e"}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:"\f240"}.fa-battery-3:before,.fa-battery-three-quarters:before{content:"\f241"}.fa-battery-2:before,.fa-battery-half:before{content:"\f242"}.fa-battery-1:before,.fa-battery-quarter:before{content:"\f243"}.fa-battery-0:before,.fa-battery-empty:before{content:"\f244"}.fa-mouse-pointer:before{content:"\f245"}.fa-i-cursor:before{content:"\f246"}.fa-object-group:before{content:"\f247"}.fa-object-ungroup:before{content:"\f248"}.fa-sticky-note:before{content:"\f249"}.fa-sticky-note-o:before{content:"\f24a"}.fa-cc-jcb:before{content:"\f24b"}.fa-cc-diners-club:before{content:"\f24c"}.fa-clone:before{content:"\f24d"}.fa-balance-scale:before{content:"\f24e"}.fa-hourglass-o:before{content:"\f250"}.fa-hourglass-1:before,.fa-hourglass-start:before{content:"\f251"}.fa-hourglass-2:before,.fa-hourglass-half:before{content:"\f252"}.fa-hourglass-3:before,.fa-hourglass-end:before{content:"\f253"}.fa-hourglass:before{content:"\f254"}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:"\f255"}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:"\f256"}.fa-hand-scissors-o:before{content:"\f257"}.fa-hand-lizard-o:before{content:"\f258"}.fa-hand-spock-o:before{content:"\f259"}.fa-hand-pointer-o:before{content:"\f25a"}.fa-hand-peace-o:before{content:"\f25b"}.fa-trademark:before{content:"\f25c"}.fa-registered:before{content:"\f25d"}.fa-creative-commons:before{content:"\f25e"}.fa-gg:before{content:"\f260"}.fa-gg-circle:before{content:"\f261"}.fa-tripadvisor:before{content:"\f262"}.fa-odnoklassniki:before{content:"\f263"}.fa-odnoklassniki-square:before{content:"\f264"}.fa-get-pocket:before{content:"\f265"}.fa-wikipedia-w:before{content:"\f266"}.fa-safari:before{content:"\f267"}.fa-chrome:before{content:"\f268"}.fa-firefox:before{content:"\f269"}.fa-opera:before{content:"\f26a"}.fa-internet-explorer:before{content:"\f26b"}.fa-tv:before,.fa-television:before{content:"\f26c"}.fa-contao:before{content:"\f26d"}.fa-500px:before{content:"\f26e"}.fa-amazon:before{content:"\f270"}.fa-calendar-plus-o:before{content:"\f271"}.fa-calendar-minus-o:before{content:"\f272"}.fa-calendar-times-o:before{content:"\f273"}.fa-calendar-check-o:before{content:"\f274"}.fa-industry:before{content:"\f275"}.fa-map-pin:before{content:"\f276"}.fa-map-signs:before{content:"\f277"}.fa-map-o:before{content:"\f278"}.fa-map:before{content:"\f279"}.fa-commenting:before{content:"\f27a"}.fa-commenting-o:before{content:"\f27b"}.fa-houzz:before{content:"\f27c"}.fa-vimeo:before{content:"\f27d"}.fa-black-tie:before{content:"\f27e"}.fa-fonticons:before{content:"\f280"}.fa-reddit-alien:before{content:"\f281"}.fa-edge:before{content:"\f282"}.fa-credit-card-alt:before{content:"\f283"}.fa-codiepie:before{content:"\f284"}.fa-modx:before{content:"\f285"}.fa-fort-awesome:before{content:"\f286"}.fa-usb:before{content:"\f287"}.fa-product-hunt:before{content:"\f288"}.fa-mixcloud:before{content:"\f289"}.fa-scribd:before{content:"\f28a"}.fa-pause-circle:before{content:"\f28b"}.fa-pause-circle-o:before{content:"\f28c"}.fa-stop-circle:before{content:"\f28d"}.fa-stop-circle-o:before{content:"\f28e"}.fa-shopping-bag:before{content:"\f290"}.fa-shopping-basket:before{content:"\f291"}.fa-hashtag:before{content:"\f292"}.fa-bluetooth:before{content:"\f293"}.fa-bluetooth-b:before{content:"\f294"}.fa-percent:before{content:"\f295"}.fa-gitlab:before{content:"\f296"}.fa-wpbeginner:before{content:"\f297"}.fa-wpforms:before{content:"\f298"}.fa-envira:before{content:"\f299"}.fa-universal-access:before{content:"\f29a"}.fa-wheelchair-alt:before{content:"\f29b"}.fa-question-circle-o:before{content:"\f29c"}.fa-blind:before{content:"\f29d"}.fa-audio-description:before{content:"\f29e"}.fa-volume-control-phone:before{content:"\f2a0"}.fa-braille:before{content:"\f2a1"}.fa-assistive-listening-systems:before{content:"\f2a2"}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:"\f2a3"}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:"\f2a4"}.fa-glide:before{content:"\f2a5"}.fa-glide-g:before{content:"\f2a6"}.fa-signing:before,.fa-sign-language:before{content:"\f2a7"}.fa-low-vision:before{content:"\f2a8"}.fa-viadeo:before{content:"\f2a9"}.fa-viadeo-square:before{content:"\f2aa"}.fa-snapchat:before{content:"\f2ab"}.fa-snapchat-ghost:before{content:"\f2ac"}.fa-snapchat-square:before{content:"\f2ad"}.fa-pied-piper:before{content:"\f2ae"}.fa-first-order:before{content:"\f2b0"}.fa-yoast:before{content:"\f2b1"}.fa-themeisle:before{content:"\f2b2"}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:"\f2b3"}.fa-fa:before,.fa-font-awesome:before{content:"\f2b4"}.fa-handshake-o:before{content:"\f2b5"}.fa-envelope-open:before{content:"\f2b6"}.fa-envelope-open-o:before{content:"\f2b7"}.fa-linode:before{content:"\f2b8"}.fa-address-book:before{content:"\f2b9"}.fa-address-book-o:before{content:"\f2ba"}.fa-vcard:before,.fa-address-card:before{content:"\f2bb"}.fa-vcard-o:before,.fa-address-card-o:before{content:"\f2bc"}.fa-user-circle:before{content:"\f2bd"}.fa-user-circle-o:before{content:"\f2be"}.fa-user-o:before{content:"\f2c0"}.fa-id-badge:before{content:"\f2c1"}.fa-drivers-license:before,.fa-id-card:before{content:"\f2c2"}.fa-drivers-license-o:before,.fa-id-card-o:before{content:"\f2c3"}.fa-quora:before{content:"\f2c4"}.fa-free-code-camp:before{content:"\f2c5"}.fa-telegram:before{content:"\f2c6"}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:"\f2c7"}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:"\f2c8"}.fa-thermometer-2:before,.fa-thermometer-half:before{content:"\f2c9"}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:"\f2ca"}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:"\f2cb"}.fa-shower:before{content:"\f2cc"}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:"\f2cd"}.fa-podcast:before{content:"\f2ce"}.fa-window-maximize:before{content:"\f2d0"}.fa-window-minimize:before{content:"\f2d1"}.fa-window-restore:before{content:"\f2d2"}.fa-times-rectangle:before,.fa-window-close:before{content:"\f2d3"}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:"\f2d4"}.fa-bandcamp:before{content:"\f2d5"}.fa-grav:before{content:"\f2d6"}.fa-etsy:before{content:"\f2d7"}.fa-imdb:before{content:"\f2d8"}.fa-ravelry:before{content:"\f2d9"}.fa-eercast:before{content:"\f2da"}.fa-microchip:before{content:"\f2db"}.fa-snowflake-o:before{content:"\f2dc"}.fa-superpowers:before{content:"\f2dd"}.fa-wpexplorer:before{content:"\f2de"}.fa-meetup:before{content:"\f2e0"}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto} diff --git a/docs/assets/fonts/fontawesome-webfont.eot b/docs/assets/fonts/fontawesome-webfont.eot new file mode 100644 index 00000000..e9f60ca9 Binary files /dev/null and b/docs/assets/fonts/fontawesome-webfont.eot differ diff --git a/docs/assets/fonts/fontawesome-webfont.svg b/docs/assets/fonts/fontawesome-webfont.svg new file mode 100644 index 00000000..855c845e --- /dev/null +++ b/docs/assets/fonts/fontawesome-webfont.svg @@ -0,0 +1,2671 @@ + + + + +Created by FontForge 20120731 at Mon Oct 24 17:37:40 2016 + By ,,, +Copyright Dave Gandy 2016. All rights reserved. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/assets/fonts/fontawesome-webfont.ttf b/docs/assets/fonts/fontawesome-webfont.ttf new file mode 100644 index 00000000..35acda2f Binary files /dev/null and b/docs/assets/fonts/fontawesome-webfont.ttf differ diff --git a/docs/assets/fonts/fontawesome-webfont.woff b/docs/assets/fonts/fontawesome-webfont.woff new file mode 100644 index 00000000..400014a4 Binary files /dev/null and b/docs/assets/fonts/fontawesome-webfont.woff differ diff --git a/docs/assets/fonts/fontawesome-webfont.woff2 b/docs/assets/fonts/fontawesome-webfont.woff2 new file mode 100644 index 00000000..4d13fc60 Binary files /dev/null and b/docs/assets/fonts/fontawesome-webfont.woff2 differ diff --git a/docs/assets/img/Help.jpg b/docs/assets/img/Help.jpg new file mode 100644 index 00000000..086031ec Binary files /dev/null and b/docs/assets/img/Help.jpg differ diff --git a/docs/assets/img/about/1.jpg b/docs/assets/img/about/1.jpg new file mode 100644 index 00000000..abc17a0a Binary files /dev/null and b/docs/assets/img/about/1.jpg differ diff --git a/docs/assets/img/about/2.jpg b/docs/assets/img/about/2.jpg new file mode 100644 index 00000000..24b4e5bc Binary files /dev/null and b/docs/assets/img/about/2.jpg differ diff --git a/docs/assets/img/about/3.jpg b/docs/assets/img/about/3.jpg new file mode 100644 index 00000000..6ed1b35a Binary files /dev/null and b/docs/assets/img/about/3.jpg differ diff --git a/docs/assets/img/about/4.jpg b/docs/assets/img/about/4.jpg new file mode 100644 index 00000000..f8d4346c Binary files /dev/null and b/docs/assets/img/about/4.jpg differ diff --git a/docs/assets/img/calendar.png b/docs/assets/img/calendar.png new file mode 100644 index 00000000..e9a9d44a Binary files /dev/null and b/docs/assets/img/calendar.png differ diff --git a/docs/assets/img/classPicture.jpg b/docs/assets/img/classPicture.jpg new file mode 100644 index 00000000..e9049259 Binary files /dev/null and b/docs/assets/img/classPicture.jpg differ diff --git a/docs/assets/img/classPictureThumb.jpg b/docs/assets/img/classPictureThumb.jpg new file mode 100644 index 00000000..e1020cbd Binary files /dev/null and b/docs/assets/img/classPictureThumb.jpg differ diff --git a/docs/assets/img/code.jpg b/docs/assets/img/code.jpg new file mode 100644 index 00000000..26b9a23e Binary files /dev/null and b/docs/assets/img/code.jpg differ diff --git a/docs/assets/img/codeThumb.jpg b/docs/assets/img/codeThumb.jpg new file mode 100644 index 00000000..e89f1874 Binary files /dev/null and b/docs/assets/img/codeThumb.jpg differ diff --git a/docs/assets/img/download.jpg b/docs/assets/img/download.jpg new file mode 100644 index 00000000..e811f0a6 Binary files /dev/null and b/docs/assets/img/download.jpg differ diff --git a/docs/assets/img/favnew.png b/docs/assets/img/favnew.png new file mode 100644 index 00000000..ada77a0c Binary files /dev/null and b/docs/assets/img/favnew.png differ diff --git a/docs/assets/img/gantt.png b/docs/assets/img/gantt.png new file mode 100644 index 00000000..5179c27a Binary files /dev/null and b/docs/assets/img/gantt.png differ diff --git a/docs/assets/img/home-bg.jpg b/docs/assets/img/home-bg.jpg new file mode 100644 index 00000000..f33aab4e Binary files /dev/null and b/docs/assets/img/home-bg.jpg differ diff --git a/docs/assets/img/movingforward.jpeg b/docs/assets/img/movingforward.jpeg new file mode 100644 index 00000000..7032710e Binary files /dev/null and b/docs/assets/img/movingforward.jpeg differ diff --git a/docs/assets/img/movingforwardThumb.jpeg b/docs/assets/img/movingforwardThumb.jpeg new file mode 100644 index 00000000..26dceebd Binary files /dev/null and b/docs/assets/img/movingforwardThumb.jpeg differ diff --git a/docs/assets/img/new.png b/docs/assets/img/new.png new file mode 100644 index 00000000..9181e6bc Binary files /dev/null and b/docs/assets/img/new.png differ diff --git a/docs/assets/img/pearplanner.jpg b/docs/assets/img/pearplanner.jpg new file mode 100644 index 00000000..81e0fa21 Binary files /dev/null and b/docs/assets/img/pearplanner.jpg differ diff --git a/docs/assets/img/pearplannerThumb.jpg b/docs/assets/img/pearplannerThumb.jpg new file mode 100644 index 00000000..0c6b4f58 Binary files /dev/null and b/docs/assets/img/pearplannerThumb.jpg differ diff --git a/docs/assets/img/reminder.png b/docs/assets/img/reminder.png new file mode 100644 index 00000000..c56aa22d Binary files /dev/null and b/docs/assets/img/reminder.png differ diff --git a/docs/assets/js/agency.js b/docs/assets/js/agency.js new file mode 100644 index 00000000..70a2d49d --- /dev/null +++ b/docs/assets/js/agency.js @@ -0,0 +1,50 @@ +(function($) { + "use strict"; // Start of use strict + + // Smooth scrolling using jQuery easing + $('a.js-scroll-trigger[href*="#"]:not([href="#"])').click(function() { + if (location.pathname.replace(/^\//, '') == this.pathname.replace(/^\//, '') && location.hostname == this.hostname) { + var target = $(this.hash); + target = target.length ? target : $('[name=' + this.hash.slice(1) + ']'); + if (target.length) { + $('html, body').animate({ + scrollTop: (target.offset().top - 54) + }, 1000, "easeInOutExpo"); + return false; + } + } + }); + + // Closes responsive menu when a scroll trigger link is clicked + $('.js-scroll-trigger').click(function() { + $('.navbar-collapse').collapse('hide'); + }); + + // Activate scrollspy to add active class to navbar items on scroll + $('body').scrollspy({ + target: '#mainNav', + offset: 56 + }); + + // Collapse Navbar + var navbarCollapse = function() { + if ($("#mainNav").offset().top > 100) { + $("#mainNav").addClass("navbar-shrink"); + } else { + $("#mainNav").removeClass("navbar-shrink"); + } + }; + // Collapse now if page is not at top + navbarCollapse(); + // Collapse the navbar when page is scrolled + $(window).scroll(navbarCollapse); + + // Hide navbar when modals trigger + $('.portfolio-modal').on('show.bs.modal', function(e) { + $(".navbar").addClass("d-none"); + }) + $('.portfolio-modal').on('hidden.bs.modal', function(e) { + $(".navbar").removeClass("d-none"); + }) + +})(jQuery); // End of use strict \ No newline at end of file diff --git a/docs/assets/js/bs-animation.js b/docs/assets/js/bs-animation.js new file mode 100644 index 00000000..1c66d12f --- /dev/null +++ b/docs/assets/js/bs-animation.js @@ -0,0 +1,6 @@ +$(document).ready(function(){ + AOS.init({ disable: 'mobile' }); + $('[data-bs-hover-animate]') + .mouseenter( function(){ var elem = $(this); elem.addClass('animated ' + elem.attr('data-bs-hover-animate')) }) + .mouseleave( function(){ var elem = $(this); elem.removeClass('animated ' + elem.attr('data-bs-hover-animate')) }); +}); \ No newline at end of file diff --git a/docs/assets/js/jquery.min.js b/docs/assets/js/jquery.min.js new file mode 100644 index 00000000..49d1fcfb --- /dev/null +++ b/docs/assets/js/jquery.min.js @@ -0,0 +1,2 @@ +/*! jQuery v3.3.1 | (c) JS Foundation and other contributors | jquery.org/license */ +!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";var n=[],r=e.document,i=Object.getPrototypeOf,o=n.slice,a=n.concat,s=n.push,u=n.indexOf,l={},c=l.toString,f=l.hasOwnProperty,p=f.toString,d=p.call(Object),h={},g=function e(t){return"function"==typeof t&&"number"!=typeof t.nodeType},y=function e(t){return null!=t&&t===t.window},v={type:!0,src:!0,noModule:!0};function m(e,t,n){var i,o=(t=t||r).createElement("script");if(o.text=e,n)for(i in v)n[i]&&(o[i]=n[i]);t.head.appendChild(o).parentNode.removeChild(o)}function x(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[c.call(e)]||"object":typeof e}var b="3.3.1",w=function(e,t){return new w.fn.init(e,t)},T=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g;w.fn=w.prototype={jquery:"3.3.1",constructor:w,length:0,toArray:function(){return o.call(this)},get:function(e){return null==e?o.call(this):e<0?this[e+this.length]:this[e]},pushStack:function(e){var t=w.merge(this.constructor(),e);return t.prevObject=this,t},each:function(e){return w.each(this,e)},map:function(e){return this.pushStack(w.map(this,function(t,n){return e.call(t,n,t)}))},slice:function(){return this.pushStack(o.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(e<0?t:0);return this.pushStack(n>=0&&n0&&t-1 in e)}var E=function(e){var t,n,r,i,o,a,s,u,l,c,f,p,d,h,g,y,v,m,x,b="sizzle"+1*new Date,w=e.document,T=0,C=0,E=ae(),k=ae(),S=ae(),D=function(e,t){return e===t&&(f=!0),0},N={}.hasOwnProperty,A=[],j=A.pop,q=A.push,L=A.push,H=A.slice,O=function(e,t){for(var n=0,r=e.length;n+~]|"+M+")"+M+"*"),z=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),X=new RegExp(W),U=new RegExp("^"+R+"$"),V={ID:new RegExp("^#("+R+")"),CLASS:new RegExp("^\\.("+R+")"),TAG:new RegExp("^("+R+"|[*])"),ATTR:new RegExp("^"+I),PSEUDO:new RegExp("^"+W),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+P+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},G=/^(?:input|select|textarea|button)$/i,Y=/^h\d$/i,Q=/^[^{]+\{\s*\[native \w/,J=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,K=/[+~]/,Z=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),ee=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},te=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ne=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},re=function(){p()},ie=me(function(e){return!0===e.disabled&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{L.apply(A=H.call(w.childNodes),w.childNodes),A[w.childNodes.length].nodeType}catch(e){L={apply:A.length?function(e,t){q.apply(e,H.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function oe(e,t,r,i){var o,s,l,c,f,h,v,m=t&&t.ownerDocument,T=t?t.nodeType:9;if(r=r||[],"string"!=typeof e||!e||1!==T&&9!==T&&11!==T)return r;if(!i&&((t?t.ownerDocument||t:w)!==d&&p(t),t=t||d,g)){if(11!==T&&(f=J.exec(e)))if(o=f[1]){if(9===T){if(!(l=t.getElementById(o)))return r;if(l.id===o)return r.push(l),r}else if(m&&(l=m.getElementById(o))&&x(t,l)&&l.id===o)return r.push(l),r}else{if(f[2])return L.apply(r,t.getElementsByTagName(e)),r;if((o=f[3])&&n.getElementsByClassName&&t.getElementsByClassName)return L.apply(r,t.getElementsByClassName(o)),r}if(n.qsa&&!S[e+" "]&&(!y||!y.test(e))){if(1!==T)m=t,v=e;else if("object"!==t.nodeName.toLowerCase()){(c=t.getAttribute("id"))?c=c.replace(te,ne):t.setAttribute("id",c=b),s=(h=a(e)).length;while(s--)h[s]="#"+c+" "+ve(h[s]);v=h.join(","),m=K.test(e)&&ge(t.parentNode)||t}if(v)try{return L.apply(r,m.querySelectorAll(v)),r}catch(e){}finally{c===b&&t.removeAttribute("id")}}}return u(e.replace(B,"$1"),t,r,i)}function ae(){var e=[];function t(n,i){return e.push(n+" ")>r.cacheLength&&delete t[e.shift()],t[n+" "]=i}return t}function se(e){return e[b]=!0,e}function ue(e){var t=d.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function le(e,t){var n=e.split("|"),i=n.length;while(i--)r.attrHandle[n[i]]=t}function ce(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function fe(e){return function(t){return"input"===t.nodeName.toLowerCase()&&t.type===e}}function pe(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function de(e){return function(t){return"form"in t?t.parentNode&&!1===t.disabled?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&ie(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function he(e){return se(function(t){return t=+t,se(function(n,r){var i,o=e([],n.length,t),a=o.length;while(a--)n[i=o[a]]&&(n[i]=!(r[i]=n[i]))})})}function ge(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}n=oe.support={},o=oe.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},p=oe.setDocument=function(e){var t,i,a=e?e.ownerDocument||e:w;return a!==d&&9===a.nodeType&&a.documentElement?(d=a,h=d.documentElement,g=!o(d),w!==d&&(i=d.defaultView)&&i.top!==i&&(i.addEventListener?i.addEventListener("unload",re,!1):i.attachEvent&&i.attachEvent("onunload",re)),n.attributes=ue(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ue(function(e){return e.appendChild(d.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=Q.test(d.getElementsByClassName),n.getById=ue(function(e){return h.appendChild(e).id=b,!d.getElementsByName||!d.getElementsByName(b).length}),n.getById?(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){return e.getAttribute("id")===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n=t.getElementById(e);return n?[n]:[]}}):(r.filter.ID=function(e){var t=e.replace(Z,ee);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},r.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&g){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),r.find.TAG=n.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):n.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},r.find.CLASS=n.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&g)return t.getElementsByClassName(e)},v=[],y=[],(n.qsa=Q.test(d.querySelectorAll))&&(ue(function(e){h.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&y.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||y.push("\\["+M+"*(?:value|"+P+")"),e.querySelectorAll("[id~="+b+"-]").length||y.push("~="),e.querySelectorAll(":checked").length||y.push(":checked"),e.querySelectorAll("a#"+b+"+*").length||y.push(".#.+[+~]")}),ue(function(e){e.innerHTML="";var t=d.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&y.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&y.push(":enabled",":disabled"),h.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&y.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),y.push(",.*:")})),(n.matchesSelector=Q.test(m=h.matches||h.webkitMatchesSelector||h.mozMatchesSelector||h.oMatchesSelector||h.msMatchesSelector))&&ue(function(e){n.disconnectedMatch=m.call(e,"*"),m.call(e,"[s!='']:x"),v.push("!=",W)}),y=y.length&&new RegExp(y.join("|")),v=v.length&&new RegExp(v.join("|")),t=Q.test(h.compareDocumentPosition),x=t||Q.test(h.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},D=t?function(e,t){if(e===t)return f=!0,0;var r=!e.compareDocumentPosition-!t.compareDocumentPosition;return r||(1&(r=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!n.sortDetached&&t.compareDocumentPosition(e)===r?e===d||e.ownerDocument===w&&x(w,e)?-1:t===d||t.ownerDocument===w&&x(w,t)?1:c?O(c,e)-O(c,t):0:4&r?-1:1)}:function(e,t){if(e===t)return f=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e===d?-1:t===d?1:i?-1:o?1:c?O(c,e)-O(c,t):0;if(i===o)return ce(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?ce(a[r],s[r]):a[r]===w?-1:s[r]===w?1:0},d):d},oe.matches=function(e,t){return oe(e,null,null,t)},oe.matchesSelector=function(e,t){if((e.ownerDocument||e)!==d&&p(e),t=t.replace(z,"='$1']"),n.matchesSelector&&g&&!S[t+" "]&&(!v||!v.test(t))&&(!y||!y.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return oe(t,d,null,[e]).length>0},oe.contains=function(e,t){return(e.ownerDocument||e)!==d&&p(e),x(e,t)},oe.attr=function(e,t){(e.ownerDocument||e)!==d&&p(e);var i=r.attrHandle[t.toLowerCase()],o=i&&N.call(r.attrHandle,t.toLowerCase())?i(e,t,!g):void 0;return void 0!==o?o:n.attributes||!g?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null},oe.escape=function(e){return(e+"").replace(te,ne)},oe.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},oe.uniqueSort=function(e){var t,r=[],i=0,o=0;if(f=!n.detectDuplicates,c=!n.sortStable&&e.slice(0),e.sort(D),f){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return c=null,e},i=oe.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=i(e)}else if(3===o||4===o)return e.nodeValue}else while(t=e[r++])n+=i(t);return n},(r=oe.selectors={cacheLength:50,createPseudo:se,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(Z,ee),e[3]=(e[3]||e[4]||e[5]||"").replace(Z,ee),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||oe.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&oe.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return V.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=a(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(Z,ee).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=E[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&E(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=oe.attr(r,e);return null==i?"!="===t:!t||(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i.replace($," ")+" ").indexOf(n)>-1:"|="===t&&(i===n||i.slice(0,n.length+1)===n+"-"))}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,g=o!==a?"nextSibling":"previousSibling",y=t.parentNode,v=s&&t.nodeName.toLowerCase(),m=!u&&!s,x=!1;if(y){if(o){while(g){p=t;while(p=p[g])if(s?p.nodeName.toLowerCase()===v:1===p.nodeType)return!1;h=g="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?y.firstChild:y.lastChild],a&&m){x=(d=(l=(c=(f=(p=y)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1])&&l[2],p=d&&y.childNodes[d];while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if(1===p.nodeType&&++x&&p===t){c[e]=[T,d,x];break}}else if(m&&(x=d=(l=(c=(f=(p=t)[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]||[])[0]===T&&l[1]),!1===x)while(p=++d&&p&&p[g]||(x=d=0)||h.pop())if((s?p.nodeName.toLowerCase()===v:1===p.nodeType)&&++x&&(m&&((c=(f=p[b]||(p[b]={}))[p.uniqueID]||(f[p.uniqueID]={}))[e]=[T,x]),p===t))break;return(x-=i)===r||x%r==0&&x/r>=0}}},PSEUDO:function(e,t){var n,i=r.pseudos[e]||r.setFilters[e.toLowerCase()]||oe.error("unsupported pseudo: "+e);return i[b]?i(t):i.length>1?(n=[e,e,"",t],r.setFilters.hasOwnProperty(e.toLowerCase())?se(function(e,n){var r,o=i(e,t),a=o.length;while(a--)e[r=O(e,o[a])]=!(n[r]=o[a])}):function(e){return i(e,0,n)}):i}},pseudos:{not:se(function(e){var t=[],n=[],r=s(e.replace(B,"$1"));return r[b]?se(function(e,t,n,i){var o,a=r(e,null,i,[]),s=e.length;while(s--)(o=a[s])&&(e[s]=!(t[s]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),t[0]=null,!n.pop()}}),has:se(function(e){return function(t){return oe(e,t).length>0}}),contains:se(function(e){return e=e.replace(Z,ee),function(t){return(t.textContent||t.innerText||i(t)).indexOf(e)>-1}}),lang:se(function(e){return U.test(e||"")||oe.error("unsupported lang: "+e),e=e.replace(Z,ee).toLowerCase(),function(t){var n;do{if(n=g?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return(n=n.toLowerCase())===e||0===n.indexOf(e+"-")}while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===h},focus:function(e){return e===d.activeElement&&(!d.hasFocus||d.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:de(!1),disabled:de(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,!0===e.selected},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!r.pseudos.empty(e)},header:function(e){return Y.test(e.nodeName)},input:function(e){return G.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:he(function(){return[0]}),last:he(function(e,t){return[t-1]}),eq:he(function(e,t,n){return[n<0?n+t:n]}),even:he(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:he(function(e,t,n){for(var r=n<0?n+t:n;++r1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function be(e,t,n){for(var r=0,i=t.length;r-1&&(o[l]=!(a[l]=f))}}else v=we(v===a?v.splice(h,v.length):v),i?i(null,a,v,u):L.apply(a,v)})}function Ce(e){for(var t,n,i,o=e.length,a=r.relative[e[0].type],s=a||r.relative[" "],u=a?1:0,c=me(function(e){return e===t},s,!0),f=me(function(e){return O(t,e)>-1},s,!0),p=[function(e,n,r){var i=!a&&(r||n!==l)||((t=n).nodeType?c(e,n,r):f(e,n,r));return t=null,i}];u1&&xe(p),u>1&&ve(e.slice(0,u-1).concat({value:" "===e[u-2].type?"*":""})).replace(B,"$1"),n,u0,i=e.length>0,o=function(o,a,s,u,c){var f,h,y,v=0,m="0",x=o&&[],b=[],w=l,C=o||i&&r.find.TAG("*",c),E=T+=null==w?1:Math.random()||.1,k=C.length;for(c&&(l=a===d||a||c);m!==k&&null!=(f=C[m]);m++){if(i&&f){h=0,a||f.ownerDocument===d||(p(f),s=!g);while(y=e[h++])if(y(f,a||d,s)){u.push(f);break}c&&(T=E)}n&&((f=!y&&f)&&v--,o&&x.push(f))}if(v+=m,n&&m!==v){h=0;while(y=t[h++])y(x,b,a,s);if(o){if(v>0)while(m--)x[m]||b[m]||(b[m]=j.call(u));b=we(b)}L.apply(u,b),c&&!o&&b.length>0&&v+t.length>1&&oe.uniqueSort(u)}return c&&(T=E,l=w),x};return n?se(o):o}return s=oe.compile=function(e,t){var n,r=[],i=[],o=S[e+" "];if(!o){t||(t=a(e)),n=t.length;while(n--)(o=Ce(t[n]))[b]?r.push(o):i.push(o);(o=S(e,Ee(i,r))).selector=e}return o},u=oe.select=function(e,t,n,i){var o,u,l,c,f,p="function"==typeof e&&e,d=!i&&a(e=p.selector||e);if(n=n||[],1===d.length){if((u=d[0]=d[0].slice(0)).length>2&&"ID"===(l=u[0]).type&&9===t.nodeType&&g&&r.relative[u[1].type]){if(!(t=(r.find.ID(l.matches[0].replace(Z,ee),t)||[])[0]))return n;p&&(t=t.parentNode),e=e.slice(u.shift().value.length)}o=V.needsContext.test(e)?0:u.length;while(o--){if(l=u[o],r.relative[c=l.type])break;if((f=r.find[c])&&(i=f(l.matches[0].replace(Z,ee),K.test(u[0].type)&&ge(t.parentNode)||t))){if(u.splice(o,1),!(e=i.length&&ve(u)))return L.apply(n,i),n;break}}}return(p||s(e,d))(i,t,!g,n,!t||K.test(e)&&ge(t.parentNode)||t),n},n.sortStable=b.split("").sort(D).join("")===b,n.detectDuplicates=!!f,p(),n.sortDetached=ue(function(e){return 1&e.compareDocumentPosition(d.createElement("fieldset"))}),ue(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||le("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ue(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||le("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),ue(function(e){return null==e.getAttribute("disabled")})||le(P,function(e,t,n){var r;if(!n)return!0===e[t]?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),oe}(e);w.find=E,w.expr=E.selectors,w.expr[":"]=w.expr.pseudos,w.uniqueSort=w.unique=E.uniqueSort,w.text=E.getText,w.isXMLDoc=E.isXML,w.contains=E.contains,w.escapeSelector=E.escape;var k=function(e,t,n){var r=[],i=void 0!==n;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&w(e).is(n))break;r.push(e)}return r},S=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},D=w.expr.match.needsContext;function N(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}var A=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,t,n){return g(t)?w.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?w.grep(e,function(e){return e===t!==n}):"string"!=typeof t?w.grep(e,function(e){return u.call(t,e)>-1!==n}):w.filter(t,e,n)}w.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?w.find.matchesSelector(r,e)?[r]:[]:w.find.matches(e,w.grep(t,function(e){return 1===e.nodeType}))},w.fn.extend({find:function(e){var t,n,r=this.length,i=this;if("string"!=typeof e)return this.pushStack(w(e).filter(function(){for(t=0;t1?w.uniqueSort(n):n},filter:function(e){return this.pushStack(j(this,e||[],!1))},not:function(e){return this.pushStack(j(this,e||[],!0))},is:function(e){return!!j(this,"string"==typeof e&&D.test(e)?w(e):e||[],!1).length}});var q,L=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/;(w.fn.init=function(e,t,n){var i,o;if(!e)return this;if(n=n||q,"string"==typeof e){if(!(i="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:L.exec(e))||!i[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(i[1]){if(t=t instanceof w?t[0]:t,w.merge(this,w.parseHTML(i[1],t&&t.nodeType?t.ownerDocument||t:r,!0)),A.test(i[1])&&w.isPlainObject(t))for(i in t)g(this[i])?this[i](t[i]):this.attr(i,t[i]);return this}return(o=r.getElementById(i[2]))&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):g(e)?void 0!==n.ready?n.ready(e):e(w):w.makeArray(e,this)}).prototype=w.fn,q=w(r);var H=/^(?:parents|prev(?:Until|All))/,O={children:!0,contents:!0,next:!0,prev:!0};w.fn.extend({has:function(e){var t=w(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&w.find.matchesSelector(n,e))){o.push(n);break}return this.pushStack(o.length>1?w.uniqueSort(o):o)},index:function(e){return e?"string"==typeof e?u.call(w(e),this[0]):u.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(w.uniqueSort(w.merge(this.get(),w(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function P(e,t){while((e=e[t])&&1!==e.nodeType);return e}w.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return k(e,"parentNode")},parentsUntil:function(e,t,n){return k(e,"parentNode",n)},next:function(e){return P(e,"nextSibling")},prev:function(e){return P(e,"previousSibling")},nextAll:function(e){return k(e,"nextSibling")},prevAll:function(e){return k(e,"previousSibling")},nextUntil:function(e,t,n){return k(e,"nextSibling",n)},prevUntil:function(e,t,n){return k(e,"previousSibling",n)},siblings:function(e){return S((e.parentNode||{}).firstChild,e)},children:function(e){return S(e.firstChild)},contents:function(e){return N(e,"iframe")?e.contentDocument:(N(e,"template")&&(e=e.content||e),w.merge([],e.childNodes))}},function(e,t){w.fn[e]=function(n,r){var i=w.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=w.filter(r,i)),this.length>1&&(O[e]||w.uniqueSort(i),H.test(e)&&i.reverse()),this.pushStack(i)}});var M=/[^\x20\t\r\n\f]+/g;function R(e){var t={};return w.each(e.match(M)||[],function(e,n){t[n]=!0}),t}w.Callbacks=function(e){e="string"==typeof e?R(e):w.extend({},e);var t,n,r,i,o=[],a=[],s=-1,u=function(){for(i=i||e.once,r=t=!0;a.length;s=-1){n=a.shift();while(++s-1)o.splice(n,1),n<=s&&s--}),this},has:function(e){return e?w.inArray(e,o)>-1:o.length>0},empty:function(){return o&&(o=[]),this},disable:function(){return i=a=[],o=n="",this},disabled:function(){return!o},lock:function(){return i=a=[],n||t||(o=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=[e,(n=n||[]).slice?n.slice():n],a.push(n),t||u()),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!r}};return l};function I(e){return e}function W(e){throw e}function $(e,t,n,r){var i;try{e&&g(i=e.promise)?i.call(e).done(t).fail(n):e&&g(i=e.then)?i.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}w.extend({Deferred:function(t){var n=[["notify","progress",w.Callbacks("memory"),w.Callbacks("memory"),2],["resolve","done",w.Callbacks("once memory"),w.Callbacks("once memory"),0,"resolved"],["reject","fail",w.Callbacks("once memory"),w.Callbacks("once memory"),1,"rejected"]],r="pending",i={state:function(){return r},always:function(){return o.done(arguments).fail(arguments),this},"catch":function(e){return i.then(null,e)},pipe:function(){var e=arguments;return w.Deferred(function(t){w.each(n,function(n,r){var i=g(e[r[4]])&&e[r[4]];o[r[1]](function(){var e=i&&i.apply(this,arguments);e&&g(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,i?[e]:arguments)})}),e=null}).promise()},then:function(t,r,i){var o=0;function a(t,n,r,i){return function(){var s=this,u=arguments,l=function(){var e,l;if(!(t=o&&(r!==W&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?c():(w.Deferred.getStackHook&&(c.stackTrace=w.Deferred.getStackHook()),e.setTimeout(c))}}return w.Deferred(function(e){n[0][3].add(a(0,e,g(i)?i:I,e.notifyWith)),n[1][3].add(a(0,e,g(t)?t:I)),n[2][3].add(a(0,e,g(r)?r:W))}).promise()},promise:function(e){return null!=e?w.extend(e,i):i}},o={};return w.each(n,function(e,t){var a=t[2],s=t[5];i[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),o[t[0]]=function(){return o[t[0]+"With"](this===o?void 0:this,arguments),this},o[t[0]+"With"]=a.fireWith}),i.promise(o),t&&t.call(o,o),o},when:function(e){var t=arguments.length,n=t,r=Array(n),i=o.call(arguments),a=w.Deferred(),s=function(e){return function(n){r[e]=this,i[e]=arguments.length>1?o.call(arguments):n,--t||a.resolveWith(r,i)}};if(t<=1&&($(e,a.done(s(n)).resolve,a.reject,!t),"pending"===a.state()||g(i[n]&&i[n].then)))return a.then();while(n--)$(i[n],s(n),a.reject);return a.promise()}});var B=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;w.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&B.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},w.readyException=function(t){e.setTimeout(function(){throw t})};var F=w.Deferred();w.fn.ready=function(e){return F.then(e)["catch"](function(e){w.readyException(e)}),this},w.extend({isReady:!1,readyWait:1,ready:function(e){(!0===e?--w.readyWait:w.isReady)||(w.isReady=!0,!0!==e&&--w.readyWait>0||F.resolveWith(r,[w]))}}),w.ready.then=F.then;function _(){r.removeEventListener("DOMContentLoaded",_),e.removeEventListener("load",_),w.ready()}"complete"===r.readyState||"loading"!==r.readyState&&!r.documentElement.doScroll?e.setTimeout(w.ready):(r.addEventListener("DOMContentLoaded",_),e.addEventListener("load",_));var z=function(e,t,n,r,i,o,a){var s=0,u=e.length,l=null==n;if("object"===x(n)){i=!0;for(s in n)z(e,t,s,n[s],!0,o,a)}else if(void 0!==r&&(i=!0,g(r)||(a=!0),l&&(a?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(w(e),n)})),t))for(;s1,null,!0)},removeData:function(e){return this.each(function(){K.remove(this,e)})}}),w.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=J.get(e,t),n&&(!r||Array.isArray(n)?r=J.access(e,t,w.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=w.queue(e,t),r=n.length,i=n.shift(),o=w._queueHooks(e,t),a=function(){w.dequeue(e,t)};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,a,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return J.get(e,n)||J.access(e,n,{empty:w.Callbacks("once memory").add(function(){J.remove(e,[t+"queue",n])})})}}),w.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,he=/^$|^module$|\/(?:java|ecma)script/i,ge={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ge.optgroup=ge.option,ge.tbody=ge.tfoot=ge.colgroup=ge.caption=ge.thead,ge.th=ge.td;function ye(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&N(e,t)?w.merge([e],n):n}function ve(e,t){for(var n=0,r=e.length;n-1)i&&i.push(o);else if(l=w.contains(o.ownerDocument,o),a=ye(f.appendChild(o),"script"),l&&ve(a),n){c=0;while(o=a[c++])he.test(o.type||"")&&n.push(o)}return f}!function(){var e=r.createDocumentFragment().appendChild(r.createElement("div")),t=r.createElement("input");t.setAttribute("type","radio"),t.setAttribute("checked","checked"),t.setAttribute("name","t"),e.appendChild(t),h.checkClone=e.cloneNode(!0).cloneNode(!0).lastChild.checked,e.innerHTML="",h.noCloneChecked=!!e.cloneNode(!0).lastChild.defaultValue}();var be=r.documentElement,we=/^key/,Te=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,Ce=/^([^.]*)(?:\.(.+)|)/;function Ee(){return!0}function ke(){return!1}function Se(){try{return r.activeElement}catch(e){}}function De(e,t,n,r,i,o){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)De(e,s,n,r,t[s],o);return e}if(null==r&&null==i?(i=n,r=n=void 0):null==i&&("string"==typeof n?(i=r,r=void 0):(i=r,r=n,n=void 0)),!1===i)i=ke;else if(!i)return e;return 1===o&&(a=i,(i=function(e){return w().off(e),a.apply(this,arguments)}).guid=a.guid||(a.guid=w.guid++)),e.each(function(){w.event.add(this,t,i,r,n)})}w.event={global:{},add:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.get(e);if(y){n.handler&&(n=(o=n).handler,i=o.selector),i&&w.find.matchesSelector(be,i),n.guid||(n.guid=w.guid++),(u=y.events)||(u=y.events={}),(a=y.handle)||(a=y.handle=function(t){return"undefined"!=typeof w&&w.event.triggered!==t.type?w.event.dispatch.apply(e,arguments):void 0}),l=(t=(t||"").match(M)||[""]).length;while(l--)d=g=(s=Ce.exec(t[l])||[])[1],h=(s[2]||"").split(".").sort(),d&&(f=w.event.special[d]||{},d=(i?f.delegateType:f.bindType)||d,f=w.event.special[d]||{},c=w.extend({type:d,origType:g,data:r,handler:n,guid:n.guid,selector:i,needsContext:i&&w.expr.match.needsContext.test(i),namespace:h.join(".")},o),(p=u[d])||((p=u[d]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(e,r,h,a)||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),i?p.splice(p.delegateCount++,0,c):p.push(c),w.event.global[d]=!0)}},remove:function(e,t,n,r,i){var o,a,s,u,l,c,f,p,d,h,g,y=J.hasData(e)&&J.get(e);if(y&&(u=y.events)){l=(t=(t||"").match(M)||[""]).length;while(l--)if(s=Ce.exec(t[l])||[],d=g=s[1],h=(s[2]||"").split(".").sort(),d){f=w.event.special[d]||{},p=u[d=(r?f.delegateType:f.bindType)||d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=o=p.length;while(o--)c=p[o],!i&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(o,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&!1!==f.teardown.call(e,h,y.handle)||w.removeEvent(e,d,y.handle),delete u[d])}else for(d in u)w.event.remove(e,d+t[l],n,r,!0);w.isEmptyObject(u)&&J.remove(e,"handle events")}},dispatch:function(e){var t=w.event.fix(e),n,r,i,o,a,s,u=new Array(arguments.length),l=(J.get(this,"events")||{})[t.type]||[],c=w.event.special[t.type]||{};for(u[0]=t,n=1;n=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||!0!==l.disabled)){for(o=[],a={},n=0;n-1:w.find(i,this,null,[l]).length),a[i]&&o.push(r);o.length&&s.push({elem:l,handlers:o})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,Ae=/\s*$/g;function Le(e,t){return N(e,"table")&&N(11!==t.nodeType?t:t.firstChild,"tr")?w(e).children("tbody")[0]||e:e}function He(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function Oe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Pe(e,t){var n,r,i,o,a,s,u,l;if(1===t.nodeType){if(J.hasData(e)&&(o=J.access(e),a=J.set(t,o),l=o.events)){delete a.handle,a.events={};for(i in l)for(n=0,r=l[i].length;n1&&"string"==typeof y&&!h.checkClone&&je.test(y))return e.each(function(i){var o=e.eq(i);v&&(t[0]=y.call(this,i,o.html())),Re(o,t,n,r)});if(p&&(i=xe(t,e[0].ownerDocument,!1,e,r),o=i.firstChild,1===i.childNodes.length&&(i=o),o||r)){for(u=(s=w.map(ye(i,"script"),He)).length;f")},clone:function(e,t,n){var r,i,o,a,s=e.cloneNode(!0),u=w.contains(e.ownerDocument,e);if(!(h.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||w.isXMLDoc(e)))for(a=ye(s),r=0,i=(o=ye(e)).length;r0&&ve(a,!u&&ye(e,"script")),s},cleanData:function(e){for(var t,n,r,i=w.event.special,o=0;void 0!==(n=e[o]);o++)if(Y(n)){if(t=n[J.expando]){if(t.events)for(r in t.events)i[r]?w.event.remove(n,r):w.removeEvent(n,r,t.handle);n[J.expando]=void 0}n[K.expando]&&(n[K.expando]=void 0)}}}),w.fn.extend({detach:function(e){return Ie(this,e,!0)},remove:function(e){return Ie(this,e)},text:function(e){return z(this,function(e){return void 0===e?w.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return Re(this,arguments,function(e){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||Le(this,e).appendChild(e)})},prepend:function(){return Re(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=Le(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return Re(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(w.cleanData(ye(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return w.clone(this,e,t)})},html:function(e){return z(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!Ae.test(e)&&!ge[(de.exec(e)||["",""])[1].toLowerCase()]){e=w.htmlPrefilter(e);try{for(;n=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-o-u-s-.5))),u}function et(e,t,n){var r=$e(e),i=Fe(e,t,r),o="border-box"===w.css(e,"boxSizing",!1,r),a=o;if(We.test(i)){if(!n)return i;i="auto"}return a=a&&(h.boxSizingReliable()||i===e.style[t]),("auto"===i||!parseFloat(i)&&"inline"===w.css(e,"display",!1,r))&&(i=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),(i=parseFloat(i)||0)+Ze(e,t,n||(o?"border":"content"),a,r,i)+"px"}w.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Fe(e,"opacity");return""===n?"1":n}}}},cssNumber:{animationIterationCount:!0,columnCount:!0,fillOpacity:!0,flexGrow:!0,flexShrink:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,a,s=G(t),u=Xe.test(t),l=e.style;if(u||(t=Je(s)),a=w.cssHooks[t]||w.cssHooks[s],void 0===n)return a&&"get"in a&&void 0!==(i=a.get(e,!1,r))?i:l[t];"string"==(o=typeof n)&&(i=ie.exec(n))&&i[1]&&(n=ue(e,t,i),o="number"),null!=n&&n===n&&("number"===o&&(n+=i&&i[3]||(w.cssNumber[s]?"":"px")),h.clearCloneStyle||""!==n||0!==t.indexOf("background")||(l[t]="inherit"),a&&"set"in a&&void 0===(n=a.set(e,n,r))||(u?l.setProperty(t,n):l[t]=n))}},css:function(e,t,n,r){var i,o,a,s=G(t);return Xe.test(t)||(t=Je(s)),(a=w.cssHooks[t]||w.cssHooks[s])&&"get"in a&&(i=a.get(e,!0,n)),void 0===i&&(i=Fe(e,t,r)),"normal"===i&&t in Ve&&(i=Ve[t]),""===n||n?(o=parseFloat(i),!0===n||isFinite(o)?o||0:i):i}}),w.each(["height","width"],function(e,t){w.cssHooks[t]={get:function(e,n,r){if(n)return!ze.test(w.css(e,"display"))||e.getClientRects().length&&e.getBoundingClientRect().width?et(e,t,r):se(e,Ue,function(){return et(e,t,r)})},set:function(e,n,r){var i,o=$e(e),a="border-box"===w.css(e,"boxSizing",!1,o),s=r&&Ze(e,t,r,a,o);return a&&h.scrollboxSize()===o.position&&(s-=Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-parseFloat(o[t])-Ze(e,t,"border",!1,o)-.5)),s&&(i=ie.exec(n))&&"px"!==(i[3]||"px")&&(e.style[t]=n,n=w.css(e,t)),Ke(e,n,s)}}}),w.cssHooks.marginLeft=_e(h.reliableMarginLeft,function(e,t){if(t)return(parseFloat(Fe(e,"marginLeft"))||e.getBoundingClientRect().left-se(e,{marginLeft:0},function(){return e.getBoundingClientRect().left}))+"px"}),w.each({margin:"",padding:"",border:"Width"},function(e,t){w.cssHooks[e+t]={expand:function(n){for(var r=0,i={},o="string"==typeof n?n.split(" "):[n];r<4;r++)i[e+oe[r]+t]=o[r]||o[r-2]||o[0];return i}},"margin"!==e&&(w.cssHooks[e+t].set=Ke)}),w.fn.extend({css:function(e,t){return z(this,function(e,t,n){var r,i,o={},a=0;if(Array.isArray(t)){for(r=$e(e),i=t.length;a1)}});function tt(e,t,n,r,i){return new tt.prototype.init(e,t,n,r,i)}w.Tween=tt,tt.prototype={constructor:tt,init:function(e,t,n,r,i,o){this.elem=e,this.prop=n,this.easing=i||w.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=o||(w.cssNumber[n]?"":"px")},cur:function(){var e=tt.propHooks[this.prop];return e&&e.get?e.get(this):tt.propHooks._default.get(this)},run:function(e){var t,n=tt.propHooks[this.prop];return this.options.duration?this.pos=t=w.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):tt.propHooks._default.set(this),this}},tt.prototype.init.prototype=tt.prototype,tt.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=w.css(e.elem,e.prop,""))&&"auto"!==t?t:0},set:function(e){w.fx.step[e.prop]?w.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[w.cssProps[e.prop]]&&!w.cssHooks[e.prop]?e.elem[e.prop]=e.now:w.style(e.elem,e.prop,e.now+e.unit)}}},tt.propHooks.scrollTop=tt.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},w.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},w.fx=tt.prototype.init,w.fx.step={};var nt,rt,it=/^(?:toggle|show|hide)$/,ot=/queueHooks$/;function at(){rt&&(!1===r.hidden&&e.requestAnimationFrame?e.requestAnimationFrame(at):e.setTimeout(at,w.fx.interval),w.fx.tick())}function st(){return e.setTimeout(function(){nt=void 0}),nt=Date.now()}function ut(e,t){var n,r=0,i={height:e};for(t=t?1:0;r<4;r+=2-t)i["margin"+(n=oe[r])]=i["padding"+n]=e;return t&&(i.opacity=i.width=e),i}function lt(e,t,n){for(var r,i=(pt.tweeners[t]||[]).concat(pt.tweeners["*"]),o=0,a=i.length;o1)},removeAttr:function(e){return this.each(function(){w.removeAttr(this,e)})}}),w.extend({attr:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return"undefined"==typeof e.getAttribute?w.prop(e,t,n):(1===o&&w.isXMLDoc(e)||(i=w.attrHooks[t.toLowerCase()]||(w.expr.match.bool.test(t)?dt:void 0)),void 0!==n?null===n?void w.removeAttr(e,t):i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:(e.setAttribute(t,n+""),n):i&&"get"in i&&null!==(r=i.get(e,t))?r:null==(r=w.find.attr(e,t))?void 0:r)},attrHooks:{type:{set:function(e,t){if(!h.radioValue&&"radio"===t&&N(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,i=t&&t.match(M);if(i&&1===e.nodeType)while(n=i[r++])e.removeAttribute(n)}}),dt={set:function(e,t,n){return!1===t?w.removeAttr(e,n):e.setAttribute(n,n),n}},w.each(w.expr.match.bool.source.match(/\w+/g),function(e,t){var n=ht[t]||w.find.attr;ht[t]=function(e,t,r){var i,o,a=t.toLowerCase();return r||(o=ht[a],ht[a]=i,i=null!=n(e,t,r)?a:null,ht[a]=o),i}});var gt=/^(?:input|select|textarea|button)$/i,yt=/^(?:a|area)$/i;w.fn.extend({prop:function(e,t){return z(this,w.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[w.propFix[e]||e]})}}),w.extend({prop:function(e,t,n){var r,i,o=e.nodeType;if(3!==o&&8!==o&&2!==o)return 1===o&&w.isXMLDoc(e)||(t=w.propFix[t]||t,i=w.propHooks[t]),void 0!==n?i&&"set"in i&&void 0!==(r=i.set(e,n,t))?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=w.find.attr(e,"tabindex");return t?parseInt(t,10):gt.test(e.nodeName)||yt.test(e.nodeName)&&e.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),h.optSelected||(w.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),w.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){w.propFix[this.toLowerCase()]=this});function vt(e){return(e.match(M)||[]).join(" ")}function mt(e){return e.getAttribute&&e.getAttribute("class")||""}function xt(e){return Array.isArray(e)?e:"string"==typeof e?e.match(M)||[]:[]}w.fn.extend({addClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).addClass(e.call(this,t,mt(this)))});if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])r.indexOf(" "+o+" ")<0&&(r+=o+" ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,i,o,a,s,u=0;if(g(e))return this.each(function(t){w(this).removeClass(e.call(this,t,mt(this)))});if(!arguments.length)return this.attr("class","");if((t=xt(e)).length)while(n=this[u++])if(i=mt(n),r=1===n.nodeType&&" "+vt(i)+" "){a=0;while(o=t[a++])while(r.indexOf(" "+o+" ")>-1)r=r.replace(" "+o+" "," ");i!==(s=vt(r))&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):g(e)?this.each(function(n){w(this).toggleClass(e.call(this,n,mt(this),t),t)}):this.each(function(){var t,i,o,a;if(r){i=0,o=w(this),a=xt(e);while(t=a[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else void 0!==e&&"boolean"!==n||((t=mt(this))&&J.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||!1===e?"":J.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;t=" "+e+" ";while(n=this[r++])if(1===n.nodeType&&(" "+vt(mt(n))+" ").indexOf(t)>-1)return!0;return!1}});var bt=/\r/g;w.fn.extend({val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=g(e),this.each(function(n){var i;1===this.nodeType&&(null==(i=r?e.call(this,n,w(this).val()):e)?i="":"number"==typeof i?i+="":Array.isArray(i)&&(i=w.map(i,function(e){return null==e?"":e+""})),(t=w.valHooks[this.type]||w.valHooks[this.nodeName.toLowerCase()])&&"set"in t&&void 0!==t.set(this,i,"value")||(this.value=i))});if(i)return(t=w.valHooks[i.type]||w.valHooks[i.nodeName.toLowerCase()])&&"get"in t&&void 0!==(n=t.get(i,"value"))?n:"string"==typeof(n=i.value)?n.replace(bt,""):null==n?"":n}}}),w.extend({valHooks:{option:{get:function(e){var t=w.find.attr(e,"value");return null!=t?t:vt(w.text(e))}},select:{get:function(e){var t,n,r,i=e.options,o=e.selectedIndex,a="select-one"===e.type,s=a?null:[],u=a?o+1:i.length;for(r=o<0?u:a?o:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),o}}}}),w.each(["radio","checkbox"],function(){w.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=w.inArray(w(e).val(),t)>-1}},h.checkOn||(w.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),h.focusin="onfocusin"in e;var wt=/^(?:focusinfocus|focusoutblur)$/,Tt=function(e){e.stopPropagation()};w.extend(w.event,{trigger:function(t,n,i,o){var a,s,u,l,c,p,d,h,v=[i||r],m=f.call(t,"type")?t.type:t,x=f.call(t,"namespace")?t.namespace.split("."):[];if(s=h=u=i=i||r,3!==i.nodeType&&8!==i.nodeType&&!wt.test(m+w.event.triggered)&&(m.indexOf(".")>-1&&(m=(x=m.split(".")).shift(),x.sort()),c=m.indexOf(":")<0&&"on"+m,t=t[w.expando]?t:new w.Event(m,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=x.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+x.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=i),n=null==n?[t]:w.makeArray(n,[t]),d=w.event.special[m]||{},o||!d.trigger||!1!==d.trigger.apply(i,n))){if(!o&&!d.noBubble&&!y(i)){for(l=d.delegateType||m,wt.test(l+m)||(s=s.parentNode);s;s=s.parentNode)v.push(s),u=s;u===(i.ownerDocument||r)&&v.push(u.defaultView||u.parentWindow||e)}a=0;while((s=v[a++])&&!t.isPropagationStopped())h=s,t.type=a>1?l:d.bindType||m,(p=(J.get(s,"events")||{})[t.type]&&J.get(s,"handle"))&&p.apply(s,n),(p=c&&s[c])&&p.apply&&Y(s)&&(t.result=p.apply(s,n),!1===t.result&&t.preventDefault());return t.type=m,o||t.isDefaultPrevented()||d._default&&!1!==d._default.apply(v.pop(),n)||!Y(i)||c&&g(i[m])&&!y(i)&&((u=i[c])&&(i[c]=null),w.event.triggered=m,t.isPropagationStopped()&&h.addEventListener(m,Tt),i[m](),t.isPropagationStopped()&&h.removeEventListener(m,Tt),w.event.triggered=void 0,u&&(i[c]=u)),t.result}},simulate:function(e,t,n){var r=w.extend(new w.Event,n,{type:e,isSimulated:!0});w.event.trigger(r,null,t)}}),w.fn.extend({trigger:function(e,t){return this.each(function(){w.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return w.event.trigger(e,t,n,!0)}}),h.focusin||w.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){w.event.simulate(t,e.target,w.event.fix(e))};w.event.special[t]={setup:function(){var r=this.ownerDocument||this,i=J.access(r,t);i||r.addEventListener(e,n,!0),J.access(r,t,(i||0)+1)},teardown:function(){var r=this.ownerDocument||this,i=J.access(r,t)-1;i?J.access(r,t,i):(r.removeEventListener(e,n,!0),J.remove(r,t))}}});var Ct=e.location,Et=Date.now(),kt=/\?/;w.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||w.error("Invalid XML: "+t),n};var St=/\[\]$/,Dt=/\r?\n/g,Nt=/^(?:submit|button|image|reset|file)$/i,At=/^(?:input|select|textarea|keygen)/i;function jt(e,t,n,r){var i;if(Array.isArray(t))w.each(t,function(t,i){n||St.test(e)?r(e,i):jt(e+"["+("object"==typeof i&&null!=i?t:"")+"]",i,n,r)});else if(n||"object"!==x(t))r(e,t);else for(i in t)jt(e+"["+i+"]",t[i],n,r)}w.param=function(e,t){var n,r=[],i=function(e,t){var n=g(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!w.isPlainObject(e))w.each(e,function(){i(this.name,this.value)});else for(n in e)jt(n,e[n],t,i);return r.join("&")},w.fn.extend({serialize:function(){return w.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=w.prop(this,"elements");return e?w.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!w(this).is(":disabled")&&At.test(this.nodeName)&&!Nt.test(e)&&(this.checked||!pe.test(e))}).map(function(e,t){var n=w(this).val();return null==n?null:Array.isArray(n)?w.map(n,function(e){return{name:t.name,value:e.replace(Dt,"\r\n")}}):{name:t.name,value:n.replace(Dt,"\r\n")}}).get()}});var qt=/%20/g,Lt=/#.*$/,Ht=/([?&])_=[^&]*/,Ot=/^(.*?):[ \t]*([^\r\n]*)$/gm,Pt=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Mt=/^(?:GET|HEAD)$/,Rt=/^\/\//,It={},Wt={},$t="*/".concat("*"),Bt=r.createElement("a");Bt.href=Ct.href;function Ft(e){return function(t,n){"string"!=typeof t&&(n=t,t="*");var r,i=0,o=t.toLowerCase().match(M)||[];if(g(n))while(r=o[i++])"+"===r[0]?(r=r.slice(1)||"*",(e[r]=e[r]||[]).unshift(n)):(e[r]=e[r]||[]).push(n)}}function _t(e,t,n,r){var i={},o=e===Wt;function a(s){var u;return i[s]=!0,w.each(e[s]||[],function(e,s){var l=s(t,n,r);return"string"!=typeof l||o||i[l]?o?!(u=l):void 0:(t.dataTypes.unshift(l),a(l),!1)}),u}return a(t.dataTypes[0])||!i["*"]&&a("*")}function zt(e,t){var n,r,i=w.ajaxSettings.flatOptions||{};for(n in t)void 0!==t[n]&&((i[n]?e:r||(r={}))[n]=t[n]);return r&&w.extend(!0,e,r),e}function Xt(e,t,n){var r,i,o,a,s=e.contents,u=e.dataTypes;while("*"===u[0])u.shift(),void 0===r&&(r=e.mimeType||t.getResponseHeader("Content-Type"));if(r)for(i in s)if(s[i]&&s[i].test(r)){u.unshift(i);break}if(u[0]in n)o=u[0];else{for(i in n){if(!u[0]||e.converters[i+" "+u[0]]){o=i;break}a||(a=i)}o=o||a}if(o)return o!==u[0]&&u.unshift(o),n[o]}function Ut(e,t,n,r){var i,o,a,s,u,l={},c=e.dataTypes.slice();if(c[1])for(a in e.converters)l[a.toLowerCase()]=e.converters[a];o=c.shift();while(o)if(e.responseFields[o]&&(n[e.responseFields[o]]=t),!u&&r&&e.dataFilter&&(t=e.dataFilter(t,e.dataType)),u=o,o=c.shift())if("*"===o)o=u;else if("*"!==u&&u!==o){if(!(a=l[u+" "+o]||l["* "+o]))for(i in l)if((s=i.split(" "))[1]===o&&(a=l[u+" "+s[0]]||l["* "+s[0]])){!0===a?a=l[i]:!0!==l[i]&&(o=s[0],c.unshift(s[1]));break}if(!0!==a)if(a&&e["throws"])t=a(t);else try{t=a(t)}catch(e){return{state:"parsererror",error:a?e:"No conversion from "+u+" to "+o}}}return{state:"success",data:t}}w.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:Ct.href,type:"GET",isLocal:Pt.test(Ct.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":$t,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":w.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?zt(zt(e,w.ajaxSettings),t):zt(w.ajaxSettings,e)},ajaxPrefilter:Ft(It),ajaxTransport:Ft(Wt),ajax:function(t,n){"object"==typeof t&&(n=t,t=void 0),n=n||{};var i,o,a,s,u,l,c,f,p,d,h=w.ajaxSetup({},n),g=h.context||h,y=h.context&&(g.nodeType||g.jquery)?w(g):w.event,v=w.Deferred(),m=w.Callbacks("once memory"),x=h.statusCode||{},b={},T={},C="canceled",E={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s){s={};while(t=Ot.exec(a))s[t[1].toLowerCase()]=t[2]}t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=T[e.toLowerCase()]=T[e.toLowerCase()]||e,b[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)E.always(e[E.status]);else for(t in e)x[t]=[x[t],e[t]];return this},abort:function(e){var t=e||C;return i&&i.abort(t),k(0,t),this}};if(v.promise(E),h.url=((t||h.url||Ct.href)+"").replace(Rt,Ct.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(M)||[""],null==h.crossDomain){l=r.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Bt.protocol+"//"+Bt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=w.param(h.data,h.traditional)),_t(It,h,n,E),c)return E;(f=w.event&&h.global)&&0==w.active++&&w.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Mt.test(h.type),o=h.url.replace(Lt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(qt,"+")):(d=h.url.slice(o.length),h.data&&(h.processData||"string"==typeof h.data)&&(o+=(kt.test(o)?"&":"?")+h.data,delete h.data),!1===h.cache&&(o=o.replace(Ht,"$1"),d=(kt.test(o)?"&":"?")+"_="+Et+++d),h.url=o+d),h.ifModified&&(w.lastModified[o]&&E.setRequestHeader("If-Modified-Since",w.lastModified[o]),w.etag[o]&&E.setRequestHeader("If-None-Match",w.etag[o])),(h.data&&h.hasContent&&!1!==h.contentType||n.contentType)&&E.setRequestHeader("Content-Type",h.contentType),E.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+$t+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)E.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(!1===h.beforeSend.call(g,E,h)||c))return E.abort();if(C="abort",m.add(h.complete),E.done(h.success),E.fail(h.error),i=_t(Wt,h,n,E)){if(E.readyState=1,f&&y.trigger("ajaxSend",[E,h]),c)return E;h.async&&h.timeout>0&&(u=e.setTimeout(function(){E.abort("timeout")},h.timeout));try{c=!1,i.send(b,k)}catch(e){if(c)throw e;k(-1,e)}}else k(-1,"No Transport");function k(t,n,r,s){var l,p,d,b,T,C=n;c||(c=!0,u&&e.clearTimeout(u),i=void 0,a=s||"",E.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(b=Xt(h,E,r)),b=Ut(h,b,E,l),l?(h.ifModified&&((T=E.getResponseHeader("Last-Modified"))&&(w.lastModified[o]=T),(T=E.getResponseHeader("etag"))&&(w.etag[o]=T)),204===t||"HEAD"===h.type?C="nocontent":304===t?C="notmodified":(C=b.state,p=b.data,l=!(d=b.error))):(d=C,!t&&C||(C="error",t<0&&(t=0))),E.status=t,E.statusText=(n||C)+"",l?v.resolveWith(g,[p,C,E]):v.rejectWith(g,[E,C,d]),E.statusCode(x),x=void 0,f&&y.trigger(l?"ajaxSuccess":"ajaxError",[E,h,l?p:d]),m.fireWith(g,[E,C]),f&&(y.trigger("ajaxComplete",[E,h]),--w.active||w.event.trigger("ajaxStop")))}return E},getJSON:function(e,t,n){return w.get(e,t,n,"json")},getScript:function(e,t){return w.get(e,void 0,t,"script")}}),w.each(["get","post"],function(e,t){w[t]=function(e,n,r,i){return g(n)&&(i=i||r,r=n,n=void 0),w.ajax(w.extend({url:e,type:t,dataType:i,data:n,success:r},w.isPlainObject(e)&&e))}}),w._evalUrl=function(e){return w.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},w.fn.extend({wrapAll:function(e){var t;return this[0]&&(g(e)&&(e=e.call(this[0])),t=w(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return g(e)?this.each(function(t){w(this).wrapInner(e.call(this,t))}):this.each(function(){var t=w(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=g(e);return this.each(function(n){w(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){w(this).replaceWith(this.childNodes)}),this}}),w.expr.pseudos.hidden=function(e){return!w.expr.pseudos.visible(e)},w.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},w.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Gt=w.ajaxSettings.xhr();h.cors=!!Gt&&"withCredentials"in Gt,h.ajax=Gt=!!Gt,w.ajaxTransport(function(t){var n,r;if(h.cors||Gt&&!t.crossDomain)return{send:function(i,o){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||i["X-Requested-With"]||(i["X-Requested-With"]="XMLHttpRequest");for(a in i)s.setRequestHeader(a,i[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?o(0,"error"):o(s.status,s.statusText):o(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),w.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),w.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return w.globalEval(e),e}}}),w.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),w.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(i,o){t=w(" + + + + + + + + + + + + + + + + + + + +
+
+
+
+

Help

+

For help please, click the following link HERE to download the user manual to assist in your use of RaiderPlanner.

+
+
+
+
+ + + + + + + + diff --git a/docs/images/RaiderExceptionERDiagram.jpg b/docs/images/RaiderExceptionERDiagram.jpg new file mode 100644 index 00000000..53e9b5bd Binary files /dev/null and b/docs/images/RaiderExceptionERDiagram.jpg differ diff --git a/docs/images/ddlsetup.png b/docs/images/ddlsetup.png new file mode 100644 index 00000000..a8ea7bff Binary files /dev/null and b/docs/images/ddlsetup.png differ diff --git a/docs/images/derbyconnectionprofile.png b/docs/images/derbyconnectionprofile.png new file mode 100644 index 00000000..7e506513 Binary files /dev/null and b/docs/images/derbyconnectionprofile.png differ diff --git a/docs/images/derbydriver.png b/docs/images/derbydriver.png new file mode 100644 index 00000000..51024d2b Binary files /dev/null and b/docs/images/derbydriver.png differ diff --git a/docs/images/derbydriverping.png b/docs/images/derbydriverping.png new file mode 100644 index 00000000..5dd08639 Binary files /dev/null and b/docs/images/derbydriverping.png differ diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 00000000..95671a29 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,363 @@ + + + + + + + RaiderPlanner + + + + + + + + + + + + + + + +
+
+
+
Welcome To 
+
RaiderPlanner
Tell me more
+
+
+
+
+
+
+

Stay on track.

+

Life gets complicatied, and keeping track of homework and exam dates is not the first thing on your mind. RaiderPlanner will be ther to help you.

+
+
+
+
+

Calender

+

All tasks and activities will be updated to your calender, providing 
you with a monthly overview without 
the hassle of manulaly putting in the dates .

+
+
+

Reminders when you need them

+

You won't be forgetting about that exam next week with an alert popping up to tell you it's time to study!

+
+
+

Track your Progress

+

Use the Gantt diagram to view how far you have come and how much more you need to accomplish!

+
+
+
+
+
+
+
+
+

About

+
+
+
+
+
    +
  • +
    +
    +
    +

    December 2016

    +

    Our Humble Beginnings

    +
    +
    +

    Students from the University of East Anglia located in Norwich, England took on a Study Planner project as part of their course work for "CMP-5012B - Software Engineering I". Which led to PearPlanner.

    +
    +
    +
  • +
  • +
    +
    +
    +

    RaiderPlanner

    +

    A new project is Born

    +
    +
    +

    RaiderPlanner is the PearPlanner's direct successor, a class project comprising most of the coursework for Wright State University's "Introduction to the Design of IT Syetems" class (CEG 3120). The class uses GitHub + as the file storage repository.

    +
    +
    +
  • +
  • +
    +
    +
    +

    Purpose

    +
    +
    +

    The purpose of this project is to implement a program containing the pertinent information from a given semester (coursework, deadline information, exam information etc.) with a simple Graphical User Interface.

    +
    +
    +
  • +
  • +
    +
    +
    +

    Capabilities

    +

    4 major ones:

    +
    +
    +
      +
    1. Load module, coursework and deadline information from a defined file format.
    2. +
    3. Define study tasks, details, milestones and deadlines.
    4. +
    5. Record study activities that contribute towards completing study tasks and milestones.
    6. +
    7. Visualize activities, dependencies, intermediate milestones and deadlines using Gantt chart representation as well as a study progress dashboard that highlights upcoming deadlines and progress towards completing + milestones and time spent for each module.
    8. +
    +
    +
    +
  • +
  • +
    +

    Be Part
     Of Our
     Story!

    +
    +
  • +
+
+
+
+
+
+
+
+
+

A short video highlighting RaiderPlanner's current features:

+
+
+
+
+
+
+
+ +
+
+
+
+

The Way Ahead

+
+
+
+ +
+
+
+
+

Key Features

+
+
+
+ +
+
+
+
+

PearPlanner

+
+
+
+ +
+
+
+
+

Point of Contact

+
+
+
+ +
+
+
+
+

Downloads

+
+
+
+ +
+
+
+
+

User Manual

+
+
+
+

How to Create a New Account

+ +
+
+
+

Contact US:

+

Contact section under construction

+
+ + + + + + + + + + + + + + + + + diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 00000000..7a3265ee Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 00000000..9e7c1379 --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists +distributionUrl=https://services.gradle.org/distributions/gradle-4.10.2-bin.zip diff --git a/gradlew b/gradlew new file mode 100755 index 00000000..cccdd3d5 --- /dev/null +++ b/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 00000000..f9553162 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/icon.ico b/icon.ico new file mode 100644 index 00000000..d78defb4 Binary files /dev/null and b/icon.ico differ diff --git a/icon.png b/icon.png index ae0f444f..ecbe74d6 100644 Binary files a/icon.png and b/icon.png differ diff --git a/lib/AppleJavaExtensions-1.6.jar b/lib/AppleJavaExtensions-1.6.jar deleted file mode 100644 index 2e9fef92..00000000 Binary files a/lib/AppleJavaExtensions-1.6.jar and /dev/null differ diff --git a/lib/guava-21.0.jar b/lib/guava-21.0.jar deleted file mode 100644 index 06181959..00000000 Binary files a/lib/guava-21.0.jar and /dev/null differ diff --git a/lib/hamcrest-all-1.3.jar b/lib/hamcrest-all-1.3.jar deleted file mode 100644 index 6f62ba00..00000000 Binary files a/lib/hamcrest-all-1.3.jar and /dev/null differ diff --git a/lib/hamcrest-core-1.3.jar b/lib/hamcrest-core-1.3.jar deleted file mode 100644 index 9d5fe16e..00000000 Binary files a/lib/hamcrest-core-1.3.jar and /dev/null differ diff --git a/lib/hamcrest-generator-1.3.jar b/lib/hamcrest-generator-1.3.jar deleted file mode 100644 index ad99b5ac..00000000 Binary files a/lib/hamcrest-generator-1.3.jar and /dev/null differ diff --git a/lib/hamcrest-integration-1.3.jar b/lib/hamcrest-integration-1.3.jar deleted file mode 100644 index e07447a0..00000000 Binary files a/lib/hamcrest-integration-1.3.jar and /dev/null differ diff --git a/lib/hamcrest-junit-2.0.0.0.jar b/lib/hamcrest-junit-2.0.0.0.jar deleted file mode 100644 index 3585300b..00000000 Binary files a/lib/hamcrest-junit-2.0.0.0.jar and /dev/null differ diff --git a/lib/hamcrest-library-1.3.jar b/lib/hamcrest-library-1.3.jar deleted file mode 100644 index 9eac80d7..00000000 Binary files a/lib/hamcrest-library-1.3.jar and /dev/null differ diff --git a/lib/jfoenix.jar b/lib/jfoenix.jar deleted file mode 100644 index e47e8dc9..00000000 Binary files a/lib/jfoenix.jar and /dev/null differ diff --git a/lib/jfxtras-agenda-8.0-r5.jar b/lib/jfxtras-agenda-8.0-r5.jar deleted file mode 100644 index 20fc5c11..00000000 Binary files a/lib/jfxtras-agenda-8.0-r5.jar and /dev/null differ diff --git a/lib/jfxtras-common-8.0-r5.jar b/lib/jfxtras-common-8.0-r5.jar deleted file mode 100644 index 95204d2e..00000000 Binary files a/lib/jfxtras-common-8.0-r5.jar and /dev/null differ diff --git a/lib/jfxtras-controls-8.0-r5.jar b/lib/jfxtras-controls-8.0-r5.jar deleted file mode 100644 index 4c5b1842..00000000 Binary files a/lib/jfxtras-controls-8.0-r5.jar and /dev/null differ diff --git a/lib/jfxtras-labs-samples-8.0-r6-20160701.060717-1.jar b/lib/jfxtras-labs-samples-8.0-r6-20160701.060717-1.jar deleted file mode 100644 index f566d837..00000000 Binary files a/lib/jfxtras-labs-samples-8.0-r6-20160701.060717-1.jar and /dev/null differ diff --git a/lib/junit-4.12.jar b/lib/junit-4.12.jar deleted file mode 100644 index 3a7fc266..00000000 Binary files a/lib/junit-4.12.jar and /dev/null differ diff --git a/lib/testfx-core-4.0.0-20150226.214553-8-sources.jar b/lib/testfx-core-4.0.0-20150226.214553-8-sources.jar deleted file mode 100644 index f217e9a9..00000000 Binary files a/lib/testfx-core-4.0.0-20150226.214553-8-sources.jar and /dev/null differ diff --git a/lib/testfx-core-4.0.6-alpha.jar b/lib/testfx-core-4.0.6-alpha.jar deleted file mode 100644 index baa5e275..00000000 Binary files a/lib/testfx-core-4.0.6-alpha.jar and /dev/null differ diff --git a/lib/testfx-junit-4.0.6-alpha.jar b/lib/testfx-junit-4.0.6-alpha.jar deleted file mode 100644 index c6d3d029..00000000 Binary files a/lib/testfx-junit-4.0.6-alpha.jar and /dev/null differ diff --git a/lib/testfx-junit5-4.0.6-alpha.jar b/lib/testfx-junit5-4.0.6-alpha.jar deleted file mode 100644 index 35ad2da6..00000000 Binary files a/lib/testfx-junit5-4.0.6-alpha.jar and /dev/null differ diff --git a/lib/testfx-legacy-4.0.6-alpha.jar b/lib/testfx-legacy-4.0.6-alpha.jar deleted file mode 100644 index c667b28f..00000000 Binary files a/lib/testfx-legacy-4.0.6-alpha.jar and /dev/null differ diff --git a/saves/SamplePlanner.dat b/saves/SamplePlanner.dat new file mode 100644 index 00000000..4a95af11 Binary files /dev/null and b/saves/SamplePlanner.dat differ diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 00000000..dec82d74 --- /dev/null +++ b/settings.gradle @@ -0,0 +1,18 @@ +/* + * This settings file was generated by the Gradle 'init' task. + * + * The settings file is used to specify which projects to include in your build. + * In a single project build this file can be empty or even removed. + * + * Detailed information about configuring a multi-project build in Gradle can be found + * in the user guide at https://docs.gradle.org/4.1/userguide/multi_project_builds.html + */ + +/* +// To declare projects as part of a multi-project build use the 'include' method +include 'shared' +include 'api' +include 'services:webservice' +*/ +rootProject.name = 'RaiderPlanner.git' + diff --git a/src/Content/addactivity.png b/src/Content/addactivity.png deleted file mode 100644 index d565f283..00000000 Binary files a/src/Content/addactivity.png and /dev/null differ diff --git a/src/Content/back.png b/src/Content/back.png deleted file mode 100644 index daf391a3..00000000 Binary files a/src/Content/back.png and /dev/null differ diff --git a/src/Content/calendar.png b/src/Content/calendar.png deleted file mode 100644 index af07f27d..00000000 Binary files a/src/Content/calendar.png and /dev/null differ diff --git a/src/Content/importhubfile.png b/src/Content/importhubfile.png deleted file mode 100644 index faa79948..00000000 Binary files a/src/Content/importhubfile.png and /dev/null differ diff --git a/src/Content/milestones.png b/src/Content/milestones.png deleted file mode 100644 index afbb74ec..00000000 Binary files a/src/Content/milestones.png and /dev/null differ diff --git a/src/Content/module.png b/src/Content/module.png deleted file mode 100644 index 0038ca63..00000000 Binary files a/src/Content/module.png and /dev/null differ diff --git a/src/Content/notification0.png b/src/Content/notification0.png deleted file mode 100644 index 1fd01db3..00000000 Binary files a/src/Content/notification0.png and /dev/null differ diff --git a/src/Content/studydashboard.png b/src/Content/studydashboard.png deleted file mode 100644 index 30372359..00000000 Binary files a/src/Content/studydashboard.png and /dev/null differ diff --git a/src/Content/studyprofiles.png b/src/Content/studyprofiles.png deleted file mode 100644 index 5e9f48e5..00000000 Binary files a/src/Content/studyprofiles.png and /dev/null differ diff --git a/src/Controller/AccountController.java b/src/Controller/AccountController.java deleted file mode 100644 index f6b7f376..00000000 --- a/src/Controller/AccountController.java +++ /dev/null @@ -1,146 +0,0 @@ -package Controller; - -import Model.Account; -import Model.Person; -import javafx.application.Platform; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.Button; -import javafx.scene.control.CheckBox; -import javafx.scene.control.TextField; -import javafx.scene.layout.GridPane; -import javafx.stage.Stage; - -import java.net.URL; -import java.util.ResourceBundle; - -/** - * Created by Zilvinas on 04/05/2017. - */ -public class AccountController implements Initializable -{ - @FXML private TextField account_no; - @FXML private TextField salutation; - @FXML private TextField full_name; - @FXML private TextField email; - @FXML private CheckBox fam_last; - @FXML private Button submit; - @FXML private GridPane pane; - - private Account account; - private boolean success = false; - - public Account getAccount() - { - return account; - } - - public boolean isSuccess() - { - return success; - } - - /** - * Handle changes to the text fields - */ - public void handleChange() - { - if (Person.validSalutation(this.salutation.getText().trim()) && - Person.validName(this.full_name.getText().trim()) && - (this.email.getText().trim().isEmpty() || Person.validEmail(this.email.getText().trim())) && - !account_no.getText().trim().isEmpty()) - - this.submit.setDisable(false); - } - - /** - * Validate data in the Salutation field - */ - public void validateSalutation() - { - if (!Person.validSalutation(this.salutation.getText().trim())) - { - this.salutation.setStyle("-fx-text-box-border:red;"); - this.submit.setDisable(true); - } else - { - this.salutation.setStyle(""); - this.handleChange(); - } - } - - /** - * Validate data in the Name field - */ - public void validateName() - { - if (!Person.validName(this.full_name.getText().trim())) - { - this.full_name.setStyle("-fx-text-box-border:red;"); - this.submit.setDisable(true); - } else - { - this.full_name.setStyle(""); - this.handleChange(); - } - } - - /** - * Validate data in the Email field - */ - public void validateEmail() - { - if (this.email.getText().trim().isEmpty() || Person.validEmail(this.email.getText().trim())) - { - this.email.setStyle(""); - this.handleChange(); - } else - { - this.email.setStyle("-fx-text-box-border:red;"); - this.submit.setDisable(true); - } - } - - /** - * Validate data in the Account Number field - */ - public void validateNumber() - { - if (account_no.getText().trim().isEmpty()) - this.submit.setDisable(true); - else - this.handleChange(); - } - - /** - * Submit the form and create a new Account - */ - public void handleSubmit() - { - Person p = new Person(this.salutation.getText().trim(), this.full_name.getText().trim(), - this.fam_last.isSelected()); - - if (!this.email.getText().trim().isEmpty()) - p.setEmail(this.email.getText().trim()); - - this.account = new Account(p, this.account_no.getText().trim()); - this.success = true; - Stage stage = (Stage) this.submit.getScene().getWindow(); - stage.close(); - } - - /** - * Handle Quit button - */ - public void handleQuit() - { - Stage stage = (Stage) this.submit.getScene().getWindow(); - stage.close(); - } - - @Override - public void initialize(URL location, ResourceBundle resources) - { - Platform.runLater(() -> this.pane.requestFocus()); - } -} \ No newline at end of file diff --git a/src/Controller/ActivityController.java b/src/Controller/ActivityController.java deleted file mode 100644 index 2cf5a316..00000000 --- a/src/Controller/ActivityController.java +++ /dev/null @@ -1,281 +0,0 @@ -package Controller; - -import Model.Activity; -import Model.QuantityType; -import Model.Task; -import View.UIManager; -import javafx.application.Platform; -import javafx.beans.binding.BooleanBinding; -import javafx.collections.FXCollections; -import javafx.collections.ListChangeListener; -import javafx.collections.ObservableList; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.*; -import javafx.scene.layout.GridPane; -import javafx.stage.Stage; - -import java.net.URL; -import java.time.LocalDate; -import java.time.ZoneId; -import java.util.ResourceBundle; - -/** - * Created by Zilvinas on 13/05/2017. - */ -public class ActivityController implements Initializable -{ - private Activity activity; - private boolean success = false; - - public Activity getActivity() - { - return this.activity; - } - - public boolean isSuccess() - { - return success; - } - - // Panes: - @FXML private GridPane pane; - - // Buttons: - @FXML private Button submit; - @FXML private Button addTask; - @FXML private Button removeTask; - - // Text: - @FXML private TextField name; - @FXML private TextArea details; - @FXML private ComboBox quantityType; - @FXML private TextField quantity; - @FXML private TextField duration; - @FXML private DatePicker date; - - // Labels: - @FXML private Label title; - - // Lists: - @FXML private ListView tasks; - - /** - * Handle changes to the input fields - */ - public void handleChange() - { - // Check the input fields: - if (!this.name.getText().trim().isEmpty() && - !this.quantity.getText().trim().isEmpty() && - !this.date.getValue().isBefore(LocalDate.now()) && - !this.duration.getText().trim().isEmpty() && - this.quantityType.getSelectionModel().getSelectedIndex() != -1 && - this.tasks.getItems().size() > 0) - - this.submit.setDisable(false); - // ================= - } - - /** - * Validate data in the Duration field - */ - public void validateDuration() - { - if (!MainController.isNumeric(this.duration.getText()) || Integer.parseInt(this.duration.getText()) < 0) - { - this.duration.setStyle("-fx-text-box-border:red;"); - this.submit.setDisable(true); - } else - { - this.duration.setStyle(""); - this.handleChange(); - } - } - - /** - * Validate data in the Quantity field - */ - public void validateQuantity() - { - if (!MainController.isNumeric(this.quantity.getText()) || Integer.parseInt(this.quantity.getText()) < 0) - { - this.quantity.setStyle("-fx-text-box-border:red;"); - this.submit.setDisable(true); - } else - { - this.quantity.setStyle(""); - this.handleChange(); - } - } - - /** - * Validate data in the Date field - */ - public void validateDate() - { - if (this.date.getValue().isBefore(LocalDate.now())) - { - this.date.setStyle("-fx-border-color:red;"); - this.submit.setDisable(true); - } else - { - this.date.setStyle(""); - this.handleChange(); - } - } - - /** - * Handle the 'Add Task' button action - */ - public void addTask() - { - // Table items: - ObservableList list = FXCollections.observableArrayList(MainController.getSPC().getCurrentTasks()); - list.removeAll(this.tasks.getItems()); - if (this.activity != null) - list.remove(this.activity.getTasks()); - list.removeIf(e -> !e.dependenciesComplete()); - // ================= - - // Parse selected Tasks: - this.tasks.getItems().addAll(TaskController.taskSelectionWindow(list)); - // ================= - } - - /** - * Submit the form and create a new Activity - */ - public void handleSubmit() - { - if (this.activity == null) - { - // Create a new Activity: - this.activity = new Activity(this.name.getText(), this.details.getText(), this.date.getValue(), - Integer.parseInt(this.duration.getText()), Integer.parseInt(this.quantity.getText()), - this.quantityType.getValue()); - // ================= - this.activity.addTasks(this.tasks.getItems()); - } - - this.success = true; - Stage stage = (Stage) this.submit.getScene().getWindow(); - stage.close(); - } - - /** - * Handle Quit button - */ - public void handleQuit() - { - Stage stage = (Stage) this.submit.getScene().getWindow(); - stage.close(); - } - - /** - * Constructor for the ActivityController - */ - public ActivityController() - { - } - - /** - * Constructor for an ActivityController with an existing Activity - * - * @param activity - */ - public ActivityController(Activity activity) - { - this.activity = activity; - } - - @Override public void initialize(URL location, ResourceBundle resources) - { - this.quantityType.getItems().addAll(QuantityType.listOfNames()); - this.date.setValue(LocalDate.now()); - - // TODO draggable tasks (same as requirement) - // ListChangeListener: - this.tasks.getItems().addListener((ListChangeListener) c -> handleChange()); - // ================= - - // Bind properties on buttons: - this.removeTask.disableProperty().bind(new BooleanBinding() - { - { - bind(tasks.getSelectionModel().getSelectedItems()); - } - - @Override - protected boolean computeValue() - { - return !(tasks.getItems().size() > 0 && tasks.getSelectionModel().getSelectedItem() != null); - } - }); - // ================= - - // Button actions: - this.removeTask.setOnAction(e -> { - if (UIManager.confirm("Are you sure you want to remove this Task from the list?")) - { - Task t = this.tasks.getSelectionModel().getSelectedItem(); - this.tasks.getItems().remove(t); - if (this.activity != null) - this.activity.removeTask(t); - } - }); - - this.tasks.setCellFactory(e -> { - ListCell cell = new ListCell() - { - @Override - protected void updateItem(final Task item, final boolean empty) - { - super.updateItem(item, empty); - // If completed, mark: - if (!empty && item != null) - { - setText(item.toString()); - if (item.isCheckedComplete()) - this.getStyleClass().add("current-item"); - } else - { - setText(null); - this.getStyleClass().remove("current-item"); - } - } - }; - return cell; - }); - // ================= - - // Handle Activity details: - if (this.activity != null) - { - // Disable/modify elements: - this.title.setText("Activity"); - this.addTask.setVisible(false); - this.removeTask.setVisible(false); - this.name.setEditable(false); - this.details.setEditable(false); - this.duration.setEditable(false); - this.quantity.setEditable(false); - this.date.setDisable(true); - this.quantityType.setDisable(true); - // ================= - - // Fill in data: - this.name.setText(this.activity.getName()); - this.details.setText(this.activity.getDetails().getAsString()); - this.duration.setText(Integer.toString(this.activity.getDuration())); - this.quantity.setText(Integer.toString(this.activity.getActivityQuantity())); - this.date.setValue(this.activity.getDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate()); - this.quantityType.getSelectionModel().select(this.activity.getType().getName()); - this.tasks.getItems().addAll(this.activity.getTasks()); - // ================= - } else this.handleChange(); - // ================= - - Platform.runLater(() -> this.pane.requestFocus()); - } -} diff --git a/src/Controller/DataController.java b/src/Controller/DataController.java deleted file mode 100644 index 9f64006a..00000000 --- a/src/Controller/DataController.java +++ /dev/null @@ -1,486 +0,0 @@ -package Controller; - -import Model.*; -import View.ConsoleIO; -import View.UIManager; -import org.w3c.dom.Document; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import javax.xml.parsers.DocumentBuilder; -import javax.xml.parsers.DocumentBuilderFactory; -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; - -/** - * Created by bendickson on 5/4/17. - */ -public class DataController -{ - - - /** - * checks if there is a settings file and it is valid - * returns true if this is the case - * returns false if it isn't - */ - static public boolean existingSettingsFile() - { - return false; - // not implmented yet - } - - static private void processVCEupdate(VersionControlEntity vce) - { - if (vce.addToLibrary() || VersionControlEntity.get(vce.getUID()).update(vce)) - { - ConsoleIO.setConsoleMessage(vce + " added", true); - } else - { - ConsoleIO.setConsoleMessage(vce + " not added", true); - } - } - - static private HubFile processUpdateHubFile(NodeList nList) throws Exception - { - HubFile r = null; - XMLcontroller xmlTools = new XMLcontroller(); - - HashMap fValues = xmlTools.getSchemaValues(nList, - HubFile.SCHEMA_UPDATE_FILE); - - int rootVersion = fValues.get("version").getInt(); - NodeList updates = fValues.get("updates").getNodeList(); - - int i = -1; - int ii = updates.getLength(); - while (++i < ii) - { - Node n = updates.item(i); - if (n.getNodeType() == Node.ELEMENT_NODE && HubFile.updateableNode(n.getNodeName()) && - XMLcontroller.matchesSchema(n.getChildNodes(), HubFile.SCHEMA_VCE)) - { - String nodeName = n.getNodeName(); - HashMap nodeValues = - xmlTools.getSchemaValues(n.getChildNodes(), HubFile.schemaList.get(nodeName)); - - int version = nodeValues.get("version").getInt(); - String uid = nodeValues.get("uid").getString(); - if (VersionControlEntity.inLibrary(uid)) - { - if (VersionControlEntity.get(uid).getVersion() < version) - { - NodeList nc = XMLcontroller.getNodes(n); - switch (nodeName) - { - case "person": - processVCEupdate(HubFile.createPerson(nc)); - break; - case "building": - processVCEupdate(HubFile.createBuilding(nc)); - - break; - case "room": - processVCEupdate(HubFile.createRoom(nc, VersionControlEntity.getLibrary())); - break; - case "module": - // not yet added - break; - case "exam": - processVCEupdate(HubFile.createExam(nc, VersionControlEntity.getLibrary())); - break; - case "coursework": - processVCEupdate(HubFile.createCoursework(nc, VersionControlEntity.getLibrary())); - break; - case "timetableEventType": - processVCEupdate(HubFile.createTimetableEventType(nc)); - break; - case "timetableEvent": - processVCEupdate(HubFile.createTimetableEvent(nc, VersionControlEntity.getLibrary())); - break; - case "event": - // not yet added - break; - case "deadline": - // not yet added - break; - case "examEvent": - // not yet added - break; - } - } - } - } - } - - - return r; - } - - public static T inList(HashMap list, String uid) throws Exception - { - VersionControlEntity vce = null; - if (list.containsKey(uid)) - { - vce = list.get(uid); - } else if (VersionControlEntity.inLibrary(uid)) - { - vce = VersionControlEntity.get(uid); - } - // can't do instanceof T annoyingly, so hopefully casting to T and failing will work instead - if (vce != null) - { - try - { - return (T) vce; - } catch (Exception e) - { - throw new Exception("Incorrect type referenced for '" + uid + "'"); - } - } - throw new Exception("UID referenced is not in database for '" + uid + "'"); - } - - static public void addVCEproperties(VersionControlEntity vce, HashMap values) - { - vce.addProperties(values.get("name").getString(), - values.get("details").getMultilineString()); - String UID = values.get("uid").getString(); - vce.makeImporter(); - vce.setUID(UID, values.get("version").getInt()); - - } - - static private HubFile processNewHubFile(NodeList nList) throws Exception - { - int beginLog = ConsoleIO.getLogSize(); - ConsoleIO.setConsoleMessage("Importing New Hub File", true); - HubFile r = null; - XMLcontroller xmlTools = new XMLcontroller(); - - HashMap fValues = xmlTools.getSchemaValues(nList, - HubFile.SCHEMA_NEW_STUDYPROFILE); - - int version = fValues.get("version").getInt(); - NodeList assetNodes = fValues.get("assets").getNodeList(); - NodeList studyProfileNodes = fValues.get("studyProfile").getNodeList(); - - if (XMLcontroller.matchesSchema(assetNodes, HubFile.SCHEMA_ASSETS) && - XMLcontroller.matchesSchema(studyProfileNodes, HubFile.SCHEMA_STUDYPROFILE)) - { - ConsoleIO.setConsoleMessage("Schema Validates: assets", true); - ConsoleIO.setConsoleMessage("Schema Validates: studyProfile", true); - - HashMap studyProfileValues = - xmlTools.getSchemaValues(studyProfileNodes, HubFile.SCHEMA_STUDYPROFILE); - - int year = studyProfileValues.get("year").getInt(); - int semester = studyProfileValues.get("semester").getInt(); - - if (MainController.getSPC().containsStudyProfile(year, semester)) - { - throw new Exception("Study profile for " + year + " semester " + semester + " already imported"); - } - - HashMap assetList = new HashMap<>(); - HashMap assetValues = xmlTools.getSchemaValues(assetNodes, - HubFile.SCHEMA_ASSETS); - - ArrayList newAssets = new ArrayList<>(); - ArrayList newModules = new ArrayList<>(); - - Node n; - NodeList nc; - int i, ii; - String UID; - - // Add all the Person classes - if (assetValues.containsKey("persons")) - { - - NodeList personList = assetValues.get("persons").getNodeList(); - Person tp; - i = -1; - ii = personList.getLength(); - ConsoleIO.setConsoleMessage("Reading persons tag, " + Integer.toString(ii) + " nodes:", true); - while (++i < ii) - { - n = personList.item(i); - if (n.getNodeType() == Node.ELEMENT_NODE) - { - nc = XMLcontroller.getNodes(n); - if (n.getNodeName().equals("person") && XMLcontroller.matchesSchema(nc, - HubFile.SCHEMA_PERSON)) - { - - ConsoleIO.setConsoleMessage("Valid Node found:", true); - - tp = HubFile.createPerson(nc); - - assetList.put(tp.getUID(), tp); - - ConsoleIO.setConsoleMessage("Adding person: " + tp.toString(), true); - } - } - } - } - // add all the Buildings - - if (assetValues.containsKey("buildings")) - { - NodeList buildingList = assetValues.get("buildings").getNodeList(); - Building tb; - i = -1; - ii = buildingList.getLength(); - ConsoleIO.setConsoleMessage("Reading buildings tag, " + Integer.toString(ii) + " nodes:", true); - while (++i < ii) - { - n = buildingList.item(i); - if (n.getNodeType() == Node.ELEMENT_NODE) - { - nc = XMLcontroller.getNodes(n); - if (n.getNodeName().equals("building") && XMLcontroller.matchesSchema(nc, HubFile.SCHEMA_BUILDING)) - { - ConsoleIO.setConsoleMessage("Valid Node found:", true); - - - tb = HubFile.createBuilding(nc); - - assetList.put(tb.getUID(), tb); - ConsoleIO.setConsoleMessage("Adding buiding: " + tb.toString(), true); - } - } - } - } - - // add all the Rooms - if (assetValues.containsKey("rooms")) - { - NodeList roomList = assetValues.get("rooms").getNodeList(); - Room tr; - i = -1; - ii = roomList.getLength(); - ConsoleIO.setConsoleMessage("Reading rooms tag, " + Integer.toString(ii) + " nodes:", true); - while (++i < ii) - { - n = roomList.item(i); - if (n.getNodeType() == Node.ELEMENT_NODE) - { - nc = XMLcontroller.getNodes(n); - if (n.getNodeName().equals("room") && XMLcontroller.matchesSchema(nc, HubFile.SCHEMA_ROOM)) - { - ConsoleIO.setConsoleMessage("Valid Node found:", true); - - tr = HubFile.createRoom(nc, assetList); - assetList.put(tr.getUID(), tr); - ConsoleIO.setConsoleMessage("Adding room: " + tr.toString(), true); - } - } - } - } - // add all the TimeTableTypes - if (assetValues.containsKey("timetableEventTypes")) - { - NodeList ttetList = assetValues.get("timetableEventTypes").getNodeList(); - i = -1; - ii = ttetList.getLength(); - ConsoleIO.setConsoleMessage("Reading timetableEventTypes tag, " + Integer.toString(ii) + " nodes:", true); - while (++i < ii) - { - n = ttetList.item(i); - if (n.getNodeType() == Node.ELEMENT_NODE) - { - nc = XMLcontroller.getNodes(n); - if (n.getNodeName().equals("timetableEventType") && - XMLcontroller.matchesSchema(nc, HubFile.SCHEMA_TIMETABLE_EVENT_TYPE)) - { - ConsoleIO.setConsoleMessage("Valid Node found:", true); - TimeTableEventType ttet = HubFile.createTimetableEventType(nc); - - - assetList.put(ttet.getUID(), ttet); - ConsoleIO.setConsoleMessage("Adding timetable event: " + ttet.toString(), true); - } - } - - } - } - - - NodeList modules = studyProfileValues.get("modules").getNodeList(); - i = -1; - ii = modules.getLength(); - while (++i < ii) - { - if (modules.item(i).getNodeType() == Node.ELEMENT_NODE && modules.item(i).getNodeName().equals("module") - && modules.item(i).hasChildNodes() && XMLcontroller.matchesSchema(modules.item(i).getChildNodes(), HubFile.SCHEMA_MODULE)) - { - HashMap moduleValues = - xmlTools.getSchemaValues(modules.item(i).getChildNodes(), HubFile.SCHEMA_MODULE); - - String linkedPerson = moduleValues.get("organiser").getString(); - - Person organiser = inList(assetList, linkedPerson); - String moduleCode = moduleValues.get("moduleCode").getString(); - Module thisModule = new Module(organiser, moduleCode); - - - addVCEproperties(thisModule, moduleValues); - - assetList.put(moduleValues.get("uid").getString(), thisModule); - - NodeList assignments = moduleValues.get("assignments").getNodeList(); - int j = -1; - int jj = assignments.getLength(); - ConsoleIO.setConsoleMessage("Reading assignments tag, " + Integer.toString(ii) + " nodes:", true); - while (++j < jj) - { - if (assignments.item(j).getNodeType() == Node.ELEMENT_NODE) - { - nc = assignments.item(j).getChildNodes(); - switch (assignments.item(j).getNodeName()) - { - case "coursework": - if (XMLcontroller.matchesSchema(assignments.item(j).getChildNodes(), - HubFile.SCHEMA_COURSEWORK)) - { - ConsoleIO.setConsoleMessage("Valid Node found:", true); - Coursework newCoursework = HubFile.createCoursework(nc, assetList); - assetList.put(newCoursework.getUID(), newCoursework); - thisModule.addAssignment(newCoursework); - ConsoleIO.setConsoleMessage("Adding coursework: " + newCoursework.toString(), true); - } - break; - - case "exam": - if (XMLcontroller.matchesSchema(assignments.item(j).getChildNodes(), - HubFile.SCHEMA_EXAM)) - { - ConsoleIO.setConsoleMessage("Valid Node found:", true); - - - Exam newExam = HubFile.createExam(nc, assetList); - assetList.put(newExam.getUID(), newExam); - thisModule.addAssignment(newExam); - ConsoleIO.setConsoleMessage("Adding exam: " + newExam.toString(), true); - } - - break; - } - - } - } - NodeList timetable = moduleValues.get("timetable").getNodeList(); - j = -1; - jj = timetable.getLength(); - ConsoleIO.setConsoleMessage("Reading timetable tag, " + Integer.toString(ii) + " nodes:", true); - while (++j < jj) - { - if (timetable.item(j).getNodeType() == Node.ELEMENT_NODE && - timetable.item(j).getNodeName().equals("timetableEvent") && - XMLcontroller.matchesSchema(timetable.item(j).getChildNodes(), - HubFile.SCHEMA_TIMETABLE_EVENT)) - - { - ConsoleIO.setConsoleMessage("Valid Node found:", true); - nc = timetable.item(j).getChildNodes(); - - TimetableEvent newTTE = HubFile.createTimetableEvent(nc, assetList); - assetList.put(newTTE.getUID(), newTTE); - thisModule.addTimetableEvent(newTTE); - - ConsoleIO.setConsoleMessage("Adding TimetableEvent: " + newTTE.toString(), true); - } - } - newModules.add(thisModule); - } - } - - - String name = studyProfileValues.get("name").getString(); - MultilineString details = studyProfileValues.get("details").getMultilineString(); - UID = studyProfileValues.get("uid").getString(); - - - ConsoleIO.setConsoleMessage("Attempting to import " + Integer.toString(assetList.size()) - + " items to VersionControl...", true); - ConsoleIO.setConsoleMessage("Starting with " + VersionControlEntity.libraryReport() + " entries"); - - ArrayList calendarItems = new ArrayList<>(); - - for (String key : assetList.keySet()) - { - ConsoleIO.setConsoleMessage("Adding Asset: " + key, true); - if (assetList.get(key).addToLibrary()) - { - ConsoleIO.setConsoleMessage("New Asset, adding... ", true); - if (assetList.get(key) instanceof Event) - { - - ConsoleIO.setConsoleMessage("Event - adding to Calendar: ", true); - calendarItems.add((Event) assetList.get(key)); - } - } else if (assetList.get(key).isImporter()) - { - ConsoleIO.setConsoleMessage("In library, attempting update: ", true); - ConsoleIO.setConsoleMessage(assetList.get(key).toString() + - (VersionControlEntity.get(key).update(assetList.get(key)) ? " updated" : " not updated"), true); - } else - { - ConsoleIO.setConsoleMessage(assetList.get(key).toString() + " not imported", true); - } - } - ConsoleIO.setConsoleMessage("Ending with " + VersionControlEntity.libraryReport() + " entries"); - ConsoleIO.saveLog("import_report.txt", beginLog, ConsoleIO.getLogSize()); - - - r = new HubFile(version, year, semester, newModules, newAssets, calendarItems, name, details, UID); - } - return r; - } - - static public HubFile loadHubFile(File tempFile) - { - HubFile r = null; - if (tempFile.exists()) - { - try - { - // learned from: - // https://www.mkyong.com/java/how-to-read-xml-file-in-java-dom-parser/ - DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); - DocumentBuilder dBuilder = dbFactory.newDocumentBuilder(); - Document doc = dBuilder.parse(tempFile); - doc.getDocumentElement().normalize(); - - String rootElementTag = doc.getDocumentElement().getNodeName(); - Node rootElement = doc.getDocumentElement(); - - // check it is a hubfile - if (rootElementTag.equals("hubfile") && rootElement.hasChildNodes()) - { - NodeList nList = XMLcontroller.getNodes(rootElement); - if (XMLcontroller.matchesSchema(nList, HubFile.SCHEMA_NEW_STUDYPROFILE)) - { - r = processNewHubFile(nList); - } else if (XMLcontroller.matchesSchema(nList, HubFile.SCHEMA_UPDATE_FILE)) - { - r = processUpdateHubFile(nList); - } else - { - UIManager.reportError("Invalid Parent Nodes"); - } - } else - { - UIManager.reportError("Invalid XML file"); - } - - } catch (Exception e) - { - UIManager.reportError("Invalid File: \n" + e.getMessage()); - } - } - return r; - } -} diff --git a/src/Controller/MainController.java b/src/Controller/MainController.java deleted file mode 100644 index 281d758e..00000000 --- a/src/Controller/MainController.java +++ /dev/null @@ -1,223 +0,0 @@ -package Controller; - -import Model.Account; -import Model.HubFile; -import Model.Notification; -import Model.StudyPlanner; -import View.UIManager; - -import javax.crypto.*; -import javax.crypto.spec.SecretKeySpec; -import java.io.*; -import java.security.InvalidKeyException; -import java.security.NoSuchAlgorithmException; -import java.util.GregorianCalendar; - -/** - * Created by bendickson on 5/4/17. - */ -public class MainController -{ - public static UIManager ui = new UIManager(); - - private static StudyPlannerController SPC; - - // Used for serialization: - private static SecretKey key64 = new SecretKeySpec(new byte[]{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07}, "Blowfish"); - private static String fileName = "StudyPlanner.dat"; - - /** - * Returns a StudyPlannerController. - * - * @return StudyPlannerController. - */ - public static StudyPlannerController getSPC() - { - return SPC; - } - - /** - * Initializes the Study Planner by either registering a new account or - * importing an existing Study Planner file. - */ - public static void initialise() - { - File plannerFile = new File("StudyPlanner.dat"); - try - { - // If a file is present: - if (plannerFile.exists()) - { - Cipher cipher = Cipher.getInstance("Blowfish"); - cipher.init(Cipher.DECRYPT_MODE, key64); - CipherInputStream cipherInputStream = new CipherInputStream(new BufferedInputStream(new FileInputStream(fileName)), cipher); - ObjectInputStream inputStream = new ObjectInputStream(cipherInputStream); - SealedObject sealedObject = (SealedObject) inputStream.readObject(); - SPC = new StudyPlannerController((StudyPlanner) sealedObject.getObject(cipher)); - /** - * Begin note to examiner: - */ - if (SPC.getPlanner().getCurrentStudyProfile() != null && SPC.getPlanner().getCurrentStudyProfile().getName().equals("First year Gryffindor")) - UIManager.reportSuccess("Note to examiner: This is a pre-loaded StudyPlanner, as used by Harry Potter, to reset the software delete or rename the 'StudyPlanner.dat' file in the root directory."); - /** - * End note to examiner. - */ - } else - // If not, prompt to create a new account: - { - Account newAccount = ui.createAccount(); - SPC = new StudyPlannerController(newAccount); - // Welcome notification: - Notification not = new Notification("Welcome!", new GregorianCalendar(), "Thank you for using PearPlanner!"); - SPC.getPlanner().addNotification(not); - } - } catch (FileNotFoundException e) - { - UIManager.reportError("File does not exist"); - System.exit(1); - } catch (ClassNotFoundException e) - { - UIManager.reportError("Invalid file"); - System.exit(1); - } catch (NoSuchAlgorithmException e) - { - UIManager.reportError("Cannot decode the given file"); - System.exit(1); - } catch (BadPaddingException e) - { - UIManager.reportError("Invalid file"); - System.exit(1); - } catch (InvalidKeyException e) - { - UIManager.reportError("Cannot decode the given file"); - System.exit(1); - } catch (NoSuchPaddingException e) - { - UIManager.reportError("Invalid file"); - System.exit(1); - } catch (IOException e) - { - UIManager.reportError("Invalid file"); - System.exit(1); - } catch (IllegalBlockSizeException e) - { - UIManager.reportError("Invalid file"); - System.exit(1); - } catch (Exception e) - { - UIManager.reportError(e.getMessage()); - System.exit(1); - } - } - - /** - * Display the main menu. - */ - public static void main() - { - try - { - ui.mainMenu(); - } catch (Exception e) - { - UIManager.reportError(e.getMessage()); - } - } - - /** - * Handles importing a new file. - * - * @return whether imported successfully. - */ - public static boolean importFile() - { - // Call a dialog: - File tempFile = ui.loadFileDialog(); - if (tempFile != null) - { - // If a file was selected, process the file: - HubFile fileData = DataController.loadHubFile(tempFile); - if (fileData != null) - { - if (!fileData.isUpdate() && !MainController.SPC.createStudyProfile(fileData)) - UIManager.reportError("This Study Profile is already created!"); - else - return true; - } - } - return false; - } - - /** - * Save the current state of the program to file - * - * @return whether saved successfully. - */ - public static boolean save() - { - try - { - SPC.save(MainController.key64, MainController.fileName); - return true; - } catch (Exception e) - { - UIManager.reportError("FAILED TO SAVE YOUR DATA!"); - return false; - } - } - - /** - * Apparently (according to Stackoverflow) the Java Standard library doesn't have a - * standard check for testing if a string value is a number or not?!) - *

- * Therefore, we are using this proposed isNumeric method from: - *

- * http://stackoverflow.com/a/1102916 - * - * @param str String to be tested - * @return whether the given String is numeric. - */ - public static boolean isNumeric(String str) - { - try - { - double d = Double.parseDouble(str); - } catch (NumberFormatException nfe) - { - return false; - } - return true; - } - - /** - * Early console based implementation. - * - * @param menu menu option. - */ - private static void consoleUI(String menu) - { - while (!menu.equals("")) - { - switch (menu) - { - case "Quit Program": - menu = ""; - break; - case "Main Menu": - case "Return to Main Menu": - menu = View.ConsoleIO.view_main(); - break; - case "Create Study Profile": - menu = View.ConsoleIO.view_createSP(); - break; - case "View Study Profile": - menu = View.ConsoleIO.view_viewSP(SPC); - break; - case "Load Study Profile File": - menu = View.ConsoleIO.view_loadSP(SPC); - default: - menu = ""; - } - } - } -} diff --git a/src/Controller/MenuController.java b/src/Controller/MenuController.java deleted file mode 100644 index c39b14d2..00000000 --- a/src/Controller/MenuController.java +++ /dev/null @@ -1,1222 +0,0 @@ -package Controller; - -import Model.*; -import View.GanttishDiagram; -import View.UIManager; -import javafx.animation.TranslateTransition; -import javafx.beans.binding.BooleanBinding; -import javafx.beans.property.BooleanProperty; -import javafx.beans.property.SimpleIntegerProperty; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.value.ObservableValue; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.embed.swing.SwingFXUtils; -import javafx.event.ActionEvent; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.geometry.HPos; -import javafx.geometry.Insets; -import javafx.geometry.Orientation; -import javafx.geometry.Pos; -import javafx.scene.Cursor; -import javafx.scene.control.*; -import javafx.scene.control.cell.PropertyValueFactory; -import javafx.scene.image.Image; -import javafx.scene.input.ClipboardContent; -import javafx.scene.input.Dragboard; -import javafx.scene.input.MouseButton; -import javafx.scene.input.TransferMode; -import javafx.scene.layout.*; -import javafx.scene.text.TextAlignment; -import javafx.util.Duration; - -import java.awt.image.BufferedImage; -import java.io.IOException; -import java.net.URL; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Locale; -import java.util.ResourceBundle; - -/** - * Created by Zilvinas on 05/05/2017. - */ -public class MenuController implements Initializable -{ - public enum Window - { - Empty, Dashboard, Profiles, Modules, Milestones - } - - private Window current; - private boolean isNavOpen; - - // Labels: - private Label welcome; - @FXML Label title; - - // Buttons: - @FXML private Button openMenu; - @FXML private Button showNotification; - @FXML private Button showDash; - @FXML private Button addActivity; - @FXML private Button studyProfiles; - @FXML private Button milestones; - @FXML private Button modules; - @FXML private Button calendar; - - // Panes: - @FXML private AnchorPane navList; - @FXML private AnchorPane notifications; - @FXML private GridPane notificationList; - @FXML private GridPane mainContent; - @FXML private HBox topBox; - - public void main(Window wind) - { - this.current = wind; - this.main(); - } - - public void main() - { - if (isNavOpen) - openMenu.fire(); - - this.updateNotifications(); - this.updateMenu(); - - switch (this.current) - { - case Dashboard: - { - if (MainController.getSPC().getPlanner().getCurrentStudyProfile() != null) - this.loadDashboard(); - break; - } - case Profiles: - { - this.loadStudyProfiles(); - break; - } - case Modules: - { - this.loadModules(); - break; - } - case Milestones: - { - this.loadMilestones(); - break; - } - } - } - - /** - * Display the Study Dashboard pane - */ - public void loadDashboard() - { - // Update main pane: - this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); - this.topBox.getChildren().clear(); - this.topBox.getChildren().add(this.welcome); - this.title.setText("Study Dashboard"); - // ================= - - StudyProfile profile = MainController.getSPC().getPlanner().getCurrentStudyProfile(); - - // Display studyProfile: - Label studyProfile = new Label(profile.getName()); - studyProfile.getStyleClass().add("title"); - GridPane.setMargin(studyProfile, new Insets(10)); - this.mainContent.addRow(1, studyProfile); - - FlowPane modules = new FlowPane(); - modules.setHgap(30); - modules.setVgap(20); - modules.setPrefWrapLength(1000); - for (Module module : profile.getModules()) - { - VBox vbox = new VBox(); - vbox.setSpacing(5); - vbox.setMinWidth(200); - vbox.setMaxWidth(200); - vbox.setAlignment(Pos.CENTER); - - Label name = new Label(module.getName()); - name.setTextAlignment(TextAlignment.CENTER); - vbox.getChildren().add(name); - - BufferedImage buff = GanttishDiagram.getBadge(module.calculateProgress(), true, 1); - Image image = SwingFXUtils.toFXImage(buff, null); - Pane badge = new Pane(); - VBox.setMargin(badge, new Insets(0, 0, 0, 50)); - badge.setPrefHeight(100); - badge.setBackground(new Background( - new BackgroundImage(image, BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.DEFAULT, - new BackgroundSize(BackgroundSize.AUTO, BackgroundSize.AUTO, false, false, true, false)))); - - vbox.getChildren().add(badge); - Button view = new Button("View"); - view.setOnAction(e -> module.open(this.current)); - vbox.getChildren().add(view); - - modules.getChildren().add(vbox); - } - // ================= - - GridPane.setColumnSpan(modules, GridPane.REMAINING); - GridPane.setMargin(modules, new Insets(10)); - this.mainContent.addRow(2, modules); - } - - /** - * Display the 'Add Activity' window - */ - public void addActivity() - { - try - { - Activity activity = MainController.ui.addActivity(); - if (activity != null) - MainController.getSPC().addActivity(activity); - openMenu.fire(); - - } catch (Exception e) - { - UIManager.reportError("Unable to open View file"); - } - } - - /** - * Display the Milestones pane - */ - public void loadMilestones() - { - // Update main pane: - this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); - this.topBox.getChildren().clear(); - this.title.setText(""); - // ================= - - // Display milestones: - Label milestones = new Label("Milestones"); - milestones.getStyleClass().add("title"); - this.mainContent.addRow(1, milestones); - // ================= - - // Columns: - TableColumn nameColumn = new TableColumn<>("Milestone"); - nameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); - - TableColumn deadlineColumn = new TableColumn<>("Deadline"); - deadlineColumn.setCellValueFactory(new PropertyValueFactory<>("deadline")); - deadlineColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); - - TableColumn completedColumn = new TableColumn<>("Tasks completed"); - completedColumn.setCellValueFactory(new PropertyValueFactory<>("taskCompletedAsString")); - completedColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); - - TableColumn progressColumn = new TableColumn<>("Progress"); - progressColumn.setCellValueFactory(new PropertyValueFactory<>("progressPercentage")); - progressColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); - - ObservableList list = FXCollections.observableArrayList - (MainController.getSPC().getPlanner().getCurrentStudyProfile().getMilestones()); - // ================= - - // Create a table: - TableView table = new TableView<>(); - table.setItems(list); - table.getColumns().addAll(nameColumn, deadlineColumn, completedColumn, progressColumn); - table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); - GridPane.setHgrow(table, Priority.ALWAYS); - GridPane.setVgrow(table, Priority.ALWAYS); - // ================= - - // Set click event: - table.setRowFactory(e -> { - TableRow row = new TableRow() - { - @Override - protected void updateItem(final Milestone item, final boolean empty) - { - super.updateItem(item, empty); - // If Milestone completed, mark: - if (!empty && item != null && item.isComplete()) - this.getStyleClass().add("current-item"); - } - }; - row.setOnMouseClicked(event -> { - if (!row.isEmpty() && event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) - { - try - { - MainController.ui.milestoneDetails(row.getItem()); - this.main(); - } catch (IOException e1) - { - UIManager.reportError("Unable to open View file"); - } - } - }); - return row; - }); - // ================= - - this.mainContent.addRow(2, table); - this.mainContent.getStyleClass().add("list-item"); - - // Actions toolbar: - HBox actions = new HBox(); - GridPane.setHgrow(actions, Priority.ALWAYS); - actions.setSpacing(5); - actions.setPadding(new Insets(5, 5, 10, 0)); - // ================= - - // Buttons: - Button add = new Button("Add a new Milestone"); - - Button remove = new Button("Remove"); - remove.setDisable(true); - // ================= - - // Bind properties on buttons: - remove.disableProperty().bind(new BooleanBinding() - { - { - bind(table.getSelectionModel().getSelectedItems()); - } - - @Override - protected boolean computeValue() - { - return !(list.size() > 0 && table.getSelectionModel().getSelectedItem() != null); - } - }); - // ================= - - // Bind actions on buttons: - add.setOnAction(e -> { - try - { - Milestone milestone = MainController.ui.addMilestone(); - if (milestone != null) - { - list.add(milestone); - MainController.getSPC().addMilestone(milestone); - } - } catch (IOException e1) - { - UIManager.reportError("Unable to open View file"); - } catch (Exception e1) - { - } - }); - - remove.setOnAction(e -> { - if (UIManager.confirm("Are you sure you want to remove this milestone?")) - { - Milestone m = table.getSelectionModel().getSelectedItem(); - list.remove(m); - MainController.getSPC().removeMilestone(m); - } - }); - // ================= - - actions.getChildren().addAll(add, remove); - - mainContent.addRow(3, actions); - // ================= - } - - /** - * Display the Study Profiles pane - */ - public void loadStudyProfiles() - { - // Update main pane: - this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); - this.topBox.getChildren().clear(); - this.title.setText(""); - // ================= - - // Display profiles: - Label profiles = new Label("Study Profiles"); - profiles.getStyleClass().add("title"); - this.mainContent.addRow(1, profiles); - // ================= - - // Columns: - TableColumn nameColumn = new TableColumn<>("Profile name"); - nameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); - - TableColumn yearColumn = new TableColumn<>("Year"); - yearColumn.setCellValueFactory(new PropertyValueFactory<>("year")); - yearColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); - - TableColumn semesterColumn = new TableColumn<>("Semester"); - semesterColumn.setCellValueFactory(new PropertyValueFactory<>("semesterNo")); - semesterColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); - - ObservableList list = FXCollections.observableArrayList(MainController.getSPC().getPlanner().getStudyProfiles()); - // ================= - - // Create a table: - - TableView table = new TableView<>(); - table.setItems(list); - table.getColumns().addAll(nameColumn, yearColumn, semesterColumn); - table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); - GridPane.setHgrow(table, Priority.ALWAYS); - GridPane.setVgrow(table, Priority.ALWAYS); - // ================= - - // Set click event: - table.setRowFactory(e -> { - TableRow row = new TableRow() - { - @Override - protected void updateItem(final StudyProfile item, final boolean empty) - { - super.updateItem(item, empty); - // If current Profile, mark: - if (!empty && item != null && item.isCurrent()) - this.getStyleClass().add("current-item"); - } - }; - row.setOnMouseClicked(event -> { - if (!row.isEmpty() && event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) - { - try - { - MainController.ui.studyProfileDetails(row.getItem()); - this.main(); - } catch (IOException e1) - { - UIManager.reportError("Unable to open View file"); - } - } - }); - return row; - }); - // ================= - - this.mainContent.addRow(2, table); - this.mainContent.getStyleClass().add("list-item"); - } - - /** - * Display the Modules pane - */ - public void loadModules() - { - // Update main pane: - this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); - this.topBox.getChildren().clear(); - this.title.setText(""); - // ================= - - // Display modules: - Label modules = new Label("Modules"); - modules.getStyleClass().add("title"); - this.mainContent.addRow(1, modules); - // ================= - - // Columns: - TableColumn codeColumn = new TableColumn<>("Module code"); - codeColumn.setCellValueFactory(new PropertyValueFactory<>("moduleCode")); - - TableColumn nameColumn = new TableColumn<>("Module name"); - nameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); - - TableColumn timeSpent = new TableColumn<>("Time spent"); - timeSpent.setCellValueFactory(new PropertyValueFactory("timeSpent") - { - @Override public ObservableValue call(TableColumn.CellDataFeatures param) - { - return new SimpleIntegerProperty(MainController.getSPC().getPlanner().getTimeSpent((Module) param.getValue())); - } - }); - timeSpent.setStyle("-fx-alignment: CENTER-RIGHT;"); - - ObservableList list = FXCollections.observableArrayList - (MainController.getSPC().getPlanner().getCurrentStudyProfile().getModules()); - // ================= - - // Create a table: - TableView table = new TableView<>(); - table.setItems(list); - table.getColumns().addAll(codeColumn, nameColumn, timeSpent); - table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); - GridPane.setHgrow(table, Priority.ALWAYS); - GridPane.setVgrow(table, Priority.ALWAYS); - // ================= - - // Set click event: - table.setRowFactory(e -> { - TableRow row = new TableRow<>(); - row.setOnMouseClicked(event -> { - if (!row.isEmpty() && event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) - { - this.loadModule(row.getItem(), this.current, null); - } - }); - return row; - }); - // ================= - - this.mainContent.addRow(2, table); - this.mainContent.getStyleClass().add("list-item"); - } - - /** - * Display the Module pane - */ - public void loadModule(Module module, Window previousWindow, ModelEntity previous) - { - // Update main pane: - this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); - this.topBox.getChildren().clear(); - this.title.setText(""); - // ================= - - // Create a back button: - this.backButton(previousWindow, previous); - // ================= - - // Display modules: - Label modules = new Label(module.getModuleCode() + " " + module.getName()); - modules.getStyleClass().add("title"); - this.mainContent.addRow(1, modules); - // ================= - - // Create a details pane: - VBox detailsBox = new VBox(5); - Label details = new Label(module.getDetails().getAsString()); - details.setWrapText(true); - detailsBox.getChildren().addAll(new Label("Organised by: " + module.getOrganiser()), details); - GridPane.setVgrow(detailsBox, Priority.SOMETIMES); - GridPane.setHgrow(detailsBox, Priority.ALWAYS); - // ================= - - mainContent.addRow(2, detailsBox); - - // Assignments: - TableColumn nameColumn = new TableColumn<>("Assignment"); - nameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); - - TableColumn deadlineColumn = new TableColumn<>("Date"); - deadlineColumn.setCellValueFactory(new PropertyValueFactory("deadlineString") - { - @Override public ObservableValue call(TableColumn.CellDataFeatures param) - { - SimpleStringProperty value = new SimpleStringProperty(); - if (param.getValue() instanceof Coursework) - { - Coursework c = (Coursework) param.getValue(); - value.setValue(c.getDeadlineString()); - } else if (param.getValue() instanceof Exam) - { - Exam e = (Exam) param.getValue(); - value.setValue(e.getTimeSlot().getDateString()); - } - return value; - } - }); - deadlineColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); - - TableColumn weightingColumn = new TableColumn<>("Weighting"); - weightingColumn.setCellValueFactory(new PropertyValueFactory<>("weighting")); - weightingColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); - - ObservableList list = FXCollections.observableArrayList(module.getAssignments()); - // ================= - - // Create a moduleContent: - TableView moduleContent = new TableView<>(); - moduleContent.setItems(list); - moduleContent.getColumns().addAll(nameColumn, deadlineColumn, weightingColumn); - moduleContent.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); - GridPane.setHgrow(moduleContent, Priority.ALWAYS); - GridPane.setVgrow(moduleContent, Priority.ALWAYS); - // ================= - - // Set click event: - moduleContent.setRowFactory(e -> { - TableRow row = new TableRow<>(); - row.setOnMouseClicked(event -> { - if (!row.isEmpty() && event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) - { - this.loadAssignment(row.getItem(), Window.Empty, module); - } - }); - return row; - }); - // ================= - - this.mainContent.addRow(3, moduleContent); - } - - /** - * Display the Assignment pane - */ - public void loadAssignment(Assignment assignment, Window previousWindow, ModelEntity previous) - { - // Update main pane: - this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); - this.topBox.getChildren().clear(); - this.title.setText(""); - // ================= - - // Create a back button: - this.backButton(previousWindow, previous); - // ================= - - // Display modules: - Label assignments = new Label(assignment.getName()); - assignments.getStyleClass().add("title"); - this.mainContent.addRow(1, assignments); - // ================= - - // Ganttish chart button: - Button gantt = new Button("Generate a Ganttish Diagram"); - gantt.setOnAction(e -> MainController.ui.showGantt(assignment)); - GridPane.setHalignment(gantt, HPos.RIGHT); - GridPane.setColumnSpan(gantt, GridPane.REMAINING); - this.mainContent.add(gantt, 0, 1); - // ================= - - // Create a details pane: - VBox detailsBox = new VBox(5); - Label details = new Label(assignment.getDetails().getAsString()); - details.setWrapText(true); - String date = ""; - if (assignment instanceof Coursework) - { - Coursework c = (Coursework) assignment; - date = "Deadline: " + c.getDeadlineString(); - } else if (assignment instanceof Exam) - { - Exam e = (Exam) assignment; - date = "Date: " + e.getTimeSlot().getDateString(); - } - detailsBox.getChildren().addAll(new Label("Weighting: " + assignment.getWeighting()), - new Label(date), - new Label("Set by: " + assignment.getSetBy().getPreferredName()), - new Label("Marked by: " + assignment.getMarkedBy().getPreferredName()), - new Label("Reviewed by: " + assignment.getReviewedBy().getPreferredName()), details); - GridPane.setVgrow(detailsBox, Priority.SOMETIMES); - GridPane.setHgrow(detailsBox, Priority.ALWAYS); - // ================= - - mainContent.addRow(2, detailsBox); - - // Content pane: - GridPane content = new GridPane(); - GridPane.setVgrow(content, Priority.ALWAYS); - GridPane.setHgrow(content, Priority.ALWAYS); - content.setVgap(5); - // ================= - - // Requirements columns: - TableColumn rNameColumn = new TableColumn<>("Requirement"); - rNameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); - - TableColumn remainingColumn = new TableColumn<>("Remaining"); - remainingColumn.setCellValueFactory(new PropertyValueFactory<>("remainingQuantity")); - remainingColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); - - TableColumn typeColumn = new TableColumn<>("Quantity type"); - typeColumn.setCellValueFactory(new PropertyValueFactory<>("quantityType")); - - ObservableList requirementList = FXCollections.observableArrayList(assignment.getRequirements()); - // ================= - - // Create Requirements table: - TableView requirements = new TableView<>(); - requirements.setItems(requirementList); - requirements.getColumns().addAll(rNameColumn, remainingColumn, typeColumn); - requirements.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); - GridPane.setHgrow(requirements, Priority.ALWAYS); - GridPane.setVgrow(requirements, Priority.ALWAYS); - // ================= - - // Set RowFactory: - requirements.setRowFactory(e -> MenuController.requirementRowFactory(requirements, assignment)); - // ================= - - content.addColumn(0, requirements); - - // Actions toolbar: - HBox actionsReq = new HBox(); - GridPane.setHgrow(actionsReq, Priority.ALWAYS); - actionsReq.setSpacing(5); - actionsReq.setPadding(new Insets(5, 5, 10, 0)); - // ================= - - // Buttons: - Button addNewReq = new Button("Add a new requirement"); - - Button deleteReq = new Button("Remove"); - deleteReq.setDisable(true); - // ================= - - // Bind properties on buttons: - deleteReq.disableProperty().bind(new BooleanBinding() - { - { - bind(requirements.getSelectionModel().getSelectedItems()); - } - - @Override - protected boolean computeValue() - { - return !(requirementList.size() > 0 && requirements.getSelectionModel().getSelectedItem() != null); - } - }); - // ================= - - // Bind actions on buttons: - addNewReq.setOnAction(e -> { - try - { - Requirement req = MainController.ui.addRequirement(); - if (req != null) - { - requirementList.add(req); - assignment.addRequirement(req); - requirements.refresh(); - } - } catch (IOException e1) - { - UIManager.reportError("Unable to open View file"); - } catch (Exception e1) - { - } - }); - - deleteReq.setOnAction(e -> { - if (UIManager.confirm("Are you sure you want to remove this requirement?")) - { - Requirement r = requirements.getSelectionModel().getSelectedItem(); - requirementList.remove(r); - assignment.removeRequirement(r); - requirements.refresh(); - } - }); - // ================= - - actionsReq.getChildren().addAll(addNewReq, deleteReq); - - content.add(actionsReq, 0, 1); - // ================= - - // Tasks columns: - TableColumn nameColumn = new TableColumn<>("Task"); - nameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); - - TableColumn deadlineColumn = new TableColumn<>("Deadline"); - deadlineColumn.setCellValueFactory(new PropertyValueFactory<>("deadline")); - deadlineColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); - - TableColumn canComplete = new TableColumn<>("Can be completed?"); - canComplete.setCellValueFactory(new PropertyValueFactory<>("possibleToComplete")); - canComplete.setStyle("-fx-alignment: CENTER-RIGHT;"); - - ObservableList list = FXCollections.observableArrayList(assignment.getTasks()); - // ================= - - // Create Tasks table: - TableView tasks = new TableView<>(); - tasks.setItems(list); - tasks.getColumns().addAll(nameColumn, deadlineColumn, canComplete); - tasks.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); - GridPane.setHgrow(tasks, Priority.ALWAYS); - GridPane.setVgrow(tasks, Priority.ALWAYS); - // ================= - - // Set click event: - tasks.setRowFactory(e -> { - TableRow row = new TableRow() - { - @Override - protected void updateItem(final Task item, final boolean empty) - { - super.updateItem(item, empty); - // If completed, mark: - if (!empty && item != null && item.isCheckedComplete()) - this.getStyleClass().add("current-item"); - else - this.getStyleClass().remove("current-item"); - } - }; - row.setOnMouseClicked(event -> { - if (!row.isEmpty() && event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) - { - try - { - MainController.ui.taskDetails(row.getItem()); - tasks.refresh(); - } catch (IOException e1) - { - UIManager.reportError("Unable to open View file"); - } - } - }); - return row; - }); - // ================= - - content.addColumn(1, tasks); - - // Actions toolbar: - HBox actionsTask = new HBox(); - GridPane.setHgrow(actionsTask, Priority.ALWAYS); - actionsTask.setSpacing(5); - actionsTask.setPadding(new Insets(5, 5, 10, 0)); - // ================= - - // Buttons: - Button addNew = new Button("Add a new task"); - - Button check = new Button("Toggle complete"); - check.getStyleClass().add("set-button"); - check.setDisable(true); - - Button delete = new Button("Remove"); - delete.setDisable(true); - // ================= - - // Bind properties on buttons: - delete.disableProperty().bind(new BooleanBinding() - { - { - bind(tasks.getSelectionModel().getSelectedItems()); - } - - @Override - protected boolean computeValue() - { - return !(list.size() > 0 && tasks.getSelectionModel().getSelectedItem() != null); - } - }); - - check.disableProperty().bind(new BooleanBinding() - { - { - bind(tasks.getSelectionModel().getSelectedItems()); - } - - @Override - protected boolean computeValue() - { - return !(list.size() > 0 && tasks.getSelectionModel().getSelectedItem() != null && - tasks.getSelectionModel().getSelectedItem().canCheckComplete()); - } - }); - // ================= - - // Bind actions on buttons: - addNew.setOnAction(e -> { - try - { - Task task = MainController.ui.addTask(); - if (task != null) - { - list.add(task); - assignment.addTask(task); - } - this.updateMenu(); - } catch (IOException e1) - { - UIManager.reportError("Unable to open View file"); - } catch (Exception e1) - { - } - }); - - check.setOnAction(e -> { - tasks.getSelectionModel().getSelectedItem().toggleComplete(); - tasks.refresh(); - }); - - delete.setOnAction(e -> { - if (UIManager.confirm("Are you sure you want to remove this task?")) - { - Task t = tasks.getSelectionModel().getSelectedItem(); - list.remove(t); - assignment.removeTask(t); - this.updateMenu(); - } - }); - // ================= - - // Gap: - HBox gap = new HBox(); - HBox.setHgrow(gap, Priority.ALWAYS); - // ================= - - actionsTask.getChildren().addAll(addNew, gap, check, delete); - - content.add(actionsTask, 1, 1); - - this.mainContent.addRow(3, content); - } - - /** - * Handles the 'Mark all as read' button event - */ - public void handleMarkAll() - { - Notification[] nots = MainController.getSPC().getPlanner().getUnreadNotifications(); - // Mark all notifications as read: - for (int i = 0; i < nots.length; ++i) - { - int index = this.notificationList.getChildren().size() - 1 - i; - nots[i].read(); - // Remove cursor: - if (nots[i].getLink() == null) - this.notificationList.getChildren().get(index).setCursor(Cursor.DEFAULT); - - // Change style: - this.notificationList.getChildren().get(index).getStyleClass().remove("unread-item"); - } - - // Handle styles: - this.showNotification.getStyleClass().remove("unread-button"); - if (!this.showNotification.getStyleClass().contains("read-button")) - this.showNotification.getStyleClass().add("read-button"); - } - - /** - * Handles clicking on a specific notification - * - * @param id - */ - public void handleRead(int id) - { - // Get notification: - int idInList = MainController.getSPC().getPlanner().getNotifications().length - 1 - id; - Notification not = MainController.getSPC().getPlanner().getNotifications()[idInList]; - - // If not read: - if (!not.isRead()) - { - // Mark notification as read: - not.read(); - - // Swap styles: - this.notificationList.getChildren().get(id).getStyleClass().remove("unread-item"); - if (MainController.getSPC().getPlanner().getUnreadNotifications().length <= 0) - { - this.showNotification.getStyleClass().remove("unread-button"); - if (!this.showNotification.getStyleClass().contains("read-button")) - this.showNotification.getStyleClass().add("read-button"); - } - - if (not.getLink() == null) - this.notificationList.getChildren().get(id).setCursor(Cursor.DEFAULT); - } - - if (not.getLink() != null) - { - not.getLink().open(this.current); - this.main(); - } - } - - /** - * Handles the 'Import HUB file' event - */ - public void importFile() - { - if (MainController.importFile()) - UIManager.reportSuccess("File imported successfully!"); - this.main(); - } - - @Override - public void initialize(URL location, ResourceBundle resources) - { - this.prepareAnimations(); - this.isNavOpen = false; - - // Set button actions: - this.showDash.setOnAction(e -> this.main(Window.Dashboard)); - this.studyProfiles.setOnAction(e -> this.main(Window.Profiles)); - this.modules.setOnAction(e -> this.main(Window.Modules)); - this.milestones.setOnAction(e -> this.main(Window.Milestones)); - this.calendar.setOnAction(e -> MainController.ui.showCalendar()); - // ================= - - // Welcome text: - this.welcome = new Label("Welcome back, " + MainController.getSPC().getPlanner().getUserName() + "!"); - this.welcome.setPadding(new Insets(10, 15, 10, 15)); - this.topBox.getChildren().add(this.welcome); - // ================= - - this.mainContent.setVgap(10); - this.mainContent.setPadding(new Insets(15)); - - // Render dashboard: - this.main(Window.Dashboard); - // ================= - } - - /** - * Prepare notifications - */ - private void updateNotifications() - { - MainController.getSPC().checkForNotifications(); - - // Set notification button style: - if (MainController.getSPC().getPlanner().getUnreadNotifications().length > 0) - { - if (!this.showNotification.getStyleClass().contains("unread-button")) - { - this.showNotification.getStyleClass().remove("read-button"); - this.showNotification.getStyleClass().add("unread-button"); - } - } else if (!this.showNotification.getStyleClass().contains("read-button")) - { - this.showNotification.getStyleClass().add("read-button"); - this.showNotification.getStyleClass().remove("unread-button"); - } - - // Process notifications: - this.notificationList.getChildren().clear(); - Notification[] n = MainController.getSPC().getPlanner().getNotifications(); - for (int i = n.length - 1; i >= 0; i--) - { - GridPane pane = new GridPane(); - - // Check if has a link or is unread: - if (n[i].getLink() != null || !n[i].isRead()) - { - pane.setCursor(Cursor.HAND); - pane.setId(Integer.toString(n.length - i - 1)); - pane.setOnMouseClicked(e -> this.handleRead(Integer.parseInt(pane.getId()))); - - // Check if unread: - if (!n[i].isRead()) - pane.getStyleClass().add("unread-item"); - } - - // Create labels: - Label title = new Label(n[i].getTitle()); - title.getStyleClass().add("notificationItem-title"); - title.setMaxWidth(250.0); - - Label details = n[i].getDetails() != null ? new Label(n[i].getDetailsAsString()) : new Label(); - details.getStyleClass().add("notificationItem-details"); - details.setMaxWidth(250.0); - - String dateFormatted = n[i].getDateTime().get(Calendar.DAY_OF_MONTH) + " " + - n[i].getDateTime().getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault()) + " at " + - n[i].getDateTime().get(Calendar.HOUR) + " " + - n[i].getDateTime().getDisplayName(Calendar.AM_PM, Calendar.LONG, Locale.getDefault()); - Label date = new Label(dateFormatted); - date.getStyleClass().addAll("notificationItem-date"); - GridPane.setHalignment(date, HPos.RIGHT); - GridPane.setHgrow(date, Priority.ALWAYS); - - pane.addRow(1, title); - pane.addRow(2, details); - pane.addRow(3, date); - pane.addRow(4, new Separator(Orientation.HORIZONTAL)); - this.notificationList.addRow(n.length - i - 1, pane); - } - } - - /** - * Handles menu options - */ - private void updateMenu() - { - this.addActivity.setDisable(false); - this.milestones.setDisable(false); - this.studyProfiles.setDisable(false); - this.modules.setDisable(false); - this.calendar.setDisable(false); - - // Disable relevant menu options: - if (MainController.getSPC().getPlanner().getCurrentStudyProfile() == null) - { - this.addActivity.setDisable(true); - this.milestones.setDisable(true); - this.studyProfiles.setDisable(true); - this.modules.setDisable(true); - this.calendar.setDisable(true); - } else - { - if (MainController.getSPC().getCurrentTasks().size() <= 0) - { - this.addActivity.setDisable(true); - this.milestones.setDisable(true); - } - - if (MainController.getSPC().getPlanner().getCurrentStudyProfile().getModules().length <= 0) - this.modules.setDisable(true); - } - } - - /** - * Creates a back button - */ - public void backButton(Window previousWindow, ModelEntity previous) - { - if (previous != null || previousWindow != Window.Empty) - { - Button back = new Button(); - back.getStyleClass().addAll("button-image", "back-button"); - - if (previous == null && previousWindow != Window.Empty) - back.setOnAction(e -> this.main(previousWindow)); - else - back.setOnAction(e -> previous.open(this.current)); - - this.topBox.getChildren().add(back); - } - } - - /** - * Prepares animations for the main window - */ - private void prepareAnimations() - { - TranslateTransition openNav = new TranslateTransition(new Duration(300), navList); - openNav.setToX(0); - TranslateTransition closeNav = new TranslateTransition(new Duration(300), navList); - openMenu.setOnAction((ActionEvent e) -> { - this.isNavOpen = !isNavOpen; - if (navList.getTranslateX() != 0) - { - openNav.play(); - } else - { - closeNav.setToX(-(navList.getWidth())); - closeNav.play(); - } - }); - - TranslateTransition openNot = new TranslateTransition(new Duration(350), notifications); - openNot.setToY(0); - TranslateTransition closeNot = new TranslateTransition(new Duration(350), notifications); - - showNotification.setOnAction((ActionEvent e) -> { - if (notifications.getTranslateY() != 0) - { - openNot.play(); - } else - { - closeNot.setToY(-(notifications.getHeight()) - 56.0); - closeNot.play(); - } - }); - } - - /** - * RowFactory for a TableView of Requirement. - * - * @param e TableView that contains the RowFactory. - * @return new RowFactory - */ - protected static TableRow requirementRowFactory(TableView e, Assignment assignment) - { - TableRow row = new TableRow() - { - @Override - protected void updateItem(final Requirement item, final boolean empty) - { - super.updateItem(item, empty); - // If completed, mark: - if (!empty && item != null) - { - setText(item.toString()); - if (item.isComplete()) - this.getStyleClass().add("current-item"); - } else - { - setText(null); - this.getStyleClass().remove("current-item"); - } - e.refresh(); - } - }; - - row.setOnMouseClicked(event -> { - if (!row.isEmpty() && event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) - { - try - { - MainController.ui.requirementDetails(row.getItem()); - e.refresh(); - } catch (IOException e1) - { - UIManager.reportError("Unable to open View file"); - } - } - }); - - row.setOnDragDetected(event -> { - - if (row.getItem() == null) return; - Dragboard dragboard = row.startDragAndDrop(TransferMode.MOVE); - ClipboardContent content = new ClipboardContent(); - content.put(TaskController.format, row.getItem()); - dragboard.setContent(content); - event.consume(); - }); - - row.setOnDragOver(event -> { - if (event.getGestureSource() != row && event.getDragboard().hasContent(TaskController.format)) - event.acceptTransferModes(TransferMode.MOVE); - event.consume(); - }); - - row.setOnDragEntered(event -> { - if (event.getGestureSource() != row && event.getDragboard().hasContent(TaskController.format)) - row.setOpacity(0.3); - }); - - row.setOnDragExited(event -> { - if (event.getGestureSource() != row && event.getDragboard().hasContent(TaskController.format)) - row.setOpacity(1); - }); - - row.setOnDragDropped(event -> { - - if (row.getItem() == null) - return; - - Dragboard db = event.getDragboard(); - boolean success = false; - - if (event.getDragboard().hasContent(TaskController.format)) - { - ObservableList items = e.getItems(); - Requirement dragged = (Requirement) db.getContent(TaskController.format); - - int draggedID = items.indexOf(dragged); - int thisID = items.indexOf(row.getItem()); - - e.getItems().set(draggedID, row.getItem()); - e.getItems().set(thisID, dragged); - - ArrayList reqs = assignment.getRequirements(); - reqs.set(draggedID, row.getItem()); - reqs.set(thisID, dragged); - - success = true; - e.refresh(); - } - event.setDropCompleted(success); - event.consume(); - }); - return row; - } -} diff --git a/src/Controller/MilestoneController.java b/src/Controller/MilestoneController.java deleted file mode 100644 index 7ba2c906..00000000 --- a/src/Controller/MilestoneController.java +++ /dev/null @@ -1,252 +0,0 @@ -package Controller; - -import Model.Milestone; -import Model.Task; -import View.UIManager; -import javafx.application.Platform; -import javafx.beans.binding.BooleanBinding; -import javafx.collections.FXCollections; -import javafx.collections.ListChangeListener; -import javafx.collections.ObservableList; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.*; -import javafx.scene.layout.GridPane; -import javafx.stage.Stage; - -import java.net.URL; -import java.time.LocalDate; -import java.time.ZoneId; -import java.util.ResourceBundle; - -/** - * Created by Žilvinas on 14/05/2017. - */ -public class MilestoneController implements Initializable -{ - private Milestone milestone; - private boolean success = false; - - public Milestone getMilestone() - { - return this.milestone; - } - - public boolean isSuccess() - { - return success; - } - - // Panes: - @FXML private GridPane pane; - - // Buttons: - @FXML private Button submit; - @FXML private Button add; - @FXML private Button remove; - - // Text: - @FXML private TextArea details; - @FXML private DatePicker deadline; - @FXML private TextField name; - - // Labels: - @FXML private Label title; - @FXML private Label completed; - - // Lists: - @FXML private ListView tasks; - - /** - * Handle changes to the input fields - */ - public void handleChange() - { - // Check the input fields: - if (!this.name.getText().trim().isEmpty() && - !this.deadline.getEditor().getText().trim().isEmpty() && - this.tasks.getItems().size() > 0) - - this.submit.setDisable(false); - // ================= - - // Process tasks: - if (this.milestone != null) - { - this.milestone.replaceTasks(this.tasks.getItems()); - - if (!this.milestone.isComplete()) - { - this.completed.setVisible(false); - } else - this.completed.setVisible(true); - } - // ================= - } - - /** - * Validate data in the Deadline field - */ - public void validateDeadline() - { - if (this.deadline.getValue().isBefore(LocalDate.now())) - { - this.deadline.setStyle("-fx-border-color:red;"); - this.submit.setDisable(true); - } else - { - this.deadline.setStyle(""); - this.handleChange(); - } - } - - /** - * Handle the 'Add Task' button action - */ - public void addTask() - { - // Table items: - ObservableList list = FXCollections.observableArrayList(MainController.getSPC().getCurrentTasks()); - list.removeAll(this.tasks.getItems()); - if (this.milestone != null) - list.removeAll(this.milestone.getTasks()); - // ================= - - // Parse selected Tasks: - this.tasks.getItems().addAll(TaskController.taskSelectionWindow(list)); - // ================= - } - - /** - * Submit the form and create a new Milestone - */ - public void handleSubmit() - { - if (this.milestone == null) - { - // Create a new Milestone: - this.milestone = new Milestone(this.name.getText(), this.details.getText(), this.deadline.getValue()); - this.milestone.addTasks(this.tasks.getItems()); - // ================= - } else - { - // Update the current Milestone: - this.milestone.setName(this.name.getText()); - this.milestone.setDetails(this.details.getText()); - this.milestone.setDeadline(this.deadline.getValue()); - // ================= - } - - this.success = true; - Stage stage = (Stage) this.submit.getScene().getWindow(); - stage.close(); - } - - /** - * Handle Quit button - */ - public void handleQuit() - { - Stage stage = (Stage) this.submit.getScene().getWindow(); - stage.close(); - } - - // Constructors: - - /** - * Constructor for the MilestoneController - */ - public MilestoneController() - { - } - - /** - * Constructor for an MilestoneController with an existing Milestone - * - * @param milestone - */ - public MilestoneController(Milestone milestone) - { - this.milestone = milestone; - } - - @Override public void initialize(URL location, ResourceBundle resources) - { - // Bind properties on buttons: - this.remove.disableProperty().bind(new BooleanBinding() - { - { - bind(tasks.getSelectionModel().getSelectedItems()); - } - - @Override - protected boolean computeValue() - { - return !(tasks.getItems().size() > 0 && tasks.getSelectionModel().getSelectedItem() != null); - } - }); - // ================= - - // Button actions: - this.remove.setOnAction(e -> { - if (UIManager.confirm("Are you sure you want to remove this dependency?")) - { - Task t = this.tasks.getSelectionModel().getSelectedItem(); - this.tasks.getItems().remove(t); - if (this.milestone != null) - this.milestone.removeTask(t); - } - }); - - this.tasks.setCellFactory(e -> { - ListCell cell = new ListCell() - { - @Override - protected void updateItem(final Task item, final boolean empty) - { - super.updateItem(item, empty); - // If completed, mark: - if (!empty && item != null) - { - setText(item.toString()); - if (item.isCheckedComplete()) - this.getStyleClass().add("current-item"); - } else - { - setText(null); - this.getStyleClass().remove("current-item"); - } - } - }; - return cell; - }); - // ================= - - // Handle Milestone details: - if (this.milestone != null) - { - // Disable/modify elements: - this.title.setText("Milestone"); - - if (this.milestone.isComplete()) - { - this.completed.setVisible(true); - } - // ================= - - // Fill in data: - this.name.setText(this.milestone.getName()); - this.details.setText(this.milestone.getDetails().getAsString()); - this.deadline.setValue(this.milestone.getDeadlineDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate()); - this.tasks.getItems().addAll(this.milestone.getTasks()); - // ================= - } - // ================= - - // ListChangeListener: - this.tasks.getItems().addListener((ListChangeListener) c -> handleChange()); - // ================= - - Platform.runLater(() -> this.pane.requestFocus()); - } -} diff --git a/src/Controller/RequirementController.java b/src/Controller/RequirementController.java deleted file mode 100644 index 9478fdf4..00000000 --- a/src/Controller/RequirementController.java +++ /dev/null @@ -1,271 +0,0 @@ -package Controller; - -import Model.Activity; -import Model.QuantityType; -import Model.Requirement; -import View.UIManager; -import javafx.application.Platform; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.Node; -import javafx.scene.control.*; -import javafx.scene.control.cell.PropertyValueFactory; -import javafx.scene.input.MouseButton; -import javafx.scene.layout.GridPane; -import javafx.stage.Stage; - -import java.io.IOException; -import java.net.URL; -import java.util.ResourceBundle; - -/** - * Created by Zilvinas on 13/05/2017. - */ -public class RequirementController implements Initializable -{ - private Requirement requirement; - private boolean success = false; - - public Requirement getRequirement() - { - return this.requirement; - } - - public boolean isSuccess() - { - return success; - } - - // Panes: - @FXML private GridPane pane; - @FXML private TableView activities; - @FXML private TableColumn nameColumn; - @FXML private TableColumn quantityColumn; - @FXML private TableColumn dateColumn; - @FXML private ContextMenu context; - - // Buttons: - @FXML private Button submit; - @FXML private Button addQuantity; - @FXML private MenuItem quantityMenu; - - // Text: - @FXML private TextArea details; - @FXML private ComboBox quantityType; - @FXML private TextField name; - @FXML private TextField quantity; - @FXML private TextField time; - @FXML private TextField quantityName; - - // Labels: - @FXML private Label title; - @FXML private Label completed; - - /** - * Handle changes to the input fields - */ - public void handleChange() - { - // Check the input fields: - if (!this.name.getText().trim().isEmpty() && - !this.quantity.getText().trim().isEmpty() && - !this.time.getText().trim().isEmpty() && - this.quantityType.getSelectionModel().getSelectedIndex() != -1) - - this.submit.setDisable(false); - // ================= - } - - /** - * Validate data in the Time field - */ - public void validateTime() - { - if (!MainController.isNumeric(this.time.getText()) || Double.parseDouble(this.time.getText()) < 0) - { - this.time.setStyle("-fx-text-box-border:red;"); - this.submit.setDisable(true); - } else - { - this.time.setStyle(""); - this.handleChange(); - } - } - - /** - * Validate data in the Quantity field - */ - public void validateQuantity() - { - if (!MainController.isNumeric(this.quantity.getText()) || Integer.parseInt(this.quantity.getText()) < 0) - { - this.quantity.setStyle("-fx-text-box-border:red;"); - this.submit.setDisable(true); - } else - { - this.quantity.setStyle(""); - this.handleChange(); - } - if (this.requirement != null) - this.completed.setVisible(false); - } - - /** - * Validate data in the QuantityType field - */ - public void validateNewQuantity() - { - if (!this.quantityName.getText().trim().isEmpty()) - this.quantityMenu.setDisable(false); - else - this.quantityMenu.setDisable(true); - } - - /** - * Add a new QuantityType - */ - public void newQuantity() - { - if (UIManager.confirm("Create a new Quantity '" + this.quantityName.getText() + '?')) - { - // Create a new type: - QuantityType t = QuantityType.create(this.quantityName.getText()); - // ================= - - // Update the current list: - this.quantityType.getItems().clear(); - this.quantityType.getItems().addAll(QuantityType.listOfNames()); - this.quantityType.getSelectionModel().select(t.getName()); - // ================= - } - this.quantityName.clear(); - this.quantityMenu.setDisable(true); - } - - /** - * Submit the form and create a new Task - */ - public void handleSubmit() - { - if (this.requirement == null) - { - // Create a new Requirement: - this.requirement = new Requirement(this.name.getText(), this.details.getText(), - Double.parseDouble(this.time.getText()), Integer.parseInt(this.quantity.getText()), - this.quantityType.getValue()); - // ================= - } else - { - // Update the current requirement: - this.requirement.setName(this.name.getText()); - this.requirement.setDetails(this.details.getText()); - this.requirement.setEstimatedTimeInHours(Double.parseDouble(this.time.getText())); - this.requirement.setInitialQuantity(Integer.parseInt(this.quantity.getText())); - this.requirement.setQuantityType(this.quantityType.getValue()); - // ================= - - } - this.success = true; - Stage stage = (Stage) this.submit.getScene().getWindow(); - stage.close(); - } - - /** - * Handle Quit button - */ - public void handleQuit() - { - Stage stage = (Stage) this.submit.getScene().getWindow(); - stage.close(); - } - - /** - * Constructor for the RequirementController - */ - public RequirementController() - { - } - - /** - * Constructor for a RequirementController with an existing Requirement - * - * @param requirement - */ - public RequirementController(Requirement requirement) - { - this.requirement = requirement; - } - - @Override public void initialize(URL location, ResourceBundle resources) - { - this.quantityType.getItems().addAll(QuantityType.listOfNames()); - - // Row actions: - this.activities.setRowFactory(e -> { - TableRow row = new TableRow<>(); - row.setOnMouseClicked(event -> { - if (!row.isEmpty() && event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) - { - try - { - MainController.ui.activityDetails(row.getItem()); - this.activities.refresh(); - } catch (IOException e1) - { - UIManager.reportError("Unable to open View file"); - } - } - }); - return row; - }); - // ================= - - // Quantity actions: - this.addQuantity.setOnMousePressed(event -> { - if (event.isPrimaryButtonDown()) - context.show(addQuantity, event.getScreenX(), event.getScreenY()); - }); - // ================= - - // Hide the Activities table: - if (this.requirement == null) - { - this.pane.getChildren().remove(this.activities); - this.pane.getRowConstraints().remove(3); - - Node bottomNode = this.pane.getChildren().get(0); - this.pane.getChildren().remove(bottomNode); - this.pane.getRowConstraints().remove(3); - - GridPane.setColumnSpan(bottomNode, 2); - this.pane.addRow(3, bottomNode); - } - // ================= - else - { - // Disable/modify elements: - this.title.setText("Requirement"); - - if (this.requirement.isComplete()) - this.completed.setVisible(true); - - // Activity columns: - nameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); - quantityColumn.setCellValueFactory(new PropertyValueFactory<>("activityQuantity")); - dateColumn.setCellValueFactory(new PropertyValueFactory<>("dateString")); - // ================= - - // Fill in data: - this.name.setText(this.requirement.getName()); - this.details.setText(this.requirement.getDetails().getAsString()); - this.time.setText(Double.toString(this.requirement.getEstimatedTimeInHours())); - this.quantity.setText(Integer.toString(this.requirement.getInitialQuantity())); - this.quantityType.getSelectionModel().select(this.requirement.getQuantityType().getName()); - this.activities.getItems().addAll(this.requirement.getActivityLog()); - // ================= - } - // ================= - - Platform.runLater(() -> this.pane.requestFocus()); - } -} diff --git a/src/Controller/StudyPlannerController.java b/src/Controller/StudyPlannerController.java deleted file mode 100644 index 51d40dcb..00000000 --- a/src/Controller/StudyPlannerController.java +++ /dev/null @@ -1,332 +0,0 @@ -package Controller; - -import Model.*; - -import javax.crypto.*; -import java.io.BufferedOutputStream; -import java.io.FileOutputStream; -import java.io.ObjectOutputStream; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.GregorianCalendar; - -/** - * Created by bendickson on 5/4/17. - */ -public class StudyPlannerController -{ - private StudyPlanner planner; - - public StudyPlanner getPlanner() - { - return planner; - } - - /** - * Save the current StudyPlanner into a serialized file. - * - * @param key64 SecretKey used for encoding. - * @param fileName name of the file. - * @return whether saved successfully. - */ - public boolean save(SecretKey key64, String fileName) - { - try - { - Cipher cipher = Cipher.getInstance("Blowfish"); - cipher.init(Cipher.ENCRYPT_MODE, key64); - SealedObject sealedObject = new SealedObject(this.planner, cipher); - CipherOutputStream cipherOutputStream = new CipherOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)), cipher); - ObjectOutputStream outputStream = new ObjectOutputStream(cipherOutputStream); - outputStream.writeObject(sealedObject); - outputStream.close(); - return true; - } catch (Exception e) - { - e.printStackTrace(); - return false; - } - } - - /** - * ' - * Checks whether a StudyProfile for this year and semester is loaded in. - * - * @param year year to be checked - * @param semester semester number to be checked - * @return whether this StudyProfile exists in this StudyPlanner - */ - public boolean containsStudyProfile(int year, int semester) - { - return planner.containsStudyProfile(year, semester); - } - - /** - * if valid, this method creates a new StudyProfile and returns true - * if invalid, it returns false - * - * @param hubFile HubFile containing the newly loaded in profile - * @return whether created successfully. - */ - public boolean createStudyProfile(HubFile hubFile) - { - if (!this.planner.containsStudyProfile(hubFile.getYear(), hubFile.getSemester())) - { - // Create a profile: - StudyProfile profile = new StudyProfile(hubFile); - this.planner.addStudyProfile(profile); - if (this.planner.getCurrentStudyProfile() == null) - { - this.planner.setCurrentStudyProfile(profile); - profile.setCurrent(true); - } - // ================= - - // Fill the global calendar with newly imported events: - ArrayList cal = hubFile.getCalendarList(); - int i = -1; - int ii = cal.size(); - while (++i < ii) - { - //ConsoleIO.setConsoleMessage("Adding " + cal.get(i).toString() + " to calendar", true); - this.planner.addEventToCalendar(cal.get(i)); - profile.addEventToCalendar(cal.get(i)); - } - // ================= - - // Notify user: - Notification not = new Notification("New study profile created!", new GregorianCalendar(), - "\"" + profile.getName() + "\"", profile); - this.planner.addNotification(not); - // ================= - - return true; - } - return false; - } - - /** - * returns a list of tasks in the current StudyProfile if it exists - * or an empty list if it doesn't - */ - public ArrayList getCurrentTasks() - { - if (this.getPlanner().getCurrentStudyProfile() != null) - return this.getPlanner().getCurrentStudyProfile().getTasks(); - else - return new ArrayList<>(); - } - - /** - * Checker whether the user needs to be notified about something. - * (Deadlines etc.) - */ - public void checkForNotifications() - { - // TODO notifications - /*int hours1 = 168, hours2 = 48; // temporary values until a Settings page is present - - for (Map.Entry entry : this.planner.getDeadlineNotifications().entrySet()) - { - if (entry.getKey() instanceof Assignment) - { - if (!entry.getValue()[0]) - { - GregorianCalendar temp = new GregorianCalendar(); - temp.add(Calendar.HOUR, -hours1); - Date date = temp.getTime(); - - if (entry.getKey() instanceof Coursework) - { - if (date.after((((Coursework) entry.getKey()).getDeadline().getDate()))) - { - Notification not = new Notification("Assignment due in a week!", - new GregorianCalendar(), entry.getKey().getName(), entry.getKey()); - MainController.getSPC().getPlanner().addNotification(not); - entry.getValue()[0] = true; - } - } - if (entry.getKey() instanceof Exam) - { - if (date.after((((Exam) entry.getKey()).getTimeSlot().getDate()))) - { - Notification not = new Notification("You have an exam in a week!", - new GregorianCalendar(), entry.getKey().getName(), entry.getKey()); - MainController.getSPC().getPlanner().addNotification(not); - entry.getValue()[0] = true; - } - } - } else if (!entry.getValue()[1]) - { - GregorianCalendar temp = new GregorianCalendar(); - temp.add(Calendar.HOUR, -hours2); - Date date = temp.getTime(); - - if (entry.getKey() instanceof Coursework) - { - if (date.after((((Coursework) entry.getKey()).getDeadline().getDate()))) - { - Notification not = new Notification("Assignment due in a two days!", - new GregorianCalendar(), entry.getKey().getName(), entry.getKey()); - MainController.getSPC().getPlanner().addNotification(not); - entry.getValue()[1] = true; - } - } - if (entry.getKey() instanceof Exam) - { - if (date.after((((Exam) entry.getKey()).getTimeSlot().getDate()))) - { - Notification not = new Notification("You have an exam in two days!", - new GregorianCalendar(), entry.getKey().getName(), entry.getKey()); - MainController.getSPC().getPlanner().addNotification(not); - entry.getValue()[1] = true; - } - } - } else this.planner.getDeadlineNotifications().remove(entry); - } - }*/ - } - - /** - * Adds a new Activity to this StudyPlanner. - * - * @param activity Activity to be added. - */ - public void addActivity(Activity activity) - { - this.planner.addActivity(activity); - } - - /** - * Adds a new Milestone to this StudyPlanner. - * - * @param milestone Milestone to be added. - */ - public void addMilestone(Milestone milestone) - { - this.planner.getCurrentStudyProfile().addMilestone(milestone); - } - - /** - * Removes the given Milestone from this StudyPlanner - * - * @param milestone Milestone to be removed. - * @return Whether the Milestone was removed successfully. - */ - public boolean removeMilestone(Milestone milestone) - { - return this.planner.getCurrentStudyProfile().removeMilestone(milestone); - } - - /** - * Add a new QuantityType to this StudyPlanner. - * - * @param quantity QuantityType to be added - * @return whether added successfully. - */ - public boolean addQuantityType(QuantityType quantity) - { - if (!this.planner.getQuantityTypes().contains(quantity)) - { - this.planner.getQuantityTypes().add(quantity); - return true; - } - return false; - } - - /** - * Add a new TaskType to this StudyPlanner. - * - * @param taskType TaskType to be added - * @return whether added successfully. - */ - public boolean addTaskType(TaskType taskType) - { - if (!this.planner.getTaskTypes().contains(taskType)) - { - this.planner.getTaskTypes().add(taskType); - return true; - } - return false; - } - - // Constructors: - public StudyPlannerController() throws NoSuchAlgorithmException, NoSuchPaddingException - { - // checks if there is a existing settings file - if (DataController.existingSettingsFile()) - { - // import data - } else - { - // create a blank one - - // create an Account - String fullName = ""; - boolean familyNameLast = false; - String salutation = ""; - String email = ""; - - // CONSOLE INPUT - to be replaced by javaFX - fullName = View.ConsoleIO.getDataString("Enter Name:"); - while (!Person.validName(fullName)) - { - fullName = View.ConsoleIO.getDataString("I'm sorry " + fullName + - " I'm afraid I can't do that.\nName must only contain letters and spaces.\nPlease Enter Name:"); - } - salutation = View.ConsoleIO.getDataString("Enter salutation:"); - while (!Person.validSalutation(salutation)) - { - salutation = View.ConsoleIO.getDataString("Salutation must only contain letters.\nPlease Enter Salutation:"); - } - familyNameLast = View.ConsoleIO.getDataBool("Is the family name last (y/n)"); - - email = View.ConsoleIO.getDataString("Enter Email Address:"); - while (!Person.validEmail(email)) - { - email = View.ConsoleIO.getDataString("Invalid email address.\nPlease enter a valid email address:"); - } - Person studentDetails = new Person(salutation, fullName, familyNameLast, email); - String studentAccountNumber = ""; - - View.ConsoleIO.setConsoleMessage("Hello " + studentDetails.getSalutation() + " " + studentDetails.getFamilyName()); - View.ConsoleIO.setConsoleMessage("Thank you for creating a study profile."); - - - Account newAccount = new Account(studentDetails, studentAccountNumber); - - - planner = new StudyPlanner(newAccount); - } - } - - /** - * Constructor for testing UI - * - * @param newAccount - */ - public StudyPlannerController(Account newAccount) throws NoSuchAlgorithmException, NoSuchPaddingException - { - planner = new StudyPlanner(newAccount); - } - - /** - * Used when loading from a file - * - * @param planner StudyPlanner to be loaded. - */ - public StudyPlannerController(StudyPlanner planner) - { - this.planner = planner; - - // Process Quantity and Task types. - if (!this.planner.getQuantityTypes().isEmpty()) - this.planner.getQuantityTypes().forEach(e -> QuantityType.create(e)); - - if (!this.planner.getTaskTypes().isEmpty()) - this.planner.getTaskTypes().forEach(e -> TaskType.create(e)); - - if (!this.planner.emptyVersionControlLibrary()) - this.planner.rebuildVersionControlLibrary(); - } -} diff --git a/src/Controller/StudyProfileController.java b/src/Controller/StudyProfileController.java deleted file mode 100644 index 7c8584f1..00000000 --- a/src/Controller/StudyProfileController.java +++ /dev/null @@ -1,72 +0,0 @@ -package Controller; - -import Model.StudyProfile; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.scene.control.Button; -import javafx.scene.control.Label; -import javafx.stage.Stage; - -import java.net.URL; -import java.util.ResourceBundle; - -/** - * Created by Žilvinas on 10/05/2017. - */ -public class StudyProfileController implements Initializable -{ - private StudyProfile profile; - - // Labels: - @FXML private Label title; - @FXML private Label name; - @FXML private Label details; - @FXML private Label modules; - @FXML private Label milestones; - @FXML private Label extensions; - - // Buttons: - @FXML private Button setCurrent; - - /** - * Set this StudyProfile as the current profile - */ - public void setCurrent() - { - MainController.getSPC().getPlanner().setCurrentStudyProfile(this.profile); - this.setCurrent.setDisable(true); - } - - /** - * Close this window - */ - public void handleClose() - { - Stage stage = (Stage) this.title.getScene().getWindow(); - stage.close(); - } - - /** - * Constructor for the StudyProfileController - */ - public StudyProfileController(StudyProfile profile) - { - this.profile = profile; - } - - @Override - public void initialize(URL location, ResourceBundle resources) - { - this.title.setText("Year " + this.profile.getYear() + ", Semester " + this.profile.getSemesterNo()); - this.name.setText(this.profile.getName()); - this.details.setText(this.profile.getDetails().getAsString()); - this.details.setWrapText(true); - - this.modules.setText(this.profile.getModules().length + " module(s)."); - this.milestones.setText(this.profile.getMilestones().length + " milestone(s)."); - this.extensions.setText(this.profile.getExtensions().length + " extension application(s)."); - - if (MainController.getSPC().getPlanner().getCurrentStudyProfile().equals(this.profile)) - this.setCurrent.setDisable(true); - } -} \ No newline at end of file diff --git a/src/Controller/TaskController.java b/src/Controller/TaskController.java deleted file mode 100644 index 1a11425b..00000000 --- a/src/Controller/TaskController.java +++ /dev/null @@ -1,615 +0,0 @@ -package Controller; - -import Model.Assignment; -import Model.Requirement; -import Model.Task; -import Model.TaskType; -import View.UIManager; -import javafx.application.Platform; -import javafx.beans.binding.BooleanBinding; -import javafx.beans.property.SimpleStringProperty; -import javafx.beans.value.ObservableValue; -import javafx.collections.FXCollections; -import javafx.collections.ListChangeListener; -import javafx.collections.ObservableList; -import javafx.fxml.FXML; -import javafx.fxml.Initializable; -import javafx.geometry.Insets; -import javafx.geometry.Pos; -import javafx.scene.Scene; -import javafx.scene.control.*; -import javafx.scene.control.cell.PropertyValueFactory; -import javafx.scene.image.Image; -import javafx.scene.input.*; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.Priority; -import javafx.scene.layout.VBox; -import javafx.scene.paint.Paint; -import javafx.stage.Modality; -import javafx.stage.Stage; - -import java.io.IOException; -import java.net.URL; -import java.time.LocalDate; -import java.time.ZoneId; -import java.util.ResourceBundle; - -/** - * Created by Zilvinas on 12/05/2017. - */ -public class TaskController implements Initializable -{ - public static DataFormat format = new DataFormat("object/Requirement"); - - private Task task; - private boolean success = false; - - public Task getTask() - { - return this.task; - } - - public boolean isSuccess() - { - return success; - } - - // Buttons: - @FXML private Button submit; - @FXML private Button addReq; - @FXML private Button addDep; - @FXML private Button removeReq; - @FXML private Button removeDep; - @FXML private ToggleButton markComplete; - @FXML private Button addTaskType; - @FXML private MenuItem taskTypeMenu; - - // Panes: - @FXML private GridPane pane; - @FXML private ContextMenu context; - - // Text: - @FXML private TextArea details; - @FXML private ComboBox taskType; - @FXML private DatePicker deadline; - @FXML private TextField name; - @FXML private TextField weighting; - @FXML private TextField taskTypeName; - - // Labels: - @FXML private Label title; - @FXML private Label completed; - @FXML private Label canComplete; - - // Lists: - @FXML private ListView requirements; - @FXML private ListView dependencies; - - /** - * Handle changes to the input fields - */ - public void handleChange() - { - // Check the input fields: - if (!this.name.getText().trim().isEmpty() && - !this.weighting.getText().trim().isEmpty() && - !this.deadline.getEditor().getText().trim().isEmpty() && !this.deadline.getValue().isBefore(LocalDate.now()) && - this.taskType.getSelectionModel().getSelectedIndex() != -1) - - this.submit.setDisable(false); - // ================= - - // Process requirements and dependencies: - if (this.task != null) - { - this.task.replaceDependencies(this.dependencies.getItems()); - this.task.replaceRequirements(this.requirements.getItems()); - - if (!this.task.isCheckedComplete() && this.task.canCheckComplete()) - { - this.canComplete.setText("Can be completed."); - this.canComplete.setTextFill(Paint.valueOf("green")); - this.canComplete.setVisible(true); - this.markComplete.setDisable(false); - } else if (!this.task.canCheckComplete()) - { - this.task.setComplete(false); - this.canComplete.setText("Cannot be completed at this point."); - this.canComplete.setTextFill(Paint.valueOf("red")); - this.canComplete.setVisible(true); - this.markComplete.setDisable(true); - } - } - // ================= - } - - /** - * Validate data in the Weighting field - */ - public void validateWeighting() - { - if (!MainController.isNumeric(this.weighting.getText()) || Integer.parseInt(this.weighting.getText()) > 100 - || Integer.parseInt(this.weighting.getText()) < 0) - { - this.weighting.setStyle("-fx-text-box-border:red;"); - this.submit.setDisable(true); - } else - { - this.weighting.setStyle(""); - this.handleChange(); - } - } - - /** - * Validate data in the Deadline field - */ - public void validateDeadline() - { - if (this.deadline.getValue().isBefore(LocalDate.now())) - { - this.deadline.setStyle("-fx-border-color:red;"); - this.submit.setDisable(true); - } else - { - this.deadline.setStyle(""); - this.handleChange(); - } - } - - /** - * Handle the 'Add requirement' button action - */ - public void addRequirement() - { - try - { - Requirement req = MainController.ui.addRequirement(); - if (req != null) - this.requirements.getItems().add(req); - } catch (IOException e1) - { - UIManager.reportError("Unable to open View file"); - } catch (Exception e1) - { - } - } - - /** - * Handle the 'Add dependency' button action - */ - public void addDependency() - { - // Table items: - ObservableList list = FXCollections.observableArrayList(MainController.getSPC().getCurrentTasks()); - list.removeAll(this.dependencies.getItems()); - if (this.task != null) - { - list.remove(this.task); - list.removeAll(this.task.getDependencies()); - } - // ================= - - // Parse selected Tasks: - this.dependencies.getItems().addAll(TaskController.taskSelectionWindow(list)); - // ================= - } - - /** - * Handles the 'Mark as complete' button action - */ - public void toggleComplete() - { - if (this.task.isCheckedComplete()) - { - this.task.toggleComplete(); - this.completed.setVisible(false); - this.canComplete.setVisible(true); - } else if (this.task.canCheckComplete()) - { - this.task.toggleComplete(); - this.completed.setVisible(true); - this.canComplete.setVisible(false); - } - } - - /** - * Validate data in the TaskType field - */ - public void validateNewTaskType() - { - if (!this.taskTypeName.getText().trim().isEmpty()) - this.taskTypeMenu.setDisable(false); - else - this.taskTypeMenu.setDisable(true); - } - - /** - * Add a new TaskType - */ - public void newTaskType() - { - if (UIManager.confirm("Create a new Task type '" + this.taskTypeName.getText() + '?')) - { - // Create a new type: - TaskType t = TaskType.create(this.taskTypeName.getText()); - // ================= - - // Update the current list: - this.taskType.getItems().clear(); - this.taskType.getItems().addAll(TaskType.listOfNames()); - this.taskType.getSelectionModel().select(t.getName()); - // ================= - } - this.taskTypeName.clear(); - this.taskTypeMenu.setDisable(true); - } - - /** - * Submit the form and create a new Task - */ - public void handleSubmit() - { - if (this.task == null) - { - // Create a new Task: - this.task = new Task(this.name.getText(), this.details.getText(), this.deadline.getValue(), - Integer.parseInt(this.weighting.getText()), this.taskType.getValue()); - - for (Requirement req : this.requirements.getItems()) - this.task.addRequirement(req); - - for (Task t : this.dependencies.getItems()) - this.task.addDependency(t); - // ================= - } else - { - // Update the current task: - this.task.setName(this.name.getText()); - this.task.setDetails(this.details.getText()); - this.task.setDeadline(this.deadline.getValue()); - this.task.setWeighting(Integer.parseInt(this.weighting.getText())); - this.task.setType(this.taskType.getValue()); - // ================= - } - - this.success = true; - Stage stage = (Stage) this.submit.getScene().getWindow(); - stage.close(); - } - - /** - * Handle Quit button - */ - public void handleQuit() - { - Stage stage = (Stage) this.submit.getScene().getWindow(); - stage.close(); - } - - /** - * Constructor for the TaskController - */ - public TaskController() - { - } - - /** - * Constructor for a TaskController with an existing Task - * - * @param task - */ - public TaskController(Task task) - { - this.task = task; - } - - @Override - public void initialize(URL location, ResourceBundle resources) - { - this.taskType.getItems().addAll(TaskType.listOfNames()); - - // Bind properties on buttons: - this.removeDep.disableProperty().bind(new BooleanBinding() - { - { - bind(dependencies.getSelectionModel().getSelectedItems()); - } - - @Override - protected boolean computeValue() - { - return !(dependencies.getItems().size() > 0 && dependencies.getSelectionModel().getSelectedItem() != null); - } - }); - - this.removeReq.disableProperty().bind(new BooleanBinding() - { - { - bind(requirements.getSelectionModel().getSelectedItems()); - } - - @Override - protected boolean computeValue() - { - return !(requirements.getItems().size() > 0 && requirements.getSelectionModel().getSelectedItem() != null); - } - }); - // ================= - - // Button actions: - this.removeDep.setOnAction(e -> { - if (UIManager.confirm("Are you sure you want to remove this dependency?")) - { - Task t = this.dependencies.getSelectionModel().getSelectedItem(); - this.dependencies.getItems().remove(t); - if (this.task != null) - this.task.removeDependency(t); - } - }); - - this.removeReq.setOnAction(e -> { - if (UIManager.confirm("Are you sure you want to remove this requirement?")) - { - Requirement r = this.requirements.getSelectionModel().getSelectedItem(); - this.requirements.getItems().remove(r); - if (this.task != null) - this.task.removeRequirement(r); - } - }); - - this.requirements.setCellFactory(this::requirementCellFactory); - // ================= - - // TaskType actions: - this.addTaskType.setOnMousePressed(event -> { - if (event.isPrimaryButtonDown()) - context.show(addTaskType, event.getScreenX(), event.getScreenY()); - }); - // ================= - - // Handle Task details: - if (this.task != null) - { - // Disable/modify elements: - this.title.setText("Task"); - this.markComplete.setVisible(true); - this.canComplete.setTextFill(Paint.valueOf("green")); - - if (this.task.isCheckedComplete()) - { - this.completed.setVisible(true); - this.markComplete.setSelected(true); - this.markComplete.setDisable(false); - } else if (this.task.canCheckComplete()) - { - this.markComplete.setDisable(false); - this.canComplete.setVisible(true); - } else - { - this.canComplete.setText("Cannot be completed at this point."); - this.canComplete.setTextFill(Paint.valueOf("red")); - this.canComplete.setVisible(true); - } - // ================= - - // Fill in data: - this.name.setText(this.task.getName()); - this.details.setText(this.task.getDetails().getAsString()); - this.weighting.setText(Integer.toString(this.task.getWeighting())); - this.taskType.getSelectionModel().select(this.task.getType().getName()); - this.deadline.setValue(this.task.getDeadlineDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate()); - this.dependencies.getItems().addAll(this.task.getDependencies()); - this.requirements.getItems().addAll(this.task.getRequirements()); - // ================= - } - // ================= - - // ListChangeListener: - this.dependencies.getItems().addListener((ListChangeListener) c -> handleChange()); - this.requirements.getItems().addListener((ListChangeListener) c -> handleChange()); - // ================= - - Platform.runLater(() -> this.pane.requestFocus()); - } - - /** - * RowFactory for a TableRow of Task. - * - * @param e TableView that contains the TableRow. - * @return new TableRow - */ - protected static TableRow taskRowFactory(TableView e) - { - TableRow row = new TableRow() - { - @Override - protected void updateItem(final Task item, final boolean empty) - { - super.updateItem(item, empty); - // If Task is completed, mark: - if (!empty && item != null && item.isCheckedComplete()) - this.getStyleClass().add("current-item"); // TODO something wrong with TableRow styles - } - }; - row.setOnMouseClicked(event -> { - if (!row.isEmpty() && event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) - { - Stage current = (Stage) row.getScene().getWindow(); - current.close(); - } - }); - return row; - } - - /** - * Creates a Task selection window. - * - * @param list List of Tasks to be put into the window. - * @return A list of selected Tasks - */ - protected static ObservableList taskSelectionWindow(ObservableList list) - { - // Layout: - VBox layout = new VBox(); - layout.setSpacing(10); - layout.setAlignment(Pos.BOTTOM_RIGHT); - // ================= - - // Tasks columns: - TableColumn nameColumn = new TableColumn<>("Task"); - nameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); - - TableColumn assignmentColumn = new TableColumn<>("Assignments"); - assignmentColumn.setCellValueFactory(new PropertyValueFactory("assignments") - { - @Override public ObservableValue call(TableColumn.CellDataFeatures param) - { - SimpleStringProperty value = new SimpleStringProperty(""); - for (Assignment a : ((Task) param.getValue()).getAssignmentReferences()) - value.set(value.getValue() + a.getName() + "\n"); - return value; - } - }); - - TableColumn deadlineColumn = new TableColumn<>("Deadline"); - deadlineColumn.setCellValueFactory(new PropertyValueFactory<>("deadline")); - deadlineColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); - // ================= - - // Create a table: - TableView tasks = new TableView<>(); - tasks.setItems(list); - tasks.getColumns().addAll(nameColumn, assignmentColumn, deadlineColumn); - tasks.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); - // ================= - - // Table attributes: - tasks.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); - GridPane.setHgrow(tasks, Priority.ALWAYS); - GridPane.setVgrow(tasks, Priority.ALWAYS); - // ================= - - // Set click event: - tasks.setRowFactory(TaskController::taskRowFactory); - // ================= - - // Button: - Button OK = new Button("OK"); - OK.setOnAction(e -> { - Stage current = (Stage) OK.getScene().getWindow(); - current.close(); - }); - VBox.setMargin(OK, new Insets(5)); - OK.setDefaultButton(true); - // ================= - - layout.getChildren().addAll(tasks, OK); - - // Set a new scene: - Stage stage = new Stage(); - stage.initModality(Modality.APPLICATION_MODAL); - stage.setScene(new Scene(layout, 550, 300)); - stage.setTitle("Select dependencies"); - stage.getIcons().add(new Image("file:icon.png")); - stage.showAndWait(); - // ================= - - return tasks.getSelectionModel().getSelectedItems(); - } - - /** - * CellFactory for a ListView of Requirement. - * - * @param e ListView that contains the ListCell. - * @return new ListCell - */ - protected ListCell requirementCellFactory(ListView e) - { - ListCell cell = new ListCell() - { - @Override - protected void updateItem(final Requirement item, final boolean empty) - { - super.updateItem(item, empty); - // If completed, mark: - if (!empty && item != null) - { - setText(item.toString()); - if (item.isComplete()) - this.getStyleClass().add("current-item"); - } else - { - setText(null); - this.getStyleClass().remove("current-item"); - } - } - }; - - cell.setOnMouseClicked(event -> { - if (!cell.isEmpty() && event.getButton() == MouseButton.PRIMARY && event.getClickCount() == 2) - { - try - { - MainController.ui.requirementDetails(cell.getItem()); - e.refresh(); - } catch (IOException e1) - { - UIManager.reportError("Unable to open View file"); - } - } - }); - - cell.setOnDragDetected(event -> { - - if (cell.getItem() == null) return; - Dragboard dragboard = cell.startDragAndDrop(TransferMode.MOVE); - ClipboardContent content = new ClipboardContent(); - content.put(TaskController.format, cell.getItem()); - dragboard.setContent(content); - event.consume(); - }); - - cell.setOnDragOver(event -> { - if (event.getGestureSource() != cell && event.getDragboard().hasContent(TaskController.format)) - event.acceptTransferModes(TransferMode.MOVE); - event.consume(); - }); - - cell.setOnDragEntered(event -> { - if (event.getGestureSource() != cell && event.getDragboard().hasContent(TaskController.format)) - cell.setOpacity(0.3); - }); - - cell.setOnDragExited(event -> { - if (event.getGestureSource() != cell && event.getDragboard().hasContent(TaskController.format)) - cell.setOpacity(1); - }); - - cell.setOnDragDropped(event -> { - - if (cell.getItem() == null) - return; - - Dragboard db = event.getDragboard(); - boolean success = false; - - if (event.getDragboard().hasContent(TaskController.format)) - { - ObservableList items = e.getItems(); - Requirement dragged = (Requirement) db.getContent(TaskController.format); - - int draggedID = items.indexOf(dragged); - int thisID = items.indexOf(cell.getItem()); - - e.getItems().set(draggedID, cell.getItem()); - e.getItems().set(thisID, dragged); - - success = true; - } - event.setDropCompleted(success); - event.consume(); - }); - return cell; - } -} - diff --git a/src/Controller/XMLcontroller.java b/src/Controller/XMLcontroller.java deleted file mode 100644 index d2a30be6..00000000 --- a/src/Controller/XMLcontroller.java +++ /dev/null @@ -1,233 +0,0 @@ -package Controller; - -import Model.MultilineString; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.util.HashMap; -import java.util.HashSet; - -import static Controller.MainController.isNumeric; - -/** - * Created by bendickson on 5/6/17. - */ -public class XMLcontroller -{ - public enum ImportAs - { - BOOLEAN, STRING, INTEGER, DOUBLE, MULTILINESTRING, NODELIST - } - - public class NodeReturn - { - private ImportAs importedAs; - private String stringValue; - private int integerValue; - private double doubleValue; - private MultilineString multilineStringValue; - private NodeList nodeList; - private boolean booleanValue = false; - - public boolean getBoolean() - { - if (importedAs == ImportAs.BOOLEAN) - { - return booleanValue; - } else - { - return false; - } - } - - public String getString() - { - if (importedAs == ImportAs.STRING) - { - return stringValue; - } else - { - return null; - } - } - - public MultilineString getMultilineString() - { - if (importedAs == ImportAs.MULTILINESTRING) - { - return multilineStringValue; - } else - { - return null; - } - } - - public int getInt() - { - if (importedAs == ImportAs.INTEGER) - { - return integerValue; - } else - { - return 0; - } - } - - public double getDouble() - { - if (importedAs == ImportAs.DOUBLE) - { - return doubleValue; - } else - { - return 0; - } - } - - public NodeList getNodeList() - { - if (importedAs == ImportAs.NODELIST) - { - return nodeList; - } else - { - return null; - } - } - - NodeReturn(boolean nv) - { - importedAs = ImportAs.BOOLEAN; - booleanValue = nv; - } - - NodeReturn(int nv) - { - importedAs = ImportAs.INTEGER; - integerValue = nv; - } - - NodeReturn(double nv) - { - importedAs = ImportAs.DOUBLE; - doubleValue = nv; - } - - NodeReturn(String nv) - { - importedAs = ImportAs.STRING; - stringValue = nv; - } - - NodeReturn(MultilineString nv) - { - importedAs = ImportAs.MULTILINESTRING; - multilineStringValue = nv; - } - - NodeReturn(NodeList nv) - { - importedAs = ImportAs.NODELIST; - nodeList = nv; - } - } - - public HashMap getSchemaValues(NodeList nodes, HashMap schema) - { - HashMap r = new HashMap<>(); - int i = -1; - int ii = nodes.getLength(); - String nodeName; - String temp; - while (++i < ii) - { - if (nodes.item(i).getNodeType() == Node.ELEMENT_NODE) - { - nodeName = nodes.item(i).getNodeName(); - if (schema.containsKey(nodeName) && !r.containsKey(nodeName)) - { - switch (schema.get(nodeName)) - { - case BOOLEAN: - r.put(nodeName, new NodeReturn(nodes.item(i).getTextContent().equals("true"))); - break; - case STRING: - r.put(nodeName, new NodeReturn(nodes.item(i).getTextContent())); - break; - case MULTILINESTRING: - r.put(nodeName, new NodeReturn(new MultilineString(nodes.item(i).getTextContent()))); - break; - case INTEGER: - temp = nodes.item(i).getTextContent(); - if (isNumeric(temp)) - { - r.put(nodeName, new NodeReturn(Integer.parseInt(temp))); - } - break; - case DOUBLE: - temp = nodes.item(i).getTextContent(); - if (isNumeric(temp)) - { - r.put(nodeName, new NodeReturn(Double.parseDouble(temp))); - } - break; - case NODELIST: - if (nodes.item(i).hasChildNodes()) - { - r.put(nodeName, new NodeReturn(nodes.item(i).getChildNodes())); - } - } - } - } - } - return r; - } - - static public NodeList getNodes(Node parentNode) - { - NodeList tList = parentNode.getChildNodes(); - int i = tList.getLength(); - while (0 < i--) - { - if (tList.item(i).getNodeType() != Node.ELEMENT_NODE) - { - parentNode.removeChild(tList.item(i)); - } - } - return parentNode.getChildNodes(); - } - - @Deprecated - static public boolean validNodeList(NodeList nodes, String[] nodeNames) - { - int i = -1; - int ii = nodeNames.length; - if (nodes.getLength() != ii) - { - return false; - } - while (++i < ii) - { - if (!nodes.item(i).getNodeName().equals(nodeNames[i])) - { - return false; - } - } - return true; - } - - static public boolean matchesSchema(NodeList nodes, HashMap schema) - { - HashSet match = new HashSet<>(); - int i = -1; - int ii = nodes.getLength(); - while (++i < ii) - { - if (nodes.item(i).getNodeType() == Node.ELEMENT_NODE && schema.containsKey(nodes.item(i).getNodeName())) - { - match.add(nodes.item(i).getNodeName()); - } - } - return match.size() == schema.size(); - } -} diff --git a/src/Model/Account.java b/src/Model/Account.java deleted file mode 100644 index 64cd9745..00000000 --- a/src/Model/Account.java +++ /dev/null @@ -1,45 +0,0 @@ -package Model; - -import java.io.Serializable; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class Account implements Serializable -{ - // private data - private Person studentDetails; - private String studentNumber; - - // public methods - - // getters - public Person getStudentDetails() - { - return studentDetails; - } - - public String getStudentNumber() - { - return studentNumber; - } - - // setters - public void setStudentDetails(Person newStudentDetails) - { - studentDetails = newStudentDetails; - } - - public void setStudentNumber(String newStudentNumber) - { - studentNumber = newStudentNumber; - } - - // constructors - public Account(Person studentDetails, String studentNumber) - { - this.studentDetails = studentDetails; - this.studentNumber = studentNumber; - } -} diff --git a/src/Model/Activity.java b/src/Model/Activity.java deleted file mode 100644 index 1fab8555..00000000 --- a/src/Model/Activity.java +++ /dev/null @@ -1,202 +0,0 @@ -package Model; - -import Controller.MainController; -import Controller.MenuController; -import View.UIManager; - -import java.io.IOException; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.Locale; -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class Activity extends Event -{ - private ArrayList tasks = new ArrayList<>(); - private int duration; - private int activityQuantity; - private QuantityType type; - - // Getters: - - /** - * Returns an array of Tasks this Activity relates to. - * - * @return array of Tasks. - */ - public Task[] getTasks() - { - return this.tasks.toArray(new Task[this.tasks.size()]); - } - - /** - * Returns the Duration of this Activity. - * - * @return integer representation of the Duration. - */ - public int getDuration() - { - return duration; - } - - /** - * Returns the Quantity of this Activity. - * - * @return integer representation of the Activity Quantity. - */ - public int getActivityQuantity() - { - return activityQuantity; - } - - /** - * Returns the Quantity Type of this Activity. - * - * @return QuantityType object. - */ - public QuantityType getType() - { - return type; - } - - /** - * Returns a formatted date of this Activity - * - * @return string representation of a Date. - */ - public String getDateString() - { - return this.date.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.getDefault()) + " " + - this.date.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault()) + " " + - this.date.get(Calendar.DAY_OF_MONTH); - } - - // Setters: - - /** - * Add a single Task to this Activity. - * - * @param task Task to be added. - * @return whether the provided Task was added successfully. - */ - public boolean addTask(Task task) - { - if (this.tasks.contains(task)) - return false; - - this.tasks.add(task); - return true; - } - - /** - * Add all given Tasks to this Activity. - * - * @param tasks a Collection of Tasks to be added. - * @return whether the provided Tasks were added successfully. - */ - public boolean addTasks(Collection tasks) - { - if (this.tasks.contains(tasks)) - return false; - - this.tasks.addAll(tasks); - return true; - } - - /** - * Replace the current list of Tasks with the provided Tasks. - * - * @param tasks Collection of Tasks. - */ - public void replaceTasks(Collection tasks) - { - this.tasks.clear(); - this.tasks.addAll(tasks); - } - - /** - * Remove a given Task from this Activity. - * - * @param task Task to be removed. - */ - public void removeTask(Task task) - { - this.tasks.remove(task); - } - - /** - * Set the Duration of this Activity. - * - * @param duration integer value of the Duration. - */ - public void setDuration(int duration) - { - this.duration = duration; - } - - /** - * Set the Quantity of this Activity. - * - * @param activityQuantity integer value of the Quantity. - */ - public void setActivityQuantity(int activityQuantity) - { - this.activityQuantity = activityQuantity; - } - - /** - * Set the QuantityType of this Activity. - * - * @param type String representation of the QuantityType. - */ - public void setType(String type) - { - if (QuantityType.exists(type)) - this.type = QuantityType.get(type); - } - - @Override - public void open(MenuController.Window current) - { - try - { - MainController.ui.activityDetails(this); - } catch (IOException e) - { - UIManager.reportError("Unable to open View file"); - } - } - - // Constructors: - public Activity(String name, String details, LocalDate date, int duration, int activityQuantity, String type) - { - super(date.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")) + "T00:00:01Z"); - this.setName(name); - this.setDetails(details); - this.duration = duration; - this.activityQuantity = activityQuantity; - this.type = QuantityType.get(type); - } - - /** - * Creates a copy of the given Activity. - * - * @param activity Activity object to be copied. - */ - public Activity(Activity activity) - { - super(); - this.name = activity.name; - this.details = activity.details; - this.date = activity.date; - this.duration = activity.duration; - this.activityQuantity = activity.activityQuantity; - this.type = activity.type; - this.tasks = activity.tasks; - } -} \ No newline at end of file diff --git a/src/Model/Assignment.java b/src/Model/Assignment.java deleted file mode 100644 index ef352a66..00000000 --- a/src/Model/Assignment.java +++ /dev/null @@ -1,227 +0,0 @@ -package Model; - -import Controller.MainController; -import Controller.MenuController; -import View.UIManager; - -import java.io.IOException; -import java.util.ArrayList; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class Assignment extends VersionControlEntity -{ - protected ArrayList tasks = new ArrayList<>(); - protected ArrayList requirements = new ArrayList<>(); - protected int weighting; - protected Person setBy = null; - protected Person markedBy = null; - protected Person reviewedBy = null; - protected int marks; - protected StateType state; // this may not be needed as we can work it out - - @Override - protected void replace(VersionControlEntity receivedVCE) - { - if (receivedVCE instanceof Assignment) - { - Assignment castedVCE = (Assignment) receivedVCE; - // this.tasks = castedVCE.getTasks(); - // this.requirements = castedVCE.getRequirements(); - this.weighting = castedVCE.getWeighting(); - if (castedVCE.getSetBy() != null) - { - this.setBy = castedVCE.getSetBy(); - } - if (castedVCE.getMarkedBy() != null) - { - this.markedBy = castedVCE.getMarkedBy(); - } - if (castedVCE.getReviewedBy() != null) - { - this.reviewedBy = castedVCE.getReviewedBy(); - } - this.marks = castedVCE.getMarks(); - // this.state = castedVCE.getState(); - } - super.replace(receivedVCE); - } - - public enum StateType - { - IN_PROGRESS, DEADLINE_PASSED, NOT_STARTED - } - - // public methods - // getters - @Override - public String toString() - { - return "Assignment '" + name + "'"; - } - - public String toString(boolean verbose) - { - if (verbose) - { - StringBuilder r = new StringBuilder(); - r.append(toString()); - r.append("\n"); - r.append("Total marks: " + Integer.toString(marks)); - r.append("\n"); - r.append("Total weighting: " + Integer.toString(weighting)); - - r.append("\n"); - r.append("Set By: " + setBy.toString()); - r.append("\n"); - r.append("Marked By: " + markedBy.toString()); - r.append("\n"); - r.append("Reviewed By: " + reviewedBy.toString()); - - return r.toString(); - } else - { - return toString(); - } - } - - public ArrayList getTasks() - { - return tasks; - } - - public ArrayList getRequirements() - { - return requirements; - } - - public int getWeighting() - { - return weighting; - } - - public Person getSetBy() - { - return setBy; - } - - public Person getMarkedBy() - { - return markedBy; - } - - public Person getReviewedBy() - { - return reviewedBy; - } - - public int getMarks() - { - return marks; - } - - public StateType getState() - { - return state; - } - - // Setters: - - /** - * Add a Task to this Assignment. - * - * @param task Task to be added - */ - public void addTask(Task task) - { - this.tasks.add(task); - task.addAssignmentReference(this); - } - - /** - * Removes the given Task from this assignment. - * - * @param task Task to be removed - * @return true if found and deleted, false otherwise - */ - public boolean removeTask(Task task) - { - task.removeAssignmentReference(this); - return this.tasks.remove(task); - } - - /** - * Add a Requirement to this Assignment. - * - * @param requirement Task to be added - */ - public void addRequirement(Requirement requirement) - { - this.requirements.add(requirement); - } - - /** - * Removes the given Requirement from this Assignment. - * - * @param requirement Requirement to be removed - * @return true if found and deleted, false otherwise - */ - public boolean removeRequirement(Requirement requirement) - { - return this.requirements.remove(requirement); - } - - /** - * Calculates how much of this Assignment has been completed in percentage. - * - * @return int (0-100) - */ - public int calculateProgress() - { - if (this.requirements.size() == 0 && this.tasks.size() == 0) - return 0; - - int sum = 0, n = 0; - for (Requirement req : this.requirements) - { - sum += req.requirementProgress() * 100; - n++; - } - - for (Task task : this.tasks) - { - if (task.getRequirements().length > 0) - { - sum += task.calculateProgress(); - n++; - } - } - - return sum / n; - } - - @Override - public void open(MenuController.Window current) - { - try - { - MainController.ui.assignmentDetails(this, current); - } catch (IOException e) - { - UIManager.reportError("Unable to open View file"); - } - } - - // Constructor - public Assignment(int cWeighting, Person cSetBy, Person cMarkedBy, Person cReviewedBy, int cMarks) - { - weighting = cWeighting; - setBy = cSetBy; - markedBy = cMarkedBy; - reviewedBy = cReviewedBy; - marks = cMarks; - //MainController.getSPC().getPlanner().getDeadlineNotifications().put(this, new boolean[2]); - } -} diff --git a/src/Model/Book.java b/src/Model/Book.java deleted file mode 100644 index 42124a30..00000000 --- a/src/Model/Book.java +++ /dev/null @@ -1,35 +0,0 @@ -package Model; - -import java.util.ArrayList; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class Book /*extends Requirement*/ -{ - // private data - private ArrayList chapters; - - // public methods - - // getters - public ArrayList getChapters() - { - // initial set up code below - check if this needs updating - return chapters; - } - - // setters - public void setChapters(ArrayList newChapters) - { - // initial set up code below - check if this needs updating - chapters = newChapters; - } - - // constructor - public Book(ArrayList chapters) - { - this.chapters = chapters; - } -} diff --git a/src/Model/Building.java b/src/Model/Building.java deleted file mode 100644 index 1e73c3eb..00000000 --- a/src/Model/Building.java +++ /dev/null @@ -1,88 +0,0 @@ -package Model; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class Building extends VersionControlEntity -{ - // private Data - private String code = null; - private double latitude; - private double longitude; - - @Override - protected void replace(VersionControlEntity receivedVCE) - { - if (receivedVCE instanceof Building) - { - Building castedVCE = (Building) receivedVCE; - if (castedVCE.getCode() != null) - { - this.code = castedVCE.getCode(); - } - this.latitude = castedVCE.getLatitude(); - this.longitude = castedVCE.getLongitude(); - } - - super.replace(receivedVCE); - } - - // getters - public String getName() - { - return name; - } - - public String getCode() - { - return code; - } - - public double getLatitude() - { - return latitude; - } - - public double getLongitude() - { - return longitude; - } - - // setters - public void setName(String newName) - { - name = newName; - } - - public void setCode(String newCode) - { - code = newCode; - } - - public void setLatitude(double newLatitude) - { - latitude = newLatitude; - } - - public void setLongitude(double newLongitude) - { - longitude = newLongitude; - } - - // constructor - public Building(String cCode, double cLatitude, double cLongitude) - { - code = cCode; - latitude = cLatitude; - longitude = cLongitude; - } - - @Override - public String toString() - { - return code + " " + name + " ( " + Double.toString(latitude) + - " , " + Double.toString(longitude) + " )"; - } -} - diff --git a/src/Model/Coursework.java b/src/Model/Coursework.java deleted file mode 100644 index 3ffbed03..00000000 --- a/src/Model/Coursework.java +++ /dev/null @@ -1,100 +0,0 @@ -package Model; - -import java.text.SimpleDateFormat; -import java.util.ArrayList; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class Coursework extends Assignment -{ - private Event startDate; - private Deadline deadline; - private ArrayList extensions; - - // private methods - @Override - protected void replace(VersionControlEntity receivedVCE) - { - if (receivedVCE instanceof Coursework) - { - Coursework castedVCE = (Coursework) receivedVCE; - if (castedVCE.getStartDate() != null) - { - this.startDate = castedVCE.getStartDate(); - } - if (castedVCE.getDeadline() != null) - { - this.deadline = castedVCE.getDeadline(); - } - if (castedVCE.getExtensions() != null) - { - this.extensions = castedVCE.getExtensions(); - } - } - - super.replace(receivedVCE); - } - // public methods - - // getters - public Event getStartDate() - { - return startDate; - } - - public Deadline getDeadline() - { - return deadline; - } - - public ArrayList getExtensions() - { - return extensions; - } - - public ArrayList getNotes() - { - return notes; - } - - /** - * Returns a String representing the deadline. - * Used in JavaFX. - * - * @return String - */ - public String getDeadlineString() - { - return new SimpleDateFormat("dd/MM/yyyy HH:MM").format(this.deadline.getDate()); - } - - // setters - public void addNote(Note newNote) - { - if (!notes.contains(newNote)) - { - notes.add(newNote); - } - } - - public void removeNote(Note oldNote) - { - if (notes.contains(oldNote)) - { - notes.remove(oldNote); - } - } - - // Constructors - public Coursework(int cWeighting, Person cSetBy, Person cMarkedBy, Person cReviewedBy, int cMarks, Event cStartDate, - Deadline cDeadline, ArrayList cExtensions) - { - super(cWeighting, cSetBy, cMarkedBy, cReviewedBy, cMarks); - startDate = cStartDate; - deadline = cDeadline; - extensions = (ArrayList) cExtensions.clone(); - } - -} diff --git a/src/Model/Deadline.java b/src/Model/Deadline.java deleted file mode 100644 index 46273d6f..00000000 --- a/src/Model/Deadline.java +++ /dev/null @@ -1,14 +0,0 @@ -package Model; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class Deadline extends Event -{ - // maybe we can get rid of this - public Deadline(String cDate) - { - super(cDate); - } -} diff --git a/src/Model/Event.java b/src/Model/Event.java deleted file mode 100644 index 53514e38..00000000 --- a/src/Model/Event.java +++ /dev/null @@ -1,99 +0,0 @@ -package Model; - -import Controller.MainController; - -import java.util.Date; -import java.util.GregorianCalendar; -import java.util.regex.Pattern; - -/** - * ${FILENAME} - * Created by Andrew Odintsov on 4/27/17. - */ -public class Event extends VersionControlEntity -{ - protected GregorianCalendar date = null; - - private static Pattern dateRegex = Pattern.compile("(\\d\\d)/(\\d\\d)/(\\d\\d\\d\\d)T(\\d\\d):(\\d\\d):(\\d\\d)Z"); - - // public methods - - /** - * Validates the given String - * - * @param dateString a String containing a Date - * @return whether the given String is a valide date - */ - public static boolean validDateString(String dateString) - { - return dateRegex.matcher(dateString).matches(); - } - - // getters - - /** - * Returns a Date object containing this Date - * - * @return Date object - */ - public Date getDate() - { - return this.date.getTime(); - } - - public GregorianCalendar getCalendar() - { - return date; - } - - public String toString() - { - return this.date.getTime().toString(); - } - - // setters: - public void setDate(String dateString) - { - // 09/04/2017T15:00:00Z - if (validDateString(dateString)) - { - String sDay = dateString.substring(0, 2); - String sMonth = dateString.substring(3, 5); - String sYear = dateString.substring(6, 10); - String sHour = dateString.substring(11, 13); - String sMinute = dateString.substring(14, 16); - String sSecond = dateString.substring(17, 19); - if (MainController.isNumeric(sDay) && MainController.isNumeric(sMonth) && MainController.isNumeric(sYear) && - MainController.isNumeric(sHour) && MainController.isNumeric(sMinute) && - MainController.isNumeric(sSecond)) - { - date = new GregorianCalendar(Integer.parseInt(sYear), Integer.parseInt(sMonth) - 1, Integer.parseInt(sDay) - , Integer.parseInt(sHour), Integer.parseInt(sMinute), Integer.parseInt(sSecond)); - } - } - } - - @Override - protected void replace(VersionControlEntity receivedVCE) - { - if (receivedVCE instanceof Event) - { - Event castedVCE = (Event) receivedVCE; - if (castedVCE.getCalendar() != null) - { - this.date = castedVCE.getCalendar(); - } - } - super.replace(receivedVCE); - } - - // Constructors: - public Event(String cDate) - { - setDate(cDate); - } - - public Event() - { - } -} diff --git a/src/Model/Exam.java b/src/Model/Exam.java deleted file mode 100644 index 9b46f163..00000000 --- a/src/Model/Exam.java +++ /dev/null @@ -1,60 +0,0 @@ -package Model; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class Exam extends Assignment -{ - // private data - private Exam resit = null; - private ExamEvent timeSlot = null; - - @Override - protected void replace(VersionControlEntity receivedVCE) - { - if (receivedVCE instanceof Exam) - { - Exam castedVCE = (Exam) receivedVCE; - if (castedVCE.getResit() != null) - { - this.resit = castedVCE.getResit(); - } - if (castedVCE.getTimeSlot() != null) - { - this.timeSlot = castedVCE.getTimeSlot(); - } - } - - super.replace(receivedVCE); - } - - // public methods - - // getters - public Exam getResit() - { - return resit; - } - - public ExamEvent getTimeSlot() - { - return timeSlot; - } - - - // constructors - public Exam(int cWeighting, Person cSetBy, Person cMarkedBy, Person cReviewedBy, int cMarks, ExamEvent cTimeSlot, Exam cResit) - { - super(cWeighting, cSetBy, cMarkedBy, cReviewedBy, cMarks); - timeSlot = cTimeSlot; - resit = cResit; - } - - public Exam(int cWeighting, Person cSetBy, Person cMarkedBy, Person cReviewedBy, int cMarks, ExamEvent cTimeSlot) - { - super(cWeighting, cSetBy, cMarkedBy, cReviewedBy, cMarks); - timeSlot = cTimeSlot; - resit = null; - } -} diff --git a/src/Model/ExamEvent.java b/src/Model/ExamEvent.java deleted file mode 100644 index 7c658933..00000000 --- a/src/Model/ExamEvent.java +++ /dev/null @@ -1,59 +0,0 @@ -package Model; - -import java.text.SimpleDateFormat; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class ExamEvent extends Event -{ - private Room room; - private int duration; - - @Override - protected void replace(VersionControlEntity receivedVCE) - { - if (receivedVCE instanceof ExamEvent) - { - ExamEvent castedVCE = (ExamEvent) receivedVCE; - if (castedVCE.getRoom() != null) - { - this.room = castedVCE.getRoom(); - } - this.duration = castedVCE.getDuration(); - } - super.replace(receivedVCE); - } - - // Getters: - - /** - * Returns a String representing the event. - * Used in JavaFX. - * - * @return String - */ - public String getDateString() - { - return new SimpleDateFormat("dd/MM/yyyy HH:MM").format(this.date.getTime()); - - } - - public int getDuration() - { - return duration; - } - - public Room getRoom() - { - return room; - } - - public ExamEvent(String cDate, Room cRoom, int cDuration) - { - super(cDate); - room = cRoom; - duration = cDuration; - } -} diff --git a/src/Model/Extension.java b/src/Model/Extension.java deleted file mode 100644 index 4c321990..00000000 --- a/src/Model/Extension.java +++ /dev/null @@ -1,75 +0,0 @@ -package Model; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class Extension extends VersionControlEntity -{ - // private data - private Deadline newDeadline; - private MultilineString circumstances; - private ApprovalStatus approvalStatus; - - @Override - protected void replace(VersionControlEntity receivedVCE) - { - if (receivedVCE instanceof Extension) - { - Extension castedVCE = (Extension) receivedVCE; - this.newDeadline = castedVCE.getNewDeadline(); - this.circumstances = castedVCE.getCircumstances(); - this.approvalStatus = castedVCE.getApprovalStatus(); - } - - super.replace(receivedVCE); - } - - public enum ApprovalStatus - { - PENDING, APPROVED, DECLINED - } - - // public methods - - // getters - public Deadline getNewDeadline() - { - // initial set up code below - check if this needs updating - return newDeadline; - } - - public MultilineString getCircumstances() - { - // initial set up code below - check if this needs updating - return circumstances; - } - - public ApprovalStatus getApprovalStatus() - { - // initial set up code below - check if this needs updating - return approvalStatus; - } - - // setters - public void setCircumstances(MultilineString newCircumstances) - { - // initial set up code below - check if this needs updating - circumstances = newCircumstances; - } - - public void setNewDeadline(Deadline newNewDeadline) - { - // initial set up code below - check if this needs updating - newDeadline = newNewDeadline; - } - - public void setApprovalStatus(ApprovalStatus newApprovalStatus) - { - // initial set up code below - check if this needs updating - approvalStatus = newApprovalStatus; - } - - // constructor - -} diff --git a/src/Model/ExtensionApplication.java b/src/Model/ExtensionApplication.java deleted file mode 100644 index 64475b49..00000000 --- a/src/Model/ExtensionApplication.java +++ /dev/null @@ -1,39 +0,0 @@ -package Model; - -import java.io.Serializable; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class ExtensionApplication implements Serializable -{ - // private data - private Extension extension; - private String moduleCode; - private String assignmentUID; - private Account account; - - // public methods - - // getters - public Extension getExtension() - { - // initial set up code below - check if this needs updating - return extension; - } - - public String getModuleCode() - { - // initial set up code below - check if this needs updating - return moduleCode; - } - - public String getAssignmentUID() - { - // initial set up code below - check if this needs updating - return assignmentUID; - } - - // constructors -} diff --git a/src/Model/HubFile.java b/src/Model/HubFile.java deleted file mode 100644 index 0c4af26e..00000000 --- a/src/Model/HubFile.java +++ /dev/null @@ -1,639 +0,0 @@ -package Model; - -import Controller.DataController; -import Controller.XMLcontroller; -import org.w3c.dom.NodeList; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.HashMap; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class HubFile implements Serializable -{ - // private data - private ArrayList assets = new ArrayList(); - private ArrayList modules = new ArrayList<>(); - private ArrayList extensions = new ArrayList<>(); - private ArrayList updates = new ArrayList<>(); - private ArrayList calendarList = new ArrayList<>(); - private int version; - private int semester; - private int year; - private boolean updateFile; - private String semesterName; - private String semesterUID; - private MultilineString semesterDetails; - - // public methods - - // getters - public ArrayList getModules() - { - return modules; - } - - public ArrayList getExtensions() - { - return extensions; - } - - public ArrayList getCalendarList() - { - return calendarList; - } - - public ArrayList getUpdates() - { - return updates; - } - - public int getVersion() - { - return version; - } - - public int getSemester() - { - return semester; - } - - public int getYear() - { - return year; - } - - public String getSemesterName() - { - return semesterName; - } - - public String getSemesterUID() - { - return semesterUID; - } - - public MultilineString getSemesterDetails() - { - return semesterDetails; - } - - public boolean isUpdate() - { - return updateFile; - } - - @Override - public String toString() - { - return "HubFile for " + Integer.toString(year) + " semester: " + Integer.toString(semester) + " | Module Count: " + - Integer.toString(modules.size()); - } - - public String toString(boolean verbose) - { - if (verbose) - { - StringBuilder r = new StringBuilder(); - - r.append(toString()); - int i = -1; - int ii = modules.size(); - while (++i < ii) - { - r.append(modules.get(i).toString(true)); - } - - return r.toString(); - } else - { - return toString(); - } - } - // constructors - - /** - * Constructor for new Study Profile - * - * @param v version - * @param y year - * @param s semester - * @param m Module list - * @param a VersionControlEntity list - * @param cal Calendar events list - */ - public HubFile(int v, int y, int s, ArrayList m, ArrayList a, ArrayList cal) - { - version = v; - year = y; - semester = s; - modules = (ArrayList) m.clone(); - assets = (ArrayList) a.clone(); - calendarList = (ArrayList) cal.clone(); - updateFile = false; - } - - public HubFile(int v, int y, int s, ArrayList m, ArrayList a, ArrayList cal, - String n, MultilineString d, String u) - { - this(v, y, s, m, a, cal); - semesterName = n; - semesterDetails = d; - semesterUID = u; - } - - /** - * Constructor for update - * - * @param v version - * @param e ExtensionApplication list - * @param u VersionControlEntity list - */ - public HubFile(int v, ArrayList e, ArrayList u) - { - version = v; - extensions = (ArrayList) e.clone(); - updates = (ArrayList) u.clone(); - updateFile = true; - } - - public static boolean updateableNode(String name) - { - return schemaList.containsKey(name); - } - - // schemas - // note, for the time being these are hard coded into the code - // long term, these would be imported from a settings file - - // special SCHEMA for update file - public static HashMap SCHEMA_VCE; - - static - { - SCHEMA_VCE = new HashMap<>(); - SCHEMA_VCE.put("name", XMLcontroller.ImportAs.STRING); - SCHEMA_VCE.put("details", XMLcontroller.ImportAs.MULTILINESTRING); - SCHEMA_VCE.put("uid", XMLcontroller.ImportAs.STRING); - SCHEMA_VCE.put("version", XMLcontroller.ImportAs.INTEGER); - } - - public static HashMap SCHEMA_ROOT; - - static - { - SCHEMA_ROOT = new HashMap<>(); - SCHEMA_ROOT.put("hubfile", XMLcontroller.ImportAs.NODELIST); - } - - public static HashMap SCHEMA_NEW_STUDYPROFILE; - - static - { - SCHEMA_NEW_STUDYPROFILE = new HashMap<>(); - SCHEMA_NEW_STUDYPROFILE.put("version", XMLcontroller.ImportAs.INTEGER); - SCHEMA_NEW_STUDYPROFILE.put("assets", XMLcontroller.ImportAs.NODELIST); - SCHEMA_NEW_STUDYPROFILE.put("studyProfile", XMLcontroller.ImportAs.NODELIST); - } - - public static HashMap SCHEMA_UPDATE_FILE; - - static - { - SCHEMA_UPDATE_FILE = new HashMap<>(); - SCHEMA_UPDATE_FILE.put("version", XMLcontroller.ImportAs.INTEGER); - SCHEMA_UPDATE_FILE.put("extensions", XMLcontroller.ImportAs.NODELIST); - SCHEMA_UPDATE_FILE.put("updates", XMLcontroller.ImportAs.NODELIST); - SCHEMA_UPDATE_FILE.put("new", XMLcontroller.ImportAs.NODELIST); - } - - public static HashMap SCHEMA_ASSETS; - - static - { - SCHEMA_ASSETS = new HashMap<>(); - SCHEMA_ASSETS.put("persons", XMLcontroller.ImportAs.NODELIST); - SCHEMA_ASSETS.put("buildings", XMLcontroller.ImportAs.NODELIST); - SCHEMA_ASSETS.put("rooms", XMLcontroller.ImportAs.NODELIST); - SCHEMA_ASSETS.put("timetableEventTypes", XMLcontroller.ImportAs.NODELIST); - } - - public static HashMap SCHEMA_STUDYPROFILE; - - static - { - SCHEMA_STUDYPROFILE = new HashMap<>(); - SCHEMA_STUDYPROFILE.put("name", XMLcontroller.ImportAs.STRING); - SCHEMA_STUDYPROFILE.put("details", XMLcontroller.ImportAs.MULTILINESTRING); - SCHEMA_STUDYPROFILE.put("uid", XMLcontroller.ImportAs.STRING); - SCHEMA_STUDYPROFILE.put("year", XMLcontroller.ImportAs.INTEGER); - SCHEMA_STUDYPROFILE.put("semester", XMLcontroller.ImportAs.INTEGER); - SCHEMA_STUDYPROFILE.put("modules", XMLcontroller.ImportAs.NODELIST); - } - - public static HashMap SCHEMA_PERSON; - - static - { - SCHEMA_PERSON = new HashMap<>(); - SCHEMA_PERSON.put("name", XMLcontroller.ImportAs.STRING); - SCHEMA_PERSON.put("details", XMLcontroller.ImportAs.MULTILINESTRING); - SCHEMA_PERSON.put("version", XMLcontroller.ImportAs.INTEGER); - SCHEMA_PERSON.put("uid", XMLcontroller.ImportAs.STRING); - SCHEMA_PERSON.put("givenNames", XMLcontroller.ImportAs.STRING); - SCHEMA_PERSON.put("familyName", XMLcontroller.ImportAs.STRING); - SCHEMA_PERSON.put("salutation", XMLcontroller.ImportAs.STRING); - SCHEMA_PERSON.put("email", XMLcontroller.ImportAs.STRING); - SCHEMA_PERSON.put("familyNameLast", XMLcontroller.ImportAs.BOOLEAN); - } - - public static HashMap SCHEMA_BUILDING; - - static - { - SCHEMA_BUILDING = new HashMap<>(); - SCHEMA_BUILDING.put("name", XMLcontroller.ImportAs.STRING); - SCHEMA_BUILDING.put("details", XMLcontroller.ImportAs.MULTILINESTRING); - SCHEMA_BUILDING.put("version", XMLcontroller.ImportAs.INTEGER); - SCHEMA_BUILDING.put("uid", XMLcontroller.ImportAs.STRING); - SCHEMA_BUILDING.put("code", XMLcontroller.ImportAs.STRING); - SCHEMA_BUILDING.put("latitude", XMLcontroller.ImportAs.DOUBLE); - SCHEMA_BUILDING.put("longitude", XMLcontroller.ImportAs.DOUBLE); - } - - public static HashMap SCHEMA_ROOM; - - static - { - SCHEMA_ROOM = new HashMap<>(); - SCHEMA_ROOM.put("name", XMLcontroller.ImportAs.STRING); - SCHEMA_ROOM.put("details", XMLcontroller.ImportAs.MULTILINESTRING); - SCHEMA_ROOM.put("version", XMLcontroller.ImportAs.INTEGER); - SCHEMA_ROOM.put("uid", XMLcontroller.ImportAs.STRING); - SCHEMA_ROOM.put("building", XMLcontroller.ImportAs.STRING); - SCHEMA_ROOM.put("roomNumber", XMLcontroller.ImportAs.STRING); - } - - public static HashMap SCHEMA_TIMETABLE_EVENT_TYPE; - - static - { - SCHEMA_TIMETABLE_EVENT_TYPE = new HashMap<>(); - SCHEMA_TIMETABLE_EVENT_TYPE.put("name", XMLcontroller.ImportAs.STRING); - SCHEMA_TIMETABLE_EVENT_TYPE.put("details", XMLcontroller.ImportAs.MULTILINESTRING); - SCHEMA_TIMETABLE_EVENT_TYPE.put("version", XMLcontroller.ImportAs.INTEGER); - SCHEMA_TIMETABLE_EVENT_TYPE.put("uid", XMLcontroller.ImportAs.STRING); - } - - public static HashMap SCHEMA_MODULE; - - static - { - SCHEMA_MODULE = new HashMap<>(); - SCHEMA_MODULE.put("name", XMLcontroller.ImportAs.STRING); - SCHEMA_MODULE.put("details", XMLcontroller.ImportAs.MULTILINESTRING); - SCHEMA_MODULE.put("version", XMLcontroller.ImportAs.INTEGER); - SCHEMA_MODULE.put("uid", XMLcontroller.ImportAs.STRING); - SCHEMA_MODULE.put("organiser", XMLcontroller.ImportAs.STRING); - SCHEMA_MODULE.put("moduleCode", XMLcontroller.ImportAs.STRING); - SCHEMA_MODULE.put("timetable", XMLcontroller.ImportAs.NODELIST); - SCHEMA_MODULE.put("assignments", XMLcontroller.ImportAs.NODELIST); - } - - public static HashMap SCHEMA_COURSEWORK; - - static - { - SCHEMA_COURSEWORK = new HashMap<>(); - SCHEMA_COURSEWORK.put("name", XMLcontroller.ImportAs.STRING); - SCHEMA_COURSEWORK.put("details", XMLcontroller.ImportAs.MULTILINESTRING); - SCHEMA_COURSEWORK.put("version", XMLcontroller.ImportAs.INTEGER); - SCHEMA_COURSEWORK.put("uid", XMLcontroller.ImportAs.STRING); - - SCHEMA_COURSEWORK.put("weighting", XMLcontroller.ImportAs.INTEGER); - SCHEMA_COURSEWORK.put("setBy", XMLcontroller.ImportAs.STRING); - SCHEMA_COURSEWORK.put("markedBy", XMLcontroller.ImportAs.STRING); - SCHEMA_COURSEWORK.put("reviewedBy", XMLcontroller.ImportAs.STRING); - SCHEMA_COURSEWORK.put("marks", XMLcontroller.ImportAs.INTEGER); - SCHEMA_COURSEWORK.put("startDate", XMLcontroller.ImportAs.NODELIST); - SCHEMA_COURSEWORK.put("deadline", XMLcontroller.ImportAs.NODELIST); - SCHEMA_COURSEWORK.put("extensions", XMLcontroller.ImportAs.NODELIST); - } - - public static HashMap SCHEMA_EXAM; - - static - { - SCHEMA_EXAM = new HashMap<>(); - SCHEMA_EXAM.put("name", XMLcontroller.ImportAs.STRING); - SCHEMA_EXAM.put("details", XMLcontroller.ImportAs.MULTILINESTRING); - SCHEMA_EXAM.put("version", XMLcontroller.ImportAs.INTEGER); - SCHEMA_EXAM.put("uid", XMLcontroller.ImportAs.STRING); - - SCHEMA_EXAM.put("resit", XMLcontroller.ImportAs.STRING); - SCHEMA_EXAM.put("timeslot", XMLcontroller.ImportAs.NODELIST); - SCHEMA_EXAM.put("weighting", XMLcontroller.ImportAs.INTEGER); - SCHEMA_EXAM.put("setBy", XMLcontroller.ImportAs.STRING); - SCHEMA_EXAM.put("markedBy", XMLcontroller.ImportAs.STRING); - SCHEMA_EXAM.put("reviewedBy", XMLcontroller.ImportAs.STRING); - SCHEMA_EXAM.put("marks", XMLcontroller.ImportAs.INTEGER); - } - - public static HashMap SCHEMA_TIMETABLE_EVENT; - - static - { - SCHEMA_TIMETABLE_EVENT = new HashMap<>(); - SCHEMA_TIMETABLE_EVENT.put("name", XMLcontroller.ImportAs.STRING); - SCHEMA_TIMETABLE_EVENT.put("details", XMLcontroller.ImportAs.MULTILINESTRING); - SCHEMA_TIMETABLE_EVENT.put("version", XMLcontroller.ImportAs.INTEGER); - SCHEMA_TIMETABLE_EVENT.put("uid", XMLcontroller.ImportAs.STRING); - - SCHEMA_TIMETABLE_EVENT.put("date", XMLcontroller.ImportAs.STRING); - SCHEMA_TIMETABLE_EVENT.put("room", XMLcontroller.ImportAs.STRING); - SCHEMA_TIMETABLE_EVENT.put("lecturer", XMLcontroller.ImportAs.STRING); - SCHEMA_TIMETABLE_EVENT.put("timetableEventType", XMLcontroller.ImportAs.STRING); - SCHEMA_TIMETABLE_EVENT.put("duration", XMLcontroller.ImportAs.INTEGER); - } - - public static HashMap SCHEMA_EVENT; - - static - { - SCHEMA_EVENT = new HashMap<>(); - SCHEMA_EVENT.put("name", XMLcontroller.ImportAs.STRING); - SCHEMA_EVENT.put("details", XMLcontroller.ImportAs.MULTILINESTRING); - SCHEMA_EVENT.put("version", XMLcontroller.ImportAs.INTEGER); - SCHEMA_EVENT.put("uid", XMLcontroller.ImportAs.STRING); - - SCHEMA_EVENT.put("date", XMLcontroller.ImportAs.STRING); - } - - public static HashMap SCHEMA_EXAMEVENT; - - static - { - SCHEMA_EXAMEVENT = new HashMap<>(); - SCHEMA_EXAMEVENT.put("name", XMLcontroller.ImportAs.STRING); - SCHEMA_EXAMEVENT.put("details", XMLcontroller.ImportAs.MULTILINESTRING); - SCHEMA_EXAMEVENT.put("version", XMLcontroller.ImportAs.INTEGER); - SCHEMA_EXAMEVENT.put("uid", XMLcontroller.ImportAs.STRING); - - SCHEMA_EXAMEVENT.put("date", XMLcontroller.ImportAs.STRING); - SCHEMA_EXAMEVENT.put("room", XMLcontroller.ImportAs.STRING); - SCHEMA_EXAMEVENT.put("duration", XMLcontroller.ImportAs.INTEGER); - } - - public static HashMap> schemaList = new HashMap<>(); - - static - { - schemaList.put("person",SCHEMA_PERSON); - schemaList.put("examEvent",SCHEMA_EXAMEVENT); - schemaList.put("event",SCHEMA_EVENT); - schemaList.put("deadline",SCHEMA_EVENT); - schemaList.put("timetableEvent",SCHEMA_TIMETABLE_EVENT); - schemaList.put("exam",SCHEMA_EXAM); - schemaList.put("coursework",SCHEMA_COURSEWORK); - schemaList.put("module",SCHEMA_MODULE); - schemaList.put("timetableEventType",SCHEMA_TIMETABLE_EVENT_TYPE); - schemaList.put("building",SCHEMA_BUILDING); - schemaList.put("room",SCHEMA_ROOM); - } - - - private static XMLcontroller xmlTools = new XMLcontroller(); - - public static Person createPerson(NodeList nc) - { - HashMap pValues = xmlTools.getSchemaValues(nc, - HubFile.SCHEMA_PERSON); - - Person r = new Person(pValues.get("salutation").getString(), pValues.get("givenNames").getString(), - pValues.get("familyName").getString(), pValues.get("familyNameLast").getBoolean(), - pValues.get("email").getString()); - - DataController.addVCEproperties(r, pValues); - return r; - } - - public static Building createBuilding(NodeList nc) - { - HashMap pValues = xmlTools.getSchemaValues(nc, - HubFile.SCHEMA_BUILDING); - - Building r = new Building(pValues.get("code").getString(), pValues.get("latitude").getDouble(), - pValues.get("longitude").getDouble()); - - DataController.addVCEproperties(r, pValues); - return r; - } - - public static Room createRoom(NodeList nc, HashMap assetList) - { - HashMap pValues = xmlTools.getSchemaValues(nc, - HubFile.SCHEMA_ROOM); - - Room r; - String linkedBuilding = pValues.get("building").getString(); - if (assetList.containsKey(linkedBuilding) && - assetList.get(linkedBuilding) instanceof Building) - { - r = new Room(pValues.get("roomNumber").getString(), - (Building) assetList.get(linkedBuilding)); - } else - { - r = new Room(pValues.get("roomNumber").getString()); - } - DataController.addVCEproperties(r, pValues); - return r; - } - - public static TimeTableEventType createTimetableEventType(NodeList nc) - { - HashMap pValues = xmlTools.getSchemaValues(nc, - HubFile.SCHEMA_TIMETABLE_EVENT_TYPE); - - TimeTableEventType r = new TimeTableEventType(); - - DataController.addVCEproperties(r, pValues); - return r; - } - - public static Coursework createCoursework(NodeList nc, HashMap assetList) throws Exception - { - Coursework r; - HashMap courseworkValues = xmlTools.getSchemaValues(nc, - HubFile.SCHEMA_COURSEWORK); - - - Person cwSetBy, cwMarkedBy, cwReviewedBy; - Event cwStartDate; - Deadline cwDeadline; - - // extensions to be added later - ArrayList cwExtensions = new ArrayList<>(); - - String linkedSetBy = courseworkValues.get("setBy").getString(); - String linkedMarkedBy = courseworkValues.get("markedBy").getString(); - String linkedReviewedBy = courseworkValues.get("reviewedBy").getString(); - - - cwSetBy = DataController.inList(assetList, linkedSetBy); - cwMarkedBy = DataController.inList(assetList, linkedMarkedBy); - cwReviewedBy = DataController.inList(assetList, linkedReviewedBy); - - - if (courseworkValues.containsKey("startDate") && - XMLcontroller.matchesSchema(courseworkValues.get("startDate").getNodeList(), - HubFile.SCHEMA_EVENT)) - { - HashMap eventValues = - xmlTools.getSchemaValues(courseworkValues.get("startDate").getNodeList(), - HubFile.SCHEMA_EVENT); - cwStartDate = new Event(eventValues.get("date").getString()); - - - DataController.addVCEproperties(cwStartDate, eventValues); - assetList.put(eventValues.get("uid").getString(), cwStartDate); - - } else - { - cwStartDate = null; - } - if (courseworkValues.containsKey("deadline") && - XMLcontroller.matchesSchema(courseworkValues.get("deadline").getNodeList(), - HubFile.SCHEMA_EVENT)) - { - HashMap eventValues = - xmlTools.getSchemaValues(courseworkValues.get("deadline").getNodeList(), - HubFile.SCHEMA_EVENT); - - cwDeadline = new Deadline(eventValues.get("date").getString()); - - - DataController.addVCEproperties(cwDeadline, eventValues); - assetList.put(eventValues.get("uid").getString(), cwDeadline); - - } else - { - cwDeadline = null; - } - - - r = new Coursework(courseworkValues.get("weighting").getInt(), - cwSetBy, cwMarkedBy, cwReviewedBy, courseworkValues.get("marks").getInt(), - cwStartDate, cwDeadline, cwExtensions); - - DataController.addVCEproperties(r, courseworkValues); - return r; - } - - public static Exam createExam(NodeList nc, HashMap assetList) throws Exception - { - - HashMap examValues = xmlTools.getSchemaValues(nc, - HubFile.SCHEMA_EXAM); - - - Person exSetBy, exMarkedBy, exReviewedBy; - ExamEvent exTimeSlot; - ArrayList cwExtensions = new ArrayList<>(); - - - String linkedSetBy = examValues.get("setBy").getString(); - String linkedMarkedBy = examValues.get("markedBy").getString(); - String linkedReviewedBy = examValues.get("reviewedBy").getString(); - - exSetBy = DataController.inList(assetList, linkedSetBy); - exMarkedBy = DataController.inList(assetList, linkedMarkedBy); - exReviewedBy = DataController.inList(assetList, linkedReviewedBy); - - if (examValues.containsKey("timeslot") && - XMLcontroller.matchesSchema(examValues.get("timeslot").getNodeList(), - HubFile.SCHEMA_EXAMEVENT)) - { - HashMap eventValues = - xmlTools.getSchemaValues(examValues.get("timeslot").getNodeList(), - HubFile.SCHEMA_EXAMEVENT); - //Room exRoom; - String linkedRoom = eventValues.get("room").getString(); - Room exRoom = DataController.inList(assetList, linkedRoom); - - - exTimeSlot = new ExamEvent(eventValues.get("date").getString(), exRoom, - eventValues.get("duration").getInt()); - - - DataController.addVCEproperties(exTimeSlot, eventValues); - assetList.put(eventValues.get("uid").getString(), exTimeSlot); - - } else - { - exTimeSlot = null; - } - - Exam exExamResit = null; - if (examValues.containsKey("resit")) - { - String linkedResit = examValues.get("resit").getString(); - try - { - exExamResit = DataController.inList(assetList, linkedResit); - } catch (Exception e) - { - // do nothing! - //exExamResit = null; - } - } - - - Exam newExam; - if (exExamResit == null) - { - newExam = new Exam(examValues.get("weighting").getInt(), - exSetBy, exMarkedBy, exReviewedBy, examValues.get("marks").getInt(), - exTimeSlot); - } else - { - newExam = new Exam(examValues.get("weighting").getInt(), - exSetBy, exMarkedBy, exReviewedBy, examValues.get("marks").getInt(), - exTimeSlot, exExamResit); - } - - DataController.addVCEproperties(newExam, examValues); - - return newExam; - } - - public static TimetableEvent createTimetableEvent(NodeList nc, HashMap assetList) throws Exception - { - TimetableEvent newTTE; - - - HashMap tteValues = xmlTools.getSchemaValues(nc, - HubFile.SCHEMA_TIMETABLE_EVENT); - - - String linkedRoom = tteValues.get("room").getString(); - String linkedLecturer = tteValues.get("lecturer").getString(); - String linkedTTET = tteValues.get("timetableEventType").getString(); - - Room tRoom = DataController.inList(assetList, linkedRoom); - Person tLecturer = DataController.inList(assetList, linkedLecturer); - TimeTableEventType tTTET = DataController.inList(assetList, linkedTTET); - - - newTTE = new TimetableEvent(tteValues.get("date").getString(), tRoom, tLecturer - , tTTET, tteValues.get("duration").getInt()); - DataController.addVCEproperties(newTTE, tteValues); - - - return newTTE; - } - - -} diff --git a/src/Model/Milestone.java b/src/Model/Milestone.java deleted file mode 100644 index b75e3e6f..00000000 --- a/src/Model/Milestone.java +++ /dev/null @@ -1,212 +0,0 @@ -package Model; - -import java.text.SimpleDateFormat; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class Milestone extends ModelEntity -{ - // private data - private ArrayList tasks = new ArrayList<>(); - private Deadline deadline; - - // public methods - - // Getters: - - /** - * Check whether this Milestone is complete. - * - * @return true if completed, false otherwise. - */ - public boolean isComplete() - { - for (Task t : this.tasks) - if (!t.isCheckedComplete()) - return false; - return true; - } - - /** - * Computes progress based on Task weighting and Tasks completed. - * Used for JavaFX - * - * @return String representation of progress in percentage. - */ - public String getProgressPercentage() - { - double completed = 0; - for (Task t : this.tasks) - if (t.isCheckedComplete()) completed += t.getWeighting(); - - int result = (int) (completed / this.totalWeighting() * 100); - - return Integer.toString(result) + '%'; - } - - /** - * Returns an array of Tasks associated with this Milestone. - * - * @return an array of Tasks. - */ - public Task[] getTasks() - { - return this.tasks.toArray(new Task[this.tasks.size()]); - } - - /** - * Returns the number of Tasks completed - * - * @return integer representation of completed Tasks. - */ - public int tasksCompleted() - { - int completed = 0; - for (Task t : this.tasks) - if (t.isCheckedComplete()) - completed++; - return completed; - } - - /** - * Returns the number of Tasks associated with this Milestone. - * - * @return number of Tasks. - */ - public int size() - { - return this.tasks.size(); - } - - /** - * Return the sum of all Tasks weighting. - * - * @return integer representation of weightings sum. - */ - public int totalWeighting() - { - int sum = 0; - for (Task t : this.tasks) sum += t.getWeighting(); - return sum; - } - - /** - * Returns a String representation of completed Tasks (e.g. 3/4). - * Used in JavaFX. - * - * @return String represenation of completed Tasks. - */ - public String getTaskCompletedAsString() - { - return this.tasksCompleted() + "/" + this.tasks.size(); - } - - /** - * Returns a String representation of the Deadline for this Milestone. - * Used in JavaFX. - * - * @return String representation of a Deadline. - */ - public String getDeadline() - { - return new SimpleDateFormat("dd/MM/yyyy").format(this.deadline.getDate()); - } - - /** - * Returns a Date object representing the Deadline of this Milestone. - * - * @return Date object. - */ - public Date getDeadlineDate() - { - return this.deadline.getDate(); - } - - // Setters: - - /** - * Add the given Task to this Milestone. - * - * @param task Task to be added. - * @return Whether the Task was successfully added. - */ - public boolean addTask(Task task) - { - if (this.tasks.contains(task)) - return false; - - this.tasks.add(task); - return true; - } - - /** - * Checks if the Milestone contains the requested task - * - * @param task Task to be checked for - * @return Whether the teask is contained. - */ - public boolean containsTask(Task task) - { - return this.tasks.contains(task); - } - - /** - * Add all given Tasks to this Milestone. - * - * @param tasks a Collection of Tasks to be added. - * @return whether the provided Tasks were added successfully. - */ - public boolean addTasks(Collection tasks) - { - if (this.tasks.contains(tasks)) - return false; - - this.tasks.addAll(tasks); - return true; - } - - /** - * Replace the current list of Tasks with the provided Tasks. - * - * @param tasks Collection of Tasks. - */ - public void replaceTasks(Collection tasks) - { - this.tasks.clear(); - this.tasks.addAll(tasks); - } - - /** - * Remove a given Task from this Milestone. - * - * @param task Task to be removed. - */ - public void removeTask(Task task) - { - this.tasks.remove(task); - } - - /** - * Set a new deadline - * - * @param date date to be set as a new deadline - */ - public void setDeadline(LocalDate date) - { - this.deadline.setDate(date.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")) + "T00:00:01Z"); - } - - // Constructors: - public Milestone(String name, String details, LocalDate deadline) - { - super(name, details); - this.deadline = new Deadline(deadline.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")) + "T00:00:01Z"); - } -} diff --git a/src/Model/ModelEntity.java b/src/Model/ModelEntity.java deleted file mode 100644 index 3b3dfd73..00000000 --- a/src/Model/ModelEntity.java +++ /dev/null @@ -1,103 +0,0 @@ -package Model; - -import Controller.MenuController; - -import java.io.Serializable; -import java.util.ArrayList; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class ModelEntity implements Serializable -{ - protected String name = ""; - protected MultilineString details = null; - protected ArrayList notes; - - - // getters - public String getName() - { - return name; - } - - public MultilineString getDetails() - { - return details; - } - - public void setName(String newName) - { - name = newName; - } - - public void setDetails(String newDetails) - { - details = new MultilineString(newDetails); - } - - public void setDetails(String[] newDetails) - { - details = new MultilineString(newDetails); - } - - public void setDetails(ArrayList newDetails) - { - details = new MultilineString(newDetails.toArray(new String[newDetails.size()])); - } - - public void setDetails(MultilineString newDetails) - { - details = newDetails; - } - - - public void addProperties(String aName, MultilineString aDetails) - { - setName(aName); - setDetails(aDetails.clone()); - } - - public void addProperties(String aName, String aDetails) - { - setName(aName); - setDetails(aDetails); - } - - /** - * Open the appropriate UI window for this class - * To be overridden by children. - */ - public void open(MenuController.Window current) - { - } - - public ModelEntity() - { - this(""); - } - - public ModelEntity(String cName) - { - this(cName, ""); - } - - public ModelEntity(String cName, String cDetails) - { - this(cName, cDetails.split("\n")); - } - - public ModelEntity(String cName, String[] cDetails) - { - setName(cName); - setDetails(cDetails); - notes = new ArrayList<>(); - } - - public ModelEntity(String cName, String[] cDetails, ArrayList cNotes) - { - this(cName, cDetails); - notes = (ArrayList) cNotes.clone(); - } -} diff --git a/src/Model/Module.java b/src/Model/Module.java deleted file mode 100644 index 8ae912b8..00000000 --- a/src/Model/Module.java +++ /dev/null @@ -1,191 +0,0 @@ -package Model; - -import Controller.MainController; -import Controller.MenuController; -import View.UIManager; - -import java.io.IOException; -import java.util.ArrayList; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 at 20:59 - */ -public class Module extends VersionControlEntity -{ - // private data - private ArrayList assignments = new ArrayList<>(); - private Person organiser; - private String moduleCode; - private ArrayList timetable = new ArrayList<>(); - - @Override - protected void replace(VersionControlEntity receivedVCE) - { - if (receivedVCE instanceof Module) - { - Module castedVCE = (Module) receivedVCE; - if (castedVCE.getOrganiser() != null) - { - this.organiser = castedVCE.getOrganiser(); - } - if (castedVCE.getModuleCode() != null) - { - this.moduleCode = castedVCE.getModuleCode(); - } - if (castedVCE.getAssignments() != null) - { - this.assignments = castedVCE.getAssignments(); - } - if (castedVCE.getAssignments() != null) - { - this.timetable = castedVCE.getTimetable(); - } - } - - super.replace(receivedVCE); - } - - - // public methods - public String toString(boolean verbose) - { - if (verbose) - { - StringBuilder r = new StringBuilder(); - r.append(toString()); - r.append("\n"); - r.append("Organiser: " + organiser.toString()); - r.append("\n"); - r.append("Total Assignments: " + Integer.toString(assignments.size())); - r.append("\n"); - - int i = -1; - int ii = assignments.size(); - - while (++i < ii) - { - r.append("\n"); - r.append(assignments.get(i).toString(true)); - } - - return r.toString(); - - } else - { - return toString(); - } - } - - @Override - public String toString() - { - return "Module: " + this.name + " ( " + this.moduleCode + " )"; - } - - // getters - public ArrayList getAssignments() - { - return assignments; - } - - public Person getOrganiser() - { - return organiser; - } - - public String getModuleCode() - { - return moduleCode; - } - - public ArrayList getTimetable() - { - return timetable; - } - - public int getNoOfAssignments() - { - return this.assignments.size(); - } - - /** - * Calculates how much of this Module has been completed in percentage. - * - * @return int (0-100) - */ - public int calculateProgress() - { - if (this.assignments.size() == 0) - return 0; - - int sum = 0; - for (Assignment assignment : this.assignments) - sum += assignment.calculateProgress(); - return sum / this.assignments.size(); - } - - // setters - public void addAssignment(Assignment newAssignment) - { - // initial set up code below - check if this needs updating - if (!assignments.contains(newAssignment)) - { - assignments.add(newAssignment); - } - } - - public void removeAssignment(Assignment newAssignment) - { - // initial set up code below - check if this needs updating - if (assignments.contains(newAssignment)) - { - assignments.remove(newAssignment); - } - } - - public void setOrganiser(Person newOrganiser) - { - organiser = newOrganiser; - } - - public void setModuleCode(String newModuleCode) - { - moduleCode = newModuleCode; - } - - public void addTimetableEvent(TimetableEvent newTimetableEvent) - { - if (!timetable.contains(newTimetableEvent)) - { - timetable.add(newTimetableEvent); - } - } - - public void removeTimetableEvent(TimetableEvent newTimetableEvent) - { - if (timetable.contains(newTimetableEvent)) - { - timetable.remove(newTimetableEvent); - } - } - - @Override - public void open(MenuController.Window current) - { - try - { - MainController.ui.moduleDetails(this, current); - } catch (IOException e) - { - UIManager.reportError("Unable to open View file"); - } - } - - // constructors - public Module(Person cOrganiser, String cModuleCode) - { - setOrganiser(cOrganiser); - setModuleCode(cModuleCode); - } -} diff --git a/src/Model/MultilineString.java b/src/Model/MultilineString.java deleted file mode 100644 index 61fd768a..00000000 --- a/src/Model/MultilineString.java +++ /dev/null @@ -1,64 +0,0 @@ -package Model; - -import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; - -/** - * Created by bendickson on 4/27/17. - */ -public class MultilineString implements Serializable -{ - // private Data; - private ArrayList lines; - - // public methods - public MultilineString clone() - { - return new MultilineString(this.getAsArray()); - } - - // getters - - /** - * Returns the number of lines in this MultilineString - * - * @return number of lines - */ - public int getLines() - { - return lines.size(); - } - - public ArrayList getAsArrayList() - { - return lines; - } - - public String[] getAsArray() - { - String r[] = new String[lines.size()]; - r = lines.toArray(r); - return r; - } - - public String getAsString() - { - return String.join("\n", getAsArray()); - } - - public MultilineString() - { - lines = new ArrayList<>(); - } - - public MultilineString(String mString) - { - lines = new ArrayList<>(Arrays.asList(mString.split("\n"))); - } - - public MultilineString(String[] mString) - { - lines = new ArrayList<>(Arrays.asList(mString)); - } -} diff --git a/src/Model/Note.java b/src/Model/Note.java deleted file mode 100644 index 7208e1ef..00000000 --- a/src/Model/Note.java +++ /dev/null @@ -1,68 +0,0 @@ -package Model; - -import java.io.Serializable; -import java.util.GregorianCalendar; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class Note implements Serializable -{ - // private data - private String title; - private GregorianCalendar timeStamp; - private MultilineString text; - - // public methods - - // getters - public String getTitle() - { - // initial set up code below - check if this needs updating - return title; - } - - public GregorianCalendar getTimeStamp() - { - // initial set up code below - check if this needs updating - return timeStamp; - } - - public MultilineString getText() - { - return text; - } - - // setters - public void setTitle(String newTitle) - { - // initial set up code below - check if this needs updating - title = newTitle; - } - - public void setTimeStamp(GregorianCalendar newTimeStamp) - { - // initial set up code below - check if this needs updating - timeStamp = newTimeStamp; - } - - public void setTimeStamp(int Y, int M, int D, int h, int m, int s) - { - // initial set up code below - check if this needs updating - timeStamp = new GregorianCalendar(Y, M, D, h, m, s); - } - - public void setText(MultilineString newText) - { - // initial set up code below - check if this needs updating - text = newText; - } - - public Note(String title, GregorianCalendar timeStamp, MultilineString text) - { - this.title = title; - this.timeStamp = timeStamp; - this.text = text; - } -} diff --git a/src/Model/Notification.java b/src/Model/Notification.java deleted file mode 100644 index 776b57f8..00000000 --- a/src/Model/Notification.java +++ /dev/null @@ -1,97 +0,0 @@ -package Model; - -import java.io.Serializable; -import java.util.GregorianCalendar; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class Notification implements Serializable -{ - // private data - private String title; - private GregorianCalendar dateTime; - private MultilineString details; - private boolean read; - private ModelEntity link; - - // public methods - - // getters - public String getTitle() - { - return title; - } - - public GregorianCalendar getDateTime() - { - return dateTime; - } - - public MultilineString getDetails() - { - return details; - } - - public String getDetailsAsString() - { - return this.details.getAsString(); - } - - public boolean isRead() - { - return read; - } - - public ModelEntity getLink() - { - return link; - } - - public String toString() - { - return this.title + ": " + this.getDetailsAsString(); - } - - // setters - public void read() - { - this.read = true; - } - - public void unread() - { - this.read = false; - } - - public void toggle() - { - this.read = !read; - } - - // constructors - public Notification(String title, GregorianCalendar dateTime, String details, ModelEntity link) - { - this.title = title; - this.dateTime = dateTime; - this.details = new MultilineString(details); - this.read = false; - this.link = link; - } - - public Notification(String title, GregorianCalendar dateTime, String details) - { - this.title = title; - this.dateTime = dateTime; - this.details = new MultilineString(details); - this.read = false; - } - - public Notification(String title, GregorianCalendar dateTime) - { - this.title = title; - this.dateTime = dateTime; - this.read = false; - } -} \ No newline at end of file diff --git a/src/Model/Person.java b/src/Model/Person.java deleted file mode 100644 index f0cefd4c..00000000 --- a/src/Model/Person.java +++ /dev/null @@ -1,294 +0,0 @@ -package Model; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.regex.Pattern; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class Person extends VersionControlEntity -{ - // private data - private ArrayList givenNames; - private String familyName; - private String salutation; - private String email; - private boolean familyNameLast = true; - - @Override - protected void replace(VersionControlEntity receivedVCE) - { - if (receivedVCE instanceof Person) - { - Person castedVCE = (Person) receivedVCE; - this.givenNames = castedVCE.getGivenNames(); - this.familyName = castedVCE.getFamilyName(); - this.salutation = castedVCE.getSalutation(); - this.email = castedVCE.getEmail(); - this.familyNameLast = castedVCE.getFamilyNameLast(); - } - super.replace(receivedVCE); - } - - // public methods - // getters - - /** - * Returns a full name of this Person. - * - * @return a String containing a full name. - */ - public String getFullName() - { - String namesList[] = new String[givenNames.size()]; - if (familyNameLast) - { - return (salutation.length() > 0 ? salutation + " " : "") + String.join(" ", givenNames) + " " + familyName; - } else - { - return (salutation.length() > 0 ? salutation + " " : "") + familyName + " " + String.join(" ", givenNames); - } - } - - public String getFamilyName() - { - return familyName; - } - - public String getEmail() - { - return email; - } - - /** - * Returns a list of given names for this Person. - * - * @return an ArrayList of String containing given names - */ - public ArrayList getGivenNames() - { - return (ArrayList) givenNames.clone(); - } - - /** - * Checker whether this Person specified his family name as last name. - * - * @return true if last, false otherwise. - */ - public boolean getFamilyNameLast() - { - return familyNameLast; - } - - public String getSalutation() - { - return salutation; - } - - public boolean hasSalutation() - { - return salutation.length() > 0; - } - - /** - * Get the preferred name of this Person. - * - * @return a String containing the preferred name. - */ - public String getPreferredName() - { - return name.length() > 0 ? name : (givenNames.size() > 0 ? givenNames.get(0) : familyName); - } - - // setters - public void setFamilyName(String newFamilyName) - { - familyName = newFamilyName; - } - - - public void setPreferredName(String newPreferredName) - { - name = newPreferredName; - } - - /** - * Sets the given names from a string with space separated names - * - * @param nameStr String containing names - */ - public void setGivenNames(String nameStr) - { - String nameSplit[] = nameStr.split(" "); - givenNames = new ArrayList<>(Arrays.asList(nameSplit)); - } - - /** - * Sets the name from a string with space separated names - * Family name position at start or end indicated by boolean value - * - * @param nameStr String containing names - * @param isFamilyNameLast is the family name at the end? - */ - public void setName(String nameStr, boolean isFamilyNameLast) - { - String nameSplit[] = nameStr.split(" "); - familyNameLast = isFamilyNameLast; - givenNames = new ArrayList<>(); - int i = -1; - int ii = nameSplit.length; - if (familyNameLast) - { - familyName = nameSplit[--ii]; - } else - { - familyName = nameSplit[++i]; - } - while (++i < ii) - { - givenNames.add(nameSplit[i]); - } - } - - public void setEmail(String newEmail) - { - email = newEmail; - } - - public void setSalutation(String newSalutation) - { - salutation = newSalutation; - } - - // static validators - - // email regex from: http://stackoverflow.com/questions/8204680/java-regex-email - private static Pattern emailRegex = Pattern.compile("(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)|(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*:(?:(?:\\r\\n)?[ \\t])*(?:(?:(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*)(?:,\\s*(?:(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*|(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)*\\<(?:(?:\\r\\n)?[ \\t])*(?:@(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*(?:,@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*)*:(?:(?:\\r\\n)?[ \\t])*)?(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\"(?:[^\\\"\\r\\\\]|\\\\.|(?:(?:\\r\\n)?[ \\t]))*\"(?:(?:\\r\\n)?[ \\t])*))*@(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*)(?:\\.(?:(?:\\r\\n)?[ \\t])*(?:[^()<>@,;:\\\\\".\\[\\] \\000-\\031]+(?:(?:(?:\\r\\n)?[ \\t])+|\\Z|(?=[\\[\"()<>@,;:\\\\\".\\[\\]]))|\\[([^\\[\\]\\r\\\\]|\\\\.)*\\](?:(?:\\r\\n)?[ \\t])*))*\\>(?:(?:\\r\\n)?[ \\t])*))*)?;\\s*)"); - private static Pattern salutationRegex = Pattern.compile("[a-zA-Z]*"); - private static Pattern nameRegex = Pattern.compile("[a-zA-z\\s]*"); - - /** - * Checks whether the given String is a valid email. - * - * @param email String to be checked. - * @return whether valid or not. - */ - public static boolean validEmail(String email) - { - return emailRegex.matcher(email).matches(); - } - - /** - * Checks whether the given String is a valid name. - * - * @param name String to be checked. - * @return whether valid or not. - */ - public static boolean validName(String name) - { - return nameRegex.matcher(name).matches(); - } - - /** - * Checks whether the given String is a valid salutation. - * - * @param salutation String to be checked. - * @return whether valid or not. - */ - public static boolean validSalutation(String salutation) - { - // salutation is empty OR if it - return salutationRegex.matcher(salutation).matches(); - } - - // constructors - - /** - * @param cSalutation String for saluation - * @param cName "NAME1 NAME2 NAME3 .... NAMEn" - * @param cFamNameLast if true, last name is family name, if not, first is - */ - public Person(String cSalutation, String cName, Boolean cFamNameLast) - { - setSalutation(cSalutation); - setName(cName, cFamNameLast); - familyNameLast = cFamNameLast; - email = ""; - } - - /** - * @param cSalutation String for salutation - * @param cGivenNames Array list of strings for given names - * @param cFamName String for family name - * @param cFamNameLast true if family name is at the end - */ - public Person(String cSalutation, ArrayList cGivenNames, String cFamName, Boolean cFamNameLast) - { - super(true); - setFamilyName(cFamName); - givenNames = (ArrayList) cGivenNames.clone(); - setSalutation(cSalutation); - familyNameLast = cFamNameLast; - email = ""; - } - - /** - * @param cSalutation String for saluation - * @param cName "NAME1 NAME2 NAME3 .... NAMEn" - * @param cFamNameLast if true, last name is family name, if not, first is - */ - public Person(String cSalutation, String cName, Boolean cFamNameLast, String newEmail) - { - setSalutation(cSalutation); - setName(cName, cFamNameLast); - familyNameLast = cFamNameLast; - email = newEmail; - } - - /** - * Pure String Constructor - * - * @param cSalutation - * @param cGivenNames - * @param cFamName - * @param cFamNameLast - * @param newEmail - */ - public Person(String cSalutation, String cGivenNames, String cFamName, Boolean cFamNameLast, String newEmail) - { - setSalutation(cSalutation); - String cName; - if (cFamNameLast) - { - cName = cGivenNames + " " + cFamName; - } else - { - cName = cFamName + " " + cGivenNames; - } - setName(cName, cFamNameLast); - email = newEmail; - } - - /** - * @param cSalutation String for salutation - * @param cGivenNames Array list of strings for given names - * @param cFamName String for family name - * @param cFamNameLast true if family name is at the end - */ - public Person(String cSalutation, ArrayList cGivenNames, String cFamName, Boolean cFamNameLast, String newEmail) - { - setFamilyName(cFamName); - givenNames = (ArrayList) cGivenNames.clone(); - setSalutation(cSalutation); - familyNameLast = cFamNameLast; - email = newEmail; - } - - @Override - public String toString() - { - return getFullName() + " ( " + getEmail() + " )"; - } -} diff --git a/src/Model/QuantityType.java b/src/Model/QuantityType.java deleted file mode 100644 index d573aff9..00000000 --- a/src/Model/QuantityType.java +++ /dev/null @@ -1,201 +0,0 @@ -package Model; - -import Controller.MainController; - -import java.util.ArrayList; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class QuantityType extends ModelEntity -{ - private static ArrayList quantityDatabase = new ArrayList<>(); - - public static String[] listOfNames() - { - String[] rr = new String[quantityDatabase.size()]; - int ii = quantityDatabase.size(); - int i = -1; - while (++i < ii) - { - rr[i] = quantityDatabase.get(i).getName(); - } - return rr; - } - - public static QuantityType[] listOfQuantityTypes() - { - QuantityType[] r = new QuantityType[quantityDatabase.size()]; - int i = -1; - int ii = quantityDatabase.size(); - while (++i < ii) - { - r[i] = quantityDatabase.get(i); - } - return r; - } - - public static QuantityType get(String tt) - { - int i = -1; - int ii = quantityDatabase.size(); - while (++i < ii) - { - if (quantityDatabase.get(i).equals(tt)) - { - return quantityDatabase.get(i); - } - } - return DEFAULT; - } - - public static boolean exists(QuantityType qt) - { - int i = -1; - int ii = quantityDatabase.size(); - while (++i < ii) - { - if (quantityDatabase.get(i).equals(qt)) - { - return true; - } - } - return false; - } - - public static boolean exists(String tt) - { - int i = -1; - int ii = quantityDatabase.size(); - while (++i < ii) - { - if (quantityDatabase.get(i).equals(tt)) - { - return true; - } - } - return false; - } - - /** - * Create a new QuantityType. - * - * @param cName Name of the quantity. - * @param cDetails Details of the quantity. - * @return - */ - public static QuantityType create(String cName, String cDetails) - { - QuantityType t = new QuantityType(cName, cDetails); - if (MainController.getSPC() != null) - MainController.getSPC().addQuantityType(t); - return t; - } - - /** - * Create a new QuantityType. - * - * @param cName Name of the quantity. - * @return - */ - public static QuantityType create(String cName) - { - QuantityType t = new QuantityType(cName); - if (MainController.getSPC() != null) - MainController.getSPC().addQuantityType(t); - return t; - } - - /** - * Create a new QuantityType from an existing one. - * - * @param type QuantityType object - */ - public static void create(QuantityType type) - { - if (!QuantityType.quantityDatabase.contains(type)) - { - QuantityType.quantityDatabase.add(type); - if (MainController.getSPC() != null) - MainController.getSPC().addQuantityType(type); - } - } - - /** - * A toString method used in TableView - * - * @return - */ - public String toString() - { - return this.name; - } - - // this is a temporary way to populate the array until we later replace from reading a set up file - static - { - class pair - { - public String a; - public String b; - - pair(String name, String details) - { - a = name; - b = details; - } - } - pair[] staticTypes = { - new pair("Other", "Other") - , - new pair("Hours", "Work in hours") - , - new pair("Books read", "Read this number of books") - , - new pair("Videos watched", "Watched this number of videos") - , - new pair("thousand words written", "Number of thousand words written") - , - new pair("questions answered", "Number of questions answered") - }; - int i = -1; - int ii = staticTypes.length; - while (++i < ii) - { - QuantityType t = new QuantityType(staticTypes[i].a, staticTypes[i].b); - } - } - - private QuantityType(String cName) - { - super(cName); - if (!exists(this)) - { - quantityDatabase.add(this); - } - } - - private QuantityType(String cName, String cDetails) - { - super(cName, cDetails); - if (!exists(this)) - { - quantityDatabase.add(this); - } - } - - @Override - public boolean equals(Object obj) - { - QuantityType that = (QuantityType) obj; - return getName().equals(that.getName()); - } - - public boolean equals(String c) - { - return getName().equals(c); - } - - public static QuantityType DEFAULT = quantityDatabase.get(0); -} diff --git a/src/Model/Requirement.java b/src/Model/Requirement.java deleted file mode 100644 index bbec3dc7..00000000 --- a/src/Model/Requirement.java +++ /dev/null @@ -1,202 +0,0 @@ -package Model; - -import Controller.MainController; -import Controller.MenuController; -import View.UIManager; - -import java.io.IOException; -import java.util.ArrayList; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class Requirement extends ModelEntity -{ - protected boolean checkedCompleted; - protected double estimatedTimeInHours; - protected ArrayList activityLog = new ArrayList<>(); - protected int initialQuantity; - protected int remainingQuantity; - protected QuantityType quantityType; - - // public methods - - // Getters: - public boolean isComplete() - { - return this.checkedCompleted; - } - - /** - * Returns the QuantityType of this Requirement. - * - * @return - */ - public QuantityType getQuantityType() - { - return this.quantityType; - } - - /** - * Returns the estimated time of this Requirement (in hours) - * - * @return - */ - public double getEstimatedTimeInHours() - { - return estimatedTimeInHours; - } - - /** - * Returns the initial quantity of this Requirement - * - * @return - */ - public int getInitialQuantity() - { - return initialQuantity; - } - - /** - * Returns the remaining quantity of this Requirement - */ - public int getRemainingQuantity() - { - return remainingQuantity; - } - - /** - * Returns an array of ActivityEvents that are associated with this Requirement - * - * @return - */ - public Activity[] getActivityLog() - { - return this.activityLog.toArray(new Activity[this.activityLog.size()]); - } - - /** - * Returns a double value representing the progress of this Requirement - * - * @return value between 0.0 and 0.1 - */ - public double requirementProgress() - { - return (double) (this.initialQuantity - this.remainingQuantity) / this.initialQuantity; - } - - // Setters: - public void setEstimatedTimeInHours(double estimatedTimeInHours) - { - this.estimatedTimeInHours = estimatedTimeInHours; - } - - /** - * Change the initial quantity. This will update the progress of this Requirement to reflect the change. - * - * @param initialQuantity - */ - public void setInitialQuantity(int initialQuantity) - { - if (this.initialQuantity == this.remainingQuantity) - this.initialQuantity = this.remainingQuantity = initialQuantity; - else - { - this.initialQuantity = initialQuantity; - this.update(); - } - } - - public void setQuantityType(String quantityType) - { - this.quantityType = QuantityType.get(quantityType); - } - - /** - * Add an Activity to the current Requirement and update the progress of this Requirement accordingly. - * - * @param activity Activity to be added. - */ - public void addActivity(Activity activity) - { - this.activityLog.add(activity); - this.remainingQuantity -= activity.getActivityQuantity(); - if (remainingQuantity <= 0) - { - this.remainingQuantity = 0; - this.checkedCompleted = true; - } - } - - /** - * Update the current Requirement to reflect changes. - * - * @return whether any changes were made - */ - public boolean update() - { - int tempQuantity = this.remainingQuantity; - - this.remainingQuantity = this.initialQuantity; - this.checkedCompleted = false; - for (Activity activity : this.activityLog) - this.remainingQuantity -= activity.getActivityQuantity(); - - if (this.remainingQuantity <= 0) - { - this.remainingQuantity = 0; - this.checkedCompleted = true; - } - - return tempQuantity == this.remainingQuantity; - } - - /** - * Returns the Name of the Requirement (used for JavaFX) - * - * @return Name of the task - */ - @Override - public String toString() - { - return this.name; - } - - @Override - public boolean equals(Object o) - { - if (this == o) return true; - if (o == null || this.getClass() != o.getClass()) return false; - - Requirement that = (Requirement) o; - - if (checkedCompleted != that.checkedCompleted) return false; - if (Double.compare(that.estimatedTimeInHours, estimatedTimeInHours) != 0) return false; - if (initialQuantity != that.initialQuantity) return false; - if (remainingQuantity != that.remainingQuantity) return false; - if (!activityLog.equals(that.activityLog)) return false; - return quantityType.equals(that.quantityType); - } - - @Override - public void open(MenuController.Window current) - { - try - { - MainController.ui.requirementDetails(this); - } catch (IOException e) - { - UIManager.reportError("Unable to open View file"); - } - } - - // Constructors: - public Requirement(String name, String details, double time, int quantity, String type) - { - super(name, details); - this.estimatedTimeInHours = time; - this.initialQuantity = this.remainingQuantity = quantity; - this.quantityType = QuantityType.get(type); - } -} diff --git a/src/Model/Room.java b/src/Model/Room.java deleted file mode 100644 index 90d5ae05..00000000 --- a/src/Model/Room.java +++ /dev/null @@ -1,81 +0,0 @@ -package Model; - - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class Room extends VersionControlEntity -{ - // private data - private Building building = null; - private String roomNumber; - - @Override - protected void replace(VersionControlEntity receivedVCE) - { - if (receivedVCE instanceof Room) - { - Room castedVCE = (Room) receivedVCE; - if (castedVCE.getBuilding() != null) - { - this.building = castedVCE.getBuilding(); - } - this.roomNumber = castedVCE.getRoomNumber(); - } - super.replace(receivedVCE); - } - - // public methods - - // getters - public Building getBuilding() - { - return building; - } - - public String getRoomNumber() - { - return roomNumber; - } - - public String getLocation() - { - return name + "( " + roomNumber + " )"; - } - - @Override - public String toString() - { - if (building == null) - { - return name + "( " + roomNumber + " )"; - } else - { - return name + "( " + roomNumber + " ) located in " + building.toString(); - } - } - - // setters - public void setBuilding(Building newBuilding) - { - building = newBuilding; - } - - public void setRoomNumber(String newRoomNumber) - { - roomNumber = newRoomNumber; - } - - // Constructors: - public Room(String cRoomNumber, Building cBuilding) - { - setRoomNumber(cRoomNumber); - setBuilding(cBuilding); - } - - public Room(String cRoomNumber) - { - setRoomNumber(cRoomNumber); - } -} diff --git a/src/Model/StudyPlanner.java b/src/Model/StudyPlanner.java deleted file mode 100644 index 0f151382..00000000 --- a/src/Model/StudyPlanner.java +++ /dev/null @@ -1,393 +0,0 @@ -package Model; - -import javax.crypto.NoSuchPaddingException; -import java.io.Serializable; -import java.security.NoSuchAlgorithmException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class StudyPlanner implements Serializable -{ - // private data - private static final long serialVersionUID = 101L; - - private int version = -1; - private Account account; - private ArrayList quantityTypes = new ArrayList<>(); - private ArrayList taskTypes = new ArrayList<>(); - private ArrayList studyProfiles = new ArrayList<>(); - private ArrayList activityList = new ArrayList<>(); - private ArrayList timeTableEventTypes = new ArrayList<>(); - private ArrayList calendar = new ArrayList<>(); - private ArrayList notifications = new ArrayList<>(); - private HashMap deadlineNotifications = new HashMap<>(); - private ArrayList versionControlLibrary = new ArrayList<>(); - - private StudyProfile currentStudyProfile; - - // public methods - - // getters - - public ArrayList getCalendar() - { - return calendar; - } - - /** - * returns a String array of studyProfile names - * - * @return - */ - - public String[] getListOfStudyProfileNames() - { - int i = -1; - String[] r = new String[studyProfiles.size()]; - while (++i < studyProfiles.size()) - { - r[i] = studyProfiles.get(i).getName(); - } - return r; - } - - /** - * Returns an array of study profiles - * - * @return - */ - public StudyProfile[] getStudyProfiles() - { - StudyProfile[] sp = new StudyProfile[this.studyProfiles.size()]; - sp = this.studyProfiles.toArray(sp); - return sp; - } - - /** - * This was added at the last minute before releasing and should be in the Module class, however - * if we had done that our Java Serialized file, which took 3 hours prepare, would no longer have worked. - * This is temporary solution so you can still use our prepared Study Planner save file, but in the next iteration - * it will be moved to the correct place and would not consist of 4 nested loops. - * Also, for public release, we would create a file format for saving in so Java Serialization issues would not - * occur for the end user. - * - * @param module - * @return - */ - public int getTimeSpent(Module module) - { - int time = 0; - for (Assignment assignment : module.getAssignments()) - for (Task task : assignment.getTasks()) - for (Requirement requirement : task.getRequirements()) - for (Activity activity : requirement.getActivityLog()) - time += activity.getDuration(); - return time; - } - - /** - * Check whether this StudyPlanner contains a StudyProfile with the given parameters. - * - * @param sYear year - * @param sSem semester number - * @return whether this StudyProfile exists - */ - public boolean containsStudyProfile(int sYear, int sSem) - { - int i = -1; - int ii = studyProfiles.size(); - while (++i < ii) - { - if (studyProfiles.get(i).matches(sYear, sSem)) - { - return true; - } - } - return false; - } - - /** - * Add a given event to the global calendar. - * - * @param event Event to be added to the calendar. - */ - public void addEventToCalendar(Event event) - { - if (!calendar.contains(event)) - calendar.add(event); - } - - /** - * Returns the current StudyProfile - * - * @return current StudyProfile - */ - public StudyProfile getCurrentStudyProfile() - { - return this.currentStudyProfile; - } - - /** - * Get the preferred name of the user using this StudyPlanner. - * - * @return String containing the users name. - */ - public String getUserName() - { - return this.account.getStudentDetails().getPreferredName(); - } - - /** - * Get all notifications in this StudyPlanner. - * - * @return an array of notifications. - */ - public Notification[] getNotifications() - { - Notification[] r = new Notification[this.notifications.size()]; - r = this.notifications.toArray(r); - return r; - } - - /** - * Get all unread notifications in this StudyPlanner. - * - * @return an array of unread notifications. - */ - public Notification[] getUnreadNotifications() - { - Notification[] r = this.notifications.stream().filter(e -> !e.isRead()).toArray(Notification[]::new); - return r; - } - - /** - * Returns a HashMap that contains information about Deadline notifications. - * - * @return - */ - public HashMap getDeadlineNotifications() - { - return deadlineNotifications; - } - - /** - * Returns an ArrayList of QuantityTypes. - * - * @return ArrayList - */ - public ArrayList getQuantityTypes() - { - return this.quantityTypes; - } - - /** - * Returns an ArrayList of TaskTypes. - * - * @return ArrayList - */ - public ArrayList getTaskTypes() - { - return this.taskTypes; - } - - // setters - - /** - * Change the current Study Profile to a given one. - * - * @param profile a StudyProfile to be marked as current. - * @return whether changed successfully. - */ - public boolean setCurrentStudyProfile(StudyProfile profile) - { - if (this.studyProfiles.contains(profile)) - { - if (this.currentStudyProfile != null) - this.currentStudyProfile.setCurrent(false); - this.currentStudyProfile = profile; - profile.setCurrent(true); - return true; - } - return false; - } - - /** - * Change the current Study Profile to a Study Profile with the given ID. - * - * @param profileID ID of a Study Profile - * @return whether changed successfully. - */ - public boolean setCurrentStudyProfile(String profileID) - { - this.studyProfiles.forEach(e -> { - if (e.getUID().equals(profileID)) - this.setCurrentStudyProfile(e); - }); - - return this.currentStudyProfile.getUID().equals(profileID); - } - - /** - * Adds a new StudyProfile to the StudyPlanner - * - * @param profile StudyProfile to be added. - */ - public void addStudyProfile(StudyProfile profile) - { - this.studyProfiles.add(profile); - } - - /** - * Add a new notification to this StudyPlanner. - * - * @param notification Notification to be added. - */ - public void addNotification(Notification notification) - { - this.notifications.add(notification); - } - - /** - * Add an Activity to this Study Planner and update appropriate fields. - * - * @param activity Activity to be added. - */ - public void addActivity(Activity activity) - { - this.activityList.add(activity); - ArrayList assignments = new ArrayList<>(); - // Loop through all Tasks: - for (Task t : activity.getTasks()) - { - // Distribute Activity Quantity to available Requirements of a Task: - int quantity = activity.getActivityQuantity(); - for (Requirement r : t.getRequirements()) - { - if (r.getQuantityType().equals(activity.getType()) && !r.checkedCompleted) - { - quantity -= r.getRemainingQuantity(); - Activity extracted = new Activity(activity); - - if (quantity > 0) - { - extracted.setActivityQuantity(r.getRemainingQuantity()); - r.addActivity(extracted); - } else - { - extracted.setActivityQuantity(quantity + r.getRemainingQuantity()); - r.addActivity(extracted); - break; - } - } - } - // ================= - for (Assignment assignment : t.getAssignmentReferences()) - { - if (!assignments.contains(assignment)) - assignments.add(assignment); - } - } - // ================= - - // Distribute quantity to Assignment requirements: - for (Assignment a : assignments) - { - int quantity = activity.getActivityQuantity(); - for (Requirement r : a.getRequirements()) - { - if (r.getQuantityType().equals(activity.getType()) && !r.checkedCompleted) - { - quantity -= r.getRemainingQuantity(); - Activity extracted = new Activity(activity); - - if (quantity > 0) - { - extracted.setActivityQuantity(r.getRemainingQuantity()); - r.addActivity(extracted); - } else - { - extracted.setActivityQuantity(quantity + r.getRemainingQuantity()); - r.addActivity(extracted); - break; - } - } - } - } - // ================= - } - - /** - * Add a VersionControlEntity to the library. - * - * @param vce VersionControlEntity to be added. - * @return whether added successfully. - */ - public boolean addToVersionControlLibrary(VersionControlEntity vce) - { - if (versionControlLibrary.contains(vce)) - { - return false; - } else - { - versionControlLibrary.add(vce); - return true; - } - } - - /** - * Check whether the current VCE library is empty. - * - * @return true if empty, false otherwise. - */ - public boolean emptyVersionControlLibrary() - { - return versionControlLibrary.isEmpty(); - } - - /** - * Rebuild the VCE library (used when the app is reloaded). - */ - public void rebuildVersionControlLibrary() - { - versionControlLibrary.forEach(e -> e.reload()); - } - - /** - * Update the version of this StudyPlanner. - * - * @param newVersion new version number - * @return whether updated successfully. - */ - public boolean setVersion(int newVersion) - { - if (newVersion > version) - { - version = newVersion; - return true; - } else - { - return false; - } - } - - public int getVersion() - { - return version; - } - - // constructors - - public StudyPlanner(Account newAccount) throws NoSuchPaddingException, NoSuchAlgorithmException - { - this.account = newAccount; - // Add Default Quantity types: - Collections.addAll(this.quantityTypes, QuantityType.listOfQuantityTypes()); - // Add Default Task types: - Collections.addAll(this.taskTypes, TaskType.listOfTaskTypes()); - } -} diff --git a/src/Model/StudyProfile.java b/src/Model/StudyProfile.java deleted file mode 100644 index 32643693..00000000 --- a/src/Model/StudyProfile.java +++ /dev/null @@ -1,178 +0,0 @@ -package Model; - -import Controller.MainController; -import Controller.MenuController; -import View.UIManager; - -import java.io.IOException; -import java.util.ArrayList; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class StudyProfile extends VersionControlEntity -{ - // private data - private ArrayList modules; - private ArrayList milestones; - private ArrayList extensions; - private ArrayList calendar = new ArrayList<>(); - private int year; - private int semesterNo; - private boolean current; - - // public methods - - // getters: - public Module[] getModules() - { - Module[] m = new Module[this.modules.size()]; - m = this.modules.toArray(m); - return m; - } - - public Milestone[] getMilestones() - { - Milestone[] m = new Milestone[this.milestones.size()]; - m = this.milestones.toArray(m); - return m; - } - - public ExtensionApplication[] getExtensions() - { - ExtensionApplication[] e = new ExtensionApplication[this.extensions.size()]; - e = this.extensions.toArray(e); - return e; - } - - /** - * Returns a calendar containing all the Events of this Study Profile. - * - * @return ArrayList of Events - */ - public ArrayList getCalendar() - { - return calendar; - } - - - public ArrayList getTasks() - { - ArrayList tasks = new ArrayList<>(); - this.modules.forEach(e -> e.getAssignments().forEach(ee -> tasks.addAll(ee.getTasks()))); - return tasks; - } - - /** - * Whether this StudyProfile is set as current. - * - * @return true if current, else otherwise. - */ - public boolean isCurrent() - { - return current; - } - - /** - * Set/unset this StudyProfile as the current profile of the StudyPlanner. - * - * @param current - */ - public void setCurrent(boolean current) - { - this.current = current; - } - - /** - * Add an Event to the calendar of this Study Profile. - * - * @param event Event to be added. - */ - public void addEventToCalendar(Event event) - { - if (!calendar.contains(event)) - { - calendar.add(event); - } - } - - public String getName() - { - return name; - } - - public int getYear() - { - return year; - } - - public int getSemesterNo() - { - return semesterNo; - } - - /** - * Whether this StudyProfile matches the given details. - * - * @param mYear year - * @param mSemesterNo semester number - * @return true if matches, false otherwise. - */ - public boolean matches(int mYear, int mSemesterNo) - { - return mYear == year && mSemesterNo == semesterNo; - } - - // Setters: - - /** - * Adds a Milestone to this StudyProfile. - * - * @param milestone Milestone to be added. - */ - public void addMilestone(Milestone milestone) - { - this.milestones.add(milestone); - } - - /** - * Removes a Milestone from this StudyProfile. - * - * @param milestone Milestone to be removed. - * @return whether the Milestone was removed successfully. - */ - public boolean removeMilestone(Milestone milestone) - { - return this.milestones.remove(milestone); - } - - @Override - public void open(MenuController.Window current) - { - try - { - MainController.ui.studyProfileDetails(this); - } catch (IOException e) - { - UIManager.reportError("Unable to open View file"); - } - } - - // constructors - public StudyProfile(HubFile initialHubFile) - { - this.milestones = new ArrayList<>(); - - this.modules = initialHubFile.getModules(); - this.extensions = initialHubFile.getExtensions(); - - this.year = initialHubFile.getYear(); - this.semesterNo = initialHubFile.getSemester(); - this.version = initialHubFile.getVersion(); - this.name = initialHubFile.getSemesterName(); - this.details = initialHubFile.getSemesterDetails(); - - this.current = false; - } -} diff --git a/src/Model/Task.java b/src/Model/Task.java deleted file mode 100644 index 22d8aa9d..00000000 --- a/src/Model/Task.java +++ /dev/null @@ -1,383 +0,0 @@ -package Model; - -import Controller.MainController; -import Controller.MenuController; -import View.UIManager; - -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.time.LocalDate; -import java.time.format.DateTimeFormatter; -import java.util.ArrayList; -import java.util.Collection; -import java.util.Date; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class Task extends ModelEntity -{ - // private data - private ArrayList dependencies = new ArrayList<>(); - private Deadline deadline; - private ArrayList requirements = new ArrayList<>(); - private ArrayList notes; - private boolean checkedComplete; - private int weighting; - private TaskType type; - private ArrayList assignments = new ArrayList<>(); - - // public methods - - // Getters: - public String getDeadline() - { - return new SimpleDateFormat("dd/MM/yyyy").format(this.deadline.getDate()); - } - - public Date getDeadlineDate() - { - return this.deadline.getDate(); - } - - public int getWeighting() - { - return this.weighting; - } - - /** - * Wrapper for JavaFX TableView - * - * @return - */ - public boolean isCheckedComplete() - { - return canCheckComplete() && checkedComplete; - } - - public TaskType getType() - { - return this.type; - } - - public Task[] getDependencies() - { - return this.dependencies.toArray(new Task[this.dependencies.size()]); - } - - public Requirement[] getRequirements() - { - return this.requirements.toArray(new Requirement[this.requirements.size()]); - } - - public int requirementCount() - { - return requirements.size(); - } - - /** - * Returns the number of complete in Requirements in this Task. - * - * @return integer - */ - public int requirementsComplete() - { - int r = 0; - int i = -1, ii = requirements.size(); - while (++i < ii) - { - if (requirements.get(i).isComplete()) - r++; - } - return r; - } - - /** - * Calculates how much of this Task has been completed in percentage. - * - * @return int (0-100) - */ - public int calculateProgress() - { - if (this.requirementCount() == 0) - return 0; - else - return (this.requirementsComplete() * 100) / this.requirementCount(); - } - - /** - * Returns an array of Assignments to which this Task relates. - * - * @return array of Assignments. - */ - public Assignment[] getAssignmentReferences() - { - return this.assignments.toArray(new Assignment[this.assignments.size()]); - } - - /** - * Checks whether all dependencies of this Task are complete. - * - * @return true if complete, false otherwise. - */ - public boolean dependenciesComplete() - { - int i = -1; - int ii = dependencies.size(); - while (++i < ii) - { - if (!dependencies.get(i).isCheckedComplete()) - { - return false; - } - } - return true; - } - - /** - * Checks whether this Task has any dependencies. - * - * @return true if does, false otherwise. - */ - public boolean hasDependencies() - { - return dependencies.size() > 0; - } - - /** - * Same as canCheckComplete(), wrapper for TableView - * - * @return - */ - public boolean isPossibleToComplete() - { - return canCheckComplete(); - } - - /** - * Checks whether this Task can be checked as complete. If it cannot, makes sure it is marked as - * incomplete. - * - * @return - */ - public boolean canCheckComplete() - { - int i = -1; - int ii = requirements.size(); - while (++i < ii) - { - if (!requirements.get(i).isComplete()) - { - this.checkedComplete = false; - return false; - } - } - if (this.dependenciesComplete()) - return true; - else - { - this.checkedComplete = false; - return false; - } - } - - /** - * Checks whether this Task already contains a given dependency - * - * @param dep dependency to be checked for - * @return true or false - */ - public boolean containsDependency(Task dep) - { - return this.dependencies.contains(dep); - } - - /** - * Checks whether this Task already contains a given Requirement - * - * @param requirement requirement to be checked for - * @return true or false - */ - public boolean containsRequirement(Requirement requirement) - { - return this.requirements.contains(requirement); - } - - /** - * Returns the Name of the Task (used for JavaFX) - * - * @return Name of the task - */ - @Override - public String toString() - { - return this.name; - } - - // Setters: - - /** - * Add a Requirement to the current Task. - * - * @param req requirement to be added - */ - public void addRequirement(Requirement req) - { - this.requirements.add(req); - this.canCheckComplete(); - } - - /** - * Add a Task to the list of dependencies for the current Task. - * - * @param task Task to be added - */ - public void addDependency(Task task) - { - this.dependencies.add(task); - this.canCheckComplete(); - } - - /** - * Replaces the current Requirements with the given ones - * - * @param requirements list of requirements - */ - public void replaceRequirements(Collection requirements) - { - this.requirements.clear(); - this.requirements.addAll(requirements); - this.canCheckComplete(); - } - - /** - * Replaces the current Dependencies with the given ones - * - * @param dependencies list of Tasks - */ - public void replaceDependencies(Collection dependencies) - { - this.dependencies.clear(); - this.dependencies.addAll(dependencies); - this.canCheckComplete(); - } - - /** - * Removes a given Task from the dependencies list - * - * @param dependency Task to be removed - * @return whether the dependency has been removed successfully - */ - public boolean removeDependency(Task dependency) - { - return this.dependencies.remove(dependency); - } - - /** - * Removes a given Requirement from the requirements list - * - * @param requirement Requirement to be removed - * @return whether the Requirement has been removed successfully - */ - public boolean removeRequirement(Requirement requirement) - { - return this.requirements.remove(requirement); - } - - /** - * Toggle complete - */ - public void toggleComplete() - { - if (this.isCheckedComplete()) - this.checkedComplete = false; - else if (this.canCheckComplete()) - this.checkedComplete = true; - } - - /** - * Mark as complete/incomplete - * - * @param c boolean value - */ - public void setComplete(boolean c) - { - this.checkedComplete = c; - } - - /** - * Set a new deadline - * - * @param date date to be set as a new deadline - */ - public void setDeadline(LocalDate date) - { - this.deadline.setDate(date.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")) + "T00:00:01Z"); - } - - /** - * Set a new weighting for this Task - * - * @param weighting - */ - public void setWeighting(int weighting) - { - this.weighting = weighting; - } - - /** - * Set a new type for this Task - * - * @param type String representation of a type - */ - public void setType(String type) - { - if (TaskType.exists(type)) - { - this.type = TaskType.get(type); - } - } - - /** - * Add a reference to an Assignment to this task. (Used for completing Assignment Requirements). - * - * @param assignment Assignment which should be linked with this Task. - */ - public void addAssignmentReference(Assignment assignment) - { - if (!this.assignments.contains(assignment)) - this.assignments.add(assignment); - } - - /** - * Removes a reference from the list of Assignments this Task relates to. - * - * @param assignment Assignment to be removed. - */ - public void removeAssignmentReference(Assignment assignment) - { - this.assignments.remove(assignment); - } - - @Override - public void open(MenuController.Window current) - { - try - { - MainController.ui.taskDetails(this); - } catch (IOException e) - { - UIManager.reportError("Unable to open View file"); - } - } - - // Constructors: - public Task(String name, String details, LocalDate deadline, int weighting, String type) - { - super(name, details); - this.deadline = new Deadline(deadline.format(DateTimeFormatter.ofPattern("dd/MM/yyyy")) + "T00:00:01Z"); - this.weighting = weighting; - this.type = TaskType.get(type); - } -} diff --git a/src/Model/TaskType.java b/src/Model/TaskType.java deleted file mode 100644 index f4d88512..00000000 --- a/src/Model/TaskType.java +++ /dev/null @@ -1,194 +0,0 @@ -package Model; - -import Controller.MainController; - -import java.util.ArrayList; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class TaskType extends ModelEntity -{ - private static ArrayList taskDatabase = new ArrayList<>(); - - public static String[] listOfNames() - { - String[] r = new String[taskDatabase.size()]; - int i = -1; - int ii = taskDatabase.size(); - while (++i < ii) - { - r[i] = taskDatabase.get(i).getName(); - } - return r; - } - - public static TaskType[] listOfTaskTypes() - { - TaskType[] r = new TaskType[taskDatabase.size()]; - int i = -1; - int ii = taskDatabase.size(); - while (++i < ii) - { - r[i] = taskDatabase.get(i); - } - return r; - } - - public static TaskType get(String tt) - { - int i = -1; - int ii = taskDatabase.size(); - while (++i < ii) - { - if (taskDatabase.get(i).equals(tt)) - { - return taskDatabase.get(i); - } - } - return DEFAULT; - } - - public static boolean exists(TaskType tt) - { - int i = -1; - int ii = taskDatabase.size(); - while (++i < ii) - { - if (taskDatabase.get(i).equals(tt)) - { - return true; - } - } - return false; - } - - public static boolean exists(String tt) - { - int i = -1; - int ii = taskDatabase.size(); - while (++i < ii) - { - if (taskDatabase.get(i).equals(tt)) - { - return true; - } - } - return false; - } - - /** - * Create a new TaskType. - * - * @param cName Name of the TaskType. - * @param cDetails Details of the TaskType. - * @return - */ - public static TaskType create(String cName, String cDetails) - { - TaskType t = new TaskType(cName, cDetails); - if (MainController.getSPC() != null) - MainController.getSPC().addTaskType(t); - return t; - } - - /** - * Create a new TaskType. - * - * @param cName Name of the TaskType. - * @return - */ - public static TaskType create(String cName) - { - TaskType t = new TaskType(cName); - if (MainController.getSPC() != null) - MainController.getSPC().addTaskType(t); - return t; - } - - /** - * Create a new TaskType from an existing one. - * - * @param type TaskType object - */ - public static void create(TaskType type) - { - if (!TaskType.taskDatabase.contains(type)) - { - TaskType.taskDatabase.add(type); - if (MainController.getSPC() != null) - MainController.getSPC().addTaskType(type); - } - } - - // this is a temporary way to populate the array until we later replace from reading a set up file - static - { - class pair - { - public String a; - public String b; - - pair(String name, String details) - { - a = name; - b = details; - } - } - pair[] staticTypes = { - new pair("Other", "Other type of task") - , - new pair("Reading", "Read some required text") - , - new pair("Exercises", "Did some assigned exercises") - , - new pair("Listening", "Listened to a podcast") - , - new pair("Coursework", "Worked towards coursework") - , - new pair("Revision", "Revised towards exam") - , - new pair("Meeting", "Meet with other course members") - - }; - int i = -1; - int ii = staticTypes.length; - while (++i < ii) - { - TaskType t = new TaskType(staticTypes[i].a, staticTypes[i].b); - } - } - - private TaskType(String cName) - { - super(cName); - if (!exists(this)) - { - taskDatabase.add(this); - } - } - - private TaskType(String cName, String cDetails) - { - super(cName, cDetails); - if (!exists(this)) - { - taskDatabase.add(this); - } - } - - @Override - public boolean equals(Object obj) - { - TaskType that = (TaskType) obj; - return getName().equals(that.getName()); - } - - public boolean equals(String c) - { - return getName().equals(c); - } - - public static TaskType DEFAULT = taskDatabase.get(0); -} diff --git a/src/Model/TimeTableEventType.java b/src/Model/TimeTableEventType.java deleted file mode 100644 index f55a0569..00000000 --- a/src/Model/TimeTableEventType.java +++ /dev/null @@ -1,13 +0,0 @@ -package Model; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class TimeTableEventType extends VersionControlEntity -{ - public String toString() - { - return name; - } -} diff --git a/src/Model/TimetableEvent.java b/src/Model/TimetableEvent.java deleted file mode 100644 index a8bd999d..00000000 --- a/src/Model/TimetableEvent.java +++ /dev/null @@ -1,99 +0,0 @@ -package Model; - - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class TimetableEvent extends Event -{ - // private data - private Room room; - private Person lecturer; - private TimeTableEventType timeTableEventType; - private int duration; - - @Override - protected void replace(VersionControlEntity receivedVCE) - { - if (receivedVCE instanceof TimetableEvent) - { - TimetableEvent castedVCE = (TimetableEvent) receivedVCE; - this.duration = castedVCE.getDuration(); - if (castedVCE.getLecturer() != null) - { - this.lecturer = castedVCE.getLecturer(); - } - if (castedVCE.getRoom() != null) - { - this.room = castedVCE.getRoom(); - } - if (castedVCE.getTimeTableEventType() != null) - { - this.timeTableEventType = castedVCE.getTimeTableEventType(); - } - } - super.replace(receivedVCE); - } - // public methods - - // getters - public Room getRoom() - { - return room; - } - - public Person getLecturer() - { - return lecturer; - } - - public TimeTableEventType getTimeTableEventType() - { - return timeTableEventType; - } - - public int getDuration() - { - return duration; - } - - // setters - public void setRoom(Room newRoom) - { - room = newRoom; - } - - public void setLecturer(Person newLecturer) - { - lecturer = newLecturer; - } - - public void setTimeTableEventType(TimeTableEventType newTimeTableEventType) - { - timeTableEventType = newTimeTableEventType; - } - - public void setDuration(int newDuration) - { - duration = newDuration; - } - - @Override - public String toString() - { - return name + " in " + room.toString() + " at " + date.getTime(); - } - - // constructor - public TimetableEvent(String cDate, Room cRoom, Person cLecturer, TimeTableEventType cTimeTableEventType, - int cDuration) - { - super(cDate); - setRoom(cRoom); - setLecturer(cLecturer); - setTimeTableEventType(cTimeTableEventType); - setDuration(cDuration); - - } -} diff --git a/src/Model/VersionControlEntity.java b/src/Model/VersionControlEntity.java deleted file mode 100644 index aaf51f27..00000000 --- a/src/Model/VersionControlEntity.java +++ /dev/null @@ -1,255 +0,0 @@ -package Model; - -import Controller.MainController; - -import java.util.HashMap; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class VersionControlEntity extends ModelEntity -{ - protected int version; - protected String uid; - protected boolean sealed; - private static HashMap library = new HashMap<>(); - protected boolean importer = false; // used for VCEs created during XML import - // private methods - - /** - * This method overwrites the data in the received object with that received - * This method will need to overrode in every class that extends it - * - * @param receivedVCE - */ - protected void replace(VersionControlEntity receivedVCE) - { - name = receivedVCE.getName(); - details = receivedVCE.getDetails(); - version = receivedVCE.getVersion(); - // super.replace(receivedVCE); - } - - // public methods - - /** - * Update ths VCE with a given one. - * - * @param receivedVCE received VCE for updating the current one. - * @return whether updated successfully. - */ - public boolean update(VersionControlEntity receivedVCE) - { - if (uid.equals(receivedVCE.getUID()) && version < receivedVCE.getVersion()) - { - replace(receivedVCE); - return true; - } else - { - return false; - } - } - - /** - * Find the given VCE in the library and then update it. - * - * @param receivedVCE a VCE to be looked for and updated. - * @return whether found and updated successfully. - */ - public static boolean findAndUpdate(VersionControlEntity receivedVCE) - { - String UID = receivedVCE.getUID(); - if (inLibrary(UID)) - { - library.get(UID).update(receivedVCE); - return true; - } else - { - return false; - } - } - - public boolean makeImporter() - { - if (!sealed) - { - importer = true; - return true; - } else - { - return false; - } - } - - public boolean isImporter() - { - return importer; - } - - /** - * Add this VCE to the library. - * - * @return whether added successfully. - */ - public boolean addToLibrary() - { - if (importer) - { - if (inLibrary(uid)) - { - return false; - } else - { - importer = false; - sealed = true; - library.put(uid, this); - MainController.getSPC().getPlanner().addToVersionControlLibrary(this); - return true; - } - } else - { - return false; - } - } - - /** - * Get a VCE from the library by it's UID - * - * @param UID UID to be looked for. - * @return a valid VCE if found, null otherwise. - */ - public static VersionControlEntity get(String UID) - { - if (inLibrary(UID)) - { - return library.get(UID); - } else - { - return null; - } - } - - /** - * Check whether a VCE with the given UID exists in the library. - * - * @param UID UID to be checked for. - * @return true if found, false otherwise. - */ - public static boolean inLibrary(String UID) - { - return library.containsKey(UID); - } - - // getters - public int getVersion() - { - return version; - } - - public String getUID() - { - return uid; - } - - /** - * Returns the VCE library. - * - * @return HashMap containing all VCEs. - */ - public static HashMap getLibrary() - { - return library; - } - - /** - * Returns a summary of the VCEs in the library. - * - * @return String - */ - public static String libraryReport() - { - return "Total Entries: " + library.size(); - } - - /** - * Set a new UID and version for this VCE. - * - * @param newUID new UID - * @param newVersion new version - * @return whether changed successfully. - */ - public boolean setUID(String newUID, int newVersion) - { -// setUID(newUID); - if (importer) - { - setUID(newUID); - version = newVersion; - return true; - } else if (sealed || library.containsKey(newUID)) - { - return false; - } else - { - setUID(newUID); - version = newVersion; - return true; - } - } - - /** - * Set a new UID for this VCE. - * - * @param newUID new UID - * @return whether changed successfully. - */ - public boolean setUID(String newUID) - { - if (importer) - { - uid = newUID; - return true; - } else if (sealed || library.containsKey(newUID)) - { - return false; - } else - { - uid = newUID; - library.put(newUID, this); - MainController.getSPC().getPlanner().addToVersionControlLibrary(this); - return true; - } - } - - /** - * Called once the program is loaded, adds this VCE to the library if possible. - */ - public void reload() - { - if (!inLibrary(this.uid) && !importer && sealed) - { - library.put(this.uid, this); - } - } - - // Constructors - public VersionControlEntity(boolean leaveUnsealed) - { - super(); - sealed = !leaveUnsealed; - } - - public VersionControlEntity() - { - super(); - sealed = false; - } - - public VersionControlEntity(String UID) - { - super(); - sealed = setUID(UID); - } - -} diff --git a/src/Model/Video.java b/src/Model/Video.java deleted file mode 100644 index 16a34744..00000000 --- a/src/Model/Video.java +++ /dev/null @@ -1,25 +0,0 @@ -package Model; - -/** - * PearPlanner - * Created by Team BRONZE on 4/27/17 - */ -public class Video /*extends Requirement*/ -{ - // private data - private String url; - - // public methods - - // getters - public String getUrl() - { - return url; - } - - // setters - public void setUrl(String newURL) - { - url = newURL; - } -} diff --git a/src/View/Activity.fxml b/src/View/Activity.fxml deleted file mode 100644 index 2d60e8ac..00000000 --- a/src/View/Activity.fxml +++ /dev/null @@ -1,106 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/View/ConsoleIO.java b/src/View/ConsoleIO.java deleted file mode 100644 index e8d44313..00000000 --- a/src/View/ConsoleIO.java +++ /dev/null @@ -1,237 +0,0 @@ -package View; - -import Controller.DataController; -import Controller.StudyPlannerController; -import Model.HubFile; - -import java.io.BufferedWriter; -import java.io.File; -import java.io.FileOutputStream; -import java.io.OutputStreamWriter; -import java.util.ArrayList; -import java.util.Scanner; - - -/** - * Created by bendickson on 5/4/17. - */ -public class ConsoleIO -{ - static ArrayList logged = new ArrayList<>(); - - /** - * Retrieve user input/ - * - * @param message Message to be shown - * @return retrieved String - */ - static public String getDataString(String message) - { - Scanner scan = new Scanner(System.in); - String r = ""; - while (r.equals("")) - { - System.out.println(message); - r = scan.nextLine(); - } - return r; - } - - /** - * Get yes/no input. - * - * @param message Message to be shown. - * @return true for yes, false for no. - */ - static public boolean getDataBool(String message) - { - Scanner scan = new Scanner(System.in); - String r = ""; - while (!(r.equals("y") || r.equals("n"))) - { - System.out.println(message); - r = scan.next(); - } - return r.equals("y"); - } - - /** - * How many lines have been written to the log. - * - * @return number of lines. - */ - static public int getLogSize() - { - return logged.size(); - } - - /** - * Save the full log to a file. - * - * @param filePath file path - */ - static public void saveLog(String filePath) - { - saveLog(filePath, 0, logged.size()); - } - - /** - * Save specific lines to a file. - * - * @param filePath file path - * @param startLine starting line - * @param endLine end line - */ - static public void saveLog(String filePath, int startLine, int endLine) - { - if (startLine < 0) - { - startLine = 0; - } - if (endLine > logged.size()) - { - endLine = logged.size(); - } - int i = startLine - 1; - try - { - File logFile = new File(filePath); - FileOutputStream fos = new FileOutputStream(logFile); - BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos)); - while (++i < endLine) - { - if (i != startLine) - { - bw.newLine(); - } - bw.write(logged.get(i)); - } - bw.close(); - } catch (Exception e) - { - setConsoleMessage("File not written", true); - } - } - - /** - * Display a message on a console. - * - * @param message message to be displayed. - */ - static public void setConsoleMessage(String message) - { - System.out.println(message); - } - - /** - * Display a message with an option of saving it to a log. - * - * @param message message to be displayed (or logged) - * @param logMessage whether to log it - */ - static public void setConsoleMessage(String message, boolean logMessage) - { - System.out.println(message); - if (logMessage) - { - logged.add(message); - } - } - - /** - * Get user selection of a menu option. - * - * @param menuOptions available options - * @return user selection - */ - static public int getMenuOption(String[] menuOptions) - { - Scanner scan = new Scanner(System.in); - int option = -1; - while (option < 0 || option >= menuOptions.length) - { - System.out.println("Please select an option\n"); - option = -1; - while (++option < menuOptions.length) - { - System.out.printf("%d - " + menuOptions[option] + "\n", option); - } - option = scan.nextInt(); - } - return option; - } - - - // Console view below - static public String view_main() - { - View.ConsoleIO.setConsoleMessage("MAIN MENU"); - // list of options - String[] menuOptions = {"Create Study Profile", "View Study Profile", "View Notifications", "Quit Program"}; - int choice = View.ConsoleIO.getMenuOption(menuOptions); - - return menuOptions[choice]; - - } - - static public String view_createSP() - { - View.ConsoleIO.setConsoleMessage("CREATE A STUDY PROFILE"); - // list of options - String[] menuOptions = {"Load Study Profile File", "Return to Main Menu"}; - int choice = View.ConsoleIO.getMenuOption(menuOptions); - - return menuOptions[choice]; - - } - - static public String view_viewSP(StudyPlannerController SPC) - { - View.ConsoleIO.setConsoleMessage("VIEW A STUDY PROFILE"); - String[] studyProfiles = SPC.getPlanner().getListOfStudyProfileNames(); - int i = -1, ii = studyProfiles.length; - - if (ii < 1) - { - View.ConsoleIO.setConsoleMessage("No existing study profiles"); - } - - String[] menuOptions = new String[ii + 1]; - while (++i < ii) - { - menuOptions[i] = studyProfiles[i]; - } - menuOptions[ii] = "Return to Main Menu"; - - int m = -1; - while (m < ii) - { - m = View.ConsoleIO.getMenuOption(menuOptions); - if (m < ii) - { - - } - } - - return menuOptions[ii]; - } - - static public String view_loadSP(StudyPlannerController SPC) - { - View.ConsoleIO.setConsoleMessage("LOAD A STUDY PROFILE"); - - String filename = getDataString("Enter filepath:"); - File tempFile = new File(filename); - HubFile fileData = DataController.loadHubFile(tempFile); - while (!filename.equals("") && fileData == null) - { - filename = getDataString("File not valid, enter a different filepath:"); - tempFile = new File(filename); - fileData = DataController.loadHubFile(tempFile); - } - - System.out.println(fileData.toString(true)); - - return "Return to Main Menu"; - } -} diff --git a/src/View/GanttishDiagram.java b/src/View/GanttishDiagram.java deleted file mode 100644 index a6d2308f..00000000 --- a/src/View/GanttishDiagram.java +++ /dev/null @@ -1,462 +0,0 @@ -package View; - -import Model.Assignment; -import Model.StudyPlanner; -import Model.Task; - -import javax.imageio.ImageIO; -import java.awt.*; -import java.awt.image.BufferedImage; -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; - -/** - * Created by bendickson on 5/15/17. - */ -public class GanttishDiagram -{ - - static int width = 1920; - static int height = 1280; - - static int imageType = BufferedImage.TYPE_INT_ARGB; - - static int badgeSize = 128; - static int badgeRingSize = 112; - static int getBadgeRingThickness = 8; - static int fontSize = 24; - - - static int GANTT_TITLE_SPACE = 80; - static int GANTT_TITLE_FONT_SIZE = 64; - static int GANTT_COLUMN_WIDTH = 400; - static int GANTT_COLUMN_PADDING = 80; - static int GANTT_COLUMN_SPACING = 16; - static int GANTT_ENTRY_FONT_SIZE = 18; - static Paint GANTT_ENTRY_FONT_COLOR = Color.black; - static int GANTT_ENTRY_HEIGHT = 64; - static Paint GANTT_ENTRY_DEFAULT_COLOR = badgeColors.GREY.getPaint(); - static HashMap GANTT_ENTRY_COLOR = new HashMap<>(); - - static - { - GANTT_ENTRY_COLOR.put("Reading", badgeColors.PINK.getPaint()); - GANTT_ENTRY_COLOR.put("Exercises", badgeColors.ORANGE.getPaint()); - GANTT_ENTRY_COLOR.put("Revision", badgeColors.YELLOW.getPaint()); - GANTT_ENTRY_COLOR.put("Listening", badgeColors.GREEN.getPaint()); - GANTT_ENTRY_COLOR.put("Coursework", badgeColors.BLUE.getPaint()); - GANTT_ENTRY_COLOR.put("Meeting", badgeColors.PURPLE.getPaint()); - } - - static Paint GANTT_ENTRY_DEFAULT_FCOLOR = badgeColors.T_GREY.getPaint(); - static HashMap GANTT_ENTRY_FCOLOR = new HashMap<>(); - - static - { - GANTT_ENTRY_FCOLOR.put("Reading", badgeColors.T_PINK.getPaint()); - GANTT_ENTRY_FCOLOR.put("Exercises", badgeColors.T_ORANGE.getPaint()); - GANTT_ENTRY_FCOLOR.put("Revision", badgeColors.T_YELLOW.getPaint()); - GANTT_ENTRY_FCOLOR.put("Listening", badgeColors.T_GREEN.getPaint()); - GANTT_ENTRY_FCOLOR.put("Coursework", badgeColors.T_BLUE.getPaint()); - GANTT_ENTRY_FCOLOR.put("Meeting", badgeColors.T_PURPLE.getPaint()); - } - - - enum stroke - { - DASHED(true, 1), - NONE(false, 0), - SOLID(false, 1); - - double widthMult = 1.0; - boolean dashed = false; - - BasicStroke getStroke(int thickness) - { - if (widthMult == 0) - { - return new BasicStroke(0); - } else if (dashed) - { - float[] pat = {10.0f}; - return new BasicStroke((float) widthMult * 1.0f, - BasicStroke.CAP_BUTT, - BasicStroke.JOIN_MITER, - 10.0f, pat, 0.0f); - } else - { - return new BasicStroke((float) widthMult * (float) thickness); - } - } - - stroke(boolean cDashed, double cWidthMult) - { - dashed = cDashed; - widthMult = cWidthMult; - } - } - - enum badgeColors - { - FINISHED(0, 128, 255), - STARTED(64, 255, 0), - CANSTART(255, 128, 0), - CANNOTSTART(64, 64, 64), - - FINISHED_FILL(255, 255, 255), - STARTED_FILL(255, 255, 255), - CANSTART_FILL(192, 192, 192), - CANNOTSTART_FILL(128, 128, 128), - GREY(192, 192, 192), - PINK(255, 192, 192), - ORANGE(255, 128, 0), - BLUE(0, 128, 255), - GREEN(0, 255, 64), - PURPLE(64, 0, 255), - YELLOW(255, 64, 0), - T_GREY(192, 192, 192, 128), - T_PINK(255, 192, 192, 128), - T_ORANGE(255, 128, 0, 128), - T_BLUE(0, 128, 255, 128), - T_GREEN(0, 255, 64, 128), - T_PURPLE(64, 0, 255, 128), - T_YELLOW(255, 64, 0, 128); - - private int r; - private int g; - private int b; - private int a = 255; - - private int[] getColor() - { - int[] cols = new int[4]; - cols[0] = r; - cols[1] = g; - cols[2] = b; - cols[3] = a; - return cols; - } - - private Paint getPaint() - { - return new Color(r, g, b, a); - } - - badgeColors(int cr, int cg, int cb) - { - r = cr > 255 ? 255 : (cr < 0 ? 0 : cr); - g = cg > 255 ? 255 : (cg < 0 ? 0 : cg); - b = cb > 255 ? 255 : (cb < 0 ? 0 : cb); - } - - badgeColors(int cr, int cg, int cb, int ca) - { - this(cr, cg, cb); - a = ca > 255 ? 255 : (ca < 0 ? 0 : ca); - } - } - - /** - * Creates a GanttishDiagram from a given Assignment. - * - * @param fromStudyProfile StudyProfile - * @param fromAssignment Assignment for which to generate the GanttishDiagram. - * @return Generated diagram - */ - public static BufferedImage createGanttishDiagram(StudyPlanner fromStudyProfile, Assignment fromAssignment) - { - return createGanttishDiagram(fromStudyProfile, fromAssignment, ""); - } - - /** - * Creates a GanttishDiagram from a given Assignment and saves it to a file. - * - * @param fromStudyProfile StudyProfile - * @param fromAssignment Assignment for which to generate the GanttishDiagram. - * @param filePath file path - * @return Generated diagram - */ - public static BufferedImage createGanttishDiagram(StudyPlanner fromStudyProfile, Assignment fromAssignment, String filePath) - { - return createGanttishDiagram(fromStudyProfile, fromAssignment, filePath, false); - } - - /** - * Creates a GanttishDiagram from a given Assignment with the option to set the background transparent. - * - * @param fromStudyProfile StudyProfile - * @param fromAssignment Assignment for which to generate the GanttishDiagram. - * @param filePath file path - * @param transparentBackground whether the background should be white or transparent - * @return Generated diagram - */ - public static BufferedImage createGanttishDiagram(StudyPlanner fromStudyProfile, Assignment fromAssignment, - String filePath, boolean transparentBackground) - { - - HashMap> catTasks = new HashMap<>(); - - String COMPLETED = "Completed tasks"; - String STARTED = "Tasks started"; - String POSSIBLE = "Possible to start"; - String IMPOSSIBLE = "Not possible to start"; - - String[] categoryList = {COMPLETED, STARTED, POSSIBLE, IMPOSSIBLE}; - badgeColors[][] badges = {{badgeColors.FINISHED, badgeColors.FINISHED_FILL}, - {badgeColors.STARTED, badgeColors.STARTED_FILL}, - {badgeColors.CANSTART, badgeColors.CANSTART_FILL}, - {badgeColors.CANNOTSTART, badgeColors.CANNOTSTART_FILL}}; - - catTasks.put(COMPLETED, new ArrayList<>()); - catTasks.put(STARTED, new ArrayList<>()); - catTasks.put(POSSIBLE, new ArrayList<>()); - catTasks.put(IMPOSSIBLE, new ArrayList<>()); - - ArrayList assignmentTasks = fromAssignment.getTasks(); - - Iterator i = assignmentTasks.iterator(); - int longest = 0; - while (i.hasNext()) - { - Task t = i.next(); - String cat; - if (t.isCheckedComplete()) - { - cat = COMPLETED; - } else if (t.requirementsComplete() > 0) - { - cat = STARTED; - } else if (t.dependenciesComplete()) - { - cat = POSSIBLE; - } else - { - cat = IMPOSSIBLE; - } - catTasks.get(cat).add(t); - if (catTasks.get(cat).size() > longest) - { - longest = catTasks.get(cat).size(); - } - } - - int j = -1; - int jj = categoryList.length; - - int cWidth = GANTT_COLUMN_PADDING + jj * (GANTT_COLUMN_PADDING + GANTT_COLUMN_WIDTH); - int cHeight = 2 * GANTT_COLUMN_PADDING + GANTT_TITLE_SPACE + (1 + longest) * (GANTT_COLUMN_SPACING + GANTT_ENTRY_HEIGHT); - - BufferedImage r = new BufferedImage(cWidth, cHeight, imageType); - Graphics2D g2d = r.createGraphics(); - - if (!transparentBackground) - { - - g2d.setPaint(Color.white); - g2d.fillRect(0, 0, cWidth, cHeight); - } - - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - - g2d.setPaint(GANTT_ENTRY_FONT_COLOR); - drawMessage(g2d, GANTT_TITLE_FONT_SIZE, fromAssignment.getName(), GANTT_COLUMN_PADDING, GANTT_COLUMN_PADDING, - cWidth - (2 * GANTT_COLUMN_PADDING), GANTT_TITLE_SPACE); - while (++j < jj) - { - int ox = GANTT_COLUMN_PADDING + j * (GANTT_COLUMN_PADDING + GANTT_COLUMN_WIDTH); - int oy = GANTT_COLUMN_PADDING + GANTT_TITLE_SPACE; - - String name = categoryList[j]; - int k = 0; - int kk = catTasks.get(name).size() + 1; - - - g2d.setPaint(GANTT_ENTRY_FONT_COLOR); - drawMessage(g2d, GANTT_ENTRY_FONT_SIZE, name, ox, oy, GANTT_COLUMN_WIDTH, GANTT_ENTRY_HEIGHT); - - while (++k < kk) - { - Task dt = catTasks.get(name).get(k - 1); - String dtt = dt.getType().getName(); - Paint col, fcol; - if (GANTT_ENTRY_COLOR.containsKey(dtt)) - { - fcol = (GANTT_ENTRY_FCOLOR.get(dtt)); - col = (GANTT_ENTRY_COLOR.get(dtt)); - } else - { - fcol = (GANTT_ENTRY_DEFAULT_FCOLOR); - col = (GANTT_ENTRY_DEFAULT_COLOR); - } - - int oyk = oy + k * (GANTT_COLUMN_SPACING + GANTT_ENTRY_HEIGHT); - - g2d.setPaint(fcol); - g2d.fillRoundRect(ox, oyk, GANTT_COLUMN_WIDTH, GANTT_ENTRY_HEIGHT, 15, 15); - - g2d.setPaint(col); - g2d.drawRoundRect(ox, oyk, GANTT_COLUMN_WIDTH, GANTT_ENTRY_HEIGHT, 15, 15); - - g2d.setPaint(GANTT_ENTRY_FONT_COLOR); - drawMessage(g2d, GANTT_ENTRY_FONT_SIZE, dt.getName(), ox, oyk, GANTT_COLUMN_WIDTH - - GANTT_ENTRY_HEIGHT, GANTT_ENTRY_HEIGHT); - int percentage; - if (dt.requirementCount() <= 0) - { - percentage = dt.isCheckedComplete() ? 100 : 0; - } else - { - percentage = (100 * dt.requirementsComplete()) / dt.requirementCount(); - } - BufferedImage badge = getBadge(percentage, dt.dependenciesComplete(), 0.5); - - g2d.drawImage(badge, ox + GANTT_COLUMN_WIDTH - - GANTT_ENTRY_HEIGHT, oyk, null); - } - } - - - if (!filePath.equals("")) - { - try - { - File outputfile = new File(filePath); - ImageIO.write(r, "png", outputfile); - } catch (Exception e) - { - System.out.println(e.getMessage()); - } - } - return r; - } - - /** - * Generates a Badge with the given progress. - * - * @param progress integer representation of the progress. - * @param canStart true - default, false - greyed out. - * @return generated Badge - */ - public static BufferedImage getBadge(int progress, boolean canStart) - { - return getBadge(progress, canStart, 1.0f); - } - - /** - * Generates a Badge with the given progress. - * - * @param progress integer representation of the progress. - * @param canStart true - default, false - greyed out. - * @param multiplier size multiplier. - * @return generated Badge - */ - public static BufferedImage getBadge(int progress, boolean canStart, double multiplier) - { - int canvasSize = (int) ((double) badgeSize * multiplier + .5); - int ringSize = (int) ((double) badgeRingSize * multiplier + .5); - int badgeRingThickness = (int) ((double) getBadgeRingThickness * multiplier + .5); - int badgeFontSize = (int) ((double) fontSize * multiplier + .5); - - int ovalOffset = (canvasSize - ringSize) / 2; - - - BufferedImage r = new BufferedImage(canvasSize, canvasSize, imageType); - Graphics2D g2d = r.createGraphics(); - g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - - Paint ringColor; - Paint fillColor; - int arc = 100; - - if (!canStart) - { - g2d.setStroke(stroke.DASHED.getStroke(badgeRingThickness)); - ringColor = badgeColors.CANNOTSTART.getPaint(); - fillColor = badgeColors.CANNOTSTART_FILL.getPaint(); - } else if (progress <= 0) - { - g2d.setStroke(stroke.DASHED.getStroke(badgeRingThickness)); - ringColor = badgeColors.CANSTART.getPaint(); - fillColor = badgeColors.CANSTART_FILL.getPaint(); - } else if (progress < 100) - { - g2d.setStroke(stroke.SOLID.getStroke(badgeRingThickness)); - ringColor = badgeColors.STARTED.getPaint(); - fillColor = badgeColors.STARTED_FILL.getPaint(); - arc = progress; - } else - { - g2d.setStroke(stroke.SOLID.getStroke(badgeRingThickness)); - ringColor = badgeColors.FINISHED.getPaint(); - fillColor = badgeColors.FINISHED_FILL.getPaint(); - } - - g2d.setPaint(fillColor); - g2d.fillOval(ovalOffset, ovalOffset, ringSize, ringSize); - - Font font = new Font("Helvetica", Font.PLAIN, badgeFontSize); - FontMetrics metrics = g2d.getFontMetrics(font); - - String msg = progress + "%"; - - int msgX = ovalOffset + (ringSize - metrics.stringWidth(msg)) / 2; - int msgY = ovalOffset + (ringSize - metrics.getHeight()) / 2 + metrics.getAscent(); - - - if (arc == 100) - { - g2d.setPaint(ringColor); - g2d.drawOval(ovalOffset, ovalOffset, ringSize, ringSize); - } else - { - g2d.setPaint(ringColor); - int startAngle = 90; - int endAngle = -((int) (((float) arc / 100f) * 360)); - g2d.drawArc(ovalOffset, ovalOffset, ringSize, ringSize, startAngle, endAngle); - } - - - g2d.setFont(font); - g2d.drawString(msg, msgX, msgY); - - -/* try { - File outputfile = new File("badge_"+progress+".png"); - ImageIO.write(r, "png", outputfile); - } - catch(Exception e) - { - System.out.println(e.getMessage()); - }*/ - - return r; - } - - /** - * Generates a rectangle with the given message in the middle. - * - * @param g2d - * @param fontSize - * @param msg - * @param c_x - * @param c_y - * @param c_width - * @param c_height - */ - static void drawMessage(Graphics2D g2d, int fontSize, String msg, int c_x, int c_y, int c_width, int c_height) - { - - Font font = new Font("Helvetica", Font.PLAIN, fontSize); - FontMetrics metrics = g2d.getFontMetrics(font); - - - int msgX = c_x + (c_width - metrics.stringWidth(msg)) / 2; - int msgY = c_y + (c_height - metrics.getHeight()) / 2 + metrics.getAscent(); - - - g2d.setFont(font); - g2d.drawString(msg, msgX, msgY); - } -} diff --git a/src/View/Main.java b/src/View/Main.java deleted file mode 100644 index 4c3f1056..00000000 --- a/src/View/Main.java +++ /dev/null @@ -1,25 +0,0 @@ -package View; - -import Controller.MainController; -import javafx.application.Application; -import javafx.stage.Stage; - -public class Main extends Application -{ - @Override - public void start(Stage primaryStage) throws Exception - { - // Initialize the StudyPlanner: - MainController.initialise(); - // If initialization passed, call the Main menu: - MainController.main(); - //UIManager.areYouFeelingLucky(); - } - - public static void main(String[] args) - { - launch(args); - // Upon exit, save the StudyPlanner: - MainController.save(); - } -} \ No newline at end of file diff --git a/src/View/MainMenu.fxml b/src/View/MainMenu.fxml deleted file mode 100644 index ed4d7dff..00000000 --- a/src/View/MainMenu.fxml +++ /dev/null @@ -1,195 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/View/Task.fxml b/src/View/Task.fxml deleted file mode 100644 index 840a5f4c..00000000 --- a/src/View/Task.fxml +++ /dev/null @@ -1,174 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/View/UIManager.java b/src/View/UIManager.java deleted file mode 100644 index f149dcaa..00000000 --- a/src/View/UIManager.java +++ /dev/null @@ -1,635 +0,0 @@ -package View; - -import Controller.*; -import Model.*; -import javafx.application.Platform; -import javafx.embed.swing.SwingFXUtils; -import javafx.fxml.FXMLLoader; -import javafx.geometry.Insets; -import javafx.scene.Parent; -import javafx.scene.Scene; -import javafx.scene.SceneAntialiasing; -import javafx.scene.control.Alert; -import javafx.scene.control.Button; -import javafx.scene.control.ButtonType; -import javafx.scene.control.Label; -import javafx.scene.image.Image; -import javafx.scene.layout.*; -import javafx.stage.FileChooser; -import javafx.stage.Modality; -import javafx.stage.Stage; -import jfxtras.scene.control.agenda.Agenda; - -import java.awt.image.BufferedImage; -import java.io.File; -import java.io.IOException; -import java.time.LocalDateTime; -import java.time.ZoneId; -import java.util.ArrayList; - -/** - * Created by Zilvinas on 04/05/2017. - */ -public class UIManager -{ - private static Stage mainStage = new Stage(); - private static MenuController mc = new MenuController(); - - /** - * Displays a 'Create Account' window and handles the creation of - * a new Account object - * - * @return newly created Account - * @throws Exception - */ - public Account createAccount() throws Exception - { - AccountController accountControl = new AccountController(); - System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Hello World!"); - - // Load in the .fxml file: - FXMLLoader loader = new FXMLLoader(getClass().getResource("/View/CreateAccount.fxml")); - loader.setController(accountControl); - Parent root = loader.load(); - - // Set the scene: - Stage stage = new Stage(); - stage.setScene(new Scene(root, 550, 232)); - stage.setTitle("Create Account"); - stage.resizableProperty().setValue(false); - stage.getIcons().add(new Image("file:icon.png")); - stage.showAndWait(); - - // Handle creation of the Account object: - if (accountControl.isSuccess()) - { - Account newAccount = accountControl.getAccount(); - return newAccount; - } else - throw new Exception("User quit."); - - } - - /** - * Displays the main menu - * - * @throws Exception - */ - public void mainMenu() throws Exception - { - // Load in the .fxml file: - System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Hello World!"); - - FXMLLoader loader = new FXMLLoader(getClass().getResource("/View/MainMenu.fxml")); - loader.setController(UIManager.mc); - Parent root = loader.load(); - - // Set the scene: - mainStage.setScene(new Scene(root, 1000, 750, true, SceneAntialiasing.BALANCED)); - mainStage.setTitle("PearPlanner"); - mainStage.getIcons().add(new Image("file:icon.png")); - mainStage.showAndWait(); - } - - /** - * Display the 'Add Activity' window - * - * @return newly created Activity - */ - public Activity addActivity() throws Exception - { - ActivityController ac = new ActivityController(); - - // Load in the .fxml file: - FXMLLoader loader = new FXMLLoader(getClass().getResource("/View/Activity.fxml")); - loader.setController(ac); - Parent root = loader.load(); - - // Set the scene: - Stage stage = new Stage(); - stage.initModality(Modality.APPLICATION_MODAL); - stage.setScene(new Scene(root, 550, 358)); - stage.setTitle("New Activity"); - stage.resizableProperty().setValue(false); - stage.getIcons().add(new Image("file:icon.png")); - stage.showAndWait(); - - // Add the Activity to the StudyPlanner - if (ac.isSuccess()) - return ac.getActivity(); - return null; - } - - /** - * Displays the Activity details page - * - * @param activity Activity for which the details should be displayed. - */ - public void activityDetails(Activity activity) throws IOException - { - ActivityController ac = new ActivityController(activity); - - // Load in the .fxml file: - FXMLLoader loader = new FXMLLoader(getClass().getResource("/View/Activity.fxml")); - loader.setController(ac); - Parent root = loader.load(); - - // Set the scene: - Stage stage = new Stage(); - stage.initModality(Modality.APPLICATION_MODAL); - stage.setScene(new Scene(root, 550, 358)); - stage.setTitle("Activity"); - stage.resizableProperty().setValue(false); - stage.getIcons().add(new Image("file:icon.png")); - stage.showAndWait(); - } - - /** - * Displays the 'Add Milestone' window. - * - * @return newly created Milestone object. - */ - public Milestone addMilestone() throws IOException - { - MilestoneController mc = new MilestoneController(); - - // Load in the .fxml file: - FXMLLoader loader = new FXMLLoader(getClass().getResource("/View/Milestone.fxml")); - loader.setController(mc); - Parent root = loader.load(); - - // Set the scene: - Stage stage = new Stage(); - stage.initModality(Modality.APPLICATION_MODAL); - stage.setScene(new Scene(root, 550, 355)); - stage.setTitle("Milestone"); - stage.resizableProperty().setValue(false); - stage.getIcons().add(new Image("file:icon.png")); - stage.showAndWait(); - - // Add the Milestone to the StudyPlanner - if (mc.isSuccess()) - return mc.getMilestone(); - return null; - } - - /** - * Displays the Milestone details page - * - * @param milestone Milestone for which the details should be shown. - */ - public void milestoneDetails(Milestone milestone) throws IOException - { - MilestoneController mc = new MilestoneController(milestone); - - // Load in the .fxml file: - FXMLLoader loader = new FXMLLoader(getClass().getResource("/View/Milestone.fxml")); - loader.setController(mc); - Parent root = loader.load(); - - // Set the scene: - Stage stage = new Stage(); - stage.initModality(Modality.APPLICATION_MODAL); - stage.setScene(new Scene(root, 550, 355)); - stage.setTitle("Milestone"); - stage.resizableProperty().setValue(false); - stage.getIcons().add(new Image("file:icon.png")); - stage.showAndWait(); - } - - /** - * Displays the StudyProfile details page - * - * @param profile StudyProfile for which the details should be shown. - */ - public void studyProfileDetails(StudyProfile profile) throws IOException - { - StudyProfileController spc = new StudyProfileController(profile); - - // Load in the .fxml file: - FXMLLoader loader = new FXMLLoader(getClass().getResource("/View/StudyProfile.fxml")); - loader.setController(spc); - Parent root = loader.load(); - - // Set the scene: - Stage stage = new Stage(); - stage.initModality(Modality.APPLICATION_MODAL); - stage.setScene(new Scene(root, 550, 232)); - stage.setTitle(profile.getName()); - stage.resizableProperty().setValue(false); - stage.getIcons().add(new Image("file:icon.png")); - stage.showAndWait(); - } - - /** - * Displays the Module details page - * - * @param module Module for which the details should be shown. - * @param current Window from which this method is called. - * @throws IOException - */ - public void moduleDetails(Module module, MenuController.Window current) throws IOException - { - UIManager.mc.loadModule(module, current, null); - } - - /** - * Displays the Module details page - * - * @param module Module for which the details should be shown. - * @param current Window from which this method is called. - * @throws IOException - */ - public void moduleDetails(Module module, ModelEntity current) throws IOException - { - UIManager.mc.loadModule(module, MenuController.Window.Empty, current); - } - - /** - * Displays the Assignment details page - * - * @param assignment Assignment for which the details should be shown. - * @param current Window from which this method is called. - * @throws IOException - */ - public void assignmentDetails(Assignment assignment, MenuController.Window current) throws IOException - { - UIManager.mc.loadAssignment(assignment, current, null); - } - - /** - * Displays the Assignment details page - * - * @param assignment Assignment for which the details should be shown. - * @param current Window from which this method is called. - * @throws IOException - */ - public void assignmentDetails(Assignment assignment, ModelEntity current) throws IOException - { - UIManager.mc.loadAssignment(assignment, MenuController.Window.Empty, current); - } - - /** - * Creates a window for adding a new Task - * - * @return newly created Task - */ - public Task addTask() throws Exception - { - TaskController tc = new TaskController(); - - // Load in the .fxml file: - FXMLLoader loader = new FXMLLoader(getClass().getResource("/View/Task.fxml")); - loader.setController(tc); - Parent root = loader.load(); - - // Set the scene: - Stage stage = new Stage(); - stage.initModality(Modality.APPLICATION_MODAL); - stage.setScene(new Scene(root, 550, 558)); - stage.setTitle("New Task"); - stage.resizableProperty().setValue(false); - stage.getIcons().add(new Image("file:icon.png")); - stage.showAndWait(); - - // Handle creation of the Account object: - if (tc.isSuccess()) - return tc.getTask(); - return null; - } - - /** - * Displays the Task details page - * - * @param task for which the details should be displayed. - * @throws IOException - */ - public void taskDetails(Task task) throws IOException - { - TaskController tc = new TaskController(task); - - // Load in the .fxml file: - FXMLLoader loader = new FXMLLoader(getClass().getResource("/View/Task.fxml")); - loader.setController(tc); - Parent root = loader.load(); - - // Set the scene: - Stage stage = new Stage(); - stage.initModality(Modality.APPLICATION_MODAL); - stage.setScene(new Scene(root, 550, 558)); - stage.setTitle("Task"); - stage.resizableProperty().setValue(false); - stage.getIcons().add(new Image("file:icon.png")); - stage.showAndWait(); - } - - /** - * Creates a window for adding a new Requirement - * - * @return newly created Requirement - */ - public Requirement addRequirement() throws Exception - { - RequirementController rc = new RequirementController(); - - // Load in the .fxml file: - FXMLLoader loader = new FXMLLoader(getClass().getResource("/View/Requirement.fxml")); - loader.setController(rc); - Parent root = loader.load(); - - // Set the scene: - Stage stage = new Stage(); - stage.initModality(Modality.APPLICATION_MODAL); - stage.setScene(new Scene(root, 550, 260)); - stage.setTitle("New Requirement"); - stage.resizableProperty().setValue(false); - stage.getIcons().add(new Image("file:icon.png")); - stage.showAndWait(); - - // Handle creation of the Account object: - if (rc.isSuccess()) - return rc.getRequirement(); - return null; - } - - /** - * Displays the Requirement details page - * - * @param requirement Requirement for which the details should be displayed - */ - public void requirementDetails(Requirement requirement) throws IOException - { - RequirementController rc = new RequirementController(requirement); - - // Load in the .fxml file: - FXMLLoader loader = new FXMLLoader(getClass().getResource("/View/Requirement.fxml")); - loader.setController(rc); - Parent root = loader.load(); - - // Set the scene: - Stage stage = new Stage(); - stage.initModality(Modality.APPLICATION_MODAL); - stage.setScene(new Scene(root, 550, 558)); - stage.setTitle("Requirement"); - stage.resizableProperty().setValue(false); - stage.getIcons().add(new Image("file:icon.png")); - stage.showAndWait(); - } - - /** - * Displays a GanttishDiagram window for the given Assignment. - * - * @param assignment Assignment for which to generate the GanttishDiagram. - */ - public void showGantt(Assignment assignment) - { - Stage stage = new Stage(); - - // Layout: - VBox layout = new VBox(); - layout.setSpacing(10); - layout.setPadding(new Insets(15)); - layout.getStylesheets().add("/Content/stylesheet.css"); - // ================= - - // Nav bar: - HBox nav = new HBox(); - nav.setSpacing(15.0); - // ================= - - // Title: - Label title = new Label("Ganttish Diagram"); - title.getStyleClass().add("title"); - HBox x = new HBox(); - HBox.setHgrow(x, Priority.ALWAYS); - // ================= - - // Buttons: - Button save = new Button("Save"); - save.setOnAction(e -> { - String path = this.saveFileDialog(stage); - GanttishDiagram.createGanttishDiagram(MainController.getSPC().getPlanner(), assignment, path); - }); - Button close = new Button("Close"); - close.setOnAction(e -> ((Stage) close.getScene().getWindow()).close()); - // ================= - - nav.getChildren().addAll(title, x, save, close); - - // Content: - BufferedImage gantt = GanttishDiagram.createGanttishDiagram(MainController.getSPC().getPlanner(), assignment); - Image image = SwingFXUtils.toFXImage(gantt, null); - Pane content = new Pane(); - VBox.setVgrow(content, Priority.ALWAYS); - content.setBackground(new Background( - new BackgroundImage(image, BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, BackgroundPosition.DEFAULT, - new BackgroundSize(BackgroundSize.AUTO, BackgroundSize.AUTO, false, false, true, false)))); - // ================= - - layout.getChildren().addAll(nav, content); - - // Set the scene: - stage.setScene(new Scene(layout, 1300, 800, true, SceneAntialiasing.BALANCED)); - stage.setTitle("Ganttish Diagram"); - stage.resizableProperty().setValue(true); - stage.getIcons().add(new Image("file:icon.png")); - stage.setFullScreen(true); - stage.showAndWait(); - // ================= - } - - /** - * Displays a Calendar window with events of this Study Profile. - */ - public void showCalendar() - { - Stage stage = new Stage(); - - // Layout: - VBox layout = new VBox(); - layout.setSpacing(10); - layout.setPadding(new Insets(15)); - layout.getStylesheets().add("/Content/stylesheet.css"); - // ================= - - // Nav bar: - HBox nav = new HBox(); - nav.setSpacing(15.0); - // ================= - - // Title: - Label title = new Label("Calendar"); - title.getStyleClass().add("title"); - HBox x = new HBox(); - HBox.setHgrow(x, Priority.ALWAYS); - // ================= - - // Buttons: - Button save = new Button("Export"); - save.setOnAction(e -> { - // Not implemented yet. - }); - Button agendaFwd = new Button(">"); - Button agendaBwd = new Button("<"); - // ================= - - nav.getChildren().addAll(title, x, agendaBwd, agendaFwd); - - // Content: - Agenda content = new Agenda(); - VBox.setVgrow(content, Priority.ALWAYS); - content.setAllowDragging(false); - content.setAllowResize(false); - content.autosize(); - content.setActionCallback(param -> null); - content.setEditAppointmentCallback(param -> null); - // Agenda buttons: - agendaBwd.setOnMouseClicked(event -> content.setDisplayedLocalDateTime(content.getDisplayedLocalDateTime().minusDays(7))); - agendaFwd.setOnMouseClicked(event -> content.setDisplayedLocalDateTime(content.getDisplayedLocalDateTime().plusDays(7))); - // ================= - - // Populate Agenda: - ArrayList calendar = MainController.getSPC().getPlanner().getCurrentStudyProfile().getCalendar(); - for (Event e : calendar) - { - if (e instanceof TimetableEvent) - { - LocalDateTime sTime = LocalDateTime.ofInstant(e.getDate().toInstant(), ZoneId.systemDefault()); - content.appointments().addAll( - new Agenda.AppointmentImplLocal() - .withStartLocalDateTime(sTime) - .withEndLocalDateTime(sTime.plusMinutes(((TimetableEvent) e).getDuration())) - - .withSummary(e.getName() + "\n" + "@ " + ((TimetableEvent) e).getRoom().getLocation()) - .withAppointmentGroup(new Agenda.AppointmentGroupImpl().withStyleClass("group5")) - ); - } else if (e instanceof ExamEvent) - { - LocalDateTime sTime = LocalDateTime.ofInstant(e.getDate().toInstant(), ZoneId.systemDefault()); - content.appointments().addAll( - new Agenda.AppointmentImplLocal() - .withStartLocalDateTime(sTime) - .withSummary(e.getName() + "\n" + "@ " + ((ExamEvent) e).getRoom().getLocation()) - .withEndLocalDateTime(sTime.plusMinutes(((ExamEvent) e).getDuration())) - .withAppointmentGroup(new Agenda.AppointmentGroupImpl().withStyleClass("group20")) - ); - } else if (e instanceof Deadline) - { - LocalDateTime sTime = LocalDateTime.ofInstant(e.getDate().toInstant(), ZoneId.systemDefault()); - content.appointments().addAll( - new Agenda.AppointmentImplLocal() - .withStartLocalDateTime(sTime.minusMinutes(60)) - .withSummary(e.getName()) - .withEndLocalDateTime(sTime) - .withAppointmentGroup(new Agenda.AppointmentGroupImpl().withStyleClass("group1")) - ); - } else - { - LocalDateTime sTime = LocalDateTime.ofInstant(e.getDate().toInstant(), ZoneId.systemDefault()); - content.appointments().addAll( - new Agenda.AppointmentImplLocal() - .withStartLocalDateTime(sTime) - .withSummary(e.getName()) - .withEndLocalDateTime(sTime.plusMinutes(60)) - .withAppointmentGroup(new Agenda.AppointmentGroupImpl().withStyleClass("group3")) - ); - } - } - // ================= - - // ================= - - layout.getChildren().addAll(nav, content); - - // Set the scene: - stage.setScene(new Scene(layout, 1300, 800)); - stage.setTitle("Calendar"); - stage.resizableProperty().setValue(true); - stage.getIcons().add(new Image("file:icon.png")); - - Platform.runLater(() -> content.setDisplayedLocalDateTime(content.getDisplayedLocalDateTime().plusMinutes(1050))); - stage.showAndWait(); - // ================= - } - - /** - * Displays a file dialog for importing .xml files - * - * @return a File object - */ - public File loadFileDialog() - { - FileChooser fileChooser = new FileChooser(); - fileChooser.setTitle("Select a HUB file"); - fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("XML file", "*.xml")); - File file = fileChooser.showOpenDialog(mainStage); - return file; - } - - /** - * Displays a file dialog for saving an .jpeg file - * - * @return String path - */ - public String saveFileDialog(Stage stage) - { - FileChooser fileChooser = new FileChooser(); - fileChooser.setTitle("Save Ganttish Diagram"); - fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("PNG file", "*.png")); - File path = fileChooser.showSaveDialog(stage); - return path.getAbsolutePath(); - } - - /** - * Displays an error message - * - * @param message to be displayed - */ - public static void reportError(String message) - { - Alert alert = new Alert(Alert.AlertType.ERROR, message); - alert.showAndWait(); - } - - /** - * Reports that an action was successful and displays a message - * - * @param message to be displayed - */ - public static void reportSuccess(String message) - { - Alert alert = new Alert(Alert.AlertType.INFORMATION, message); - alert.showAndWait(); - } - - /** - * Reports that an action was successful - */ - public static void reportSuccess() - { - Alert alert = new Alert(Alert.AlertType.INFORMATION, "Success!"); - alert.showAndWait(); - } - - /** - * Confirm box with 'Yes' or 'No' as available options - * - * @param message message to be displayed - * @return true for yes, false for no. - */ - public static boolean confirm(String message) - { - Alert alert = new Alert(Alert.AlertType.CONFIRMATION, message, ButtonType.YES, ButtonType.NO); - alert.showAndWait(); - return alert.getResult().equals(ButtonType.YES); - } - - /** - * Please don't use - */ - public static void areYouFeelingLucky() - { - while (UIManager.confirm("Are you feeling lucky?") == (Math.random() < 0.5)) ; - } - -} \ No newline at end of file diff --git a/src/edu/wright/cs/raiderplanner/content/DashBoardHelp.png b/src/edu/wright/cs/raiderplanner/content/DashBoardHelp.png new file mode 100644 index 00000000..99df742e Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/DashBoardHelp.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/IMAGE-CREDITS.md b/src/edu/wright/cs/raiderplanner/content/IMAGE-CREDITS.md new file mode 100644 index 00000000..58545e6c --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/content/IMAGE-CREDITS.md @@ -0,0 +1,11 @@ +IMAGE CREDITS: +MainMenu.fxml: +settings_icon.png - GIMP image, created by Clayton D. Terrill + +Settings.fxml: +settings_about.png - GIMP image, created by Clayton D. Terrill +settings_account.png - GIMP image, created by Clayton D. Terrill +settings_general.png - GIMP image, created by Clayton D. Terrill +settings_goBack.png - GIMP image, created by Clayton D. Terrill +settings_theme.png - GIMP image, created by Clayton D. Terrill +information.png - https://www.flaticon.com/free-icon/info_131917#term=information&page=1&position=10 \ No newline at end of file diff --git a/src/edu/wright/cs/raiderplanner/content/addactivity.png b/src/edu/wright/cs/raiderplanner/content/addactivity.png new file mode 100644 index 00000000..f1547cee Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/addactivity.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/addactivity_small.png b/src/edu/wright/cs/raiderplanner/content/addactivity_small.png new file mode 100644 index 00000000..71a997de Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/addactivity_small.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/back.png b/src/edu/wright/cs/raiderplanner/content/back.png new file mode 100644 index 00000000..6f17201c Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/back.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/banner.png b/src/edu/wright/cs/raiderplanner/content/banner.png new file mode 100644 index 00000000..16155853 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/banner.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/calendar.png b/src/edu/wright/cs/raiderplanner/content/calendar.png new file mode 100644 index 00000000..abc93db3 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/calendar.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/chat.png b/src/edu/wright/cs/raiderplanner/content/chat.png new file mode 100644 index 00000000..68f58268 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/chat.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/close.png b/src/edu/wright/cs/raiderplanner/content/close.png new file mode 100644 index 00000000..d070d9c7 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/close.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/close_small.png b/src/edu/wright/cs/raiderplanner/content/close_small.png new file mode 100644 index 00000000..e21526e0 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/close_small.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/help.png b/src/edu/wright/cs/raiderplanner/content/help.png new file mode 100644 index 00000000..49722d46 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/help.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/importhubfile.png b/src/edu/wright/cs/raiderplanner/content/importhubfile.png new file mode 100644 index 00000000..8ab2cd90 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/importhubfile.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/importhubfile_small.png b/src/edu/wright/cs/raiderplanner/content/importhubfile_small.png new file mode 100644 index 00000000..e6ceef08 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/importhubfile_small.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/information.png b/src/edu/wright/cs/raiderplanner/content/information.png new file mode 100644 index 00000000..155ca984 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/information.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/line.png b/src/edu/wright/cs/raiderplanner/content/line.png new file mode 100644 index 00000000..dddf0596 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/line.png differ diff --git a/src/Content/menu.png b/src/edu/wright/cs/raiderplanner/content/menu.png similarity index 100% rename from src/Content/menu.png rename to src/edu/wright/cs/raiderplanner/content/menu.png diff --git a/src/edu/wright/cs/raiderplanner/content/milestones.png b/src/edu/wright/cs/raiderplanner/content/milestones.png new file mode 100644 index 00000000..17cea001 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/milestones.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/module.png b/src/edu/wright/cs/raiderplanner/content/module.png new file mode 100644 index 00000000..b0741a6f Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/module.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/notification0.png b/src/edu/wright/cs/raiderplanner/content/notification0.png new file mode 100644 index 00000000..c1ef77af Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/notification0.png differ diff --git a/src/Content/notification1.png b/src/edu/wright/cs/raiderplanner/content/notification1.png similarity index 100% rename from src/Content/notification1.png rename to src/edu/wright/cs/raiderplanner/content/notification1.png diff --git a/src/edu/wright/cs/raiderplanner/content/print.png b/src/edu/wright/cs/raiderplanner/content/print.png new file mode 100644 index 00000000..81e36b05 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/print.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/settings_about.png b/src/edu/wright/cs/raiderplanner/content/settings_about.png new file mode 100644 index 00000000..e2c60260 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/settings_about.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/settings_account.png b/src/edu/wright/cs/raiderplanner/content/settings_account.png new file mode 100644 index 00000000..a89121bd Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/settings_account.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/settings_general.png b/src/edu/wright/cs/raiderplanner/content/settings_general.png new file mode 100644 index 00000000..3eb01399 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/settings_general.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/settings_goBack.png b/src/edu/wright/cs/raiderplanner/content/settings_goBack.png new file mode 100644 index 00000000..6ebd1e56 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/settings_goBack.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/settings_icon.png b/src/edu/wright/cs/raiderplanner/content/settings_icon.png new file mode 100644 index 00000000..ef3c4003 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/settings_icon.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/settings_theme.png b/src/edu/wright/cs/raiderplanner/content/settings_theme.png new file mode 100644 index 00000000..e65e276a Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/settings_theme.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/studydashboard.png b/src/edu/wright/cs/raiderplanner/content/studydashboard.png new file mode 100644 index 00000000..ef62127f Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/studydashboard.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/studyprofiles.png b/src/edu/wright/cs/raiderplanner/content/studyprofiles.png new file mode 100644 index 00000000..1dea9f16 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/studyprofiles.png differ diff --git a/src/edu/wright/cs/raiderplanner/content/studyprofiles_small.png b/src/edu/wright/cs/raiderplanner/content/studyprofiles_small.png new file mode 100644 index 00000000..bac59cab Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/studyprofiles_small.png differ diff --git a/src/Content/stylesheet.css b/src/edu/wright/cs/raiderplanner/content/stylesheet.css similarity index 57% rename from src/Content/stylesheet.css rename to src/edu/wright/cs/raiderplanner/content/stylesheet.css index 5e2be5de..3c586a63 100644 --- a/src/Content/stylesheet.css +++ b/src/edu/wright/cs/raiderplanner/content/stylesheet.css @@ -5,20 +5,21 @@ } .unread-button { - -fx-background-image: url('/Content/notification1.png'); + -fx-background-image: url('/edu/wright/cs/raiderplanner/content/notification1.png'); } .read-button { - -fx-background-image: url('/Content/notification0.png'); + -fx-background-image: url('/edu/wright/cs/raiderplanner/content/notification0.png'); } .set-button { - -fx-base: greenyellow; + -fx-base: #4aea95; } .back-button { - -fx-background-image: url("/Content/back.png"); + -fx-background-image: url("/edu/wright/cs/raiderplanner/content/back.png"); -fx-min-width: 30; + -fx-background-color: transparent; } .notificationItem-title { @@ -58,5 +59,11 @@ } .current-item { - -fx-background-color: greenyellow; -} \ No newline at end of file + -fx-background-color: #4aea95; +} + +.print-button { + -fx-background-image: url("/edu/wright/cs/raiderplanner/content/print.png"); + -fx-min-width: 30; + -fx-background-color: transparent; +} .delete-button { -fx-base: #FF0000;} diff --git a/src/edu/wright/cs/raiderplanner/content/xcf/settings_about.xcf b/src/edu/wright/cs/raiderplanner/content/xcf/settings_about.xcf new file mode 100644 index 00000000..bbd90d21 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/xcf/settings_about.xcf differ diff --git a/src/edu/wright/cs/raiderplanner/content/xcf/settings_account.xcf b/src/edu/wright/cs/raiderplanner/content/xcf/settings_account.xcf new file mode 100644 index 00000000..3ba3c89f Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/xcf/settings_account.xcf differ diff --git a/src/edu/wright/cs/raiderplanner/content/xcf/settings_general.xcf b/src/edu/wright/cs/raiderplanner/content/xcf/settings_general.xcf new file mode 100644 index 00000000..3f25f26e Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/xcf/settings_general.xcf differ diff --git a/src/edu/wright/cs/raiderplanner/content/xcf/settings_goBack.xcf b/src/edu/wright/cs/raiderplanner/content/xcf/settings_goBack.xcf new file mode 100644 index 00000000..d7e52363 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/xcf/settings_goBack.xcf differ diff --git a/src/edu/wright/cs/raiderplanner/content/xcf/settings_icon.xcf b/src/edu/wright/cs/raiderplanner/content/xcf/settings_icon.xcf new file mode 100644 index 00000000..605f88c9 Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/xcf/settings_icon.xcf differ diff --git a/src/edu/wright/cs/raiderplanner/content/xcf/settings_theme.xcf b/src/edu/wright/cs/raiderplanner/content/xcf/settings_theme.xcf new file mode 100644 index 00000000..286fcabc Binary files /dev/null and b/src/edu/wright/cs/raiderplanner/content/xcf/settings_theme.xcf differ diff --git a/src/edu/wright/cs/raiderplanner/controller/AccountController.java b/src/edu/wright/cs/raiderplanner/controller/AccountController.java new file mode 100644 index 00000000..982c2b92 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/controller/AccountController.java @@ -0,0 +1,240 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar, Alena Brand + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import edu.wright.cs.raiderplanner.model.Account; +import edu.wright.cs.raiderplanner.model.Person; +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.Button; +import javafx.scene.control.ButtonType; +import javafx.scene.control.CheckBox; +import javafx.scene.control.ComboBox; +import javafx.scene.control.TextField; +import javafx.scene.layout.GridPane; +import javafx.stage.Stage; + +import java.net.URL; +import java.util.Optional; +import java.util.ResourceBundle; + +/** + * Handle actions associated with the GUI window for creating new accounts. + * This includes validating the data contained in the various text fields, + * retrieving the validated data, and storing the submitted data to the proper + * objects. + * + * @author Zilvinas Ceikauskas + */ +public class AccountController implements Initializable { + @FXML private TextField accountNo; + @FXML private ComboBox salutation; + @FXML private TextField fullName; + @FXML private TextField email; + @FXML private CheckBox famLast; + @FXML private Button submit; + @FXML private GridPane pane; + @FXML private Alert invalidInputAlert = new Alert(AlertType.ERROR); + @FXML private Alert emptyNameAlert = new Alert(AlertType.CONFIRMATION); + + private Account account; + private boolean success = false; + + /** + * Getter for Account. + * + * @return the Account object being managed by this controller. + */ + public Account getAccount() { + return account; + } + + /** + * Getter for Success. + * + * @return true if the last submit operation succeeded, false otherwise. + */ + public boolean isSuccess() { + return success; + } + + /** + * Determines if the user has entered a valid salutation by calling the + * validateSalutation() from the Person Class in Model, which checks that + * the entered Salutation only contains a combination of upper/lower case + * letters and returns a boolean value. Then sets the style so it is cohesive. + * @return true if the user entered a valid salutation. + */ + public boolean validateSalutation() { + if (!Person.validSalutation(this.salutation.getSelectionModel().getSelectedItem().trim())) { + return false; + } else { + this.salutation.setStyle(""); + return true; + } + } + + /** + * Determines if the user has entered a valid name by calling the validateName() + * from the Person Class in Model, which checks that the entered Name only + * contains a combination of spaces and upper/lower case letters and returns + * a boolean value. Then sets the style so it is cohesive. + * @return True if the user entered a valid name. + */ + public boolean validateName() { + if (!Person.validName(this.fullName.getText().trim())) { + return false; + } else { + this.fullName.setStyle(""); + return true; + } + } + + /** + * Determines if the user has entered a valid email checking if the textfield + * is empty and by calling the validateEmail() from the Person Class in + * Model, which uses the EmailValidator (Apache Commons Validator 1.6 API) + * to check that the email is valid and returns a boolean value. Then sets + * the style so it is cohesive. + * @return True if the user entered a valid email. + */ + public boolean validateEmail() { + if (this.email.getText().trim().isEmpty() + || Person.validEmail(this.email.getText().trim())) { + this.email.setStyle(""); + return true; + } else { + return false; + } + } + + /** + * Determines if the user has entered a valid account number by checking + * that the length of the text is 7, that the first character is a 'w', + * that the next 3 characters are digits, and that the last 3 + * characters are letters and lower case. Then sets the style so it is cohesive. + * @return True if the user entered a valid account number. + */ + public boolean validateNumber() { + if (accountNo.getText().trim().length() == 7) { + if (accountNo.getText().trim().charAt(0) != 'w') { + return false; + } else { + for (int i = 1; i < 4; ++i) { + if (!Character.isDigit(accountNo.getText().trim().charAt(i))) { + return false; + } + } + for (int i = 4; i < 7; ++i) { + if (!Character.isLetter(accountNo.getText().trim().charAt(i))) { + return false; + } else if (!Character.isLowerCase(accountNo.getText().trim().charAt(i))) { + return false; + } + } + } + } else { + return false; + } + return true; + } + + /** + * Handles the actions taken when the user tries to submit a new account. + * The appropriate warnings and errors are displayed if the user enters incorrect information. + * If a user enters an invalid input, they will be taken back to the page, to change fields. + */ + public void handleSubmit() { + String invalidMessage = ""; + boolean validSuccess = true; + boolean validName = true; + if (!validateNumber()) { + invalidMessage += "Please enter a valid W Number\n"; + validSuccess = false; + } + if (!validateName()) { + invalidMessage += "Please enter a valid name\n"; + validSuccess = false; + } + if (!validateEmail()) { + invalidMessage += "Please enter a valid email\n"; + validSuccess = false; + } + if (!validateSalutation()) { + invalidMessage += "Please enter a valid salutation\n"; + validSuccess = false; + } + if (this.fullName.getText().trim().isEmpty()) { + if (!this.handleEmptyName()) { + validName = false; + } + } + if (validSuccess && validName) { + Person pers = new Person(this.salutation.getSelectionModel().getSelectedItem().trim(), + this.fullName.getText().trim(), this.famLast.isSelected()); + this.account = new Account(pers, this.accountNo.getText().trim()); + this.success = true; + Stage stage = (Stage) this.submit.getScene().getWindow(); + stage.close(); + } else if (!validSuccess) { + invalidInputAlert.setHeaderText("Invalid Entries"); + invalidInputAlert.setContentText(invalidMessage); + invalidInputAlert.showAndWait(); + } + } + + /** + * Handle Quit button. + */ + public void handleQuit() { + Stage stage = (Stage) this.submit.getScene().getWindow(); + stage.close(); + } + + /** + * Displays dialog and handles appropriate user choices if the full name field is empty. + * @return True if the user selects Okay + */ + public boolean handleEmptyName() { + emptyNameAlert.setContentText("Are you sure you don't want to use your name?"); + Optional result = emptyNameAlert.showAndWait(); + if (result.get() == ButtonType.OK) { + return true; + } else { + return false; + } + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + Platform.runLater(() -> this.pane.requestFocus()); + submit.defaultButtonProperty().bind(submit.focusedProperty()); + submit.setOnAction(e -> { + if (submit.isFocused()) { + handleSubmit(); + } + }); + } +} diff --git a/src/edu/wright/cs/raiderplanner/controller/AccountLoader.java b/src/edu/wright/cs/raiderplanner/controller/AccountLoader.java new file mode 100644 index 00000000..be0df170 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/controller/AccountLoader.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2018 - Gabe Dodds, Joe Yancey + * + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.Button; +import javafx.scene.control.RadioButton; +import javafx.scene.control.ToggleGroup; +import javafx.scene.layout.GridPane; +import javafx.stage.Stage; + +import java.net.URL; +import java.util.ResourceBundle; + +/** + * Handle actions associated with the GUI window for loading existing accounts. + * + * @author Gabe Dodds, Joe Yancey + */ +public class AccountLoader implements Initializable { + @FXML + private ToggleGroup myToggleGroup; + @FXML + private RadioButton f1; + @FXML + private RadioButton f2; + @FXML + private Button submit; + @FXML + private GridPane pane; + @FXML + private Alert invalidInputAlert = new Alert(AlertType.ERROR); + + private boolean selection = true; + + /** + * check the selection from the user. + */ + public void radioSelect() { + if (f1.isSelected()) { + selection = false; + } else { + selection = true; + } + } + + /** + * Handle Quit button. + */ + public void handleQuit() { + Stage stage = (Stage) this.submit.getScene().getWindow(); + stage.close(); + } + + /** + * Handle submit button. + * + * @return true or false based on if an account is present. + */ + public boolean handleSubmit() { + Stage stage = (Stage) this.submit.getScene().getWindow(); + stage.close(); + return selection; + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + Platform.runLater(() -> this.pane.requestFocus()); + submit.defaultButtonProperty().bind(submit.focusedProperty()); + submit.setOnAction(e -> { + if (submit.isFocused()) { + handleSubmit(); + } + }); + } +} \ No newline at end of file diff --git a/src/edu/wright/cs/raiderplanner/controller/ActivityController.java b/src/edu/wright/cs/raiderplanner/controller/ActivityController.java new file mode 100644 index 00000000..5818496a --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/controller/ActivityController.java @@ -0,0 +1,369 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * Copyright (C) 2018 - Ian Mahaffy, Gage Berghoff + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import edu.wright.cs.raiderplanner.controller.MainController; +import edu.wright.cs.raiderplanner.model.Activity; +import edu.wright.cs.raiderplanner.model.QuantityType; +import edu.wright.cs.raiderplanner.model.Task; +import edu.wright.cs.raiderplanner.view.UiManager; +import javafx.application.Platform; +import javafx.beans.binding.BooleanBinding; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Button; +import javafx.scene.control.ComboBox; +import javafx.scene.control.DatePicker; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.control.TextArea; +import javafx.scene.control.TextField; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.GridPane; +import javafx.stage.Stage; + +import java.net.URL; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.ResourceBundle; + +/** + * Handle actions associated with the GUI window for creating new activities. + * This includes validating the data contained in the various text fields, + * retrieving the validated data, and storing the submitted data to the proper + * objects. + * + * @author Zilvinas Ceikauskas + */ + +public class ActivityController implements Initializable { + private Activity activity; + private boolean success = false; + + /** + * Default constructor. + */ + public ActivityController() { + } + + /** + * Constructor for an ActivityController with an existing Activity. + * + * @param activity the activity which will be managed by the new controller + */ + public ActivityController(Activity activity) { + this.activity = activity; + } + + /** + * Returns the Activity object being managed by this controller. + * + * @return the Activity object being managed by this controller. + */ + public Activity getActivity() { + return this.activity; + } + + /** + * Returns true if the last submit operation succeeded, false otherwise. + * + * @return true if the last submit operation succeeded, false otherwise. + */ + public boolean isSuccess() { + return success; + } + + // Panes: + @FXML private GridPane pane; + + // Buttons: + @FXML private Button submit; + @FXML private Button addTask; + @FXML private Button removeTask; + + // Text: + @FXML private TextField name; + @FXML private TextArea details; + @FXML private ComboBox quantityType; + @FXML private TextField quantity; + @FXML private TextField duration; + @FXML private DatePicker date; + + // Labels: + @FXML private Label title; + + // Lists: + @FXML private ListView tasks; + + // Tooltips: + @FXML private Label nameTooltip; + @FXML private Label durationTooltip; + @FXML private Label quantityTooltip; + @FXML private Label dateTooltip; + @FXML private Label taskTooltip; + @FXML private Label detailsTooltip; + @FXML private Label headingTooltip; + + /** + * Handle changes to the input fields. + * Checks inputs, and unlocks the submit button if inputs are valid + */ + public void handleChange() { + if (!this.name.getText().trim().isEmpty() + && !this.quantity.getText().trim().isEmpty() + && MainController.isNumeric(this.quantity.getText()) + && MainController.isInteger(this.quantity.getText()) + && Integer.parseInt(this.quantity.getText()) >= 0 + && !this.date.getValue().isBefore(LocalDate.now()) + && !this.duration.getText().trim().isEmpty() + && MainController.isNumeric(this.duration.getText()) + && MainController.isInteger(this.duration.getText()) + && Integer.parseInt(this.duration.getText()) >= 0 + && this.quantityType.getSelectionModel().getSelectedIndex() != -1 + && this.tasks.getItems().size() > 0) { + this.submit.setDisable(false); + } + } + + /** + * Validate data in the Duration field. If the isNumeric() method is called from + * the MainController Class and is false (checks if the text parameter is a double) + * or the Integer value of the text is less than 0. The duration TextField's + * border is set to red and the submit button is disabled. Otherwise the duration's + * style is set so that it is cohesive and handleChange() is called. + */ + public void validateDuration() { + if (!MainController.isNumeric(this.duration.getText())) { + this.duration.setStyle("-fx-text-box-border:red;"); + this.duration.setTooltip(new Tooltip("Duration must be numeric")); + this.submit.setDisable(true); + } else if (!MainController.isInteger(this.duration.getText())) { + this.duration.setStyle("-fx-text-box-border:red;"); + this.duration.setTooltip(new Tooltip("Duration must be a whole number")); + this.submit.setDisable(true); + } else if (Integer.parseInt(this.duration.getText()) < 0) { + this.duration.setStyle("-fx-text-box-border:red;"); + this.duration.setTooltip(new Tooltip("Duration can not be negative")); + this.submit.setDisable(true); + } else { + this.duration.setStyle(""); + this.duration.setTooltip(null); + this.handleChange(); + } + } + + /** + * Validate data in the Quantity field. If the isNumeric() method is called from + * the MainController Class and is false (checks if the text parameter is a double) + * or the Integer value of the text is less than 0. The quantity TextField's + * border is set to red and the submit button is disabled. Otherwise the quantity's + * style is set so that it is cohesive and handleChange() is called. + */ + public void validateQuantity() { + if (!MainController.isNumeric(this.quantity.getText())) { + this.quantity.setStyle("-fx-text-box-border:red;"); + this.quantity.setTooltip(new Tooltip("Quantity must be numeric")); + this.submit.setDisable(true); + } else if (!MainController.isInteger(this.quantity.getText())) { + this.quantity.setStyle("-fx-text-box-border:red;"); + this.quantity.setTooltip(new Tooltip("Quantity must be a whole number")); + this.submit.setDisable(true); + } else if (Integer.parseInt(this.quantity.getText()) < 0) { + this.quantity.setStyle("-fx-text-box-border:red;"); + this.quantity.setTooltip(new Tooltip("Quantity can not be negative")); + this.submit.setDisable(true); + } else { + this.quantity.setStyle(""); + this.quantity.setTooltip(null); + this.handleChange(); + } + } + + /** + * Validate data in the Date field. If the date is before the current date, the + * DatePicker's border is set to red and the submit button is disabled. Otherwise + * the date's style is set so that it is cohesive and the handleChange() is called. + */ + public void validateDate() { + if (this.date.getValue().isBefore(LocalDate.now())) { + this.date.setStyle("-fx-border-color:red;"); + this.date.setTooltip(new Tooltip("Date can not be in the past")); + this.submit.setDisable(true); + } else { + this.date.setStyle(""); + this.date.setTooltip(null); + this.handleChange(); + } + } + + /** + * Handle the 'Add Task' button action. + */ + public void addTask() { + // Table items: + ObservableList list = FXCollections + .observableArrayList(MainController.getSpc().getCurrentTasks()); + list.removeAll(this.tasks.getItems()); + if (this.activity != null) { + list.removeAll(this.activity.getTasks()); + } + list.removeIf(e -> !e.dependenciesComplete()); + // ================= + + // Parse selected Tasks: + this.tasks.getItems().addAll(TaskController.taskSelectionWindow(list)); + // ================= + } + + /** + * Submit the form and create a new Activity. + */ + public void handleSubmit() { + if (this.activity == null) { + this.activity = new Activity(this.name.getText(), + this.details.getText(), this.date.getValue(), + Integer.parseInt(this.duration.getText()), + Integer.parseInt(this.quantity.getText()), + this.quantityType.getValue()); + + this.activity.addTasks(this.tasks.getItems()); + } + + this.success = true; + Stage stage = (Stage) this.submit.getScene().getWindow(); + stage.close(); + } + + /** + * Binds properties on the quit button as well as sets the button actions for exiting. + */ + public void handleQuit() { + Stage stage = (Stage) this.submit.getScene().getWindow(); + stage.close(); + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + this.quantityType.getItems().addAll(QuantityType.listOfNames()); + this.date.setValue(LocalDate.now()); + + // TODO draggable tasks (same as requirement) + // ListChangeListener: + this.tasks.getItems().addListener((ListChangeListener) c -> handleChange()); + // ================= + + // Bind properties on buttons: + this.removeTask.disableProperty().bind(new BooleanBinding() { + { + bind(tasks.getSelectionModel().getSelectedItems()); + } + + @Override + protected boolean computeValue() { + return !(tasks.getItems().size() > 0 + && tasks.getSelectionModel().getSelectedItem() != null); + } + }); + // ================= + + // Button actions: + this.removeTask.setOnAction(e -> { + if (UiManager.confirm("Are you sure you want to remove this Task from the list?")) { + Task task = this.tasks.getSelectionModel().getSelectedItem(); + this.tasks.getItems().remove(task); + if (this.activity != null) { + this.activity.removeTask(task); + } + } + }); + + this.tasks.setCellFactory(e -> { + ListCell cell = new ListCell() { + @Override + protected void updateItem(final Task item, final boolean empty) { + super.updateItem(item, empty); + // If completed, mark: + if (!empty && item != null) { + setText(item.toString()); + if (item.isCheckedComplete()) { + this.getStyleClass().add("current-item"); + } + } else { + setText(null); + this.getStyleClass().remove("current-item"); + } + } + }; + return cell; + }); + // ================= + + // Handle Activity details: + if (this.activity != null) { + // Disable/modify elements: + this.title.setText("Activity"); + this.addTask.setVisible(false); + this.removeTask.setVisible(false); + this.name.setEditable(false); + this.details.setEditable(false); + this.duration.setEditable(false); + this.quantity.setEditable(false); + this.date.setDisable(true); + this.quantityType.setDisable(true); + // ================= + + // Fill in data: + this.name.setText(this.activity.getName()); + this.details.setText(this.activity.getDetails().getAsString()); + this.duration.setText(Integer.toString(this.activity.getDuration())); + this.quantity.setText(Integer.toString(this.activity.getActivityQuantity())); + this.date.setValue(this.activity.getDate().toInstant() + .atZone(ZoneId.systemDefault()).toLocalDate()); + this.quantityType.getSelectionModel().select(this.activity.getType().getName()); + this.tasks.getItems().addAll(this.activity.getTasks()); + // ================= + } else { + this.handleChange(); + } + // ================= + + // Initialize Tooltips: + nameTooltip.setTooltip(new Tooltip("Enter the name for your new activity.")); + durationTooltip.setTooltip(new Tooltip("Enter how long this Activity will take you " + + "to complete")); + quantityTooltip.setTooltip(new Tooltip("Enter how many times this activity needs to " + + "be completed")); + dateTooltip.setTooltip(new Tooltip("Enter the date tgat ")); + detailsTooltip.setTooltip(new Tooltip("Enter any additional information for this " + + "activity.")); + taskTooltip.setTooltip(new Tooltip("Add tasks to you activity to help stay organized " + + "and efficient.")); + headingTooltip.setTooltip(new Tooltip("An Activity is something that you need to do " + + "and\nfeatures a duration, activity type, date, and tasks.")); + + Platform.runLater(() -> this.pane.requestFocus()); + } +} diff --git a/src/edu/wright/cs/raiderplanner/controller/CalendarController.java b/src/edu/wright/cs/raiderplanner/controller/CalendarController.java new file mode 100644 index 00000000..9d21453f --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/controller/CalendarController.java @@ -0,0 +1,203 @@ +/* + * Copyright (C) 2018 - Michael Pantoja + * + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import edu.wright.cs.raiderplanner.model.Deadline; +import edu.wright.cs.raiderplanner.model.Event; +import edu.wright.cs.raiderplanner.model.ExamEvent; +import edu.wright.cs.raiderplanner.model.TimetableEvent; +import javafx.application.Platform; +import javafx.geometry.Insets; +import javafx.print.PrinterJob; +import javafx.scene.control.Button; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; +import jfxtras.scene.control.agenda.Agenda; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.util.ArrayList; + + +/** + * This is a class to handle the code for the calendar feature. + * @author MichaelPantoja + * + */ +public class CalendarController { + private VBox layout = new VBox(); + private HBox nav = new HBox(); + private HBox xx = new HBox(); + private Button agendaFwd = new Button(">"); + private Button agendaBwd = new Button("<"); + private Agenda content = new Agenda(); + private Button printBtn = new Button(); + private PrinterJob job = PrinterJob.createPrinterJob(); + private ArrayList calendarEvents; + private LocalDateTime stime; + + /** + * This is a class to handle the code for the chat feature. + * @author MichaelPantoja + */ + public CalendarController() { + setupLayout(); + setupNav(); + setupContent(); + setupAgendaButtons(); + populateAgenda(); + setupPrintBtn(); + calendarEvents = MainController.getSpc().getPlanner() + .getCurrentStudyProfile().getCalendar(); + Platform.runLater(() -> content + .setDisplayedLocalDateTime(content.getDisplayedLocalDateTime().plusMinutes(1050))); + } + + /** + * Gets layout for calendar + * @return The current layout of the calendar. + * + * @author MichaelPantoja + */ + public VBox getLayout() { + return layout; + } + + /** + * This function will set up the layout for the calendar. + */ + private void setupLayout() { + GridPane.setHgrow(layout, Priority.ALWAYS); + GridPane.setColumnSpan(layout, GridPane.REMAINING); + layout.setSpacing(10); + layout.setPadding(new Insets(15)); + layout.getStylesheets().add("/edu/wright/cs/raiderplanner/content/stylesheet.css"); + layout.getChildren().addAll(nav, content); + } + + /** + * This function will set up the nav bar for the calendar. + */ + private void setupNav() { + nav.setSpacing(15.0); + nav.getChildren().addAll(xx, agendaBwd, agendaFwd); + HBox.setHgrow(xx, Priority.ALWAYS); + nav.getChildren().add(1, printBtn); + } + + /** + * This function will set up the agenda for the calendar. + */ + private void setupContent() { + content.setAllowDragging(false); + content.setAllowResize(false); + content.autosize(); + content.setActionCallback(param -> null); + content.setEditAppointmentCallback(param -> null); + } + + /** + * This function will set up the agenda buttons for the calendar. + */ + private void setupAgendaButtons() { + agendaBwd.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> { + if (e.getButton() == MouseButton.PRIMARY) { + content.setDisplayedLocalDateTime(content.getDisplayedLocalDateTime().minusDays(7)); + } + }); + agendaFwd.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> { + if (e.getButton() == MouseButton.PRIMARY) { + content.setDisplayedLocalDateTime(content.getDisplayedLocalDateTime().plusDays(7)); + } + }); + VBox.setVgrow(content, Priority.ALWAYS); + } + + /** + * This function will populate the agenda for the calendar. + */ + private void populateAgenda() { + for (Event e : calendarEvents) { + // TODO - find a way to eliminate this if/else-if/instanceof anti-pattern + if (e instanceof TimetableEvent) { + stime = LocalDateTime.ofInstant(e.getDate().toInstant(), + ZoneId.systemDefault()); + content.appointments().addAll(new Agenda.AppointmentImplLocal() + .withStartLocalDateTime(stime) + .withEndLocalDateTime(stime.plusMinutes(e.getDuration())) + + .withSummary(e.getName() + "\n" + "@ " + + ((TimetableEvent) e).getRoom().getLocation()) + .withAppointmentGroup( + new Agenda.AppointmentGroupImpl().withStyleClass("group5"))); + } else if (e instanceof ExamEvent) { + stime = LocalDateTime.ofInstant(e.getDate().toInstant(), + ZoneId.systemDefault()); + content.appointments().addAll(new Agenda.AppointmentImplLocal() + .withStartLocalDateTime(stime) + .withSummary( + e.getName() + "\n" + "@ " + ((ExamEvent) e).getRoom().getLocation()) + .withEndLocalDateTime(stime.plusMinutes(e.getDuration())) + .withAppointmentGroup( + new Agenda.AppointmentGroupImpl().withStyleClass("group20"))); + } else if (e instanceof Deadline) { + stime = LocalDateTime.ofInstant(e.getDate().toInstant(), + ZoneId.systemDefault()); + content.appointments().addAll(new Agenda.AppointmentImplLocal() + .withStartLocalDateTime(stime.minusMinutes(60)).withSummary(e.getName()) + .withEndLocalDateTime(stime).withAppointmentGroup( + new Agenda.AppointmentGroupImpl().withStyleClass("group1"))); + } else { + stime = LocalDateTime.ofInstant(e.getDate().toInstant(), + ZoneId.systemDefault()); + content.appointments().addAll(new Agenda.AppointmentImplLocal() + .withStartLocalDateTime(stime).withSummary(e.getName()) + .withEndLocalDateTime(stime.plusMinutes(60)).withAppointmentGroup( + new Agenda.AppointmentGroupImpl().withStyleClass("group3"))); + } + } + } + + /** + * This function will set up the print button for the calendar. + */ + private void setupPrintBtn() { + printBtn.getStyleClass().addAll("button-image", "print-button"); + printBtn.setOnMouseClicked(event -> { + // Prints the currently selected week + if (job != null) { + if (!job.showPrintDialog(null)) { + // user cancelled + job.cancelJob(); + return; + } + content.print(job); + job.endJob(); + } + }); + } + +} diff --git a/src/edu/wright/cs/raiderplanner/controller/ChatController.java b/src/edu/wright/cs/raiderplanner/controller/ChatController.java new file mode 100644 index 00000000..23d4d8df --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/controller/ChatController.java @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2018 - Michael Pantoja + * + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import javafx.event.ActionEvent; +import javafx.geometry.Insets; +import javafx.scene.control.Button; +import javafx.scene.control.TextArea; +import javafx.scene.control.TextField; +import javafx.scene.layout.Background; +import javafx.scene.layout.BackgroundFill; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; +import javafx.scene.paint.Color; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; + +/** + * This is a class to handle the code for the chat feature. + * @author MichaelPantoja + */ +public class ChatController { + //chat variables + private static final GridPane userMessagePane = new GridPane(); + private static final HBox spacingBox = new HBox(); + private static TextField tfMessageToSend = new TextField(); + private static TextArea msgArea = new TextArea(); + private static final Button sendButton = new Button("Send"); + + /** + * Default Constructor. + */ + public ChatController() { + } + + /** + * This will prevent the message area from being edited and set the size for all the buttons + * This method will also create padding between the text and message areas and the send button. + */ + public static void createUserMessagePane() { + msgArea.setEditable(false); + tfMessageToSend.setPrefWidth(800); + userMessagePane.setPadding(new Insets(10, 10, 10, 10)); + sendButton.setBackground(new Background(new BackgroundFill(Color.AQUAMARINE, null, null))); + spacingBox.setPadding(new Insets(0, 5, 0, 5)); + userMessagePane.add(tfMessageToSend, 0, 0); + userMessagePane.add(spacingBox, 1, 0); + userMessagePane.add(sendButton, 2, 0); + sendButton.setMinWidth(100); + sendButton.setDefaultButton(true); + } + + /** + * This will load the msg_area which is where the user will see messages from other users and + * him or herself. This will also load the text field where the user will be able to send his or + * her own message to peers. + */ + public static void createMainPane() { + MenuController.getMainPane().setCenter(msgArea); + MenuController.getMainPane().setBottom(userMessagePane); + } + + /** + * This will take in the action of when the send button is pressed. If a user sends a message, + * the line of text will append to the chat log so the user can see what they sent. It follows + * the format of USER: sentence time/date. + * The text box with the user input will be set back to blank after a message is sent. + */ + public static void sendButtonAction(String userName) { + sendButton.setOnAction((ActionEvent exception1) -> { + DateTimeFormatter date = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); + LocalDateTime time = LocalDateTime.now(); + if (!(tfMessageToSend.getText().equals(""))) { + msgArea.appendText(userName + ": " + tfMessageToSend.getText()); + msgArea.appendText("\t\t\t" + date.format(time) + "\n"); + tfMessageToSend.setText(""); + } + }); + } +} diff --git a/src/edu/wright/cs/raiderplanner/controller/DataController.java b/src/edu/wright/cs/raiderplanner/controller/DataController.java new file mode 100644 index 00000000..1e6b5771 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/controller/DataController.java @@ -0,0 +1,549 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import edu.wright.cs.raiderplanner.model.Building; +import edu.wright.cs.raiderplanner.model.Coursework; +import edu.wright.cs.raiderplanner.model.Event; +import edu.wright.cs.raiderplanner.model.Exam; +import edu.wright.cs.raiderplanner.model.HubFile; +import edu.wright.cs.raiderplanner.model.Module; +import edu.wright.cs.raiderplanner.model.MultilineString; +import edu.wright.cs.raiderplanner.model.Person; +import edu.wright.cs.raiderplanner.model.Room; +import edu.wright.cs.raiderplanner.model.TimeTableEventType; +import edu.wright.cs.raiderplanner.model.TimetableEvent; +import edu.wright.cs.raiderplanner.model.VersionControlEntity; +import edu.wright.cs.raiderplanner.view.ConsoleIo; +import edu.wright.cs.raiderplanner.view.UiManager; + +import org.w3c.dom.Document; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.SAXException; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +/** + * Helper class that contains static methods for working with version control + * entities. + * + * @author Ben Dickson + */ +public class DataController { + + /** + * Private constructor to prevent object instantiation. + */ + private DataController() { + } + + /** + * Update the library of version control entities with the given VCE. + * + * @param vce the entity to update. + */ + private static void processVceUpdate(VersionControlEntity vce) { + if (vce.addToLibrary() || VersionControlEntity.get(vce.getUid()).update(vce)) { + ConsoleIo.setConsoleMessage(vce + " added", true); + } else { + ConsoleIo.setConsoleMessage(vce + " not added", true); + } + } + + /** + * Update the library of version control entities based on the given node + * list. + * + * @param nodes the list of nodes to process for the update. + * + * @return The updated HUB file. + * @throws IOException if there is a problem creating an entity from the + * serialized representation + */ + private static HubFile processUpdateHubFile(NodeList nodes) throws IOException { + + HubFile hub = null; + XmlController xmlTools = new XmlController(); + + HashMap fileValues = + xmlTools.getSchemaValues(nodes, HubFile.SCHEMA_UPDATE_FILE); + + NodeList updates = fileValues.get("updates").getNodeList(); + + for (int i = 0; i < updates.getLength(); i++) { + Node node = updates.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE + && HubFile.updateableNode(node.getNodeName()) + && XmlController.matchesSchema(node.getChildNodes(), HubFile.SCHEMA_VCE)) { + String nodeName = node.getNodeName(); + HashMap nodeValues = + xmlTools.getSchemaValues(node.getChildNodes(), + HubFile.schemaList.get(nodeName)); + + int version = nodeValues.get("version").getInt(); + String uid = nodeValues.get("uid").getString(); + if (VersionControlEntity.inLibrary(uid) + && VersionControlEntity.get(uid).getVersion() < version) { + + NodeList nc = XmlController.getNodes(node); + switch (nodeName) { + case "person": + processVceUpdate(HubFile.createPerson(nc)); + break; + case "building": + processVceUpdate(HubFile.createBuilding(nc)); + break; + case "room": + processVceUpdate(HubFile.createRoom(nc, + VersionControlEntity.getLibrary())); + break; + case "module": + // TODO: not yet added + break; + case "exam": + processVceUpdate(HubFile.createExam(nc, + VersionControlEntity.getLibrary())); + break; + case "coursework": + processVceUpdate(HubFile.createCoursework(nc, + VersionControlEntity.getLibrary())); + break; + case "timetableEventType": + processVceUpdate(HubFile.createTimetableEventType(nc)); + break; + case "timetableEvent": + processVceUpdate(HubFile.createTimetableEvent(nc, + VersionControlEntity.getLibrary())); + break; + case "event": + // not yet added + break; + case "deadline": + // not yet added + break; + case "examEvent": + // not yet added + break; + default: + throw new RuntimeException("Unknown node: " + nodeName); + } + + } + } + } + + return hub; + } + + /** + * Returns the version control entity for the given UID. + * + * @param list a list from which the version control entity may be taken. + * @param uid the identifier to look up. + * + * @return the entity from the list or the library. + * @throws IOException if the requested entity is not in the list or cannot + * be cast to the specified type + */ + public static T inList( + Map list, String uid) throws IOException { + + VersionControlEntity vce = null; + if (list.containsKey(uid)) { + vce = list.get(uid); + } else if (VersionControlEntity.inLibrary(uid)) { + vce = VersionControlEntity.get(uid); + } + // can't do instanceof T annoyingly, so hopefully casting to T and failing will work instead + // TODO find a better approach for this + if (vce != null) { + try { + return (T) vce; + } catch (Exception e) { + // TODO this should be IllegalStateException, RuntimeException, or other + throw new IOException("Incorrect type referenced for '" + uid + "'"); + } + } + // TODO this should not be here, rather callers should receive the null return + throw new IOException("UID referenced is not in database for '" + uid + "'"); + + } + + /** + * Add the given values to the specified version control entity. + * + * @param vce the entity to update. + * @param values the values to use for the update. + */ + public static void addVceProperties(VersionControlEntity vce, + Map values) { + + vce.addProperties(values.get("name").getString(), + values.get("details").getMultilineString()); + String uid = values.get("uid").getString(); + vce.makeImporter(); + vce.setUid(uid, values.get("version").getInt()); + + } + + /** + * Create a new HUB file from the given list of nodes. + * + * @param nodes the list of nodes to use for constituting the HUB file. + * + * @return the HUB file. + * @throws IOException when attempting to import an already existing study + * profile, or for errors connected with creating objects from + * their serialized representations + */ + private static HubFile processNewHubFile(NodeList nodes) throws IOException { + + int beginLog = ConsoleIo.getLogSize(); + ConsoleIo.setConsoleMessage("Importing New Hub File", true); + HubFile hub = null; + XmlController xmlTools = new XmlController(); + + HashMap fileValues = + xmlTools.getSchemaValues(nodes, HubFile.SCHEMA_NEW_STUDYPROFILE); + + int version = fileValues.get("version").getInt(); + NodeList assetNodes = fileValues.get("assets").getNodeList(); + NodeList studyProfileNodes = fileValues.get("studyProfile").getNodeList(); + + if (XmlController.matchesSchema(assetNodes, HubFile.SCHEMA_ASSETS) + && XmlController.matchesSchema(studyProfileNodes, HubFile.SCHEMA_STUDYPROFILE)) { + ConsoleIo.setConsoleMessage("Schema Validates: assets", true); + ConsoleIo.setConsoleMessage("Schema Validates: studyProfile", true); + + HashMap studyProfileValues = + xmlTools.getSchemaValues(studyProfileNodes, HubFile.SCHEMA_STUDYPROFILE); + + int year = studyProfileValues.get("year").getInt(); + int semester = studyProfileValues.get("semester").getInt(); + + if (MainController.getSpc().containsStudyProfile(year, semester)) { + // TODO this should be IllegalStateException, RuntimeException, or other + throw new IOException("Study profile for " + year + " semester " + + semester + " already imported"); + } + + HashMap assetList = new HashMap<>(); + HashMap assetValues = + xmlTools.getSchemaValues(assetNodes, HubFile.SCHEMA_ASSETS); + + ArrayList newModules = new ArrayList<>(); + + Node node; + NodeList nc; + + // Add all the Person classes + if (assetValues.containsKey("persons")) { + + NodeList personList = assetValues.get("persons").getNodeList(); + Person tp; + int pll = personList.getLength(); + ConsoleIo.setConsoleMessage("Reading persons tag, " + + Integer.toString(pll) + " nodes:", true); + for (int i = 0; i < pll; i++) { + node = personList.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) { + nc = XmlController.getNodes(node); + if (node.getNodeName().equals("person") + && XmlController.matchesSchema(nc, HubFile.SCHEMA_PERSON)) { + + ConsoleIo.setConsoleMessage("Valid Node found:", true); + + tp = HubFile.createPerson(nc); + + assetList.put(tp.getUid(), tp); + + ConsoleIo.setConsoleMessage("Adding person: " + tp.toString(), true); + } + } + } + } + + // add all the Buildings + if (assetValues.containsKey("buildings")) { + NodeList buildingList = assetValues.get("buildings").getNodeList(); + Building tb; + int bll = buildingList.getLength(); + ConsoleIo.setConsoleMessage("Reading buildings tag, " + + Integer.toString(bll) + " nodes:", true); + for (int i = 0; i < bll; i++) { + node = buildingList.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) { + nc = XmlController.getNodes(node); + if (node.getNodeName().equals("building") + && XmlController.matchesSchema(nc, HubFile.SCHEMA_BUILDING)) { + ConsoleIo.setConsoleMessage("Valid Node found:", true); + + + tb = HubFile.createBuilding(nc); + + assetList.put(tb.getUid(), tb); + ConsoleIo.setConsoleMessage("Adding buiding: " + tb.toString(), true); + } + } + } + } + + // add all the Rooms + if (assetValues.containsKey("rooms")) { + NodeList roomList = assetValues.get("rooms").getNodeList(); + Room tr; + int rll = roomList.getLength(); + ConsoleIo.setConsoleMessage("Reading rooms tag, " + + Integer.toString(rll) + " nodes:", true); + for (int i = 0; i < rll; i++) { + node = roomList.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) { + nc = XmlController.getNodes(node); + if (node.getNodeName().equals("room") + && XmlController.matchesSchema(nc, HubFile.SCHEMA_ROOM)) { + ConsoleIo.setConsoleMessage("Valid Node found:", true); + + tr = HubFile.createRoom(nc, assetList); + assetList.put(tr.getUid(), tr); + ConsoleIo.setConsoleMessage("Adding room: " + tr.toString(), true); + } + } + } + } + + // add all the TimeTableTypes + if (assetValues.containsKey("timetableEventTypes")) { + NodeList ttetList = assetValues.get("timetableEventTypes").getNodeList(); + int tll = ttetList.getLength(); + ConsoleIo.setConsoleMessage("Reading timetableEventTypes tag, " + + Integer.toString(tll) + " nodes:", true); + for (int i = 0; i < tll; i++) { + node = ttetList.item(i); + if (node.getNodeType() == Node.ELEMENT_NODE) { + nc = XmlController.getNodes(node); + if (node.getNodeName().equals("timetableEventType") + && XmlController.matchesSchema(nc, + HubFile.SCHEMA_TIMETABLE_EVENT_TYPE)) { + ConsoleIo.setConsoleMessage("Valid Node found:", true); + TimeTableEventType ttet = HubFile.createTimetableEventType(nc); + + + assetList.put(ttet.getUid(), ttet); + ConsoleIo.setConsoleMessage("Adding timetable event: " + + ttet.toString(), true); + } + } + + } + } + + + NodeList modules = studyProfileValues.get("modules").getNodeList(); + int mll = modules.getLength(); + for (int i = 0; i < mll; i++) { + if (modules.item(i).getNodeType() == Node.ELEMENT_NODE + && modules.item(i).getNodeName().equals("module") + && modules.item(i).hasChildNodes() + && XmlController.matchesSchema(modules.item(i).getChildNodes(), + HubFile.SCHEMA_MODULE)) { + + HashMap moduleValues = + xmlTools.getSchemaValues(modules.item(i).getChildNodes(), + HubFile.SCHEMA_MODULE); + + String linkedPerson = moduleValues.get("organiser").getString(); + + Person organiser = inList(assetList, linkedPerson); + String moduleCode = moduleValues.get("moduleCode").getString(); + Module thisModule = new Module(organiser, moduleCode); + + + addVceProperties(thisModule, moduleValues); + + assetList.put(moduleValues.get("uid").getString(), thisModule); + + NodeList assignments = moduleValues.get("assignments").getNodeList(); + int all = assignments.getLength(); + ConsoleIo.setConsoleMessage("Reading assignments tag, " + + Integer.toString(all) + " nodes:", true); + for (int j = 0; j < all; j++) { + if (assignments.item(j).getNodeType() == Node.ELEMENT_NODE) { + nc = assignments.item(j).getChildNodes(); + String nodeName = assignments.item(j).getNodeName(); + switch (nodeName) { + case "coursework": + if (XmlController.matchesSchema( + assignments.item(j).getChildNodes(), + HubFile.SCHEMA_COURSEWORK)) { + ConsoleIo.setConsoleMessage("Valid Node found:", true); + Coursework newCoursework = + HubFile.createCoursework(nc, assetList); + assetList.put(newCoursework.getUid(), newCoursework); + thisModule.addAssignment(newCoursework); + ConsoleIo.setConsoleMessage("Adding coursework: " + + newCoursework.toString(), true); + } + break; + + case "exam": + if (XmlController.matchesSchema( + assignments.item(j).getChildNodes(), + HubFile.SCHEMA_EXAM)) { + ConsoleIo.setConsoleMessage("Valid Node found:", true); + + + Exam newExam = HubFile.createExam(nc, assetList); + assetList.put(newExam.getUid(), newExam); + thisModule.addAssignment(newExam); + ConsoleIo.setConsoleMessage("Adding exam: " + + newExam.toString(), true); + } + + break; + default: + throw new RuntimeException("Unknown assignment tag: " + nodeName); + } + + } + } + NodeList timetable = moduleValues.get("timetable").getNodeList(); + int ttll = timetable.getLength(); + ConsoleIo.setConsoleMessage("Reading timetable tag, " + + Integer.toString(ttll) + " nodes:", true); + for (int j = 0; j < ttll; j++) { + if (timetable.item(j).getNodeType() == Node.ELEMENT_NODE + && timetable.item(j).getNodeName().equals("timetableEvent") + && XmlController.matchesSchema(timetable.item(j).getChildNodes(), + HubFile.SCHEMA_TIMETABLE_EVENT)) { + ConsoleIo.setConsoleMessage("Valid Node found:", true); + nc = timetable.item(j).getChildNodes(); + + TimetableEvent newTte = + HubFile.createTimetableEvent(nc, assetList); + assetList.put(newTte.getUid(), newTte); + thisModule.addTimetableEvent(newTte); + + ConsoleIo.setConsoleMessage("Adding TimetableEvent: " + + newTte.toString(), true); + } + } + newModules.add(thisModule); + } + } + + ConsoleIo.setConsoleMessage("Attempting to import " + + Integer.toString(assetList.size()) + + " items to VersionControl...", true); + ConsoleIo.setConsoleMessage("Starting with " + + VersionControlEntity.libraryReport() + " entries"); + + ArrayList calendarItems = new ArrayList<>(); + + for (String key : assetList.keySet()) { + ConsoleIo.setConsoleMessage("Adding Asset: " + key, true); + if (assetList.get(key).addToLibrary()) { + ConsoleIo.setConsoleMessage("New Asset, adding... ", true); + if (assetList.get(key) instanceof Event) { + + ConsoleIo.setConsoleMessage("Event - adding to Calendar: ", true); + calendarItems.add((Event) assetList.get(key)); + } + } else if (assetList.get(key).isImporter()) { + ConsoleIo.setConsoleMessage("In library, attempting update: ", true); + ConsoleIo.setConsoleMessage(assetList.get(key).toString() + + (VersionControlEntity.get(key).update(assetList.get(key)) + ? " updated" : " not updated"), true); + } else { + ConsoleIo.setConsoleMessage(assetList.get(key).toString() + + " not imported", true); + } + } + ConsoleIo.setConsoleMessage("Ending with " + + VersionControlEntity.libraryReport() + " entries"); + ConsoleIo.saveLog("import_report.txt", beginLog, ConsoleIo.getLogSize()); + + String name = studyProfileValues.get("name").getString(); + MultilineString details = studyProfileValues.get("details").getMultilineString(); + String uid = studyProfileValues.get("uid").getString(); + + hub = new HubFile(version, year, semester, newModules, + new ArrayList(), calendarItems, name, + details, uid); + } + return hub; + } + + /** + * Create a HUB file from the given file. + * + * @param tempFile the file from which to create the HUB file. + * + * @return the HUB file. + */ + public static HubFile loadHubFile(File tempFile) { + HubFile hub = null; + if (tempFile.exists()) { + try { + // learned from: + // https://www.mkyong.com/java/how-to-read-xml-file-in-java-dom-parser/ + DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder docBuilder = dbFactory.newDocumentBuilder(); + Document doc = docBuilder.parse(tempFile); + doc.getDocumentElement().normalize(); + + String rootElementTag = doc.getDocumentElement().getNodeName(); + Node rootElement = doc.getDocumentElement(); + + // check it is a hubfile + if (rootElementTag.equals("hubfile") + && rootElement.hasChildNodes()) { + NodeList nodes = XmlController.getNodes(rootElement); + if (XmlController.matchesSchema(nodes, HubFile.SCHEMA_NEW_STUDYPROFILE)) { + hub = processNewHubFile(nodes); + } else if (XmlController.matchesSchema(nodes, HubFile.SCHEMA_UPDATE_FILE)) { + hub = processUpdateHubFile(nodes); + } else { + UiManager.reportError("Invalid Parent Nodes"); + } + } else { + UiManager.reportError("Invalid XML file"); + } + + } catch (IOException e) { + UiManager.reportError("Invalid File: \n" + e.getMessage()); + } catch (SAXException e) { + UiManager.reportError("SAX Exception"); + } catch (Exception e) { + UiManager.reportError(e.getMessage()); + } + } + return hub; + } +} \ No newline at end of file diff --git a/src/edu/wright/cs/raiderplanner/controller/GradPlannerController.java b/src/edu/wright/cs/raiderplanner/controller/GradPlannerController.java new file mode 100644 index 00000000..ecff163d --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/controller/GradPlannerController.java @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2018 - Mark Riedel + * + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import edu.wright.cs.raiderplanner.model.Account; +import edu.wright.cs.raiderplanner.model.Notification; +import edu.wright.cs.raiderplanner.model.Requirement; +import edu.wright.cs.raiderplanner.view.UiManager; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import javafx.scene.image.ImageView; +import javafx.stage.Stage; + +import java.io.IOException; + +/** + * Controller for the first window that is presented when the application + * launches (i.e., New/Open/Exit buttons). + * + * @author Mark Riedel on 10/9/2018. +*/ + +public class GradPlannerController { + ObservableList semesterList = FXCollections + .observableArrayList("Semster 1, Semester2,...", + "Fall 2018,Spring 2018,...","Year 1 Fall,Year 1 Spring", + "Custom"); + @FXML + private TextField startYearFx; + @FXML + private TextField semesterOne; + @FXML + private TextField semesterTwo; + @FXML + private TextField semesterThree; + @FXML + private TextField semesterFour; + @FXML + private TextField semesterFive; + @FXML + private TextField semesterSix; + @FXML + private TextField semesterSeven; + @FXML + private TextField semesterEight; + @FXML + private ComboBox chooseSemester; + String startYear = startYearFx.getText(); + String year1 = startYear.toString(); + String secondYear = startYearFx.getText() + 1; + String year2 = secondYear.toString(); + String thirdYear = startYearFx.getText() + 2; + String year3 = thirdYear.toString(); + String fourthYear = startYearFx.getText(); + String year4 = fourthYear.toString(); + + /** + * Adds SemesterList to the Combo Box. + */ + @FXML + private void initialize() { + chooseSemester.setValue("semester"); + chooseSemester.setItems(semesterList); + } + + /** + * Allows for the TextFields Standard Configurations. + */ + public void semesterNumber() { + semesterOne.setText("Semester 1"); + semesterOne.setEditable(false); + semesterTwo.setText("Semester 2"); + semesterTwo.setEditable(false); + semesterThree.setText("Semester 3"); + semesterThree.setEditable(false); + semesterFour.setText("Semester 4"); + semesterFour.setEditable(false); + semesterFive.setText("Semester 5"); + semesterFive.setEditable(false); + semesterSix.setText("Semester 6"); + semesterSix.setEditable(false); + semesterSeven.setText("Semester 7"); + semesterSeven.setEditable(false); + semesterEight.setText("Semester 8"); + semesterEight.setEditable(false); + } + + /** + * Allows for switchable naming conventions. + */ + public void nameYear() { + semesterOne.setText("Fall " + year1); + semesterOne.setEditable(false); + semesterTwo.setText("Spring" + year1); + semesterTwo.setEditable(false); + semesterThree.setText("Fall" + year2); + semesterThree.setEditable(false); + semesterFour.setText("Spring" + year2); + semesterFour.setEditable(false); + semesterFive.setText("Fall" + year3); + semesterFive.setEditable(false); + semesterSix.setText("Spring" + year3); + semesterSix.setEditable(false); + semesterSeven.setText("Fall" + year4); + semesterSeven.setEditable(false); + semesterEight.setText("Spring" + year4); + semesterEight.setEditable(false); + } + + /** + * Allows for custom naming semesters. + */ + public void customName() { + semesterOne.setEditable(true); + semesterTwo.setEditable(true); + semesterThree.setEditable(true); + semesterFour.setEditable(true); + semesterFive.setEditable(true); + semesterSix.setEditable(true); + semesterSeven.setEditable(true); + semesterEight.setEditable(true); + } +} diff --git a/src/edu/wright/cs/raiderplanner/controller/MainController.java b/src/edu/wright/cs/raiderplanner/controller/MainController.java new file mode 100644 index 00000000..2f80b490 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/controller/MainController.java @@ -0,0 +1,539 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar, Amila Dias + * + * Copyright (C) 2018 - Clayton D. Terrill, Ian Mahaffy + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import edu.wright.cs.raiderplanner.model.Account; +import edu.wright.cs.raiderplanner.model.Event; +import edu.wright.cs.raiderplanner.model.HubFile; +import edu.wright.cs.raiderplanner.model.ICalExport; +import edu.wright.cs.raiderplanner.model.Notification; +import edu.wright.cs.raiderplanner.model.Settings; +import edu.wright.cs.raiderplanner.model.StudyPlanner; +import edu.wright.cs.raiderplanner.util.RaiderException; +import edu.wright.cs.raiderplanner.view.UiManager; +import javafx.scene.Scene; +import javafx.scene.control.Accordion; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.TitledPane; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; + +import java.awt.Desktop; +import java.io.BufferedInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.GregorianCalendar; + +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.CipherInputStream; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.SealedObject; +import javax.crypto.SecretKey; +import javax.crypto.spec.SecretKeySpec; + +/** + * A helper class of static methods and fields which are used to handle the + * loading and saving of application state data. + * + * @author Ben Dickson + */ +public class MainController { + + /** + * Private constructor to prevent object instantiation. + */ + private MainController() { + } + + // TODO - Determine if this really should be public + public static UiManager ui = new UiManager(); + public static Settings settings = new Settings(); + + // TODO - StudyPlannerController is a public class; determine if managing an + // instance in this way is best + private static StudyPlannerController spc; + + // TODO - Use an approach where a key is generated and stored on the user's system + // Used for serialization: + private static SecretKey key64 = new SecretKeySpec( + new byte[] { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 }, "Blowfish"); + private static File plannerFile = null; + + /** + * Returns the StudyPlannerController managed by this MainController. + * + * @return the StudyPlannerController managed by this MainController. + */ + public static StudyPlannerController getSpc() { + return spc; + } + + /** + * Sets the StudyPlannerController managed by this MainController. + * + * @param newSpc the new StudyPlannerController. + */ + public static void setSpc(StudyPlannerController newSpc) { + spc = newSpc; + } + + /** + * Initializes the Study Planner by either registering a new account or + * importing an existing Study Planner file. + * @throws Exception e if there is an issue registering a new account or importing a file + */ + public static void initialise() { + if (settings.getAccountStartup() == true) { + File file = new File(settings.getDefaultFilePath()); + if (file.exists() && !file.isDirectory()) { + plannerFile = file; + } else { + boolean noAccount = false; + File[] files = UiManager.getSavesFolder().listFiles(); + if (files != null) { + if (files.length == 0) { + noAccount = true; + } + if (files.length == 1 + && files[0].getName().contains("SamplePlanner.dat")) { + noAccount = true; + } + if (files.length >= 1) { + try { + noAccount = MainController.ui.accountStart(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + } + if (noAccount) { + try { + Account newAccount = MainController.ui.createAccount(noAccount); + StudyPlannerController study = + new StudyPlannerController(newAccount); + // Welcome notification: + Notification not = + new Notification("Welcome!", new GregorianCalendar(), + "Thank you for using RaiderPlanner!"); + study.getPlanner().addNotification(not); + MainController.setSpc(study); + plannerFile = MainController.ui.savePlannerFileDialog(); + if (plannerFile != null) { + if (plannerFile.getParentFile().exists()) { + if (plannerFile.getParentFile().canRead()) { + if (plannerFile.getParentFile().canWrite()) { + MainController.save(); + loadFile(plannerFile); + } else { + UiManager.reportError("Directory can't be written to."); + } + } else { + UiManager.reportError("Directory cannot be read from."); + } + } else { + UiManager.reportError("Directory does not exist."); + } + } + /*This is catching a general exception because the + * createAccount method throws a general exception*/ + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } else { + long modifiedTime = Long.MIN_VALUE; + File modifiedFile = new File(""); + for (int i = 0; i < files.length; ++i) { + if (files[i].lastModified() > modifiedTime) { + modifiedFile = files[i]; + modifiedTime = files[i].lastModified(); + } + } + plannerFile = modifiedFile; + // If a file is present: + loadFile(plannerFile); + } + } + } else { + boolean noAccount = false; + File[] files = MainController.ui.getSavesFolder().listFiles(); + if (files != null) { + if (files.length == 0) { + noAccount = true; + } + if (files.length == 1 && files[0].getName().contains("SamplePlanner.dat")) { + noAccount = true; + } + } + if (noAccount) { + try { + Account newAccount = MainController.ui.createAccount(noAccount); + StudyPlannerController study = new StudyPlannerController(newAccount); + // Welcome notification: + Notification not = new Notification("Welcome!", new GregorianCalendar(), + "Thank you for using RaiderPlanner!"); + study.getPlanner().addNotification(not); + MainController.setSpc(study); + plannerFile = MainController.ui.savePlannerFileDialog(); + if (plannerFile != null) { + if (plannerFile.getParentFile().exists()) { + if (plannerFile.getParentFile().canRead()) { + if (plannerFile.getParentFile().canWrite()) { + MainController.save(); + loadFile(plannerFile); + } else { + UiManager.reportError("Directory can not be written to."); + } + } else { + UiManager.reportError("Directory cannot be read from."); + } + + } else { + UiManager.reportError("Directory does not exist."); + } + } + /*This is catching a general exception because the + * createAccount method throws a general exception*/ + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } else { + long modifiedTime = Long.MIN_VALUE; + File modifiedFile = new File(""); + for (int i = 0; i < files.length; ++i) { + if (files[i].lastModified() > modifiedTime) { + modifiedFile = files[i]; + modifiedTime = files[i].lastModified(); + } + } + plannerFile = modifiedFile; + // If a file is present: + loadFile(plannerFile); + } + } + } + + /** + * Decrypts a file and loads it. + * @param plannerFile the file to be loaded + */ + public static void loadFile(File plannerFile) { + if (plannerFile.exists()) { + try { + Cipher cipher = Cipher.getInstance("Blowfish"); + cipher.init(Cipher.DECRYPT_MODE, key64); + try (CipherInputStream cis = new CipherInputStream( + new BufferedInputStream(new FileInputStream(plannerFile)), cipher); + ObjectInputStream ois = new ObjectInputStream(cis)) { + SealedObject sealedObject = (SealedObject) ois.readObject(); + spc = new StudyPlannerController((StudyPlanner) sealedObject.getObject(cipher)); + // Sample note + if (spc.getPlanner().getCurrentStudyProfile() != null && spc.getPlanner() + .getCurrentStudyProfile().getName().equals("First year Gryffindor")) { + UiManager.reportSuccess( + "Note: This is a pre-loaded sample StudyPlanner, as used by Harry " + + "Potter. To make your own StudyPlanner, restart the application " + + "and choose \"New File\"."); + } + } + } catch (FileNotFoundException e) { + UiManager.reportError("Cannot find this file.","file not found exception when " + + "trying loadfile, most likely due to invalid parameters"); + System.exit(1); + } catch (ClassNotFoundException e) { + UiManager.reportError("Error, Cannot find the study planner.", "class not found" + + " exception thrown when trying loadfile, most likely due to problems" + + " reading the object input stream as a sealed object"); + System.exit(1); + } catch (BadPaddingException e) { + UiManager.reportError("Error, Cannot decode the given file.", "bad padding" + + " exception thrown when trying loadfile, most likely due to " + + "problems with the cipher"); + System.exit(1); + } catch (IOException e) { + UiManager.reportError("Error, Invalid file.", "IO exception thrown when trying" + + " loadfile, most likely due to an invalide file or problems with " + + "one of the input streams"); + System.exit(1); + } catch (IllegalBlockSizeException e) { + UiManager.reportError("Error, Object too large, cannot decode the file.", + "Illegal block size exception thrown when trying loadfile, most " + + "likely due to a problem with constructing a sealed object"); + System.exit(1); + } catch (InvalidKeyException e) { + UiManager.reportError("Error, Invalid Key, Cannot decode the given file.", + "invalid key exception thrown when trying loadfile, most likely due " + + "to using an invalid key while initialising the the cypher"); + System.exit(1); + } catch (NoSuchAlgorithmException e) { + UiManager.reportError("Error, Cannot decode the given file.", "no such " + + "algorithm exception thrown when trying loadfile, most likely " + + "due to the program not being able to find the indicated " + + "cypher while initialising the the cypher"); + System.exit(1); + } catch (NoSuchPaddingException e) { + UiManager.reportError("Error, Cannot decode the given file", "no such" + + " padding exception thrown when trying loadfile, most likely" + + " due to problems with the parameters used in the get " + + "instance call made while initialising the the cypher"); + System.exit(1); + } catch (Exception e) { + UiManager.reportError(e.getMessage() + "Unknown error.", e.getMessage() + + "unknown error occured while trying loadfile"); + System.exit(1); + } + } else { + // TODO - fix this, as it is clearly a race condition + // This should never happen unless a file changes permissions + // or existence in the milliseconds that it runs the above code + // after checks in StartupController + UiManager.reportError("Failed to load file."); + System.exit(1); + } + } + + /** + * Display the main menu. + * @throws IOException e if the file doesnt exist. Any other exceptions gets error message. + */ + public static void main() throws Exception { + try { + ui.mainMenu(); + } catch (IOException e) { + UiManager.reportError("File does not exist: " + e.getMessage()); + } catch (RaiderException e) { + e.printStackTrace(); + } + } + + /** + * Display the main menu. + * Stage is already present. + * @throws IOException e if the file doesnt exist. Any other exceptions gets error message. + */ + public static void showMain() throws Exception { + try { + ui.showMain(); + } catch (IOException e) { + UiManager.reportError("File does not exist: " + e.getMessage()); + } catch (RaiderException e) { + UiManager.reportError(e.getMessage()); + } + } + + /** + * Display the settings menu. + * Stage is already present. + * @throws IOException e if the file doesnt exist. Any other exceptions gets error message. + */ + public static void showSettings() throws Exception { + try { + ui.showSettings(); + } catch (IOException e) { + UiManager.reportError("File does not exist: " + e.getMessage()); + } catch (RaiderException e) { + UiManager.reportError(e.getMessage()); + } + } + + /** + * Handles importing a new file and if the file is imported it lets the user know. + * + * @return whether imported successfully. + */ + public static boolean importFile() { + // Call a dialog: + File tempFile = ui.loadFileDialog(); + if (tempFile != null) { + // If a file was selected, process the file: + HubFile fileData = DataController.loadHubFile(tempFile); + if (fileData != null) { + if (!fileData.isUpdate() + && !MainController.spc.createStudyProfile(fileData)) { + UiManager.reportError("This Study Profile is already created!"); + } else { + return true; + } + } + } + return false; + } + + /** + * Save the current state of the program to file. + * @return true for a successful save, false otherwise + * @throws Exception e if there was an issue with saving information + */ + public static boolean save() { + try { + spc.save(MainController.key64, MainController.plannerFile.getAbsolutePath()); + return true; + } catch (Exception e) { // the save() method already catches an Exception. TODO maybe delete + UiManager.reportError("FAILED TO SAVE YOUR DATA!"); + return false; + } + } + + /** + * Sets the planner file that is loaded/saved. + * + * @param file the File object from which the planner file will be loaded or + * to which it will be saved. + */ + public static void setPlannerFile(File file) { + plannerFile = file; + } + + /** + * Checks if a string can be converted to an Integer. + * + * @param value String to be tested + * @return True if string can be converted, false otherwise + */ + public static boolean isInteger(String value) { + try { + Integer.parseInt(value); + return true; + } catch (Exception e) { + return false; + } + } + + /** + * Apparently (according to Stackoverflow) the Java Standard library doesn't have a + * standard check for testing if a string value is a number or not?!) + * + *

Therefore, we are using this proposed isNumeric method from: + * + *

http://stackoverflow.com/a/1102916 + * + * @param str String to be tested + * @return true the given String is numeric (i.e., can be parsed into a + * Double), false otherwise. + */ + public static boolean isNumeric(String str) { + try { + // No need to assign the result; the exception or lack of is what matters + Double.parseDouble(str); + } catch (NumberFormatException nfe) { + return false; + } + return true; + } + + /** + * Launches the default browser to display a URI. + */ + public static void openHelpPage() { + final Button site = new Button("Website"); + final Button pdf = new Button("user-manual"); + Label tab1 = new Label("RaiderPlanner is an application based off of the Pear Planner " + + "to help students keep" + + " track of assignments and exams, allowing them to achieve their full academic" + + " potential. " + + "Current features include a calendar, an alarm, and a Gantt diagram generator" + + " to keep track of progress"); + Label tab2 = new Label("How to use RaiderPlanner in 3 easy steps:" + + "\n" + "1: Enter valid information for all the fields on the startup page. " + + "All other information is optional." + "\n" + + "2: Choose the name of the file you want to save to." + "\n" + + "3: To unlock all the other features RaiderPlanner has to offer, click the" + + " import hub file button from the menu on the left" + + "\nNeed more help? Open up the user manual or RaiderPlanner Website here"); + Label tab3 = new Label("If you want, you can contribute to RaiderPlanner on github at" + + " the this address: https://github.com/rsanchez-wsu/RaiderPlanner" + + "\n" + "Planned features include a graduation planner, Pilot integration, and a " + + "schedule sharing feature"); + tab1.setWrapText(true); + tab2.setWrapText(true); + tab3.setWrapText(true); + VBox splitter = new VBox(); + splitter.getChildren().add(tab2); + splitter.getChildren().add(pdf); + splitter.getChildren().add(site); + TitledPane t1 = new TitledPane("What is RaiderPlanner?",tab1); + TitledPane t2 = new TitledPane("Getting Started",splitter); + TitledPane t3 = new TitledPane("Whats Next?",tab3); + Accordion root = new Accordion(); + root.getPanes().addAll(t1, t2, t3); + Stage newStage = new Stage(); + newStage.setTitle("Raider Helper"); + Scene scene = new Scene(root,400,300); + newStage.setScene(scene); + newStage.show(); + + pdf.setOnAction((event) -> { + if (Desktop.isDesktopSupported()) { + try { + File myFile = new + File("Final Documents/" + + "User Manual.pdf"); + Desktop.getDesktop().open(myFile); + } catch (IOException ex) { + System.out.println("Error: user-manual not found"); + } + } + }); + site.setOnAction((event) -> { + if (Desktop.isDesktopSupported()) { + try { + File myFile = new + File("docs/index.html"); + Desktop.getDesktop().open(myFile); + } catch (IOException ex) { + System.out.println("Error: Website not found"); + } + } + }); + } + + /** + * Function exports calendar ICS file to user defined location. + */ + public static void exportCalendar() { + ICalExport icalExport = new ICalExport(); + try { + ArrayList exportCal = + getSpc().getPlanner().getCurrentStudyProfile().getCalendar(); + for (Event e : exportCal) { + icalExport.createExportEvent(e); + } + icalExport.exportToFile(ui.saveIcsFileDialog()); + UiManager.reportSuccess("File Exported"); + } catch (NullPointerException e) { + UiManager.reportError("Calendar does not exist! Export failed"); + } + } +} diff --git a/src/edu/wright/cs/raiderplanner/controller/MenuController.java b/src/edu/wright/cs/raiderplanner/controller/MenuController.java new file mode 100644 index 00000000..20fcbe8b --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/controller/MenuController.java @@ -0,0 +1,1999 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar, Amila Dias + * + * Copyright (C) 2018 - Clayton D. Terrill, Cole Morgan + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import edu.wright.cs.raiderplanner.model.Account; +import edu.wright.cs.raiderplanner.model.Activity; +import edu.wright.cs.raiderplanner.model.Assignment; +import edu.wright.cs.raiderplanner.model.Coursework; +import edu.wright.cs.raiderplanner.model.Exam; +import edu.wright.cs.raiderplanner.model.Milestone; +import edu.wright.cs.raiderplanner.model.ModelEntity; +import edu.wright.cs.raiderplanner.model.Module; +import edu.wright.cs.raiderplanner.model.Notification; +import edu.wright.cs.raiderplanner.model.QuantityType; +import edu.wright.cs.raiderplanner.model.Requirement; +import edu.wright.cs.raiderplanner.model.Settings; +import edu.wright.cs.raiderplanner.model.StudyProfile; +import edu.wright.cs.raiderplanner.model.Task; +import edu.wright.cs.raiderplanner.view.GanttishDiagram; +import edu.wright.cs.raiderplanner.view.UiManager; +import javafx.animation.TranslateTransition; +import javafx.application.Platform; +import javafx.beans.binding.BooleanBinding; +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleIntegerProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import javafx.embed.swing.SwingFXUtils; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.fxml.FXML; +import javafx.fxml.FXMLLoader; +import javafx.fxml.Initializable; +import javafx.geometry.HPos; +import javafx.geometry.Insets; +import javafx.geometry.Orientation; +import javafx.geometry.Pos; +import javafx.scene.Cursor; +import javafx.scene.Parent; +import javafx.scene.control.Alert; +import javafx.scene.control.Alert.AlertType; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.scene.control.ScrollPane; +import javafx.scene.control.Separator; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableRow; +import javafx.scene.control.TableView; +import javafx.scene.control.TextField; +import javafx.scene.control.ToolBar; +import javafx.scene.control.Tooltip; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.effect.DropShadow; +import javafx.scene.effect.InnerShadow; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.ClipboardContent; +import javafx.scene.input.Dragboard; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import javafx.scene.input.TouchEvent; +import javafx.scene.input.TransferMode; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.Background; +import javafx.scene.layout.BackgroundImage; +import javafx.scene.layout.BackgroundPosition; +import javafx.scene.layout.BackgroundRepeat; +import javafx.scene.layout.BackgroundSize; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.ColumnConstraints; +import javafx.scene.layout.FlowPane; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Pane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.text.TextAlignment; +import javafx.stage.Screen; +import javafx.stage.Stage; +import javafx.util.Duration; + +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.ResourceBundle; + +/** + * Actions associated with the menu and its items. + * + * @author Zilvinas Ceikauskas + */ + +public class MenuController implements Initializable { + + /** + * Initializes switch names and other buttons. + */ + public enum Window { + EMPTY, DASHBOARD, PROFILES, MODULES, MILESTONES, CALENDAR, CHAT + } + + private Window current; + private boolean isNavOpen; + private boolean mouseDown = false; + private boolean initialLoad = true; + + // Screen size: + private double screenWidth = Screen.getPrimary().getVisualBounds().getWidth(); + private double screenHeight = Screen.getPrimary().getVisualBounds().getHeight(); + private double screenAverage = (screenWidth + screenHeight) / 2; + + // Shadows: + private int navShadowRadius = (int) (screenAverage * 0.03); + private int navShadowOffset = (int) (screenAverage * 0.01); + private DropShadow navShadow = new DropShadow(navShadowRadius, navShadowOffset, 0, Color.BLACK); + private DropShadow notifShadow = new DropShadow(screenAverage * 0.02, 0, 0.009, Color.BLACK); + private DropShadow moduleDefaultShadow = new DropShadow(screenAverage * 0.005, 0, 0, + Color.BLACK); + private DropShadow moduleHoverShadow = new DropShadow(screenAverage * 0.02, 0, 0, Color.BLACK); + private InnerShadow modulePressedShadow = new InnerShadow(screenAverage * 0.017, 0, 0, + Color.BLACK); + private Stage stage = null; + // Labels: + private Label welcome; + @FXML + private Label title; + + // Buttons: + @FXML + private Button openMenu; + @FXML + private Button showNotification; + @FXML + private Button showDash; + @FXML + private Button newProfile; + @FXML + private Button openProfile; + @FXML + private Button addActivity; + @FXML + private Button studyProfiles; + @FXML + private Button milestones; + @FXML + private Button modules; + @FXML + private Button calendar; + @FXML + private Button chat; + @FXML + private Button closeDrawer; + + // Panes: + @FXML + private AnchorPane navList; + @FXML + private AnchorPane notifications; + @FXML + private GridPane notificationList; + @FXML + private GridPane mainContent; + @FXML + private HBox topBox; + @FXML + private HBox exportCalBox; + + @FXML + private ToolBar toolBar; + + // chat variables + private static final BorderPane mainPane = new BorderPane(); + private final GridPane firstPane = new GridPane(); + private TextField tfName = new TextField(""); + private TextField tfHost = new TextField(""); + private final Label name = new Label("Your W Number: "); + private final Label host = new Label("Host W Number: "); + private final Button submitButton = new Button("Submit"); + private boolean calendarOpen = false; // Used to monitor status of calendar (open or closed) + private boolean chatConnection = true; + private Alert chatConnectionStatus = new Alert(AlertType.ERROR); + private Alert invalidInputAlert = new Alert(AlertType.ERROR); + private String userName; + private String hostName; + private int portNumber = 1111; + + Settings settings = new Settings(); + + /** + * Sets this.current to equal passed variable and calls this.main(). + */ + public void main(Window wind) { + this.current = wind; + this.main(); + this.applyTheme(); + } + + /** + * Main method containing switch statements. This checks to see if the + * calendar is open as well as loads to see if you need the profile, module, + * milestones, calendar, and chat. + */ + public void main() { + mainContent.setStyle(""); + if (isNavOpen) { + openMenu.fire(); + } + if (this.showNotification.getTranslateY() == 0 && !initialLoad) { + TranslateTransition closeNot = new TranslateTransition(new Duration(173), + notifications); + closeNot.setToY(-(notifications.getHeight() + this.navShadowRadius + 56 + 17)); + closeNot.play(); + } + initialLoad = false; + + this.updateNotifications(); + this.updateMenu(); + exportCalBox.managedProperty().bind(exportCalBox.visibleProperty()); + + // When user chooses different option in menu + // calendarOpen changes to monitor status within main window. + switch (this.current) { + case DASHBOARD: { + if (MainController.getSpc().getPlanner().getCurrentStudyProfile() != null) { + this.loadDashboard(); + calendarOpen = false; + } + break; + } + case PROFILES: { + this.loadStudyProfiles(); + calendarOpen = false; + break; + } + case MODULES: { + this.loadModules(); + calendarOpen = false; + break; + } + case MILESTONES: { + this.loadMilestones(); + calendarOpen = false; + break; + } + case CALENDAR: { + this.loadCalendar(); + calendarOpen = true; + break; + } + case CHAT: { + this.obtainUserInformation(); + calendarOpen = false; + break; + } + default: + calendarOpen = false; + break; + } + // Based on user choice of menu option "Export Calendar" button is shown/hidden + exportCalBox.setVisible(calendarOpen); + } + + /** + * Apply the users theme to the fxml. + */ + public void applyTheme() { + // Reload settings to make sure saved values are used + settings.loadSettings(); + // Make sure that a hex value representing a color exists + if (settings.isColorHex(settings.getToolBarColor())) { + this.toolBar.setStyle("" + + "-fx-background-color: #" + settings.getToolBarColor()); + } + if (settings.isColorHex(settings.getToolBarTextColor())) { + this.title.setStyle("" + + "-fx-font-family: Ariel" + + "; -fx-text-fill: #" + settings.getToolBarTextColor() + + "; -fx-font-size: 2.5em;"); + } + if (settings.isColorHex(settings.getToolBarIconColor())) { + this.openMenu.setStyle("" + + "-fx-background-image: " + + "url('/edu/wright/cs/raiderplanner/content/menu.png');" + + "; -fx-background-color: transparent" + + "; -fx-cursor: hand" + + "; -fx-effect: innershadow(gaussian , " + + "#" + settings.getToolBarIconColor() + ", 8, 1, 1, 1);"); + this.showNotification.setStyle("" + + "; -fx-background-color: transparent" + + "; -fx-cursor: hand" + + "; -fx-effect: innershadow(gaussian , " + + "#" + settings.getToolBarIconColor() + ", 8, 1, 1, 1);"); + this.calendar.setStyle("" + + "-fx-background-image: " + + "url('/edu/wright/cs/raiderplanner/content/calendar.png');" + + "; -fx-background-color: transparent" + + "; -fx-cursor: hand" + + "; -fx-effect: innershadow(gaussian , " + + "#" + settings.getToolBarIconColor() + ", 8, 1, 1, 1);"); + this.addActivity.setStyle("" + + "-fx-background-image: " + + "url('/edu/wright/cs/raiderplanner/content/addactivity_small.png');" + + "; -fx-background-color: transparent" + + "; -fx-cursor: hand" + + "; -fx-effect: innershadow(gaussian , " + + "#" + settings.getToolBarIconColor() + ", 8, 1, 1, 1);"); + } + } + + /** + * Display the Study Dashboard pane. + */ + public void loadDashboard() { + // set ToolTips + openMenu.setTooltip(new Tooltip("Menu")); + showNotification.setTooltip(new Tooltip("Notifications")); + addActivity.setTooltip(new Tooltip("Add activity")); + calendar.setTooltip(new Tooltip("Open Calendar")); + + // Update main pane: + this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); + this.topBox.getChildren().clear(); + this.topBox.getChildren().add(this.welcome); + this.title.setText("Study Dashboard"); + + FlowPane modulesPane = new FlowPane(); + mainContent.setStyle(""); + + + if (MainController.getSpc().getPlanner().getCurrentStudyProfile().getModules().length + == 0) { + VBox dashPic = new VBox(); + //dashPic.autosize(); + dashPic.getChildren().add(new ImageView(new + Image("/edu/wright/cs/raiderplanner/content/DashBoardHelp.png"))); + dashPic.setAlignment(Pos.CENTER); + this.mainContent.setStyle("-fx-background-color:#ffffff;"); + modulesPane.setStyle("-fx-background-color:#ffffff;"); + this.mainContent.add(dashPic, 1, 3); + } + + StudyProfile profile = MainController.getSpc().getPlanner().getCurrentStudyProfile(); + + // Display studyProfile: + Label studyProfile = new Label(profile.getName()); + studyProfile.getStyleClass().add("title"); + GridPane.setMargin(studyProfile, new Insets(10)); + this.mainContent.getColumnConstraints() + .add(new ColumnConstraints(Region.USE_COMPUTED_SIZE, Region.USE_COMPUTED_SIZE, + Region.USE_COMPUTED_SIZE, Priority.ALWAYS, HPos.CENTER, true)); + + Thread renderModules = new Thread(() -> { + Label oldLabel = new Label(this.welcome.getText()); + Thread sayLoading = new Thread(() -> { + this.welcome.setText(this.welcome.getText() + " LOADING..."); + }); + Platform.runLater(sayLoading); + + for (Module module : profile.getModules()) { + VBox vbox = new VBox(); + + // Set the width of the module to 15% of the screen resolution + if (screenWidth > screenHeight) { + vbox.setPrefWidth(screenWidth * 0.14); + } else { + // If device is in portrait mode, set vbox width based on height + vbox.setPrefWidth(screenHeight * 0.14); + } + // Set the height of the module to 112% of its width + vbox.setPrefHeight(vbox.getPrefWidth() * 1.12); + // Set margin between text and badge to 10% vbox width + vbox.setSpacing(vbox.getPrefWidth() * 0.1); + + vbox.setAlignment(Pos.CENTER); + vbox.setCursor(Cursor.HAND); + + Label nameLabel = new Label(module.getName()); + nameLabel.setTextAlignment(TextAlignment.CENTER); + + // Set left margin for title, which creates padding in case title is very long + VBox.setMargin(nameLabel, new Insets(0, 0, 0, vbox.getPrefWidth() * 0.04)); + + vbox.getChildren().add(nameLabel); + + BufferedImage buff = GanttishDiagram.getBadge(module.calculateProgress(), true, 1); + Image image = SwingFXUtils.toFXImage(buff, null); + Pane badge = new Pane(); + + // Set the distance from left edge to badge 17% of vbox width + VBox.setMargin(badge, new Insets(0, 0, 0, vbox.getPrefWidth() * 0.17)); + // Set the badge width to 66% that of vbox + badge.setPrefHeight(vbox.getPrefWidth() * 0.66); + + badge.setBackground(new Background(new BackgroundImage(image, + BackgroundRepeat.NO_REPEAT, BackgroundRepeat.NO_REPEAT, + BackgroundPosition.DEFAULT, new BackgroundSize(BackgroundSize.AUTO, + BackgroundSize.AUTO, false, false, true, false)))); + vbox.getChildren().add(badge); + + /* + * If mouse clicks on module, depress it. If mouse leaves module while depressed, + * undepress button. If mouse re-enters, then re-depress module. If mouse is not + * depressed when it enters module, show hover effect. + */ + vbox.addEventHandler(MouseEvent.MOUSE_ENTERED, e -> { + if (mouseDown) { + vbox.setEffect(this.modulePressedShadow); + } else { + vbox.setEffect(this.moduleHoverShadow); + } + }); + vbox.addEventHandler(MouseEvent.MOUSE_EXITED, e -> { + vbox.setEffect(this.moduleDefaultShadow); + }); + vbox.addEventHandler(MouseEvent.MOUSE_PRESSED, e -> { + if (e.getButton() == MouseButton.PRIMARY) { + vbox.setEffect(this.modulePressedShadow); + mouseDown = true; + } + }); + vbox.addEventHandler(MouseEvent.MOUSE_RELEASED, e -> { + if (e.getButton() == MouseButton.PRIMARY) { + vbox.setEffect(this.moduleDefaultShadow); + mouseDown = false; + } + }); + vbox.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> { + if (e.getButton() == MouseButton.PRIMARY) { + module.open(this.current); + } + }); + vbox.setOnTouchPressed(new EventHandler() { + @Override + public void handle(TouchEvent event) { + vbox.setEffect(modulePressedShadow); + } + }); + vbox.setOnTouchReleased(new EventHandler() { + @Override + public void handle(TouchEvent event) { + vbox.setEffect(moduleDefaultShadow); + } + }); + + vbox.setEffect(this.moduleDefaultShadow); + vbox.setStyle("-fx-background-color: white"); + + Thread addModule = new Thread(() -> { + modulesPane.getChildren().add(vbox); + }); + Platform.runLater(addModule); + + // Ensure shadows don't overlap with edge of FlowPane + FlowPane.setMargin(vbox, new Insets(screenHeight * 0.033, 0, screenHeight * 0.022, + screenWidth * 0.037)); + } + Thread removeLoading = new Thread(() -> { + this.welcome.setText(oldLabel.getText()); + }); + Platform.runLater(removeLoading); + }); + renderModules.start(); + + /* + * Allow modules to be scrollable if window is too small to display them all on screen + * simultaneously. + */ + ScrollPane moduleBox = new ScrollPane(); + moduleBox.setContent(modulesPane); + moduleBox.setStyle("-fx-background-color: transparent"); + moduleBox.setFitToHeight(true); + moduleBox.setFitToWidth(true); + GridPane.setColumnSpan(moduleBox, GridPane.REMAINING); + this.mainContent.addRow(2, moduleBox); + } + + /** + * Handles when the user selects the new profile button on the main screen + * and creates a profile when this occurs. + */ + public void createNewProfile() { + MainController.save(); + File plannerFile = null; + try { + Account newAccount = MainController.ui.createAccount(false); + if (newAccount != null) { + StudyPlannerController study = new StudyPlannerController(newAccount); + // Welcome notification: + Notification not = new Notification("Welcome!", new GregorianCalendar(), + "Thank you for using RaiderPlanner!"); + study.getPlanner().addNotification(not); + MainController.setSpc(study); + plannerFile = MainController.ui.savePlannerFileDialog(); + } + } catch (Exception e) { + e.printStackTrace(); + } + if (plannerFile != null) { + if (plannerFile.getParentFile().exists()) { + if (plannerFile.getParentFile().canRead()) { + if (plannerFile.getParentFile().canWrite()) { + MainController.setPlannerFile(plannerFile); + MainController.save(); + } else { + UiManager.reportError("Directory can not be written to."); + } + } else { + UiManager.reportError("Directory cannot be read from."); + } + + } else { + UiManager.reportError("Directory does not exist."); + } + } + + if (plannerFile != null) { + MainController.loadFile(plannerFile); + } + + try { + this.main(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + /** + * Handles when the user selects the open profile button on the main screen. + */ + public void openProfile() { + MainController.save(); + File plannerFile = MainController.ui.loadPlannerFileDialog(); + MainController.setPlannerFile(plannerFile); + if (plannerFile != null) { + if (plannerFile.exists()) { + if (plannerFile.canRead()) { + if (plannerFile.canWrite()) { + MainController.setPlannerFile(plannerFile); + } else { + UiManager.reportError("Cannot write to file."); + } + } else { + UiManager.reportError("Cannot read file."); + } + + } else { + UiManager.reportError("File does not exist."); + } + } + MainController.loadFile(plannerFile); + try { + MainController.ui.reloadMainMenu(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + /** + * Display the 'Add Activity' window. + * @throws IOException if you can not open the file + * @throws Exception if other unexpected issues occur + */ + public void addActivity() { + try { + Activity activity = MainController.ui.addActivity(); + if (activity != null) { + MainController.getSpc().addActivity(activity); + } + + } catch (IOException e) { + UiManager.reportError("Unable to open View file"); + } catch (Exception e) { + UiManager.reportError(e.getMessage()); + } + } + + /** + * Display the Milestones pane. + */ + public void loadMilestones() { + // Update main pane: + this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); + this.topBox.getChildren().clear(); + this.title.setText("Milestones"); + + //Add instructions for the current page + HBox instruction = new HBox(); + GridPane.setHgrow(instruction, Priority.ALWAYS); + instruction.setSpacing(50); + instruction.setPadding(new Insets(5, 5, 10, 0)); + this.welcome = new Label( + "Welcome " + MainController.getSpc().getPlanner().getUserName() + + "! Use Milestones " + "to track important tasks."); + this.welcome.setPadding(new Insets(10, 15, 10, 15)); + this.topBox.getChildren().add(this.welcome); + this.mainContent.setVgap(10); + this.mainContent.setPadding(new Insets(15)); + + // Columns: + TableColumn nameColumn = new TableColumn<>("Milestone"); + nameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); + + TableColumn deadlineColumn = new TableColumn<>("Deadline"); + deadlineColumn.setCellValueFactory(new PropertyValueFactory<>("deadline")); + deadlineColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); + + TableColumn completedColumn = new TableColumn<>("Tasks completed"); + completedColumn.setCellValueFactory(new PropertyValueFactory<>("taskCompletedAsString")); + completedColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); + + TableColumn progressColumn = new TableColumn<>("Progress"); + progressColumn.setCellValueFactory(new PropertyValueFactory<>("progressPercentage")); + progressColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); + + ArrayList> colList = new ArrayList<>( + Arrays.asList(nameColumn, deadlineColumn, completedColumn, progressColumn)); + + ObservableList list = FXCollections.observableArrayList( + MainController.getSpc().getPlanner().getCurrentStudyProfile().getMilestones()); + + // Create a table: + TableView table = new TableView<>(); + table.setItems(list); + table.getColumns().addAll(colList); + table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); + GridPane.setHgrow(table, Priority.ALWAYS); + GridPane.setVgrow(table, Priority.ALWAYS); + // ================= + + // Set click event: + table.setRowFactory(e -> { + TableRow row = new TableRow() { + @Override + protected void updateItem(final Milestone item, final boolean empty) { + super.updateItem(item, empty); + // If Milestone completed, mark: + if (!empty && item != null && item.isComplete()) { + this.getStyleClass().add("current-item"); + } + } + }; + row.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> { + if (event.getButton() == MouseButton.PRIMARY) { + if (this.isNavOpen) { + closeDrawer.fire(); + } + if (this.showNotification.getTranslateY() == 0) { + TranslateTransition closeNot = new TranslateTransition(new Duration(173), + notifications); + closeNot.setToY(-(notifications.getHeight() + this.navShadowRadius + 56 + + 17)); + closeNot.play(); + } + + if (!row.isEmpty() && event.getButton() == MouseButton.PRIMARY + && event.getClickCount() == 2) { + try { + MainController.ui.milestoneDetails(row.getItem()); + this.main(); + } catch (IOException e1) { + UiManager.reportError("Unable to open View file"); + } + } + } + }); + return row; + }); + + this.mainContent.addRow(2, table); + this.mainContent.getStyleClass().add("list-item"); + GridPane.setColumnSpan(table, GridPane.REMAINING); + + // Actions toolbar: + HBox actions = new HBox(); + GridPane.setHgrow(actions, Priority.ALWAYS); + actions.setSpacing(5); + actions.setPadding(new Insets(5, 5, 10, 0)); + + // Buttons: + Button add = new Button("Add a new Milestone"); + Button remove = new Button("Remove"); + remove.setDisable(true); + + // Bind properties on buttons: + remove.disableProperty().bind(new BooleanBinding() { + { + bind(table.getSelectionModel().getSelectedItems()); + } + + @Override + protected boolean computeValue() { + return !(list.size() > 0 && table.getSelectionModel().getSelectedItem() != null); + } + }); + + // Bind actions on buttons: + add.setOnAction(e -> { + try { + Milestone milestone = MainController.ui.addMilestone(); + if (milestone != null) { + list.add(milestone); + MainController.getSpc().addMilestone(milestone); + } + } catch (IOException e1) { + UiManager.reportError("Unable to open View file"); + } + }); + + remove.setOnAction(e -> { + if (UiManager.confirm("Are you sure you want to remove this milestone?")) { + Milestone mm = table.getSelectionModel().getSelectedItem(); + list.remove(mm); + MainController.getSpc().removeMilestone(mm); + } + }); + + actions.getChildren().addAll(add, remove); + + mainContent.addRow(3, actions); + } + + /** + * Display the Calendar pane. + */ + public void loadCalendar() { + // Update main pane: + this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); + this.topBox.getChildren().clear(); + this.title.setText("Calendar"); + + //Add instructions for the current page + HBox instruction = new HBox(); + GridPane.setHgrow(instruction, Priority.ALWAYS); + instruction.setSpacing(50); + instruction.setPadding(new Insets(5, 5, 10, 0)); + this.welcome = new Label( + "Welcome " + MainController.getSpc().getPlanner().getUserName() + + "! See your tasks " + "here."); + this.welcome.setPadding(new Insets(10, 15, 10, 15)); + this.topBox.getChildren().add(this.welcome); + this.mainContent.setVgap(10); + this.mainContent.setPadding(new Insets(15)); + CalendarController myCalendar = new CalendarController(); + this.mainContent.getChildren().add(myCalendar.getLayout()); + + } + + /** + * Display the Study Profiles pane. + */ + public void loadStudyProfiles() { + // Update main pane: + this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); + this.topBox.getChildren().clear(); + this.title.setText("Study Profiles"); + + //Add instructions for the current page + HBox instruction = new HBox(); + GridPane.setHgrow(instruction, Priority.ALWAYS); + instruction.setSpacing(50); + instruction.setPadding(new Insets(5, 5, 10, 0)); + this.welcome = new Label("Welcome " + MainController.getSpc().getPlanner().getUserName() + + "! Here you can view your study profiles. " + + "Double-click on a profile to see more informaiton."); + this.welcome.setPadding(new Insets(10, 15, 10, 15)); + this.topBox.getChildren().add(this.welcome); + this.mainContent.setVgap(10); + this.mainContent.setPadding(new Insets(15)); + + // Columns: + TableColumn nameColumn = new TableColumn<>("Profile name"); + nameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); + + TableColumn yearColumn = new TableColumn<>("Year"); + yearColumn.setCellValueFactory(new PropertyValueFactory<>("year")); + yearColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); + + TableColumn semesterColumn = new TableColumn<>("Semester"); + semesterColumn.setCellValueFactory(new PropertyValueFactory<>("semesterNo")); + semesterColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); + + ArrayList> colList = new ArrayList<>( + Arrays.asList(nameColumn, yearColumn, semesterColumn)); + + ObservableList list = FXCollections + .observableArrayList(MainController.getSpc().getPlanner().getStudyProfiles()); + + // Create a table: + + TableView table = new TableView<>(); + table.setItems(list); + //limit the number of rows to allow space for buttons below the table + GridPane.setRowSpan(table, 20); + table.getColumns().addAll(colList); + table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); + GridPane.setHgrow(table, Priority.ALWAYS); + GridPane.setVgrow(table, Priority.ALWAYS); + + // Set click event: + table.setRowFactory(e -> { + TableRow row = new TableRow() { + @Override + protected void updateItem(final StudyProfile item, final boolean empty) { + super.updateItem(item, empty); + // If current Profile, mark: + if (!empty && item != null && item.isCurrent()) { + this.getStyleClass().add("current-item"); + } + } + }; + row.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> { + if (event.getButton() == MouseButton.PRIMARY) { + if (this.isNavOpen) { + closeDrawer.fire(); + } + if (this.showNotification.getTranslateY() == 0) { + TranslateTransition closeNot = new TranslateTransition(new Duration(173), + notifications); + closeNot.setToY(-(notifications.getHeight() + this.navShadowRadius + 56 + + 17)); + closeNot.play(); + } + if (!row.isEmpty() && event.getButton() == MouseButton.PRIMARY + && event.getClickCount() == 2) { + try { + this.main(); + loadStudyProfile(row.getItem()); + } catch (IOException e1) { + UiManager.reportError("Unable to open View file"); + } + } + } + }); + return row; + }); + + this.mainContent.addRow(2, table); + GridPane.setColumnSpan(table, GridPane.REMAINING); + this.mainContent.getStyleClass().add("list-item"); + } + + /** + * Display the StudyProfile details. + */ + public void loadStudyProfile(StudyProfile profile) throws IOException { + StudyProfileController spc = new StudyProfileController(profile, + this); + // Load in the .fxml file: + FXMLLoader loader = new FXMLLoader(getClass().getResource( + "/edu/wright/cs/raiderplanner/view/StudyProfile.fxml")); + loader.setController(spc); + Parent root = loader.load(); + this.mainContent.add(root,0,25); + } + + /** + * Display the Modules pane. + */ + public void loadModules() { + // Update main pane: + this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); + this.topBox.getChildren().clear(); + this.title.setText("Modules"); + + //Add instructions for the current page + HBox instruction = new HBox(); + GridPane.setHgrow(instruction, Priority.ALWAYS); + instruction.setSpacing(50); + instruction.setPadding(new Insets(5, 5, 10, 0)); + this.welcome = new Label( + "Welcome " + MainController.getSpc().getPlanner().getUserName() + + "! Modules shows your current courses. " + + "Double-click on a course for more information."); + this.welcome.setPadding(new Insets(10, 15, 10, 15)); + this.topBox.getChildren().add(this.welcome); + this.mainContent.setVgap(10); + this.mainContent.setPadding(new Insets(15)); + // Columns: + TableColumn codeColumn = new TableColumn<>("Module code"); + codeColumn.setCellValueFactory(new PropertyValueFactory<>("moduleCode")); + + TableColumn nameColumn = new TableColumn<>("Module name"); + nameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); + + TableColumn timeSpent = new TableColumn<>("Time spent"); + timeSpent.setCellValueFactory(new PropertyValueFactory("timeSpent") { + @Override + public ObservableValue call( + TableColumn.CellDataFeatures param) { + return new SimpleIntegerProperty( + MainController.getSpc().getPlanner().getTimeSpent(param.getValue())); + } + }); + timeSpent.setStyle("-fx-alignment: CENTER-RIGHT;"); + + ArrayList> colList = new ArrayList<>( + Arrays.asList(codeColumn, nameColumn, timeSpent)); + + ObservableList list = FXCollections.observableArrayList( + MainController.getSpc().getPlanner().getCurrentStudyProfile().getModules()); + + // Create a table: + TableView table = new TableView<>(); + table.setItems(list); + //limit the number of rows to allow space for buttons below the table + GridPane.setRowSpan(table, 20); + table.getColumns().addAll(colList); + table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); + GridPane.setHgrow(table, Priority.ALWAYS); + GridPane.setVgrow(table, Priority.ALWAYS); + GridPane.setColumnSpan(table, GridPane.REMAINING); + + // Set click event: + table.setRowFactory(e -> { + TableRow row = new TableRow<>(); + row.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> { + if (event.getButton() == MouseButton.PRIMARY) { + if (this.isNavOpen) { + closeDrawer.fire(); + } + if (this.showNotification.getTranslateY() == 0) { + TranslateTransition closeNot = new TranslateTransition(new Duration(173), + notifications); + closeNot.setToY(-(notifications.getHeight() + this.navShadowRadius + 56 + + 17)); + closeNot.play(); + } + + if (!row.isEmpty() && event.getButton() == MouseButton.PRIMARY + && event.getClickCount() == 2) { + this.loadModule(row.getItem(), this.current, null); + } + } + }); + return row; + }); + this.mainContent.addRow(2, table); + this.mainContent.getStyleClass().add("list-item"); + } + + /** + * Display the Module pane. + */ + public void loadModule(Module module, Window previousWindow, ModelEntity previous) { + // Update main pane: + this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); + this.topBox.getChildren().clear(); + this.title.setText(module.getModuleCode() + " " + module.getName()); + // ================= + + //Add instructions for the current page + HBox instruction = new HBox(); + GridPane.setHgrow(instruction, Priority.ALWAYS); + instruction.setSpacing(50); + instruction.setPadding(new Insets(5, 5, 10, 0)); + this.welcome = new Label( + "Welcome " + MainController.getSpc().getPlanner().getUserName() + + "! Use this page to view modules. " + + "Double-click on a module to see more options like " + + "generate Gantish Diagram, or add Tasks/Requirements."); + this.welcome.setPadding(new Insets(10, 15, 10, 15)); + this.topBox.getChildren().add(this.welcome); + this.mainContent.setVgap(10); + this.mainContent.setPadding(new Insets(15)); + + // Create a back button: + this.backButton(previousWindow, previous); + // ================= + + // Create a details pane: + VBox detailsBox = new VBox(5); + Label details = new Label(module.getDetails().getAsString()); + details.setWrapText(true); + detailsBox.getChildren().addAll(new Label("Organised by: " + module.getOrganiser()), + details); + GridPane.setVgrow(detailsBox, Priority.SOMETIMES); + GridPane.setHgrow(detailsBox, Priority.ALWAYS); + GridPane.setColumnSpan(detailsBox, GridPane.REMAINING); + + mainContent.addRow(2, detailsBox); + + // Assignments: + TableColumn nameColumn = new TableColumn<>("Assignment"); + nameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); + + TableColumn deadlineColumn = new TableColumn<>("Date"); + deadlineColumn.setCellValueFactory( + new PropertyValueFactory("deadlineString") { + @Override + public ObservableValue call( + TableColumn.CellDataFeatures param) { + + SimpleStringProperty value = new SimpleStringProperty(); + // TODO - find a way to get rid of this instanceof + if (param.getValue() instanceof Coursework) { + Coursework cw = (Coursework) param.getValue(); + value.setValue(cw.getDeadlineString()); + } else if (param.getValue() instanceof Exam) { + Exam exam = (Exam) param.getValue(); + value.setValue(exam.getTimeSlot().getDateString()); + } + return value; + + } + }); + deadlineColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); + + TableColumn weightingColumn = new TableColumn<>("Weighting"); + weightingColumn.setCellValueFactory(new PropertyValueFactory<>("weighting")); + weightingColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); + + ArrayList> colList = new ArrayList<>( + Arrays.asList(nameColumn, deadlineColumn, weightingColumn)); + + ObservableList list = FXCollections + .observableArrayList(module.getAssignments()); + + // Create a moduleContent: + TableView moduleContent = new TableView<>(); + moduleContent.setItems(list); + moduleContent.getColumns().addAll(colList); + moduleContent.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); + GridPane.setHgrow(moduleContent, Priority.ALWAYS); + GridPane.setVgrow(moduleContent, Priority.ALWAYS); + + + // Set click event: + moduleContent.setRowFactory(e -> { + TableRow row = new TableRow<>(); + row.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> { + if (!row.isEmpty() && event.getButton() == MouseButton.PRIMARY + && event.getClickCount() == 2) { + this.loadAssignment(row.getItem(), Window.EMPTY, module); + } + }); + return row; + }); + this.mainContent.addRow(3, moduleContent); + GridPane.setColumnSpan(moduleContent, GridPane.REMAINING); + } + + /** + * This method will create a window that will prompt the user for a username and host name. If a + * name is not entered then a username is randomly chosen. When the submit button is pressed a + * new interface will be loaded which is the chat window. + */ + public void obtainUserInformation() { + + this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); + this.topBox.getChildren().clear(); + this.title.setText("Chat"); + this.mainContent.getChildren().addAll(firstPane); + createFirstWindow(); + submitButtonAction(); + } + + /** + * This method will create the peer to peer chat window. It will load the text area where the + * user will see messages from peers and a place for the user to send his or her own message. + */ + public void loadChatWindow() { + + this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); + this.topBox.getChildren().clear(); + this.title.setText("Chat"); + this.mainContent.getChildren().addAll(mainPane); + ChatController.createUserMessagePane(); + ChatController.createMainPane(); + ChatController.sendButtonAction(userName); + } + + /** + * This will load all the textfields,labels and buttons for the window that prompts the user for + * his or her username and host name and sets hint for W number format. + */ + public void createFirstWindow() { + firstPane.add(name, 0, 0); + firstPane.add(tfName, 1, 0); + firstPane.add(host, 0, 1); + firstPane.add(tfHost, 1, 1); + firstPane.add(submitButton, 1, 2); + tfName.setPromptText("W Number (ex: w000xxx)"); + tfHost.setPromptText("W Number (ex: w000xxx)"); + } + + /** + * Determines if the user has entered a valid username and sets the style accordingly. + * @return True if the user entered a valid username. + */ + public boolean validateTfName() { + if (tfName.getText().trim().length() == 7) { + if (tfName.getText().trim().charAt(0) != 'w') { + return false; + } else { + for (int i = 1; i < 4; ++i) { + if (!Character.isDigit(tfName.getText().trim().charAt(i))) { + return false; + } + } + for (int i = 4; i < 7; ++i) { + if (!Character.isLetter(tfName.getText().trim().charAt(i))) { + return false; + } else if (!Character.isLowerCase(tfName.getText().trim().charAt(i))) { + return false; + } + } + } + } else { + return false; + } + return true; + } + + /** + * Determines if the user has entered a valid host name sets the style accordingly. + * @return True if the user entered a valid host name. + */ + public boolean validateTfHost() { + if (tfHost.getText().trim().length() == 7) { + if (tfHost.getText().trim().charAt(0) != 'w') { + return false; + } else { + for (int i = 1; i < 4; ++i) { + if (!Character.isDigit(tfHost.getText().trim().charAt(i))) { + return false; + } + } + for (int i = 4; i < 7; ++i) { + if (!Character.isLetter(tfHost.getText().trim().charAt(i))) { + return false; + } else if (!Character.isLowerCase(tfHost.getText().trim().charAt(i))) { + return false; + } + } + } + } else { + return false; + } + return true; + } + + /** + * This will take in the action of when the submit button is pressed. The submit button is for + * the chat window where the user inputs his or her information. If the user does not enter a + * valid user W number/host W number, an error will pop up notifying them to enter/correct + * those values. Then the chat window will be loaded. + */ + public void submitButtonAction() { + submitButton.setOnAction((ActionEvent exception1) -> { + String invalidMessage = ""; + boolean validTfName = true; + boolean validTfHost = true; + if (chatConnection) { + if (!validateTfName()) { + invalidMessage += "Please enter a vaid user W Number\n"; + validTfName = false; + } + if (!validateTfHost()) { + invalidMessage += "Please enter a valid host W Number\n"; + validTfHost = false; + } else { + userName = tfName.getText(); + hostName = tfHost.getText(); + } + } + if (!validTfName || !validTfHost) { + invalidInputAlert.setHeaderText("Invalid Entries: Chat Connection Unsuccessful"); + invalidInputAlert.setContentText(invalidMessage); + invalidInputAlert.showAndWait(); + } else { + loadChatWindow(); + } + }); + } + + /** + * This will set the username for the peer-to-peer chat. + */ + public void setUserName(String user) { + userName = user; + } + + /** + * This will set the PortNumber for the peer-to-peer chat. + */ + public void setPortNumber(int userPort) { + portNumber = userPort; + } + + /** + * Returns the currently registered user's chat ID. + * + * @return the currently registered user's chat ID. + */ + public String getUserName() { + return userName; + } + + /** + * Returns the current host name registered to the chat user. + * + * @return the current host name registered to the chat user. + */ + public String getHostName() { + return hostName; + } + + /** + * Returns the current main pane. + * @return the current main pane + */ + public static BorderPane getMainPane() { + return mainPane; + } + + /** + * Display the Assignment pane. + */ + public void loadAssignment(Assignment assignment, Window previousWindow, ModelEntity previous) { + // Update main pane: + this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); + this.topBox.getChildren().clear(); + // ================= + + // Create a back button: + this.backButton(previousWindow, previous); + + // Display modules: + Label assignments = new Label(assignment.getName()); + assignments.getStyleClass().add("title"); + this.mainContent.addRow(1, assignments); + GridPane.setColumnSpan(assignments, GridPane.REMAINING); + + // Ganttish chart button: + Button gantt = new Button("Generate a Ganttish Diagram"); + gantt.setOnAction(e -> showGantt(assignment, previousWindow, previous)); + GridPane.setHalignment(gantt, HPos.RIGHT); + GridPane.setColumnSpan(gantt, GridPane.REMAINING); + this.mainContent.add(gantt, 0, 1); + + // Create a details pane: + VBox detailsBox = new VBox(5); + Label details = new Label(assignment.getDetails().getAsString()); + details.setWrapText(true); + String date = ""; + if (assignment instanceof Coursework) { + Coursework cc = (Coursework) assignment; + date = "Deadline: " + cc.getDeadlineString(); + } else if (assignment instanceof Exam) { + Exam e1 = (Exam) assignment; + date = "Date: " + e1.getTimeSlot().getDateString(); + } + detailsBox.getChildren().addAll(new Label("Weighting: " + assignment.getWeighting()), + new Label(date), new Label("Set by: " + assignment.getSetBy().getPreferredName()), + new Label("Marked by: " + assignment.getMarkedBy().getPreferredName()), + new Label("Reviewed by: " + assignment.getReviewedBy().getPreferredName()), + details); + GridPane.setVgrow(detailsBox, Priority.SOMETIMES); + GridPane.setHgrow(detailsBox, Priority.ALWAYS); + + mainContent.addRow(2, detailsBox); + GridPane.setColumnSpan(detailsBox, GridPane.REMAINING); + + // content pane: + GridPane content = new GridPane(); + GridPane.setVgrow(content, Priority.ALWAYS); + GridPane.setHgrow(content, Priority.ALWAYS); + GridPane.setColumnSpan(content, GridPane.REMAINING); + content.setVgap(5); + + // Requirements columns: + TableColumn reqNameColumn = new TableColumn<>("Requirement"); + reqNameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); + + TableColumn remainingColumn = new TableColumn<>("Remaining"); + remainingColumn.setCellValueFactory(new PropertyValueFactory<>("remainingQuantity")); + remainingColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); + + TableColumn typeColumn = new TableColumn<>("Quantity type"); + typeColumn.setCellValueFactory(new PropertyValueFactory<>("quantityType")); + + ArrayList> colList = new ArrayList<>( + Arrays.asList(reqNameColumn, remainingColumn, typeColumn)); + + ObservableList requirementList = FXCollections + .observableArrayList(assignment.getRequirements()); + + // Create Requirements table: + TableView requirements = new TableView<>(); + requirements.setItems(requirementList); + requirements.getColumns().addAll(colList); + requirements.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); + GridPane.setHgrow(requirements, Priority.ALWAYS); + GridPane.setVgrow(requirements, Priority.ALWAYS); + + // Set RowFactory: + requirements + .setRowFactory(e -> MenuController.requirementRowFactory(requirements, assignment)); + // ================= + + content.addColumn(0, requirements); + + // Actions toolbar: + HBox actionsReq = new HBox(); + GridPane.setHgrow(actionsReq, Priority.ALWAYS); + actionsReq.setSpacing(5); + actionsReq.setPadding(new Insets(5, 5, 10, 0)); + + // Buttons: + Button addNewReq = new Button("Add a new requirement"); + + Button deleteReq = new Button("Remove"); + deleteReq.setDisable(true); + + // Bind properties on buttons: + deleteReq.disableProperty().bind(new BooleanBinding() { + { + bind(requirements.getSelectionModel().getSelectedItems()); + } + + @Override + protected boolean computeValue() { + return !(requirementList.size() > 0 + && requirements.getSelectionModel().getSelectedItem() != null); + } + }); + + // Bind actions on buttons: + addNewReq.setOnAction(e -> { + try { + Requirement req = MainController.ui.addRequirement(); + if (req != null) { + requirementList.add(req); + assignment.addRequirement(req); + requirements.refresh(); + } + } catch (IOException e1) { + UiManager.reportError("Unable to open View file"); + } catch (Exception e1) { + UiManager.reportError(e1.getMessage()); + } + }); + + deleteReq.setOnAction(e -> { + if (UiManager.confirm("Are you sure you want to remove this requirement?")) { + Requirement rr = requirements.getSelectionModel().getSelectedItem(); + requirementList.remove(rr); + assignment.removeRequirement(rr); + requirements.refresh(); + } + }); + + actionsReq.getChildren().addAll(addNewReq, deleteReq); + + content.add(actionsReq, 0, 1); + + // Tasks columns: + TableColumn nameColumn = new TableColumn<>("Task"); + nameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); + + TableColumn deadlineColumn = new TableColumn<>("Deadline"); + deadlineColumn.setCellValueFactory(new PropertyValueFactory<>("deadline")); + deadlineColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); + + TableColumn canComplete = new TableColumn<>("Can be completed?"); + canComplete.setCellValueFactory(new PropertyValueFactory<>("possibleToComplete")); + canComplete.setStyle("-fx-alignment: CENTER-RIGHT;"); + + ArrayList> taskColList = new ArrayList<>( + Arrays.asList(nameColumn, deadlineColumn, canComplete)); + + ObservableList list = FXCollections.observableArrayList(assignment.getTasks()); + + // Create Tasks table: + TableView tasks = new TableView<>(); + tasks.setItems(list); + tasks.getColumns().addAll(taskColList); + tasks.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); + GridPane.setHgrow(tasks, Priority.ALWAYS); + GridPane.setVgrow(tasks, Priority.ALWAYS); + + // Set click event: + tasks.setRowFactory(e -> { + TableRow row = new TableRow() { + @Override + protected void updateItem(final Task item, final boolean empty) { + super.updateItem(item, empty); + // If completed, mark: + if (!empty && item != null && item.isCheckedComplete()) { + this.getStyleClass().add("current-item"); + } else { + this.getStyleClass().remove("current-item"); + } + } + }; + row.setOnMouseClicked(event -> { + if (!row.isEmpty() && event.getButton() == MouseButton.PRIMARY + && event.getClickCount() == 2) { + try { + MainController.ui.taskDetails(row.getItem()); + tasks.refresh(); + } catch (IOException e1) { + UiManager.reportError("Unable to open view file"); + } + } + }); + return row; + }); + + content.addColumn(1, tasks); + + // Actions toolbar: + HBox actionsTask = new HBox(); + GridPane.setHgrow(actionsTask, Priority.ALWAYS); + actionsTask.setSpacing(5); + actionsTask.setPadding(new Insets(5, 5, 10, 0)); + + // Buttons: + Button check = new Button("Toggle complete"); + check.getStyleClass().add("set-button"); + check.setDisable(true); + + Button delete = new Button("Remove"); + delete.setDisable(true); + Button addNew = null; + addNew = new Button("Add a new task"); + // Bind properties on buttons: + delete.disableProperty().bind(new BooleanBinding() { + { + bind(tasks.getSelectionModel().getSelectedItems()); + } + + @Override + protected boolean computeValue() { + return !(list.size() > 0 && tasks.getSelectionModel().getSelectedItem() != null); + } + }); + + check.disableProperty().bind(new BooleanBinding() { + { + bind(tasks.getSelectionModel().getSelectedItems()); + } + + @Override + protected boolean computeValue() { + return !(list.size() > 0 && tasks.getSelectionModel().getSelectedItem() != null + && tasks.getSelectionModel().getSelectedItem().canCheckComplete()); + } + }); + + // Bind actions on buttons: + addNew.setOnAction(e -> { + try { + Task task = MainController.ui.addTask(); + if (task != null) { + list.add(task); + assignment.addTask(task); + } + this.updateMenu(); + } catch (IOException e1) { + UiManager.reportError("Unable to open View file"); + } catch (Exception e1) { + UiManager.reportError(e1.getMessage()); + } + }); + + check.setOnAction(e -> { + tasks.getSelectionModel().getSelectedItem().toggleComplete(); + tasks.refresh(); + }); + + delete.setOnAction(e -> { + if (UiManager.confirm("Are you sure you want to remove this task?")) { + Task tt = tasks.getSelectionModel().getSelectedItem(); + list.remove(tt); + assignment.removeTask(tt); + this.updateMenu(); + } + }); + + // Gap: + HBox gap = new HBox(); + HBox.setHgrow(gap, Priority.ALWAYS); + + actionsTask.getChildren().addAll(addNew, gap, check, delete); + + content.add(actionsTask, 1, 1); + + this.mainContent.addRow(3, content); + GridPane.setColumnSpan(content, GridPane.REMAINING); + } + + /** + * Handles the 'Mark all as read' button event. + */ + public void handleMarkAll() { + Notification[] nots = MainController.getSpc().getPlanner().getUnreadNotifications(); + // Mark all notifications as read: + for (int i = 0; i < nots.length; ++i) { + int index = this.notificationList.getChildren().size() - 1 - i; + nots[i].read(); + // Remove cursor: + if (nots[i].getLink() == null) { + this.notificationList.getChildren().get(index).setCursor(Cursor.DEFAULT); + } + + // Change style: + this.notificationList.getChildren().get(index).getStyleClass().remove("unread-item"); + } + + // Handle styles: + this.showNotification.getStyleClass().remove("unread-button"); + if (!this.showNotification.getStyleClass().contains("read-button")) { + this.showNotification.getStyleClass().add("read-button"); + } + } + + /** + * Handles clicking on a specific notification. + * + * @param id + * The identifier of the notification which was clicked. + */ + public void handleRead(int id) { + // Get notification: + int idInList = MainController.getSpc().getPlanner().getNotifications().length - 1 - id; + Notification not = MainController.getSpc().getPlanner().getNotifications()[idInList]; + + // If not read: + if (!not.isRead()) { + // Mark notification as read: + not.read(); + + // Swap styles: + this.notificationList.getChildren().get(id).getStyleClass().remove("unread-item"); + if (MainController.getSpc().getPlanner().getUnreadNotifications().length <= 0) { + this.showNotification.getStyleClass().remove("unread-button"); + if (!this.showNotification.getStyleClass().contains("read-button")) { + this.showNotification.getStyleClass().add("read-button"); + } + } + + if (not.getLink() == null) { + this.notificationList.getChildren().get(id).setCursor(Cursor.DEFAULT); + } + } + + if (not.getLink() != null) { + not.getLink().open(this.current); + } + } + + /** + * Handles the 'Import HUB file' event. + */ + public void importFile() { + if (MainController.importFile()) { + UiManager.reportSuccess("File imported successfully!"); + } + this.main(); + } + + /** + * Handles the 'Settings' event. + * @throws Exception - Throws RaiderException + */ + public void showSettings() throws Exception { + initialLoad = true; // Required so the notifications don't appear. + MainController.showSettings(); + } + + /** + * Handles the 'Help' event. + */ + public void openHelpPage() { + MainController.openHelpPage(); + } + + /** + * Handles 'Export Calendar' event. + */ + public void exportCalendar() { + MainController.exportCalendar(); + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + this.prepareAnimations(); + this.isNavOpen = false; + + // Set shadows + notifications.setEffect(notifShadow); + navList.setEffect(navShadow); + + // Set button actions: + this.closeDrawer.setOnAction(e -> openMenu.fire()); + this.showDash.setOnAction(e -> this.main(Window.DASHBOARD)); + this.studyProfiles.setOnAction(e -> this.main(Window.PROFILES)); + this.modules.setOnAction(e -> this.main(Window.MODULES)); + this.milestones.setOnAction(e -> this.main(Window.MILESTONES)); + this.calendar.setOnAction(e -> this.main(Window.CALENDAR)); + this.chat.setOnAction(e -> this.main(Window.CHAT)); + + // Set nav to close when clicking outside of it + this.mainContent.addEventHandler(MouseEvent.MOUSE_PRESSED, e -> { + if (e.getButton() == MouseButton.PRIMARY) { + if (this.showNotification.getTranslateY() == 0) { + TranslateTransition closeNot = new TranslateTransition(new Duration(173), + notifications); + closeNot.setToY(-(notifications.getHeight() + this.navShadowRadius + 56 + 17)); + closeNot.play(); + } + if (this.isNavOpen) { + this.openMenu.fire(); + } + } + }); + + /* + * Welcome text. Displays the appropriate welcoming message depending on if the user + * is new or a returning user. Also takes into account if the user entered their + * name or not during account creation. + */ + if (MainController.getSpc().getPlanner().getCurrentStudyProfile() != null) { + if ((MainController.getSpc().getPlanner().getUserName()).isEmpty()) { + this.welcome = new Label("Welcome back!"); + } else { + this.welcome = new Label("Welcome back, " + + MainController.getSpc().getPlanner().getUserName() + "!"); + } + } else { + if ((MainController.getSpc().getPlanner().getUserName()).isEmpty()) { + this.welcome = new Label("Welcome!"); + } else { + this.welcome = new Label( + "Welcome " + MainController.getSpc().getPlanner().getUserName() + "!"); + } + } + this.welcome.setPadding(new Insets(10, 15, 10, 15)); + this.topBox.getChildren().add(this.welcome); + + this.mainContent.setVgap(10); + this.mainContent.setPadding(new Insets(15)); + + // Render dashboard: + this.main(Window.DASHBOARD); + } + + /** + * Prepare notifications. + */ + private void updateNotifications() { + MainController.getSpc().checkForNotifications(); + + // Set notification button style: + if (MainController.getSpc().getPlanner().getUnreadNotifications().length > 0) { + if (!this.showNotification.getStyleClass().contains("unread-button")) { + this.showNotification.getStyleClass().remove("read-button"); + this.showNotification.getStyleClass().add("unread-button"); + } + } else if (!this.showNotification.getStyleClass().contains("read-button")) { + this.showNotification.getStyleClass().add("read-button"); + this.showNotification.getStyleClass().remove("unread-button"); + } + + // Process notifications: + this.notificationList.getChildren().clear(); + Notification[] pendingNotifs = + MainController.getSpc().getPlanner().getNotifications(); + for (int i = pendingNotifs.length - 1; i >= 0; i--) { + GridPane pane = new GridPane(); + + // Check if has a link or is unread: + if (pendingNotifs[i].getLink() != null || !pendingNotifs[i].isRead()) { + pane.setCursor(Cursor.HAND); + pane.setId(Integer.toString(pendingNotifs.length - i - 1)); + pane.setOnMouseClicked(e -> + this.handleRead(Integer.parseInt(pane.getId())) + ); + // Check if unread: + if (!pendingNotifs[i].isRead()) { + pane.getStyleClass().add("unread-item"); + } + } + + // Create labels: + Label titleLabel = new Label(pendingNotifs[i].getTitle()); + titleLabel.getStyleClass().add("notificationItem-title"); + titleLabel.setMaxWidth(250.0); + + Label details = (pendingNotifs[i].getDetails() != null) + ? new Label(pendingNotifs[i].getDetailsAsString()) + : new Label(); + details.getStyleClass().add("notificationItem-details"); + details.setMaxWidth(250.0); + + String dateFormatted = pendingNotifs[i].getDateTime().get(Calendar.DAY_OF_MONTH) + " " + + pendingNotifs[i].getDateTime().getDisplayName(Calendar.MONTH, Calendar.LONG, + Locale.getDefault()) + + " at " + pendingNotifs[i].getDateTime().get(Calendar.HOUR) + " " + + pendingNotifs[i].getDateTime().get(Calendar.MINUTE) + " " + + pendingNotifs[i].getDateTime().getDisplayName(Calendar.AM_PM, Calendar.LONG, + Locale.getDefault()); + Label date = new Label(dateFormatted); + date.getStyleClass().addAll("notificationItem-date"); + GridPane.setHalignment(date, HPos.RIGHT); + GridPane.setHgrow(date, Priority.ALWAYS); + + pane.addRow(1, titleLabel); + pane.addRow(2, details); + pane.addRow(3, date); + pane.addRow(4, new Separator(Orientation.HORIZONTAL)); + this.notificationList.addRow(pendingNotifs.length - i - 1, pane); + } + } + + /** + * Handles menu options. + */ + private void updateMenu() { + this.addActivity.setDisable(false); + this.milestones.setDisable(false); + this.studyProfiles.setDisable(false); + this.modules.setDisable(false); + this.calendar.setDisable(false); + + // Disable relevant menu options: + if (MainController.getSpc().getPlanner().getCurrentStudyProfile() == null) { + this.addActivity.setDisable(true); + this.milestones.setDisable(true); + this.studyProfiles.setDisable(true); + this.modules.setDisable(true); + this.calendar.setDisable(true); + } +// else { +// if (MainController.getSpc().getCurrentTasks().size() <= 0) { +// this.addActivity.setDisable(true); +// this.milestones.setDisable(true); +// } + + if (MainController.getSpc().getPlanner().getCurrentStudyProfile() + .getModules().length <= 0) { + this.modules.setDisable(true); + } + //} + } + + /** + * Creates a back button. + */ + public void backButton(Window previousWindow, ModelEntity previous) { + if (previous != null || previousWindow != Window.EMPTY) { + Button back = new Button(); + back.getStyleClass().addAll("button-image", "back-button"); + + if (previous == null && previousWindow != Window.EMPTY) { + back.setOnAction(e -> this.main(previousWindow)); + } else { + back.setOnAction(e -> previous.open(this.current)); + } + + this.topBox.getChildren().add(back); + } + } + + /** + * Prepares animations for the main window. + */ + private void prepareAnimations() { + TranslateTransition openNav = new TranslateTransition(new Duration(222), navList); + openNav.setToX(0); + TranslateTransition closeNav = new TranslateTransition(new Duration(173), navList); + openMenu.setOnAction((ActionEvent e1) -> { + this.isNavOpen = !isNavOpen; + if (navList.getTranslateX() != 0) { + openNav.play(); + this.isNavOpen = true; + } else { + closeNav.setToX( + -(navList.getWidth() + this.navShadowRadius + this.navShadowOffset)); + closeNav.play(); + } + }); + + TranslateTransition openNot = new TranslateTransition(new Duration(222), notifications); + openNot.setToY(17); + TranslateTransition closeNot = new TranslateTransition(new Duration(173), notifications); + + showNotification.setOnAction((ActionEvent e1) -> { + if (notifications.getTranslateY() != 17) { + openNot.play(); + } else { + closeNot.setToY(-(notifications.getHeight() + this.navShadowRadius + 56 + 17)); + closeNot.play(); + } + }); + } + + /** + * RowFactory for a TableView of Requirement. + * + * @param e1 + * TableView that contains the RowFactory. + * + * @return new RowFactory + */ + protected static TableRow requirementRowFactory(TableView e1, + Assignment assignment) { + + TableRow row = new TableRow() { + @Override + protected void updateItem(final Requirement item, final boolean empty) { + super.updateItem(item, empty); + // If completed, mark: + if (!empty && item != null) { + setText(item.toString()); + if (item.isComplete()) { + this.getStyleClass().add("current-item"); + } + } else { + setText(null); + this.getStyleClass().remove("current-item"); + } + e1.refresh(); + } + }; + + row.setOnMouseClicked(event -> { + if (!row.isEmpty() && event.getButton() == MouseButton.PRIMARY + && event.getClickCount() == 2) { + try { + MainController.ui.requirementDetails(row.getItem()); + e1.refresh(); + } catch (IOException e) { + UiManager.reportError("Unable to open view file"); + } + } + }); + + row.setOnDragDetected(event -> { + + if (row.getItem() == null) { + return; + } + Dragboard dragboard = row.startDragAndDrop(TransferMode.MOVE); + ClipboardContent content = new ClipboardContent(); + content.put(TaskController.format, row.getItem()); + dragboard.setContent(content); + event.consume(); + }); + + row.setOnDragOver(event -> { + if (event.getGestureSource() != row + && event.getDragboard().hasContent(TaskController.format)) { + event.acceptTransferModes(TransferMode.MOVE); + event.consume(); + } + }); + + row.setOnDragEntered(event -> { + if (event.getGestureSource() != row + && event.getDragboard().hasContent(TaskController.format)) { + row.setOpacity(0.3); + } + }); + + row.setOnDragExited(event -> { + if (event.getGestureSource() != row + && event.getDragboard().hasContent(TaskController.format)) { + row.setOpacity(1); + } + }); + + row.setOnDragDropped(event -> { + + if (row.getItem() == null) { + return; + } + + Dragboard db = event.getDragboard(); + boolean success = false; + + if (event.getDragboard().hasContent(TaskController.format)) { + ObservableList items = e1.getItems(); + Requirement dragged = (Requirement) db.getContent(TaskController.format); + + int draggedId = items.indexOf(dragged); + int thisId = items.indexOf(row.getItem()); + + e1.getItems().set(draggedId, row.getItem()); + e1.getItems().set(thisId, dragged); + + ArrayList reqs = assignment.getRequirements(); + reqs.set(draggedId, row.getItem()); + reqs.set(thisId, dragged); + + success = true; + e1.refresh(); + } + event.setDropCompleted(success); + event.consume(); + }); + return row; + } + + /** + * Displays a GanttishDiagram window for the given Assignment. + * + * @param assignment + * Assignment for which to generate the GanttishDiagram. + */ + public void showGantt(Assignment assignment, Window previousWindow, ModelEntity previous) { + mainContent.getChildren().remove(1, mainContent.getChildren().size()); + topBox.getChildren().clear(); + title.setText(assignment.getName() + " Gantt Diagram"); + + // Layout: + VBox layout = new VBox(); + layout.setSpacing(10); + layout.setPadding(new Insets(15)); + layout.getStylesheets().add("/edu/wright/cs/raiderplanner/content/stylesheet.css"); + // ================= + + // Nav bar: + HBox nav = new HBox(); + nav.setSpacing(15.0); + // ================= + HBox xx = new HBox(); + HBox.setHgrow(xx, Priority.ALWAYS); + // ================= + + // Buttons: + Button back = new Button(); + back.getStyleClass().addAll("button-image", "back-button"); + back.setOnAction(e -> { + this.title.setText(assignment.getName()); + this.loadAssignment(assignment, previousWindow, previous); + }); + Button save = new Button("Save"); + stage = new Stage(); + save.setOnAction(e -> { + String path = MainController.ui.saveFileDialog(stage); + GanttishDiagram.createGanttishDiagram(MainController.getSpc().getPlanner(), assignment, + path); + }); + // ================= + + nav.getChildren().addAll(back, xx, save); + + // Content: + BufferedImage gantt = GanttishDiagram + .createGanttishDiagram(MainController.getSpc().getPlanner(), assignment); + Image image = SwingFXUtils.toFXImage(gantt, null); + Pane content = new Pane(); + VBox.setVgrow(content, Priority.ALWAYS); + content.setBackground(new Background(new BackgroundImage(image, BackgroundRepeat.NO_REPEAT, + BackgroundRepeat.NO_REPEAT, BackgroundPosition.DEFAULT, new BackgroundSize( + BackgroundSize.AUTO, BackgroundSize.AUTO, false, false, true, false)))); + // ================= + + layout.getChildren().addAll(nav, content); + layout.setMinSize(333, 555); + GridPane.setColumnSpan(layout, GridPane.REMAINING); + // Set the scene: + mainContent.getChildren().add(layout); + } + +} diff --git a/src/edu/wright/cs/raiderplanner/controller/MilestoneController.java b/src/edu/wright/cs/raiderplanner/controller/MilestoneController.java new file mode 100644 index 00000000..dbb6b5f1 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/controller/MilestoneController.java @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * Copyright (C) 2018 - Ian Mahaffy + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import edu.wright.cs.raiderplanner.model.Milestone; +import edu.wright.cs.raiderplanner.model.Task; +import edu.wright.cs.raiderplanner.view.UiManager; +import javafx.application.Platform; +import javafx.beans.binding.BooleanBinding; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Button; +import javafx.scene.control.ContentDisplay; +import javafx.scene.control.DatePicker; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.control.TextArea; +import javafx.scene.control.TextField; +import javafx.scene.control.Tooltip; +import javafx.scene.image.ImageView; +import javafx.scene.layout.GridPane; +import javafx.stage.Stage; + +import java.net.URL; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.ResourceBundle; + +/** + * Created by Žilvinas on 14/05/2017. + */ +public class MilestoneController implements Initializable { + private Milestone milestone; + private boolean success = false; + /** + * Standard getter method for milestone. + * @return milestone + */ + + public Milestone getMilestone() { + return this.milestone; + } + + /** + * Getter for checking if milestone controller initialized successful. + * @return boolean bases on initialized success + */ + public boolean isSuccess() { + return success; + } + + // Panes: + @FXML private GridPane pane; + + // Buttons: + @FXML private Button submit; + @FXML private Button add; + @FXML private Button remove; + + // Text: + @FXML private TextArea details; + @FXML private DatePicker deadline; + @FXML private TextField name; + + // Labels: + @FXML private Label title; + @FXML private Label completed; + + // Lists: + @FXML private ListView tasks; + + // Tooltips: + @FXML private Label nameTooltip; + @FXML private Label deadlineTooltip; + @FXML private Label detailsTooltip; + @FXML private Label tasksTooltip; + @FXML private Label headingTooltip; + + /** + * Handle changes to the input fields. + */ + public void handleChange() { + // Check the input fields: + if (!this.name.getText().trim().isEmpty() + && !this.deadline.getEditor().getText().trim().isEmpty() + && !this.deadline.getValue().isBefore(LocalDate.now()) + && this.tasks.getItems().size() > 0) { + this.submit.setDisable(false); + } + // ================= + + // Process tasks: + if (this.milestone != null) { + this.milestone.replaceTasks(this.tasks.getItems()); + + if (!this.milestone.isComplete()) { + this.completed.setVisible(false); + } else { + this.completed.setVisible(true); + } + } + // ================= + } + + /** + * Validate data in the Deadline field. + */ + public void validateDeadline() { + if (this.deadline.getValue().isBefore(LocalDate.now())) { + this.deadline.setStyle("-fx-border-color:red;"); + this.deadline.setTooltip(new Tooltip("Date can not be in the past")); + this.submit.setDisable(true); + } else { + this.deadline.setStyle(""); + this.deadline.setTooltip(null); + this.handleChange(); + } + } + + /** + * Handle the 'Add Task' button action. + */ + public void addTask() { + // Table items: + ObservableList list = + FXCollections.observableArrayList(MainController.getSpc().getCurrentTasks()); + list.removeAll(this.tasks.getItems()); + if (this.milestone != null) { + list.removeAll(this.milestone.getTasks()); + } + // ================= + + // Parse selected Tasks: + this.tasks.getItems().addAll(TaskController.taskSelectionWindow(list)); + // ================= + } + + /** + * Submit the form and create a new Milestone. + */ + public void handleSubmit() { + if (this.milestone == null) { + // Create a new Milestone: + this.milestone = new Milestone(this.name.getText(), + this.details.getText(), this.deadline.getValue()); + this.milestone.addTasks(this.tasks.getItems()); + // ================= + } else { + // Update the current Milestone: + this.milestone.setName(this.name.getText()); + this.milestone.setDetails(this.details.getText()); + this.milestone.setDeadline(this.deadline.getValue()); + // ================= + } + + this.success = true; + Stage stage = (Stage) this.submit.getScene().getWindow(); + stage.close(); + } + + /** + * Handle Quit button. + */ + public void handleQuit() { + Stage stage = (Stage) this.submit.getScene().getWindow(); + stage.close(); + } + + // Constructors: + + /** + * Constructor for the MilestoneController. + */ + public MilestoneController() { + } + + /** + * Constructor for an MilestoneController with an existing Milestone. + * + * @param milestone constructs milestone from milestone + */ + public MilestoneController(Milestone milestone) { + this.milestone = milestone; + } + + @Override public void initialize(URL location, ResourceBundle resources) { + // Bind properties on buttons: + this.remove.disableProperty().bind(new BooleanBinding() { + { + bind(tasks.getSelectionModel().getSelectedItems()); + } + + @Override + protected boolean computeValue() { + return !(tasks.getItems().size() > 0 + && tasks.getSelectionModel().getSelectedItem() != null); + } + }); + // ================= + + // Button actions: + this.remove.setOnAction(e -> { + if (UiManager.confirm("Are you sure you want to remove this dependency?")) { + Task tempTask = this.tasks.getSelectionModel().getSelectedItem(); + this.tasks.getItems().remove(tempTask); + if (this.milestone != null) { + this.milestone.removeTask(tempTask); + } + } + }); + + this.tasks.setCellFactory(e -> { + ListCell cell = new ListCell() { + @Override + protected void updateItem(final Task item, final boolean empty) { + super.updateItem(item, empty); + // If completed, mark: + if (!empty && item != null) { + setText(item.toString()); + if (item.isCheckedComplete()) { + this.getStyleClass().add("current-item"); + } + } else { + setText(null); + this.getStyleClass().remove("current-item"); + } + } + }; + return cell; + }); + // ================= + + // Handle Milestone details: + if (this.milestone != null) { + // Disable/modify elements: + this.title.setText("Milestone"); + + if (this.milestone.isComplete()) { + this.completed.setVisible(true); + } + // ================= + + // Fill in data: + this.name.setText(this.milestone.getName()); + this.details.setText(this.milestone.getDetails().getAsString()); + this.deadline.setValue( + this.milestone.getDeadlineDate().toInstant().atZone(ZoneId.systemDefault() + ).toLocalDate()); + this.tasks.getItems().addAll(this.milestone.getTasks()); + // ================= + } + // ================= + + // ListChangeListener: + this.tasks.getItems().addListener((ListChangeListener) c -> handleChange()); + // ================= + + // Initialize tooltip messages: + nameTooltip.setTooltip(new Tooltip("Enter the name of the milestone.")); + deadlineTooltip.setTooltip(new Tooltip("Enter a deadline for the milestone \n in " + + "the format: MM/DD/YYYY")); + detailsTooltip.setTooltip(new Tooltip("Enter any details for the milestone.")); + tasksTooltip.setTooltip(new Tooltip("Add or remove tasks from you milestone.")); + headingTooltip.setTooltip(new Tooltip("A Milestone is a goal that you can set for " + + "yourself to achieve\nin the future. You can " + + "give a deadline and tasks\nthat need to be completed to achieve this goal.")); + + Platform.runLater(() -> this.pane.requestFocus()); + } +} diff --git a/src/edu/wright/cs/raiderplanner/controller/RequirementController.java b/src/edu/wright/cs/raiderplanner/controller/RequirementController.java new file mode 100644 index 00000000..eddc40f2 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/controller/RequirementController.java @@ -0,0 +1,360 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * Copyright (C) 2018 - Ian Mahaffy, Gage Berghoff + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import com.github.plushaze.traynotification.animations.Animations; +import com.github.plushaze.traynotification.notification.Notifications; +import com.github.plushaze.traynotification.notification.TrayNotification; +import edu.wright.cs.raiderplanner.model.Activity; +import edu.wright.cs.raiderplanner.model.QuantityType; +import edu.wright.cs.raiderplanner.model.Requirement; +import edu.wright.cs.raiderplanner.view.UiManager; +import javafx.application.Platform; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.Node; +import javafx.scene.control.Button; +import javafx.scene.control.ComboBox; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.Label; +import javafx.scene.control.MenuItem; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableRow; +import javafx.scene.control.TableView; +import javafx.scene.control.TextArea; +import javafx.scene.control.TextField; +import javafx.scene.control.Tooltip; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.input.MouseButton; +import javafx.scene.layout.GridPane; +import javafx.scene.paint.Paint; +import javafx.stage.Stage; +import javafx.util.Duration; + +import java.io.IOException; +import java.net.URL; +import java.util.ResourceBundle; + +/** + * Created by Zilvinas on 13/05/2017. + */ +public class RequirementController implements Initializable { + private Requirement requirement; + private boolean success = false; + + /** + * Returns the Requirement contained by this RequirementController. + * + * @return the Requirement contained by this RequirementController. + */ + public Requirement getRequirement() { + return this.requirement; + } + + /** + * Returns true if this RequirementController has successfully handled a + * Requirement submission at any point in its existence. + * + * @return true if this RequirementController has successfully handled a + * Requirement submission at any point in its existence. + */ + public boolean isSuccess() { + return success; + } + + // Panes: + @FXML private GridPane pane; + @FXML private TableView activities; + @FXML private TableColumn nameColumn; + @FXML private TableColumn quantityColumn; + @FXML private TableColumn dateColumn; + @FXML private ContextMenu context; + + // Buttons: + @FXML private Button submit; + @FXML private Button addQuantity; + @FXML private MenuItem quantityMenu; + + // Text: + @FXML private TextArea details; + @FXML private ComboBox quantityType; + @FXML private TextField name; + @FXML private TextField quantity; + @FXML private TextField time; + @FXML private TextField quantityName; + + // Labels: + @FXML private Label title; + @FXML private Label completed; + + // Tooltips: + @FXML private Label nameTooltip; + @FXML private Label timeTooltip; + @FXML private Label quantityTooltip; + @FXML private Label detailsTooltip; + @FXML private Label headingTooltip; + + /** + * Handle changes to the input fields. + */ + public void handleChange() { + // Check the input fields: + if (!this.name.getText().trim().isEmpty() + && !this.quantity.getText().trim().isEmpty() + && MainController.isInteger(this.quantity.getText().trim()) + && !this.time.getText().trim().isEmpty() + && this.quantityType.getSelectionModel().getSelectedIndex() != -1 + && MainController.isNumeric(this.time.getText()) + && Double.parseDouble(this.time.getText()) > 0 + && getQuantity() != -1) { + this.submit.setDisable(false); + // ================= + } + } + + /** + * Validate data in the Time field. + */ + public void validateTime() { + if (!MainController.isNumeric(this.time.getText())) { + this.time.setStyle("-fx-text-box-border:red;"); + this.time.setTooltip(new Tooltip("Time must be numeric")); + this.submit.setDisable(true); + } else if (Double.parseDouble(this.time.getText()) < 0) { + this.time.setStyle("-fx-text-box-border:red;"); + this.time.setTooltip(new Tooltip("Time can not be negative")); + this.submit.setDisable(true); + } else { + this.time.setStyle(""); + this.time.setTooltip(null); + this.handleChange(); + } + } + + /** + * This will properly get the user entry from the quantity field. + * If field entry is wrong, displays red border and changes the ToolTip to explain error. + * @return Integer for the quantity, or -1 for error + */ + public int getQuantity() { + if (!MainController.isNumeric(this.quantity.getText().trim())) { + this.quantity.setTooltip(new Tooltip("Value must be numeric")); + this.quantity.setStyle("-fx-text-box-border:red;"); + this.submit.setDisable(true); + return -1; + } else if (Double.parseDouble(this.quantity.getText().trim()) < 0) { + this.quantity.setTooltip(new Tooltip("Value can not be negative")); + this.quantity.setStyle("-fx-text-box-border:red;"); + this.submit.setDisable(true); + return -1; + } else if (!MainController.isInteger(this.quantity.getText().trim())) { + this.quantity.setTooltip(new Tooltip("Value must be a whole number")); + this.quantity.setStyle("-fx-text-box-border:red;"); + this.submit.setDisable(true); + return -1; + } else { + this.quantity.setStyle(""); + this.quantity.setTooltip(null); + return Integer.parseInt(this.quantity.getText().trim()); + } + } + + /** + * Validate data in the Quantity field, including that it is an Integer. + */ + public void validateQuantity() { + getQuantity(); + this.handleChange(); + if (this.requirement != null) { + this.completed.setVisible(false); + } + } + + /** + * Validate data in the QuantityType field. + */ + public void validateNewQuantity() { + if (!this.quantityName.getText().trim().isEmpty()) { + this.quantityMenu.setDisable(false); + } else { + this.quantityMenu.setDisable(true); + } + } + + /** + * Add a new QuantityType. + */ + public void newQuantity() { + if (UiManager.confirm("Create a new Quantity '" + this.quantityName.getText() + '?')) { + // Create a new type: + QuantityType qtyType = QuantityType.create(this.quantityName.getText()); + // ================= + + // Update the current list: + this.quantityType.getItems().clear(); + this.quantityType.getItems().addAll(QuantityType.listOfNames()); + this.quantityType.getSelectionModel().select(qtyType.getName()); + // ================= + } + this.quantityName.clear(); + this.quantityMenu.setDisable(true); + } + + /** + * Submit the form and create a new Task. + */ + public void handleSubmit() { + TrayNotification trayNotif = new TrayNotification(); + trayNotif.setTitle("Raider Planner"); + trayNotif.setRectangleFill(Paint.valueOf("#2A9A84")); + trayNotif.setAnimation(Animations.POPUP); + trayNotif.setNotification(Notifications.SUCCESS); + trayNotif.showAndDismiss(Duration.seconds(2)); + + if (this.requirement == null) { + // Create a new Requirement: + trayNotif.setMessage("Requirement Successfully Created"); + + this.requirement = new Requirement(this.name.getText(), this.details.getText(), + Double.parseDouble(this.time.getText()), + getQuantity(), + this.quantityType.getValue()); + // ================= + } else { + // Update the current requirement: + this.requirement.setName(this.name.getText()); + this.requirement.setDetails(this.details.getText()); + this.requirement.setEstimatedTimeInHours(Double.parseDouble(this.time.getText())); + this.requirement.setInitialQuantity(getQuantity()); + this.requirement.setQuantityType(this.quantityType.getValue()); + trayNotif.setMessage("Requirement Successfully Updated"); + // ================= + } + this.success = true; + Stage stage = (Stage) this.submit.getScene().getWindow(); + stage.close(); + } + + /** + * Handle Quit button. + */ + public void handleQuit() { + Stage stage = (Stage) this.submit.getScene().getWindow(); + stage.close(); + } + + /** + * Constructor for the RequirementController. + */ + public RequirementController() { + } + + /** + * Constructor for a RequirementController with an existing Requirement. + * + * @param requirement the Requirement to manage + */ + public RequirementController(Requirement requirement) { + this.requirement = requirement; + } + + @Override public void initialize(URL location, ResourceBundle resources) { + this.quantityType.getItems().addAll(QuantityType.listOfNames()); + + // Row actions: + this.activities.setRowFactory(e -> { + TableRow row = new TableRow<>(); + row.setOnMouseClicked(event -> { + if (!row.isEmpty() && event.getButton() + == MouseButton.PRIMARY && event.getClickCount() == 2) { + try { + MainController.ui.activityDetails(row.getItem()); + this.activities.refresh(); + } catch (IOException e1) { + UiManager.reportError("Unable to open view file"); + } + } + }); + return row; + }); + // ================= + + // Quantity actions: + this.addQuantity.setOnMousePressed(event -> { + if (event.isPrimaryButtonDown()) { + context.show(addQuantity, event.getScreenX(), event.getScreenY()); + } + }); + // ================= + + // Hide the Activities table: + if (this.requirement == null) { + this.pane.getChildren().remove(this.activities); + this.pane.getRowConstraints().remove(3); + + Node bottomNode = this.pane.getChildren().get(0); + this.pane.getChildren().remove(bottomNode); + this.pane.getRowConstraints().remove(3); + + GridPane.setColumnSpan(bottomNode, 2); + this.pane.addRow(3, bottomNode); + // ================= + } else { + // Disable/modify elements: + this.title.setText("Requirement"); + + if (this.requirement.isComplete()) { + this.completed.setVisible(true); + } + + // Activity columns: + nameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); + quantityColumn.setCellValueFactory(new PropertyValueFactory<>("activityQuantity")); + dateColumn.setCellValueFactory(new PropertyValueFactory<>("dateString")); + // ================= + + // Fill in data: + this.name.setText(this.requirement.getName()); + this.details.setText(this.requirement.getDetails().getAsString()); + this.time.setText(Double.toString(this.requirement.getEstimatedTimeInHours())); + this.quantity.setText(Integer.toString(this.requirement.getInitialQuantity())); + this.quantityType.getSelectionModel().select( + this.requirement.getQuantityType().getName()); + this.activities.getItems().addAll(this.requirement.getActivityLog()); + // ================= + } + // ================= + + // Initialize Tooltips: + nameTooltip.setTooltip(new Tooltip("Enter the name of your new requirement.")); + timeTooltip.setTooltip(new Tooltip("Enter an approximate time that it would take " + + "\nto complete this requirement")); + quantityTooltip.setTooltip(new Tooltip("Enter how many of this requirement is " + + "required.")); + detailsTooltip.setTooltip(new Tooltip("Enter any additional information pertaining " + + "to \nthis requirement")); + headingTooltip.setTooltip(new Tooltip("A Requiremnet is something that needs to be " + + "finished\nbefore finishing a larger task.")); + + Platform.runLater(() -> this.pane.requestFocus()); + } +} diff --git a/src/edu/wright/cs/raiderplanner/controller/SettingsController.java b/src/edu/wright/cs/raiderplanner/controller/SettingsController.java new file mode 100644 index 00000000..eba97b6b --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/controller/SettingsController.java @@ -0,0 +1,696 @@ +/* + * Copyright (C) 2018 - Clayton D. Terrill + * + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import edu.wright.cs.raiderplanner.model.Settings; + +import javafx.animation.TranslateTransition; + +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.Button; +import javafx.scene.control.ColorPicker; +import javafx.scene.control.Label; +import javafx.scene.control.RadioButton; +import javafx.scene.control.ToggleGroup; +import javafx.scene.control.ToolBar; +import javafx.scene.effect.DropShadow; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; +import javafx.scene.layout.AnchorPane; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Color; +import javafx.scene.text.Font; +import javafx.scene.text.FontPosture; +import javafx.scene.text.FontWeight; +import javafx.scene.text.TextAlignment; +import javafx.stage.FileChooser; +import javafx.stage.Screen; +import javafx.util.Duration; + +import java.io.File; +import java.net.URL; +import java.util.ResourceBundle; + +/** + * Actions associated with the settings menu and its items. + * Settings menu implemented in correspondence with Issue #238. + * 2/1/2018 - Template Created + * @author Clayton D. Terrill + */ +public class SettingsController implements Initializable { + + /** + * Initializes switch names and other buttons. + * Represent states that the settings menu can be in. + * May be assigned to a button in the initialize method and + * what it does may be applied in the main method's switch statement. + */ + public enum Window { + EMPTY, ABOUT, GENERAL, ACCOUNT, THEME, NOTIFICATIONS + } + + private Window current; + private boolean isNavOpen; + + // Screen size: + private double screenWidth = Screen.getPrimary().getVisualBounds().getWidth(); + private double screenHeight = Screen.getPrimary().getVisualBounds().getHeight(); + private double screenAverage = (screenWidth + screenHeight) / 2; + + // Shadows: + private int navShadowRadius = (int) (screenAverage * 0.03); + private int navShadowOffset = (int) (screenAverage * 0.01); + private DropShadow navShadow = new DropShadow(navShadowRadius, navShadowOffset, 0, Color.BLACK); + // private DropShadow notifShadow = new DropShadow(screenAverage * 0.02, 0, 0.009, Color.BLACK); + + // Labels: + private Label welcome; + @FXML + private Label title; + + // Buttons: + @FXML + private Button openMenu; + @FXML + private Button generalBtn; + @FXML + private Button accountBtn; + @FXML + private Button themeBtn; + @FXML + private Button notificationsBtn; + @FXML + private Button aboutBtn; + @FXML + private Button closeDrawer; + + // Panes: + @FXML + private AnchorPane navList; + @FXML + private GridPane mainContent; + @FXML + private HBox topBox; + + @FXML + private ToolBar toolBar; + + private static FileChooser.ExtensionFilter datExtension = + new FileChooser.ExtensionFilter("dat file", "*.dat"); + private static File savesFolder = new File("./saves"); + + Settings settings = new Settings(); + + /** + * Sets this.current to equal passed variable and calls this.main(). + */ + public void main(Window wind) { + this.current = wind; + this.main(); + this.applyTheme(); + } + + /** + * Main method containing switch statements. + * Switch statements detect what state the app is in. + */ + public void main() { + if (isNavOpen) { + openMenu.fire(); + } + + switch (this.current) { + case ABOUT: { + loadAbout(); + break; + } + case GENERAL: { + this.loadGeneral(); + break; + } + case ACCOUNT: { + this.loadAccount(); + break; + } + case THEME: { + this.loadTheme(); + break; + } + case NOTIFICATIONS: { + this.loadNotification(); + break; + } + default: + break; + } + } + + /** + * Apply the users theme to the fxml. + */ + public void applyTheme() { + // Reload settings to make sure saved values are used + settings.loadSettings(); + // Make sure that a hex value representing a color exists + if (settings.isColorHex(settings.getToolBarColor())) { + this.toolBar.setStyle("" + + "-fx-background-color: #" + settings.getToolBarColor()); + } + if (settings.isColorHex(settings.getToolBarTextColor())) { + this.title.setStyle("" + + "-fx-font-family: Ariel" + + "; -fx-text-fill: #" + settings.getToolBarTextColor() + + "; -fx-font-size: 2.5em;"); + } + if (settings.isColorHex(settings.getToolBarIconColor())) { + this.openMenu.setStyle("" + + "-fx-background-image: " + + "url('/edu/wright/cs/raiderplanner/content/menu.png');" + + "; -fx-background-color: transparent" + + "; -fx-cursor: hand" + + "; -fx-effect: innershadow(gaussian , " + + "#" + settings.getToolBarIconColor() + ", 8, 1, 1, 1);"); + } + } + + /** + * Saves the settings. + * @param revertButtonTemp - Button disabled since current settings match saved. + * @param saveButtonTemp - Button disabled since current settings match saved. + */ + public void saveSettings(Button revertButtonTemp, Button saveButtonTemp) { + settings.saveSettings(); + revertButtonTemp.setDisable(true); + saveButtonTemp.setDisable(true); + } + + /** + * Display the About menu. + */ + public void loadAbout() { + // Clear main content and change title + this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); + this.topBox.getChildren().clear(); + this.title.setText("About"); + + // Text Labels: + Label appName = new Label("RaiderPlanner"); + appName.setFont(Font.font("Ariel", FontWeight.BOLD , 42)); + + Label versionNo = new Label("Version 0.0.0\nCopyright © 2017"); + versionNo.setFont(Font.font("Ariel", 12)); + versionNo.setTextFill(Color.GRAY); + versionNo.setTextAlignment(TextAlignment.CENTER); + + Label details = new Label("\"The best study planner since the Gantt Diagram\""); + details.setFont(Font.font("Ariel", FontPosture.ITALIC , 24)); + + Label stayOnTrack = new Label("Stay on track."); + stayOnTrack.setFont(Font.font("Ariel", FontWeight.BOLD, 18)); + + Label summary = new Label("Life gets complicated, and keeping track of homework and exam\n" + + "dates is not the first thing on your mind. RaiderPlanner will be " + + "there to help you out."); + summary.setFont(Font.font("Ariel", 18)); + summary.setTextAlignment(TextAlignment.CENTER); + + Label credits = new Label("Created By:\n" + + "Wright State University's CEG3120 class\n\n" + + "Based on the 'PearPlanner'\n" + + "Created By: \n" + + "Benjamin Dickson\n" + + "Andrew Odintsov\n" + + "Zilvinas Ceikauskas\n" + + "Bijan Ghasemi Afshar\n"); + credits.setFont(Font.font("Ariel", 14)); + credits.setTextAlignment(TextAlignment.CENTER); + + VBox detailsBox = new VBox(5); + details.setWrapText(true); + detailsBox.getChildren().addAll( + appName, + versionNo, + details, + stayOnTrack, + summary, + credits); + detailsBox.setAlignment(Pos.TOP_CENTER); + + GridPane.setVgrow(detailsBox, Priority.SOMETIMES); + GridPane.setHgrow(detailsBox, Priority.ALWAYS); + GridPane.setColumnSpan(detailsBox, GridPane.REMAINING); + + this.mainContent.addRow(2, detailsBox); + this.mainContent.setVgap(10); + this.mainContent.setPadding(new Insets(15)); + this.mainContent.setAlignment(Pos.CENTER); + } + + /** + * Display the general settings. + * TODO - Implement more general settings. + */ + public void loadGeneral() { + // Clear main content and change title + this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); + this.topBox.getChildren().clear(); + this.title.setText("General Settings"); + + // Controls for the startup preference + Label launchSetting = new Label("Open on startup:"); + launchSetting.setUnderline(true); + ToggleGroup group = new ToggleGroup(); + RadioButton defaultStartup = new RadioButton("Open Start Menu\t"); + defaultStartup.setToggleGroup(group); + defaultStartup.setSelected(true); + RadioButton accountStartup = new RadioButton("Open User Account\t"); + accountStartup.setToggleGroup(group); + Label fileName = new Label(""); + fileName.setTextFill(Color.GRAY); + Button browseAccounts = new Button("\tBrowse\t"); + //browseAccounts.setDisable(true); + VBox launchBox = new VBox(2); + launchBox.getChildren().addAll( + launchSetting, + defaultStartup, + accountStartup, + browseAccounts, + fileName); + launchBox.setAlignment(Pos.TOP_CENTER); + GridPane.setVgrow(launchBox, Priority.SOMETIMES); + GridPane.setHgrow(launchBox, Priority.ALWAYS); + GridPane.setColumnSpan(launchBox, GridPane.REMAINING); + this.mainContent.add(launchBox, 0, 1); + + // Controls for the Revert and Save Buttons at the bottom + Button revertButton = new Button("\tRevert\t"); + Button saveButton = new Button("\tSave\t\t"); + HBox saveBox = new HBox(2); + saveBox.getChildren().addAll( + revertButton, + saveButton); + saveBox.setAlignment(Pos.BOTTOM_CENTER); + GridPane.setVgrow(saveBox, Priority.SOMETIMES); + GridPane.setHgrow(saveBox, Priority.ALWAYS); + GridPane.setColumnSpan(saveBox, GridPane.REMAINING); + this.mainContent.addRow(2, saveBox); + + // Load control contents at first + fillGeneralControls(fileName, accountStartup, + browseAccounts, revertButton, saveButton); + + // Button Event Mapping + defaultStartup.setOnAction(e -> + this.toggleAccountStartup(accountStartup, browseAccounts, + fileName, revertButton, saveButton)); + accountStartup.setOnAction(e -> + this.toggleAccountStartup(accountStartup, browseAccounts, + fileName, revertButton, saveButton)); + browseAccounts.setOnAction(e -> + this.browseAccountsEvent(fileName, revertButton, saveButton)); + saveButton.setOnAction(e -> + this.saveSettings(revertButton, saveButton)); + revertButton.setOnAction(e -> + this.fillGeneralControls(fileName, accountStartup, + browseAccounts, revertButton, saveButton)); + } + + /** + * Fills the general controls with the saved setting properties. + * + * @param fileNameTemp - Label containing account file path. + * @param accountStartupTemp - RadioButton to determine if account startup is used or not. + * @param browseAccountsTemp - Button to enable or disable. + * @param revertButtonTemp - Button disabled since current settings match saved. + * @param saveButtonTemp - Button disabled since current settings match saved. + */ + public void fillGeneralControls(Label fileNameTemp, + RadioButton accountStartupTemp, Button browseAccountsTemp, + Button revertButtonTemp, Button saveButtonTemp) { + + settings.loadSettings(); + fileNameTemp.setText(settings.getDefaultFilePath()); + fileNameTemp.setVisible(settings.getAccountStartup()); + accountStartupTemp.setSelected(settings.getAccountStartup()); + browseAccountsTemp.setDisable(!accountStartupTemp.isSelected()); + revertButtonTemp.setDisable(true); + saveButtonTemp.setDisable(true); + } + + /** + * Toggles the setting property for account startup. + * + * @param accountStartupTemp - RadioButton that determines startup. + * @param browseAccountsTemp - Button to disable. + * @param fileNameTemp - Label to hide. + * @param revertButtonTemp - Button enabled if original setting changed. + * @param saveButtonTemp - Button enabled if original setting changed. + */ + public void toggleAccountStartup(RadioButton accountStartupTemp, + Button browseAccountsTemp, Label fileNameTemp, + Button revertButtonTemp, Button saveButtonTemp) { + + settings.setAccountStartup(accountStartupTemp.isSelected()); + browseAccountsTemp.setDisable(!browseAccountsTemp.isDisabled()); + fileNameTemp.setVisible(!fileNameTemp.isVisible()); + + // NOTE - the following two statements will only function correctly + // if the revert and save buttons are just used by the + // Account Startup RadioButtons. If other general settings + // are implemented, then these should just be set to true. + // As of now, these statements work well with making sure + // the settings have changed or not. + revertButtonTemp.setDisable(!revertButtonTemp.isDisabled()); + saveButtonTemp.setDisable(!saveButtonTemp.isDisabled()); + } + + /** + * Opens the file browser to find a valid dat file. + * + * @param fileNameTemp - Label that will display file path. + * @param revertButtonTemp - Button enabled if file path changed. + * @param saveButtonTemp - Button enabled if file path changed. + */ + public void browseAccountsEvent(Label fileNameTemp, + Button revertButtonTemp, Button saveButtonTemp) { + + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Select a planner to load"); + fileChooser.getExtensionFilters().add(datExtension); + if (!savesFolder.exists()) { + savesFolder.mkdirs(); + } + fileChooser.setInitialDirectory(savesFolder); + String filePath = ""; + try { + filePath = fileChooser.showOpenDialog(null).toString(); + revertButtonTemp.setDisable(false); + saveButtonTemp.setDisable(false); + } catch (Exception e) { + // TODO - Research why this happens. + // fileChooser.showOpenDialog(null) throws a general exception when the user exits + // without selecting a file. This is bad coding practice, but it allows the application + // to run and just sets the filePath string as empty. + // Using a try-catch prevents the application from breaking. + filePath = ""; + } + fileNameTemp.setText(filePath); + settings.setAccountFilePath(filePath); + } + + /** + * Display the account settings. + * TODO - Implement the account settings. + */ + public void loadAccount() { + // Clear main content and change title + this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); + this.topBox.getChildren().clear(); + this.welcome.setText("ACCOUNT"); + this.topBox.getChildren().add(this.welcome); + this.title.setText("Account Settings"); + } + + /** + * Display theme settings. + * TODO - Implement the theme settings. + */ + public void loadTheme() { + // Clear main content and change title + this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); + this.topBox.getChildren().clear(); + this.title.setText("Theme Settings"); + + // Controls for the ToolBar color preference + Label toolBarLabel = new Label("Toolbar Color:"); + toolBarLabel.setUnderline(true); + ColorPicker toolBarPicker = new ColorPicker(); + toolBarPicker.setValue(Color.RED); + VBox toolBarBox = new VBox(2); + toolBarBox.getChildren().addAll( + toolBarLabel, + toolBarPicker); + toolBarBox.setAlignment(Pos.TOP_CENTER); + + // Controls for the ToolBar Text color preference + Label toolBarTextLabel = new Label("Toolbar Text Color:"); + toolBarTextLabel.setUnderline(true); + ColorPicker toolBarTextPicker = new ColorPicker(); + toolBarTextPicker.setValue(Color.RED); + VBox toolBarTextBox = new VBox(2); + toolBarTextBox.getChildren().addAll( + toolBarTextLabel, + toolBarTextPicker); + toolBarTextBox.setAlignment(Pos.TOP_CENTER); + + // Controls for the ToolBar Text color preference + Label toolBarIconLabel = new Label("Toolbar Icon Color:"); + toolBarIconLabel.setUnderline(true); + ColorPicker toolBarIconPicker = new ColorPicker(); + toolBarIconPicker.setValue(Color.RED); + VBox toolBarIconBox = new VBox(2); + toolBarIconBox.getChildren().addAll( + toolBarIconLabel, + toolBarIconPicker); + toolBarIconBox.setAlignment(Pos.TOP_CENTER); + + // Controls for the Revert and Save Buttons at the bottom + Button revertButton = new Button("\tRevert\t"); + Button saveButton = new Button("\tSave\t\t"); + HBox saveBox = new HBox(2); + saveBox.getChildren().addAll( + revertButton, + saveButton); + saveBox.setAlignment(Pos.BOTTOM_CENTER); + GridPane.setVgrow(saveBox, Priority.SOMETIMES); + GridPane.setHgrow(saveBox, Priority.ALWAYS); + GridPane.setColumnSpan(saveBox, GridPane.REMAINING); + + // Add controls to the mainContent + this.mainContent.add(toolBarBox, 1, 1); + this.mainContent.add(toolBarTextBox, 1, 2); + this.mainContent.add(toolBarIconBox, 1, 3); + this.mainContent.addRow(4, saveBox); + + // Load control contents at first + fillThemeControls(toolBarPicker, + toolBarTextPicker, toolBarIconPicker, + revertButton, saveButton); + + // Button Event Mapping + toolBarPicker.setOnAction(e -> + setToolBarColor(String.format("%08x", toolBarPicker.getValue().hashCode()), + revertButton, saveButton)); + toolBarTextPicker.setOnAction(e -> + setToolBarText(String.format("%08x", toolBarTextPicker.getValue().hashCode()), + revertButton, saveButton)); + toolBarIconPicker.setOnAction(e -> + setToolBarIcon(String.format("%08x", toolBarIconPicker.getValue().hashCode()), + revertButton, saveButton)); + saveButton.setOnAction(e -> + this.saveSettings(revertButton, saveButton)); + revertButton.setOnAction(e -> + fillThemeControls(toolBarPicker, + toolBarTextPicker, toolBarIconPicker, + revertButton, saveButton)); + } + + /** + * Fills the theme controls with the saved setting properties. + * + * @param toolBarPicker - ColorPicker control containing the chosen color. + * @param toolBarTextPicker - ColorPicker control containing the chosen color. + * @param toolBarIconPicker - ColorPicker control containing the chosen color. + * @param revertButtonTemp - Button disabled since current settings match saved. + * @param saveButtonTemp - Button disabled since current settings match saved. + */ + public void fillThemeControls(ColorPicker toolBarPicker, + ColorPicker toolBarTextPicker, ColorPicker toolBarIconPicker, + Button revertButtonTemp, Button saveButtonTemp) { + + settings.loadSettings(); + this.applyTheme(); // For revertButton action + + // Make sure that a hex value representing a color exists + if (settings.isColorHex(settings.getToolBarColor())) { + toolBarPicker.setValue(Color.web(settings.getToolBarColor())); + } + if (settings.isColorHex(settings.getToolBarTextColor())) { + toolBarTextPicker.setValue(Color.web(settings.getToolBarTextColor())); + } + if (settings.isColorHex(settings.getToolBarIconColor())) { + toolBarIconPicker.setValue(Color.web(settings.getToolBarIconColor())); + } + + revertButtonTemp.setDisable(true); + saveButtonTemp.setDisable(true); + } + + /** + * Sets the color to be saved for the ToolBar. + * @param toolBarPicker - String with the color represented by hex. + * @param revertButtonTemp - Button enabled since a value was changed. + * @param saveButtonTemp - Button enabled since a value was changed. + */ + public void setToolBarColor(String toolBarPicker, + Button revertButtonTemp, Button saveButtonTemp) { + + // Make sure that a hex value representing a color exists + if (settings.isColorHex(toolBarPicker)) { + this.toolBar.setStyle("-fx-background-color: #" + + toolBarPicker); + settings.setToolBarColor(toolBarPicker); + } + + revertButtonTemp.setDisable(false); + saveButtonTemp.setDisable(false); + } + + /** + * Sets the color to be saved for the ToolBar Text. + * TODO - Implement font and font size customization. + * @param toolBarTextPicker - String with the color represented by hex. + * @param revertButtonTemp - Button enabled since a value was changed. + * @param saveButtonTemp - Button enabled since a value was changed. + */ + public void setToolBarText(String toolBarTextPicker, + Button revertButtonTemp, Button saveButtonTemp) { + + // Make sure that a hex value representing a color exists + if (settings.isColorHex(toolBarTextPicker)) { + this.title.setStyle("" + + "-fx-font-family: Ariel" + + "; -fx-text-fill: #" + toolBarTextPicker + + "; -fx-font-size: 2.5em;"); + settings.setToolBarTextColor(toolBarTextPicker); + } + + revertButtonTemp.setDisable(false); + saveButtonTemp.setDisable(false); + } + + /** + * Modifies the ToolBar Text. + * TODO - Implement font and font size customization. + * @param toolBarIconPicker - String with the color represented by hex. + * @param revertButtonTemp - Button enabled since a value was changed. + * @param saveButtonTemp - Button enabled since a value was changed. + */ + public void setToolBarIcon(String toolBarIconPicker, + Button revertButtonTemp, Button saveButtonTemp) { + + // Make sure that a hex value representing a color exists + if (settings.isColorHex(toolBarIconPicker)) { + this.openMenu.setStyle("" + + "-fx-background-image: url('/edu/wright/cs/raiderplanner/content/menu.png');" + + "; -fx-background-color: transparent" + + "; -fx-cursor: hand" + + "; -fx-effect: innershadow(gaussian , " + + "#" + toolBarIconPicker + ", 5, 1, 1, 1);"); + settings.setToolBarIconColor(toolBarIconPicker); + } + + revertButtonTemp.setDisable(false); + saveButtonTemp.setDisable(false); + } + + /** + * Display the notification settings. + * TODO - Implement the notification settings. + */ + public void loadNotification() { + // Clear main content and change title + this.mainContent.getChildren().remove(1, this.mainContent.getChildren().size()); + this.topBox.getChildren().clear(); + this.welcome.setText("NOTIFICATION"); + this.topBox.getChildren().add(this.welcome); + this.title.setText("Notification Settings"); + } + + /** + * Handles the 'Back' Event. + * Will close the settings menu and open the main menu. + * @throws Exception - Throws Raider Exception + */ + public void goBack() throws Exception { + MainController.showMain(); + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + this.prepareAnimations(); + this.isNavOpen = false; + + // Set shadows + navList.setEffect(navShadow); + + // Set button actions: + this.closeDrawer.setOnAction(e -> openMenu.fire()); + this.generalBtn.setOnAction(e -> this.main(Window.GENERAL)); + this.accountBtn.setOnAction(e -> this.main(Window.ACCOUNT)); + this.themeBtn.setOnAction(e -> this.main(Window.THEME)); + this.notificationsBtn.setOnAction(e -> this.main(Window.NOTIFICATIONS)); + this.aboutBtn.setOnAction(e -> this.main(Window.ABOUT)); + + // Set nav to close when clicking outside of it + this.mainContent.addEventHandler(MouseEvent.MOUSE_PRESSED, e -> { + if (e.getButton() == MouseButton.PRIMARY) { + if (this.isNavOpen) { + this.openMenu.fire(); + } + } + }); + + this.welcome = new Label(""); // Not initially needed + loadAbout(); // Using 'About' as the one to show first. + + // Render ABOUT initially: + this.main(Window.ABOUT); + } + + /** + * Prepares animations for the main window. + */ + private void prepareAnimations() { + TranslateTransition openNav = new TranslateTransition(new Duration(222), navList); + openNav.setToX(0); + TranslateTransition closeNav = new TranslateTransition(new Duration(173), navList); + openMenu.setOnAction((ActionEvent e1) -> { + this.isNavOpen = !isNavOpen; + if (navList.getTranslateX() != 0) { + openNav.play(); + this.isNavOpen = true; + } else { + closeNav.setToX( + -(navList.getWidth() + this.navShadowRadius + this.navShadowOffset)); + closeNav.play(); + } + }); + } +} diff --git a/src/edu/wright/cs/raiderplanner/controller/StartupController.java b/src/edu/wright/cs/raiderplanner/controller/StartupController.java new file mode 100644 index 00000000..80c8cc93 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/controller/StartupController.java @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2017 - Eric Levine, Mark Riedel + * + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import edu.wright.cs.raiderplanner.model.Account; +import edu.wright.cs.raiderplanner.model.Notification; +import edu.wright.cs.raiderplanner.view.UiManager; +import javafx.event.ActionEvent; +import javafx.fxml.FXML; +import javafx.scene.control.Button; +import javafx.scene.image.ImageView; +import javafx.stage.Stage; + +import java.io.File; +import java.util.GregorianCalendar; + +/** + * Controller for the first window that is presented when the application + * launches (i.e., New/Open/Exit buttons). + * + * @author Eric Levine on 9/26/2017. + */ +public class StartupController { + + @FXML + private Button newFileButton; + + @FXML + private Button openFileButton; + + @FXML + private Button exitButton; + + /** + * Handles Close button event. Shouldn't need to save as you only see this + * prompt before a file is loaded or started. + */ + @FXML + void exitB(ActionEvent event) { + System.exit(0); + } + + /** + * Handles newFileButton's action event. Creates new profile and file. + */ + @FXML + void newFileB(ActionEvent event) { + // Generates the basic planner information + File plannerFile = null; + try { + Account newAccount = MainController.ui.createAccount(true); + StudyPlannerController study = new StudyPlannerController(newAccount); + // Welcome notification: + Notification not = new Notification("Welcome!", new GregorianCalendar(), + "Thank you for using RaiderPlanner!"); + study.getPlanner().addNotification(not); + MainController.setSpc(study); + plannerFile = MainController.ui.savePlannerFileDialog(); + } catch (Exception e) { + // Apparently createAccount() throws a general exception when the user hits quit + // I know this isn't great coding practice, but if this fails the file should remain + // null and all should be fine. I just want this to return to the UI without breaking. + } + + // Attempts to save planner file + if (plannerFile != null && plannerFile.getParentFile().exists()) { + if (plannerFile.getParentFile().canRead()) { + if (plannerFile.getParentFile().canWrite()) { + MainController.setPlannerFile(plannerFile); + MainController.save(); + Stage stage = (Stage) this.openFileButton.getScene().getWindow(); + stage.close(); + } else { + UiManager.reportError("Directory cannot be written to."); + } + } else { + UiManager.reportError("Directory cannot be read from."); + } + } + } + + /** + * Handles openFileButton's action event. Opens an existing file. + */ + @FXML + void openFileB(ActionEvent event) { + // Opens an open file dialog. + File plannerFile = MainController.ui.loadPlannerFileDialog(); + + // Checks existence and permissions before setting the MainController file path. + if (plannerFile != null && plannerFile.exists()) { + if (plannerFile.canRead()) { + if (plannerFile.canWrite()) { + MainController.setPlannerFile(plannerFile); + Stage stage = (Stage) this.openFileButton.getScene().getWindow(); + stage.close(); + } else { + UiManager.reportError("Cannot write to file."); + } + } else { + UiManager.reportError("Cannot read file."); + } + } else { + UiManager.reportError("File does not exist."); + } + + } + +} diff --git a/src/edu/wright/cs/raiderplanner/controller/StudyPlannerController.java b/src/edu/wright/cs/raiderplanner/controller/StudyPlannerController.java new file mode 100644 index 00000000..bf18e677 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/controller/StudyPlannerController.java @@ -0,0 +1,304 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import edu.wright.cs.raiderplanner.model.Account; +import edu.wright.cs.raiderplanner.model.Activity; +import edu.wright.cs.raiderplanner.model.Event; +import edu.wright.cs.raiderplanner.model.HubFile; +import edu.wright.cs.raiderplanner.model.Milestone; +import edu.wright.cs.raiderplanner.model.Notification; +import edu.wright.cs.raiderplanner.model.QuantityType; +import edu.wright.cs.raiderplanner.model.StudyPlanner; +import edu.wright.cs.raiderplanner.model.StudyProfile; +import edu.wright.cs.raiderplanner.model.Task; +import edu.wright.cs.raiderplanner.model.TaskType; +import edu.wright.cs.raiderplanner.view.UiManager; + +import java.io.BufferedOutputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.util.ArrayList; +import java.util.GregorianCalendar; + +import javax.crypto.Cipher; +import javax.crypto.CipherOutputStream; +import javax.crypto.SealedObject; +import javax.crypto.SecretKey; + + +/** + * Created by bendickson on 5/4/17. + */ + +public class StudyPlannerController { + private StudyPlanner planner; + + /** + * Returns the current study planner. + * @return SPC's StudyPlanner file, planner. + */ + public StudyPlanner getPlanner() { + return planner; + } + + /** + * Save the current StudyPlanner into a serialized file. + * + * @param key64 SecretKey used for encoding. + * @param fileName name of the file. + * @return whether saved successfully. + */ + public boolean save(SecretKey key64, String fileName) { + try { + Cipher cipher = Cipher.getInstance("Blowfish"); + cipher.init(Cipher.ENCRYPT_MODE, key64); + SealedObject sealedObject = new SealedObject(this.planner, cipher); + try (CipherOutputStream cipherOutputStream = new CipherOutputStream( + new BufferedOutputStream(new FileOutputStream(fileName)), cipher); + ObjectOutputStream outputStream = new ObjectOutputStream(cipherOutputStream)) { + outputStream.writeObject(sealedObject); + return true; + } + } catch (IOException e) { + UiManager.reportError("File does not exist: " + e.getMessage()); + return false; + } catch (Exception e) { + UiManager.reportError(e.getMessage()); + return false; + } + } + + /** + * ' Checks whether a StudyProfile for this year and semester is loaded in. + * + * @param year + * year to be checked + * @param semester + * semester number to be checked + * @return whether this StudyProfile exists in this StudyPlanner + */ + public boolean containsStudyProfile(int year, int semester) { + return planner.containsStudyProfile(year, semester); + } + + /** + * If valid, this method creates a new StudyProfile and returns true. If invalid, it returns + * false. + * + * @param hubFile + * HubFile containing the newly loaded in profile + * @return whether created successfully. + */ + public boolean createStudyProfile(HubFile hubFile) { + if (!this.planner.containsStudyProfile(hubFile.getYear(), hubFile.getSemester())) { + // Create a profile: + StudyProfile profile = new StudyProfile(hubFile); + this.planner.addStudyProfile(profile); + if (this.planner.getCurrentStudyProfile() == null) { + this.planner.setCurrentStudyProfile(profile); + profile.setCurrent(true); + } + // ================= + + // Fill the global calendar with newly imported events: + ArrayList cal = hubFile.getCalendarList(); + int i1 = -1; + int i2 = cal.size(); + while (++i1 < i2) { + // ConsoleIo.setConsoleMessage("Adding " + cal.get(i).toString() + " to calendar", + // true); + this.planner.addEventToCalendar(cal.get(i1)); + profile.addEventToCalendar(cal.get(i1)); + } + // ================= + + // Notify user: + Notification not = new Notification("New study profile created!", + new GregorianCalendar(), "\"" + profile.getName() + "\"", profile); + this.planner.addNotification(not); + // ================= + + return true; + } + return false; + } + + /** + * Returns a list of tasks in the current StudyProfile if it exists or an empty list if it. + * doesn't + */ + public ArrayList getCurrentTasks() { + if (this.getPlanner().getCurrentStudyProfile() != null) { + return this.getPlanner().getCurrentStudyProfile().getTasks(); + } else { + return new ArrayList<>(); + } + } + + /** + * Calls StudyPlanner to remove profile and all connecting data. + * + * @param profile the profile to remove + */ + public void removeProfile(StudyProfile profile) { + this.planner.removeProfile(profile); + } + + /** + * Checker whether the user needs to be notified about something. (Deadlines etc.) + */ + public void checkForNotifications() { + // TODO notifications + /* + * int hours1 = 168, hours2 = 48; // temporary values until a Settings page is present + * for (Map.Entry entry : + * this.planner.getDeadlineNotifications().entrySet()) { if (entry.getKey() instanceof + * Assignment) { if (!entry.getValue()[0]) { GregorianCalendar temp = new + * GregorianCalendar(); temp.add(CALENDAR.HOUR, -hours1); Date date = temp.getTime(); + * if (entry.getKey() instanceof Coursework) { if (date.after((((Coursework) + * entry.getKey()).getDeadline().getDate()))) { Notification not = new + * Notification("Assignment due in a week!", new GregorianCalendar(), + * entry.getKey().getName(), entry.getKey()); + * MainController.getSPC().getPlanner().addNotification(not); entry.getValue()[0] = true; } + * } if (entry.getKey() instanceof Exam) { if (date.after((((Exam) + * entry.getKey()).getTimeSlot().getDate()))) { Notification not = new + * Notification("You have an exam in a week!", new GregorianCalendar(), + * entry.getKey().getName(), entry.getKey()); + * MainController.getSPC().getPlanner().addNotification(not); entry.getValue()[0] = true; } + * } } else if (!entry.getValue()[1]) { GregorianCalendar temp = new GregorianCalendar(); + * temp.add(CALENDAR.HOUR, -hours2); Date date = temp.getTime(); + * if (entry.getKey() instanceof Coursework) { if (date.after((((Coursework) + * entry.getKey()).getDeadline().getDate()))) { Notification not = new + * Notification("Assignment due in a two days!", new GregorianCalendar(), + * entry.getKey().getName(), entry.getKey()); + * MainController.getSPC().getPlanner().addNotification(not); entry.getValue()[1] = true; } + * } if (entry.getKey() instanceof Exam) { if (date.after((((Exam) + * entry.getKey()).getTimeSlot().getDate()))) { Notification not = new + * Notification("You have an exam in two days!", new GregorianCalendar(), + * entry.getKey().getName(), entry.getKey()); + * MainController.getSPC().getPlanner().addNotification(not); entry.getValue()[1] = true; } + * } } else this.planner.getDeadlineNotifications().remove(entry); } } + */ + } + + /** + * Adds a new Activity to this StudyPlanner. + * + * @param activity + * Activity to be added. + */ + public void addActivity(Activity activity) { + this.planner.addActivity(activity); + } + + /** + * Adds a new Milestone to this StudyPlanner. + * + * @param milestone + * Milestone to be added. + */ + public void addMilestone(Milestone milestone) { + this.planner.getCurrentStudyProfile().addMilestone(milestone); + } + + /** + * Removes the given Milestone from this StudyPlanner. + * + * @param milestone + * Milestone to be removed. + * @return Whether the Milestone was removed successfully. + */ + public boolean removeMilestone(Milestone milestone) { + return this.planner.getCurrentStudyProfile().removeMilestone(milestone); + } + + /** + * Add a new QuantityType to this StudyPlanner. + * + * @param quantity + * QuantityType to be added + * @return whether added successfully. + */ + public boolean addQuantityType(QuantityType quantity) { + if (!this.planner.getQuantityTypes().contains(quantity)) { + this.planner.getQuantityTypes().add(quantity); + return true; + } + return false; + } + + /** + * Add a new TaskType to this StudyPlanner. + * + * @param taskType + * TaskType to be added + * @return whether added successfully. + */ + public boolean addTaskType(TaskType taskType) { + if (!this.planner.getTaskTypes().contains(taskType)) { + this.planner.getTaskTypes().add(taskType); + return true; + } + return false; + } + + // Constructors: + /** + * Empty Constructor. + */ + public StudyPlannerController() { + } + + /** + * Constructor for testing UI. + * + * @param newAccount New account for planner + */ + public StudyPlannerController(Account newAccount) { + planner = new StudyPlanner(newAccount); + } + + /** + * Used when loading from a file. + * + * @param planner + * StudyPlanner to be loaded. + */ + public StudyPlannerController(StudyPlanner planner) { + this.planner = planner; + + // Process Quantity and Task types. + if (!this.planner.getQuantityTypes().isEmpty()) { + this.planner.getQuantityTypes().forEach(e -> QuantityType.create(e)); + } + + if (!this.planner.getTaskTypes().isEmpty()) { + this.planner.getTaskTypes().forEach(e -> TaskType.create(e)); + } + + if (!this.planner.emptyVersionControlLibrary()) { + this.planner.rebuildVersionControlLibrary(); + } + } +} diff --git a/src/edu/wright/cs/raiderplanner/controller/StudyProfileController.java b/src/edu/wright/cs/raiderplanner/controller/StudyProfileController.java new file mode 100644 index 00000000..1c2b0ba0 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/controller/StudyProfileController.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import edu.wright.cs.raiderplanner.model.Event; +import edu.wright.cs.raiderplanner.model.HubFile; +import edu.wright.cs.raiderplanner.model.Module; +import edu.wright.cs.raiderplanner.model.MultilineString; +import edu.wright.cs.raiderplanner.model.StudyProfile; +import edu.wright.cs.raiderplanner.model.VersionControlEntity; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.Button; +import javafx.scene.control.Label; +import javafx.stage.Stage; + +import java.io.IOException; +import java.net.URL; +import java.util.ArrayList; +import java.util.ResourceBundle; + +/** + * Created by Žilvinas on 10/05/2017. + */ +public class StudyProfileController implements Initializable { + private StudyProfile profile; + private MenuController mc; + + // Labels: + @FXML private Label title; + @FXML private Label name; + @FXML private Label details; + @FXML private Label modules; + @FXML private Label milestones; + @FXML private Label extensions; + + // Buttons: + @FXML private Button setCurrent; + @FXML private Button deleteProfile; + + /** + * Set this StudyProfile as the current profile. + * @throws IOException if there is an error while loading the FXML GUI + */ + public void setCurrent() throws IOException { + MainController.getSpc().getPlanner().setCurrentStudyProfile(this.profile); + this.setCurrent.setDisable(true); + mc.main(); + mc.loadStudyProfile(this.profile); + } + + /** + * remove this StudyProfile from List. + */ + public void deleteProfile() { + if (MainController.getSpc().containsStudyProfile(profile.getYear(), + profile.getSemesterNo())) { + if (profile.isCurrent()) { + MainController.getSpc().getPlanner().getCurrentStudyProfile().clearProfile(); + MainController.getSpc().getPlanner().setCurrentStudyProfile( + new StudyProfile(new HubFile(0,0,0,new ArrayList(), + new ArrayList(),new ArrayList(), + "No semester",new MultilineString("No details"),"No UId"))); + } + MainController.getSpc().getPlanner().removeProfile(profile); + } + mc.main(); + } + + /** + * Constructor for the StudyProfileController. + */ + public StudyProfileController(StudyProfile profile) { + this.profile = profile; + } + + /** + * Constructor for the StudyProfileController. + */ + public StudyProfileController(StudyProfile profile,MenuController mc) { + this.profile = profile; + this.mc = mc; + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + this.title.setText("Year " + this.profile.getYear() + + ", Semester " + this.profile.getSemesterNo()); + this.name.setText(this.profile.getName()); + this.details.setText(this.profile.getDetails().getAsString()); + this.details.setWrapText(true); + + this.modules.setText(this.profile.getModules().length + " module(s)."); + this.milestones.setText(this.profile.getMilestones().length + " milestone(s)."); + this.extensions.setText(this.profile.getExtensions().length + " extension application(s)."); + + if (MainController.getSpc().getPlanner().getCurrentStudyProfile().equals(this.profile)) { + this.setCurrent.setDisable(true); + } + } +} \ No newline at end of file diff --git a/src/edu/wright/cs/raiderplanner/controller/TaskController.java b/src/edu/wright/cs/raiderplanner/controller/TaskController.java new file mode 100644 index 00000000..46b7e660 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/controller/TaskController.java @@ -0,0 +1,761 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * Copyright (C) 2018 - Ian Mahaffy, Gage Berghoff + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import com.github.plushaze.traynotification.animations.Animations; +import com.github.plushaze.traynotification.notification.Notifications; +import com.github.plushaze.traynotification.notification.TrayNotification; +import edu.wright.cs.raiderplanner.model.Assignment; +import edu.wright.cs.raiderplanner.model.Requirement; +import edu.wright.cs.raiderplanner.model.Task; +import edu.wright.cs.raiderplanner.model.TaskType; +import edu.wright.cs.raiderplanner.view.UiManager; +import javafx.application.Platform; +import javafx.beans.binding.BooleanBinding; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.value.ObservableValue; +import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; +import javafx.collections.ObservableList; +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.ComboBox; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.DatePicker; +import javafx.scene.control.Label; +import javafx.scene.control.ListCell; +import javafx.scene.control.ListView; +import javafx.scene.control.MenuItem; +import javafx.scene.control.SelectionMode; +import javafx.scene.control.TableColumn; +import javafx.scene.control.TableRow; +import javafx.scene.control.TableView; +import javafx.scene.control.TextArea; +import javafx.scene.control.TextField; +import javafx.scene.control.ToggleButton; +import javafx.scene.control.Tooltip; +import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.image.Image; +import javafx.scene.input.ClipboardContent; +import javafx.scene.input.DataFormat; +import javafx.scene.input.Dragboard; +import javafx.scene.input.MouseButton; +import javafx.scene.input.TransferMode; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; +import javafx.scene.paint.Paint; +import javafx.stage.Modality; +import javafx.stage.Stage; +import javafx.util.Duration; + +import java.io.IOException; +import java.net.URL; +import java.time.LocalDate; +import java.time.ZoneId; +import java.util.ArrayList; +import java.util.ResourceBundle; + +/** + * Created by Zilvinas on 12/05/2017. + */ +public class TaskController implements Initializable { + public static DataFormat format = new DataFormat("object/Requirement"); + + private Task task; + private boolean success = false; + + /** + * Returns the current task associated with this TaskController. + * @return The Task associated with this TaskController. + */ + public Task getTask() { + return this.task; + } + + /** + * Returns whether the TasksController has successfully handled a Task submission. + * @return Whether this TaskController has successfully handled a Task submission. + */ + public boolean isSuccess() { + return success; + } + + // Buttons: + @FXML + private Button submit; + @FXML + private Button addReq; + @FXML + private Button addDep; + @FXML + private Button removeReq; + @FXML + private Button removeDep; + @FXML + private ToggleButton markComplete; + @FXML + private Button addTaskType; + @FXML + private Button removeTaskType; + @FXML + private MenuItem taskTypeMenu; + + // Panes: + @FXML + private GridPane pane; + @FXML + private ContextMenu context; + + // Text: + @FXML + private TextArea details; + @FXML + private ComboBox taskType; + @FXML + private DatePicker deadline; + @FXML + private TextField name; + @FXML + private TextField weighting; + @FXML + private TextField taskTypeName; + + // Labels: + @FXML + private Label title; + @FXML + private Label completed; + @FXML + private Label canComplete; + + // Lists: + @FXML + private ListView requirements; + @FXML + private ListView dependencies; + + // Tooltips: + @FXML private Label nameTooltip; + @FXML private Label weightingTooltip; + @FXML private Label deadlineTooltip; + @FXML private Label detailsTooltip; + @FXML private Label requirementsTooltip; + @FXML private Label dependenciesTooltip; + @FXML private Label headingTooltip; + + /** + * Handle changes to the input fields. + */ + public void handleChange() { + // Try to unlock: + unlockSubmit(); + // Process requirements and dependencies: + if (this.task != null) { + this.task.replaceDependencies(this.dependencies.getItems()); + this.task.replaceRequirements(this.requirements.getItems()); + + if (!this.task.isCheckedComplete() && this.task.canCheckComplete()) { + this.canComplete.setText("Can be completed."); + this.canComplete.setTextFill(Paint.valueOf("green")); + this.canComplete.setVisible(true); + this.markComplete.setDisable(false); + } else if (!this.task.canCheckComplete()) { + this.task.setComplete(false); + this.canComplete.setText("Cannot be completed at this point."); + this.canComplete.setTextFill(Paint.valueOf("red")); + this.canComplete.setVisible(true); + this.markComplete.setDisable(true); + } + } + // ================= + } + + /** + * Used to test all user entries needed before allowing the ok/submit button to be pressed. + * Checks all input fields for incorrect data, including whether weighting is an Integer. + * @return true if unlock is successful, or false if not. + */ + public boolean unlockSubmit() { + if (!this.name.getText().trim().isEmpty() && this.name.getText() != null + && getWeight() != -1 && !this.deadline.getEditor().getText().trim().isEmpty() + && !this.deadline.getValue().isBefore(LocalDate.now()) + && this.taskType.getSelectionModel().getSelectedIndex() != -1) { + this.submit.setDisable(false); + return true; + } else { + this.submit.setDisable(true); + return false; + } + } + + /** + * This will properly get the user entry from the weighting field. If field entry is wrong, + * displays red border and changes the ToolTip to explain error. + * @return Integer for the weight, 0 if left blank, or -1 for error + */ + public int getWeight() { + if (this.weighting.getText().trim().isEmpty()) { + this.weighting.setStyle(""); + this.weighting.setTooltip(null); + return 0; + } else if (!MainController.isNumeric(this.weighting.getText().trim())) { + this.weighting.setTooltip(new Tooltip("Input must be numeric.")); + this.weighting.setStyle("-fx-text-box-border:red;"); + this.submit.setDisable(true); + return -1; + } else if (Double.parseDouble(this.weighting.getText().trim()) > 100 + || Double.parseDouble(this.weighting.getText().trim()) < 0) { + this.weighting.setTooltip(new Tooltip("Value must be between 0 and 100.")); + this.weighting.setStyle("-fx-text-box-border:red;"); + this.submit.setDisable(true); + return -1; + } else if (!MainController.isInteger(this.weighting.getText().trim())) { + this.weighting.setTooltip(new Tooltip("Value must be a whole number.")); + this.weighting.setStyle("-fx-text-box-border:red;"); + this.submit.setDisable(true); + return -1; + } else { + this.weighting.setStyle(""); + this.weighting.setTooltip(null); + return Integer.parseInt(this.weighting.getText().trim()); + } + } + + /** + * Validate data in the Weighting field. Confirms that input is an Integer. + */ + public void validateWeighting() { + getWeight(); + handleChange(); + } + + /** + * Validate data in the Deadline field. + */ + public void validateDeadline() { + if (this.deadline.getValue().isBefore(LocalDate.now())) { + this.deadline.setStyle("-fx-border-color:red;"); + this.submit.setDisable(true); + this.deadline.setTooltip(new Tooltip("Date must be on or after today's date")); + } else { + this.deadline.setStyle(""); + this.deadline.setTooltip(null); + this.handleChange(); + } + } + + /** + * Handle the 'Add requirement' button action. + */ + public void addRequirement() { + try { + Requirement req = MainController.ui.addRequirement(); + if (req != null) { + this.requirements.getItems().add(req); + } + } catch (IOException e1) { + UiManager.reportError("Unable to open view file"); + } catch (Exception e1) { + UiManager.reportError(e1.getMessage()); + } + } + + /** + * Handle the 'Add dependency' button action. + */ + public void addDependency() { + // Table items: + ObservableList list = FXCollections + .observableArrayList(MainController.getSpc().getCurrentTasks()); + list.removeAll(this.dependencies.getItems()); + if (this.task != null) { + list.remove(this.task); + list.removeAll(this.task.getDependencies()); + } + // ================= + + // Parse selected Tasks: + this.dependencies.getItems().addAll(TaskController.taskSelectionWindow(list)); + // ================= + } + + /** + * Handles the 'Mark as complete' button action. After toggle is complete, tries to unlock + * submit button. + */ + public void toggleComplete() { + if (this.task.isCheckedComplete()) { + this.task.toggleComplete(); + this.completed.setVisible(false); + this.canComplete.setVisible(true); + } else if (this.task.canCheckComplete()) { + this.task.toggleComplete(); + this.completed.setVisible(true); + this.canComplete.setVisible(false); + } + this.unlockSubmit(); + } + + /** + * Validate data in the TaskType field. + */ + public void validateNewTaskType() { + if (!this.taskTypeName.getText().trim().isEmpty()) { + this.taskTypeMenu.setDisable(false); + } else { + this.taskTypeMenu.setDisable(true); + } + } + + /** + * Add a new TaskType. + */ + public void newTaskType() { + if (UiManager.confirm("Create a new Task type '" + this.taskTypeName.getText() + '?')) { + // Create a new type: + TaskType task = TaskType.create(this.taskTypeName.getText()); + // ================= + + // Update the current list: + this.taskType.getItems().clear(); + this.taskType.getItems().addAll(TaskType.listOfNames()); + this.taskType.getSelectionModel().select(task.getName()); + // ================= + } + this.taskTypeName.clear(); + this.taskTypeMenu.setDisable(true); + } + + /** + * Add a new TaskType. + */ + public void removeTaskType() { + int doom = this.taskType.getSelectionModel().getSelectedIndex(); + this.taskType.getItems().remove(doom); + ArrayList temp = TaskType.getTaskDatabase(); + temp.remove(doom); + TaskType.setTaskDatabase(temp); + // Update the current list: + this.taskType.getItems().clear(); + this.taskType.getItems().addAll(TaskType.listOfNames()); + this.taskType.getSelectionModel().select(task.getName()); + // ================= + } + + /** + * Submit the form and create a new Task. + */ + public void handleSubmit() { + TrayNotification trayNotif = new TrayNotification(); + trayNotif.setTitle("Raider Planner"); + trayNotif.setRectangleFill(Paint.valueOf("#2A9A84")); + trayNotif.setAnimation(Animations.POPUP); + trayNotif.setNotification(Notifications.SUCCESS); + trayNotif.showAndDismiss(Duration.seconds(2)); + + if (this.task == null) { + // Create a new Task: + this.task = new Task(this.name.getText(), this.details.getText(), + this.deadline.getValue(), getWeight(), this.taskType.getValue()); + for (Requirement req : this.requirements.getItems()) { + this.task.addRequirement(req); + } + + for (Task t : this.dependencies.getItems()) { + this.task.addDependency(t); + } + // ================= + trayNotif.setMessage("Task Successfully Created"); + } else { + if (unlockSubmit()) { + // Update the current task: + this.task.setName(this.name.getText()); + this.task.setDetails(this.details.getText()); + this.task.setDeadline(this.deadline.getValue()); + this.task.setWeighting(getWeight()); + this.task.setType(this.taskType.getValue()); + trayNotif.setMessage("Task Successfully Updated"); + // ================= + } + } + + this.success = true; + Stage stage = (Stage) this.submit.getScene().getWindow(); + stage.close(); + } + + /** + * Handle Quit button. + */ + public void handleQuit() { + Stage stage = (Stage) this.submit.getScene().getWindow(); + stage.close(); + } + + /** + * Constructor for the TaskController. + */ + public TaskController() { + } + + /** + * Constructor for a TaskController with an existing Task. + * + * @param task + * The current task associated with TaskController. + */ + public TaskController(Task task) { + this.task = task; + } + + @Override + public void initialize(URL location, ResourceBundle resources) { + this.taskType.getItems().addAll(TaskType.listOfNames()); + + // Bind properties on buttons: + this.removeDep.disableProperty().bind(new BooleanBinding() { + { + bind(dependencies.getSelectionModel().getSelectedItems()); + } + + @Override + protected boolean computeValue() { + return !(dependencies.getItems().size() > 0 + && dependencies.getSelectionModel().getSelectedItem() != null); + } + }); + + this.removeReq.disableProperty().bind(new BooleanBinding() { + { + bind(requirements.getSelectionModel().getSelectedItems()); + } + + @Override + protected boolean computeValue() { + return !(requirements.getItems().size() > 0 + && requirements.getSelectionModel().getSelectedItem() != null); + } + }); + // ================= + + // Button actions: + this.removeDep.setOnAction(e -> { + if (UiManager.confirm("Are you sure you want to remove this dependency?")) { + Task task = this.dependencies.getSelectionModel().getSelectedItem(); + this.dependencies.getItems().remove(task); + if (this.task != null) { + this.task.removeDependency(task); + } + } + }); + + this.removeReq.setOnAction(e -> { + if (UiManager.confirm("Are you sure you want to remove this requirement?")) { + Requirement requirement = this.requirements.getSelectionModel().getSelectedItem(); + this.requirements.getItems().remove(requirement); + if (this.task != null) { + this.task.removeRequirement(requirement); + } + } + }); + + this.requirements.setCellFactory(this::requirementCellFactory); + // ================= + + // TaskType actions: + this.addTaskType.setOnMousePressed(event -> { + if (event.isPrimaryButtonDown()) { + context.show(addTaskType, event.getScreenX(), event.getScreenY()); + } + }); + + this.removeTaskType.setOnMousePressed(event -> { + removeTaskType(); + }); + // ================= + + // Handle Task details: + if (this.task != null) { + // Disable/modify elements: + this.title.setText("Task"); + this.markComplete.setVisible(true); + this.canComplete.setTextFill(Paint.valueOf("green")); + + if (this.task.isCheckedComplete()) { + this.completed.setVisible(true); + this.markComplete.setSelected(true); + this.markComplete.setDisable(false); + } else if (this.task.canCheckComplete()) { + this.markComplete.setDisable(false); + this.canComplete.setVisible(true); + } else { + this.canComplete.setText("Cannot be completed at this point."); + this.canComplete.setTextFill(Paint.valueOf("red")); + this.canComplete.setVisible(true); + } + // ================= + // Fill in data: + this.name.setText(this.task.getName()); + this.details.setText(this.task.getDetails().getAsString()); + this.weighting.setText(Integer.toString(this.task.getWeighting())); + this.taskType.getSelectionModel().select(this.task.getType().getName()); + this.deadline.setValue(this.task.getDeadlineDate().toInstant() + .atZone(ZoneId.systemDefault()).toLocalDate()); + this.dependencies.getItems().addAll(this.task.getDependencies()); + this.requirements.getItems().addAll(this.task.getRequirements()); + // ================= + } + // ================= + + // ListChangeListener: + this.dependencies.getItems().addListener((ListChangeListener) c -> handleChange()); + this.requirements.getItems() + .addListener((ListChangeListener) c -> handleChange()); + // ================= + + // Initialize Tooltips: + nameTooltip.setTooltip(new Tooltip("Enter the name of the task.")); + detailsTooltip.setTooltip(new Tooltip("Enter any additional information about the task.")); + deadlineTooltip.setTooltip(new Tooltip("Enter a deadline that this task must be completed " + + "by\nin the format (MM/DD/YYYY)")); + weightingTooltip.setTooltip(new Tooltip("Distribute the importance of each task in your " + + "to do list\nby adding a weight.")); + requirementsTooltip.setTooltip(new Tooltip("Add the required actions needed for completing " + + "this task.")); + dependenciesTooltip.setTooltip(new Tooltip("add the dependencies for the task to be " + + "complete.")); + headingTooltip.setTooltip(new Tooltip("A Task is something that needs to be completed\n" + + "before marking a requirement, milestone, or activity\nfinidhed. " + + "It comes with a weighting feature that allows\nyou to organize tasks in order " + + "of importance.")); + + Platform.runLater(() -> this.pane.requestFocus()); + } + + /** + * RowFactory for a TableRow of Task. + * + * @param etableView + * TableView that contains the TableRow. + * @return new TableRow + */ + protected static TableRow taskRowFactory(TableView etableView) { + TableRow row = new TableRow() { + @Override + protected void updateItem(final Task item, final boolean empty) { + super.updateItem(item, empty); + // If Task is completed, mark: + if (!empty && item != null && item.isCheckedComplete()) { + this.getStyleClass().add("current-item"); + } + // TODO something wrong with TableRow styles + } + }; + row.setOnMouseClicked(event -> { + if (!row.isEmpty() && event.getButton() == MouseButton.PRIMARY + && event.getClickCount() == 2) { + Stage current = (Stage) row.getScene().getWindow(); + current.close(); + } + }); + return row; + } + + /** + * Creates a Task selection window. + * + * @param list + * List of Tasks to be put into the window. + * @return A list of selected Tasks + */ + protected static ObservableList taskSelectionWindow(ObservableList list) { + // Layout: + VBox layout = new VBox(); + layout.setSpacing(10); + layout.setAlignment(Pos.BOTTOM_RIGHT); + // ================= + + // Tasks columns: + TableColumn nameColumn = new TableColumn<>("Task"); + nameColumn.setCellValueFactory(new PropertyValueFactory<>("name")); + + TableColumn assignmentColumn = new TableColumn<>("Assignments"); + assignmentColumn.setCellValueFactory(new PropertyValueFactory("assignments") { + @Override + public ObservableValue call(TableColumn.CellDataFeatures param) { + SimpleStringProperty value = new SimpleStringProperty(""); + for (Assignment a : ((Task) param.getValue()).getAssignmentReferences()) { + value.set(value.getValue() + a.getName() + "\n"); + } + return value; + } + }); + + TableColumn deadlineColumn = new TableColumn<>("Deadline"); + deadlineColumn.setCellValueFactory(new PropertyValueFactory<>("deadline")); + deadlineColumn.setStyle("-fx-alignment: CENTER-RIGHT;"); + // ================= + + // Create a table: + TableView tasks = new TableView<>(); + tasks.setItems(list); + tasks.getColumns().addAll(nameColumn, assignmentColumn, deadlineColumn); + tasks.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); + // ================= + + // Table attributes: + tasks.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); + GridPane.setHgrow(tasks, Priority.ALWAYS); + GridPane.setVgrow(tasks, Priority.ALWAYS); + // ================= + + // Set click event: + tasks.setRowFactory(TaskController::taskRowFactory); + // ================= + + // Button: + Button ok = new Button("OK"); + ok.setOnAction(e -> { + Stage current = (Stage) ok.getScene().getWindow(); + current.close(); + }); + VBox.setMargin(ok, new Insets(5)); + ok.setDefaultButton(true); + // ================= + + layout.getChildren().addAll(tasks, ok); + + // Set a new scene: + Stage stage = new Stage(); + stage.initModality(Modality.APPLICATION_MODAL); + stage.setScene(new Scene(layout, 550, 300)); + stage.setTitle("Select dependencies"); + stage.getIcons().add(new Image("file:icon.png")); + stage.showAndWait(); + // ================= + + return tasks.getSelectionModel().getSelectedItems(); + } + + /** + * CellFactory for a ListView of Requirement. + * + * @param elistView + * ListView that contains the ListCell. + * @return new ListCell + */ + protected ListCell requirementCellFactory(ListView elistView) { + ListCell cell = new ListCell() { + @Override + protected void updateItem(final Requirement item, final boolean empty) { + super.updateItem(item, empty); + // If completed, mark: + if (!empty && item != null) { + setText(item.toString()); + if (item.isComplete()) { + this.getStyleClass().add("current-item"); + } + } else { + setText(null); + this.getStyleClass().remove("current-item"); + } + } + }; + + cell.setOnMouseClicked(event -> { + if (!cell.isEmpty() && event.getButton() == MouseButton.PRIMARY + && event.getClickCount() == 2) { + try { + MainController.ui.requirementDetails(cell.getItem()); + elistView.refresh(); + } catch (IOException e1) { + UiManager.reportError("Unable to open view file"); + } + } + }); + + cell.setOnDragDetected(event -> { + + if (cell.getItem() == null) { + return; + } + Dragboard dragboard = cell.startDragAndDrop(TransferMode.MOVE); + ClipboardContent content = new ClipboardContent(); + content.put(TaskController.format, cell.getItem()); + dragboard.setContent(content); + event.consume(); + }); + + cell.setOnDragOver(event -> { + if (event.getGestureSource() != cell + && event.getDragboard().hasContent(TaskController.format)) { + event.acceptTransferModes(TransferMode.MOVE); + } + event.consume(); + }); + + cell.setOnDragEntered(event -> { + if (event.getGestureSource() != cell + && event.getDragboard().hasContent(TaskController.format)) { + cell.setOpacity(0.3); + } + }); + + cell.setOnDragExited(event -> { + if (event.getGestureSource() != cell + && event.getDragboard().hasContent(TaskController.format)) { + cell.setOpacity(1); + } + }); + + cell.setOnDragDropped(event -> { + + if (cell.getItem() == null) { + return; + } + + Dragboard db = event.getDragboard(); + boolean success = false; + + if (event.getDragboard().hasContent(TaskController.format)) { + ObservableList items = elistView.getItems(); + Requirement dragged = (Requirement) db.getContent(TaskController.format); + + int draggedId = items.indexOf(dragged); + int thisId = items.indexOf(cell.getItem()); + + elistView.getItems().set(draggedId, cell.getItem()); + elistView.getItems().set(thisId, dragged); + + success = true; + } + event.setDropCompleted(success); + event.consume(); + }); + return cell; + } +} diff --git a/src/edu/wright/cs/raiderplanner/controller/XmlController.java b/src/edu/wright/cs/raiderplanner/controller/XmlController.java new file mode 100644 index 00000000..4a3b315c --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/controller/XmlController.java @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar, Roberto C. Sánchez + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import static edu.wright.cs.raiderplanner.controller.MainController.isNumeric; + +import edu.wright.cs.raiderplanner.model.MultilineString; + +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; + +import java.util.HashMap; +import java.util.HashSet; + +/** + * Created by bendickson on 5/6/17. + */ +public class XmlController { + + /** + * Signals the type as which to interpret an XML node being processed. + */ + public static enum ImportAs { + BOOLEAN, STRING, INTEGER, DOUBLE, MULTILINESTRING, NODELIST + } + + /** + * The class represents a node which has been parsed from an XML file as a + * specific primitive type or Object type. + */ + public static class NodeReturn { + // TODO: The premise of this class seems wrong; replace with something less fragile + // In particular, the fact that attempting to retrieve a primitive type + // when that is not the type as which the node was imported will return + // a value which may be confused with a legitimate value makes the whole + // thing rather fragile. + private ImportAs importedAs; + private String stringValue; + private int integerValue; + private double doubleValue; + private MultilineString multilineStringValue; + private NodeList nodeList; + private boolean booleanValue = false; + + /** + * Returns the boolean value parsed from the XML. + * @return The boolean value that was parsed from the XML. + */ + public boolean getBoolean() { + if (importedAs == ImportAs.BOOLEAN) { + return booleanValue; + } else { + return false; + } + } + + /** + * Returns the string value that was parsed from the XML. + * @return The String value that was parsed from the XML. + */ + public String getString() { + if (importedAs == ImportAs.STRING) { + return stringValue; + } else { + return null; + } + } + + /** + * Returns the MultilineString value that was parsed from the XML. + * @return The MultilineString value that was parsed from the XML. + */ + public MultilineString getMultilineString() { + if (importedAs == ImportAs.MULTILINESTRING) { + return multilineStringValue; + } else { + return null; + } + } + + /** + * Returns the integer value that was parsed from the XML. + * @return The int value that was parsed from the XML. + */ + public int getInt() { + if (importedAs == ImportAs.INTEGER) { + return integerValue; + } else { + return 0; + } + } + + /** + * Returns the double value that was parsed from the XML. + * @return The double value that was parsed from the XML. + */ + public double getDouble() { + if (importedAs == ImportAs.DOUBLE) { + return doubleValue; + } else { + return 0; + } + } + + /** + * Returns the NodeList value that was parsed from the XML. + * @return The NodeList value that was parsed from the XML. + */ + public NodeList getNodeList() { + if (importedAs == ImportAs.NODELIST) { + return nodeList; + } else { + return null; + } + } + + /** + * Create a NodeReturn containing the given boolean value. + * + * @param nv The boolean value that was parsed from the XML. + */ + private NodeReturn(boolean nv) { + importedAs = ImportAs.BOOLEAN; + booleanValue = nv; + } + + /** + * Create a NodeReturn containing the given int value. + * + * @param nv The int value that was parsed from the XML. + */ + private NodeReturn(int nv) { + importedAs = ImportAs.INTEGER; + integerValue = nv; + } + + /** + * Create a NodeReturn containing the given double value. + * + * @param nv The double value that was parsed from the XML. + */ + private NodeReturn(double nv) { + importedAs = ImportAs.DOUBLE; + doubleValue = nv; + } + + /** + * Create a NodeReturn containing the given String value. + * + * @param nv The String value that was parsed from the XML. + */ + private NodeReturn(String nv) { + importedAs = ImportAs.STRING; + stringValue = nv; + } + + /** + * Create a NodeReturn containing the given MultilineString value. + * + * @param nv The MultilineString value that was parsed from the XML. + */ + private NodeReturn(MultilineString nv) { + importedAs = ImportAs.MULTILINESTRING; + multilineStringValue = nv; + } + + /** + * Create a NodeReturn containing the given NodeList value. + * + * @param nv The NodeList value that was parsed from the XML. + */ + private NodeReturn(NodeList nv) { + importedAs = ImportAs.NODELIST; + nodeList = nv; + } + } + + /** + * Process the given NodeList using the given schema and return a map of + * node names and their corresponding values. + * + * @param nodes The NodeList to process + * @param schema The schema of types for each node name + * + * @return A map of node names and their corresponding values + */ + public HashMap getSchemaValues(NodeList nodes, + HashMap schema) { + HashMap hash = new HashMap<>(); + int num = -1; + int ii = nodes.getLength(); + String nodeName; + String temp; + while (++num < ii) { + if (nodes.item(num).getNodeType() == Node.ELEMENT_NODE) { + nodeName = nodes.item(num).getNodeName(); + if (schema.containsKey(nodeName) && !hash.containsKey(nodeName)) { + switch (schema.get(nodeName)) { + case BOOLEAN: + hash.put(nodeName, new NodeReturn(nodes.item(num) + .getTextContent().equals("true"))); + break; + case STRING: + hash.put(nodeName, new NodeReturn(nodes.item(num).getTextContent())); + break; + case MULTILINESTRING: + hash.put(nodeName, new NodeReturn(new MultilineString(nodes.item(num) + .getTextContent()))); + break; + case INTEGER: + temp = nodes.item(num).getTextContent(); + if (isNumeric(temp)) { + hash.put(nodeName, new NodeReturn(Integer.parseInt(temp))); + } + break; + case DOUBLE: + temp = nodes.item(num).getTextContent(); + if (isNumeric(temp)) { + hash.put(nodeName, new NodeReturn(Double.parseDouble(temp))); + } + break; + case NODELIST: + if (nodes.item(num).hasChildNodes()) { + hash.put(nodeName, new NodeReturn(nodes.item(num).getChildNodes())); + } + break; + default: + // Do nothing + } + } + } + } + return hash; + } + + /** + * Returns the NodeList of children under a given parent node. + * + * @param parentNode The parent node to process + * + * @return The NodeList of child nodes + */ + public static NodeList getNodes(Node parentNode) { + NodeList tlist = parentNode.getChildNodes(); + int count = tlist.getLength(); + while (0 < count--) { + if (tlist.item(count).getNodeType() != Node.ELEMENT_NODE) { + parentNode.removeChild(tlist.item(count)); + } + } + return parentNode.getChildNodes(); + } + + /** + * Determines if the given list of nodes and associated schema contain + * matching sets of node names. + * + * @param nodes The list of nodes to match against the schema + * @param schema The schema to match against the list of nodes + * + * @return True if the node list and schema match, false otherwise + */ + public static boolean matchesSchema(NodeList nodes, + HashMap schema) { + HashSet match = new HashSet<>(); + int num = -1; + int ii = nodes.getLength(); + while (++num < ii) { + if (nodes.item(num).getNodeType() == Node.ELEMENT_NODE + && schema.containsKey(nodes.item(num).getNodeName())) { + match.add(nodes.item(num).getNodeName()); + } + } + return match.size() == schema.size(); + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/Account.java b/src/edu/wright/cs/raiderplanner/model/Account.java new file mode 100644 index 00000000..0f385e59 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Account.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import java.io.Serializable; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class Account implements Serializable { + // private data + private Person studentDetails; + private String studentNumber; + + // public methods + + /** + * gets Student Details. + * @return studentDetails. + */ + public Person getStudentDetails() { + return studentDetails; + } + + /** + * gets Student Number. + * @return studentNumber. + */ + public String getStudentNumber() { + return studentNumber; + } + + /** + * sets Student Details. + * @param newStudentDetails : given value is Person type + */ + public void setStudentDetails(Person newStudentDetails) { + studentDetails = newStudentDetails; + } + + /** + * sets Student Number. + * @param newStudentNumber : given value is String + */ + public void setStudentNumber(String newStudentNumber) { + studentNumber = newStudentNumber; + } + + /** + * Constructor. + * @param studentDetails : given value is Person type + * @param studentNumber given value is String type + */ + public Account(Person studentDetails, String studentNumber) { + this.studentDetails = studentDetails; + this.studentNumber = studentNumber; + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/Activity.java b/src/edu/wright/cs/raiderplanner/model/Activity.java new file mode 100644 index 00000000..0689cfe7 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Activity.java @@ -0,0 +1,209 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MainController; +import edu.wright.cs.raiderplanner.controller.MenuController; +import edu.wright.cs.raiderplanner.view.UiManager; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Locale; + +/** + * An Activity with a duration and an associated list of tasks. + * + * @author Zilvinas Ceikauskas + */ +public class Activity extends Event { + + private static final long serialVersionUID = -486727251420646703L; + + private ArrayList taskList = new ArrayList<>(); + private int activityQuantity; + private QuantityType type; + + /** + * Create a new activity from the given parameters also sets the time and date format. + * + * @param name name of activity + * @param details details of the activity + * @param date date the activity will take place + * @param duration duration of the activity + * @param activityQuantity how much of the activity is there to be completed + * @param type type of activity + */ + public Activity(String name, String details, LocalDate date, int duration, + int activityQuantity, String type) { + super(date.format(DateTimeFormatter.ofPattern("MM/dd/yyyy")) + "T00:00:01Z"); + this.setName(name); + this.setDetails(details); + this.duration = duration; + this.activityQuantity = activityQuantity; + this.type = QuantityType.get(type); + } + + /** + * Create a copy of the given Activity. + * + * @param activity Activity object to be copied. + */ + public Activity(Activity activity) { + super(); + this.name = activity.name; + this.details = activity.details; + this.date = activity.date; + this.duration = activity.duration; + this.activityQuantity = activity.activityQuantity; + this.type = activity.type; + this.taskList = activity.taskList; + } + + /** + * Sets tasks to a new array and size to the task list already made. + * @return an array of Tasks related to this Activity. + */ + public Task[] getTasks() { + // TODO: Change this to return a List and change users to conform + return this.taskList.toArray(new Task[this.taskList.size()]); + } + + /** + * Getter for activity quantity + * @return the Quantity of this Activity. + */ + public int getActivityQuantity() { + return activityQuantity; + } + + /** + * @return the Quantity Type of this Activity. + */ + public QuantityType getType() { + return type; + } + + /** + * Formats the strings activity date and then returns it. + * @return a formatted String representation of this Activity's date. + */ + public String getDateString() { + return new SimpleDateFormat("MM/dd/yyyy").format(super.getDate()); + } + + /** + * Add a single Task to this Activity. + * + * @param task Task to be added. + * + * @return true if the task was added, false if the task was already in the list. + */ + public boolean addTask(Task task) { + if (this.taskList.contains(task)) { + return false; + } + + this.taskList.add(task); + return true; + } + + /** + * Add all given Tasks to this Activity. + * + * @param tasks a Collection of Tasks to be added. + * + * @return true if the collection of taskList was added (even if only one is + * new), false if all of the taskList were already in the list. + */ + public boolean addTasks(Collection tasks) { + // TODO: Returning a boolean from this seems odd; rethink the objective + if (this.taskList.containsAll(tasks)) { + return false; + } + + this.taskList.addAll(tasks); + return true; + } + + /** + * Replace the current list of Tasks with the provided Tasks. + * + * @param tasks Collection of Tasks. + */ + public void replaceTasks(Collection tasks) { + this.taskList.clear(); + this.taskList.addAll(tasks); + } + + /** + * Remove a given Task from this Activity. + * + * @param task Task to be removed. + */ + public void removeTask(Task task) { + this.taskList.remove(task); + } + + /** + * Set the Duration of this Activity. + * + * @param duration integer value of the Duration. + */ + public void setDuration(int duration) { + this.duration = duration; + } + + /** + * Set the Quantity of this Activity. + * + * @param activityQuantity integer value of the Quantity. + */ + public void setActivityQuantity(int activityQuantity) { + this.activityQuantity = activityQuantity; + } + + /** + * Set the QuantityType of this Activity. + * + * @param type String representation of the QuantityType. + */ + public void setType(String type) { + if (QuantityType.exists(type)) { + this.type = QuantityType.get(type); + } + } + + @Override + public void open(MenuController.Window current) { + try { + MainController.ui.activityDetails(this); + } catch (IOException e) { + UiManager.reportError("Unable to open view file"); + } + } + +} diff --git a/src/edu/wright/cs/raiderplanner/model/Assignment.java b/src/edu/wright/cs/raiderplanner/model/Assignment.java new file mode 100644 index 00000000..68d07e58 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Assignment.java @@ -0,0 +1,268 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MainController; +import edu.wright.cs.raiderplanner.controller.MenuController; + +import java.util.ArrayList; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public abstract class Assignment extends VersionControlEntity { + protected ArrayList tasks = new ArrayList<>(); + protected ArrayList requirements = new ArrayList<>(); + protected int weighting; + protected Person setBy = null; + protected Person markedBy = null; + protected Person reviewedBy = null; + protected int marks; + protected StateType state; // this may not be needed as we can work it out + + @Override + protected void replace(VersionControlEntity receivedVce) { + if (receivedVce instanceof Assignment) { + Assignment castedVce = (Assignment) receivedVce; + // this.tasks = castedVce.getTasks(); + // this.requirements = castedVce.getRequirements(); + this.weighting = castedVce.getWeighting(); + if (castedVce.getSetBy() != null) { + this.setBy = castedVce.getSetBy(); + } + if (castedVce.getMarkedBy() != null) { + this.markedBy = castedVce.getMarkedBy(); + } + if (castedVce.getReviewedBy() != null) { + this.reviewedBy = castedVce.getReviewedBy(); + } + this.marks = castedVce.getMarks(); + // this.state = castedVce.getState(); + } + super.replace(receivedVce); + } + + /** + * outputs State Type. + * + */ + public enum StateType { + IN_PROGRESS, DEADLINE_PASSED, NOT_STARTED + } + + // public methods + // getters + @Override + public String toString() { + return "Assignment '" + name + "'"; + } + + /** + * toString() method which formats the needed string. + * @param verbose : given value is a boolean type + * @return verboseString : returns StringBuilder object + */ + public String toString(boolean verbose) { + if (verbose) { + StringBuilder verboseString = new StringBuilder(); + verboseString.append(toString()); + verboseString.append("\n"); + verboseString.append("Total marks: " + Integer.toString(marks)); + verboseString.append("\n"); + verboseString.append("Total weighting: " + Integer.toString(weighting)); + + verboseString.append("\n"); + verboseString.append("Set By: " + setBy.toString()); + verboseString.append("\n"); + verboseString.append("Marked By: " + markedBy.toString()); + verboseString.append("\n"); + verboseString.append("Reviewed By: " + reviewedBy.toString()); + + return verboseString.toString(); + } else { + return toString(); + } + } + + /** + * gets tasks. + * @return tasks. + */ + public ArrayList getTasks() { + return tasks; + } + + /** + * gets Requirements. + * @return requirements. + */ + public ArrayList getRequirements() { + return requirements; + } + + /** + * gets Weight. + * @return weights. + */ + public int getWeighting() { + return weighting; + } + + /** + * gets SetBy. + * @return setBy. + */ + public Person getSetBy() { + return setBy; + } + + /** + * gets MarkedBy. + * @return markedBy. + */ + public Person getMarkedBy() { + return markedBy; + } + + /** + * gets ReviewedBy. + * @return reviewedBy. + */ + public Person getReviewedBy() { + return reviewedBy; + } + + /** + * gets Marks. + * @return marks. + */ + public int getMarks() { + return marks; + } + + /** + * gets State String. + * @return state. + */ + public StateType getState() { + return state; + } + + // Setters: + + /** + * Add a Task to this Assignment and task to task array list. + * + * @param task Task to be added + */ + public void addTask(Task task) { + this.tasks.add(task); + task.addAssignmentReference(this); + } + + /** + * Removes the given Task from this assignment. + * + * @param task Task to be removed + * @return true if found and deleted, false otherwise + */ + public boolean removeTask(Task task) { + task.removeAssignmentReference(this); + return this.tasks.remove(task); + } + + /** + * Add a Requirement to this Assignment. + * + * @param requirement Task to be added + */ + public void addRequirement(Requirement requirement) { + this.requirements.add(requirement); + } + + /** + * Removes the given Requirement from this Assignment. + * + * @param requirement Requirement to be removed + * @return true if found and deleted, false otherwise + */ + public boolean removeRequirement(Requirement requirement) { + return this.requirements.remove(requirement); + } + + /** + * Calculates how much of this Assignment has been completed in percentage. + * + * @return int (0-100) + * @throws ArithmeticException e if there is a number issue + */ + public int calculateProgress() { + if (this.requirements.size() == 0 && this.tasks.size() == 0) { + return 0; + } + + int sum = 0; + int num = 0; + for (Requirement req : this.requirements) { + sum += req.requirementProgress() * 100; + num++; + } + + for (Task task : this.tasks) { + if (task.getRequirements().length > 0) { + sum += task.calculateProgress(); + num++; + } + } + + // TODO Revisit #92, investigate and determine the proper course of action + try { + return sum / num; + } catch (ArithmeticException e) { + return 0; + } + } + + @Override + public void open(MenuController.Window current) { + MainController.ui.assignmentDetails(this, current); + } + + /** + * Class Constructor. + * @param cweighting : given value is int + * @param csetBy : given value is Person + * @param cmarkedBy : given value is Person + * @param creviewedBy : given value is Person + * @param cmarks : given value is int + */ + public Assignment(int cweighting, Person csetBy, Person cmarkedBy, + Person creviewedBy, int cmarks) { + weighting = cweighting; + setBy = csetBy; + markedBy = cmarkedBy; + reviewedBy = creviewedBy; + marks = cmarks; + //MainController.getSPC().getPlanner().getDeadlineNotifications().put(this, new boolean[2]); + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/Book.java b/src/edu/wright/cs/raiderplanner/model/Book.java new file mode 100644 index 00000000..4194dd88 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Book.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import java.util.ArrayList; + +/** + * PearPlanner/RaiderPlanner + * Created by Team BRONZE on 4/27/17. + */ +public class Book /*extends Requirement*/ { + // private data + private ArrayList chapters; + + // public methods + + /** + * gets Chapters. + * @return chapters + */ + public ArrayList getChapters() { + // initial set up code below - check if this needs updating + return chapters; + } + + /** + * sets Chapters. + * @param newChapters the new chapters + */ + public void setChapters(ArrayList newChapters) { + // initial set up code below - check if this needs updating + chapters = newChapters; + } + + /** + * constructor. + * @param chapters the chapters + */ + public Book(ArrayList chapters) { + this.chapters = chapters; + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/Building.java b/src/edu/wright/cs/raiderplanner/model/Building.java new file mode 100644 index 00000000..f6c6745f --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Building.java @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MenuController.Window; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class Building extends VersionControlEntity { + // private Data + private String code = null; + private double latitude; + private double longitude; + + @Override + protected void replace(VersionControlEntity receivedVce) { + if (receivedVce instanceof Building) { + Building castedVce = (Building) receivedVce; + if (castedVce.getCode() != null) { + this.code = castedVce.getCode(); + } + this.latitude = castedVce.getLatitude(); + this.longitude = castedVce.getLongitude(); + } + + super.replace(receivedVce); + } + + // getters + /** + * Returns the name of the building as a String. + * @return the name of the building + */ + public String getName() { + return name; + } + + /** + * Returns the code of the building as a String. + * @return the code of the building + */ + public String getCode() { + return code; + } + + /** + * Returns the latitude of the building as a double. + * @return the latitude of the building + */ + public double getLatitude() { + return latitude; + } + + /** + * Returns the longitude of the building as a double. + * @return the longitude of the building + */ + public double getLongitude() { + return longitude; + } + + // setters + /** + * Sets the name of the building. + * @param newName the new name of the building + */ + public void setName(String newName) { + name = newName; + } + + /** + * Sets the code of the building. + * @param newCode the new code of the building + */ + public void setCode(String newCode) { + code = newCode; + } + + /** + * Sets the latitude of the building. + * @param newLatitude the new latitude of the building + */ + public void setLatitude(double newLatitude) { + latitude = newLatitude; + } + + /** + * Sets the longitude of the building. + * @param newLongitude the new longitude of the building + */ + public void setLongitude(double newLongitude) { + longitude = newLongitude; + } + + // constructor + /** + * Class Constructor for a building with a code, latitude, and longitude. + * @param ccode the code of the building of type String + * @param clatitude the latitude of the building of type double + * @param clongitude the longitude of the building of type double + */ + public Building(String ccode, double clatitude, double clongitude) { + code = ccode; + latitude = clatitude; + longitude = clongitude; + } + + @Override + public String toString() { + return code + " " + name + " ( " + Double.toString(latitude) + + " , " + Double.toString(longitude) + " )"; + } + + /* (non-Javadoc) + * @see edu.wright.cs.raiderplanner.model.ModelEntity#open + * (edu.wright.cs.raiderplanner.controller.MenuController.Window) + */ + @Override + public void open(Window current) { + // TODO Auto-generated method stub + } +} + diff --git a/src/edu/wright/cs/raiderplanner/model/Coursework.java b/src/edu/wright/cs/raiderplanner/model/Coursework.java new file mode 100644 index 00000000..3595be8d --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Coursework.java @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; + +/** + * PearPlanner/RaiderPlanner + * Created by Team BRONZE on 4/27/17. + */ +public class Coursework extends Assignment { + private Event startDate; + private Deadline deadline; + private ArrayList extensions; + + // private methods + @Override + protected void replace(VersionControlEntity receivedVce) { + if (receivedVce instanceof Coursework) { + Coursework castedVce = (Coursework) receivedVce; + if (castedVce.getStartDate() != null) { + this.startDate = castedVce.getStartDate(); + } + if (castedVce.getDeadline() != null) { + this.deadline = castedVce.getDeadline(); + } + if (castedVce.getExtensions() != null) { + this.extensions = castedVce.getExtensions(); + } + } + + super.replace(receivedVce); + } + + /** + * Getter For Start Date. + */ + public Event getStartDate() { + return startDate; + } + + /** + * Getter For deadline. + */ + public Deadline getDeadline() { + return deadline; + } + + /** + * Getter For Extensions. + */ + public ArrayList getExtensions() { + return extensions; + } + + /** + * Getter For Notes. + * @return ArrayList of Note + */ + public ArrayList getNotes() { + return notes; + } + + /** + * Returns a String representing the deadline. + * Used in JavaFX. + * + * @return String + */ + public String getDeadlineString() { + return new SimpleDateFormat("dd/MM/yyyy HH:MM").format(this.deadline.getDate()); + } + + /** + * Setter Method for Note. + * This method adds the new Note to ArrayList + * @param newNote : given value is of type Note + */ + public void addNote(Note newNote) { + if (!notes.contains(newNote)) { + notes.add(newNote); + } + } + + /** + * Removes the old note. + * @param oldNote : given value is of Note Type + */ + public void removeNote(Note oldNote) { + if (notes.contains(oldNote)) { + notes.remove(oldNote); + } + } + + /** + * Class Constructors. + * @param cweighting : given value is of int type + * @param csetBy : given value is of Person Type + * @param cmarkedBy : given value is of Person Type + * @param creviewedBy : given value is of Person Type + * @param cmarks : given value is of int Type + * @param cstartDate : given value is of Event Type + * @param cdeadline : given value is of Deadline Type + * @param cextensions : given value is of ArrayList Type + */ + public Coursework(int cweighting, Person csetBy, Person cmarkedBy, Person creviewedBy, + int cmarks, Event cstartDate, Deadline cdeadline, ArrayList cextensions) { + super(cweighting, csetBy, cmarkedBy, creviewedBy, cmarks); + startDate = cstartDate; + deadline = cdeadline; + extensions = new ArrayList(cextensions); + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/Deadline.java b/src/edu/wright/cs/raiderplanner/model/Deadline.java new file mode 100644 index 00000000..cd9a2a96 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Deadline.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MenuController.Window; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class Deadline extends Event { + /** + * Class Constructors. + * @param cdate : given is of type String + */ + public Deadline(String cdate) { + super(cdate); + } + + /* (non-Javadoc) + * @see edu.wright.cs.raiderplanner.model.ModelEntity#open + * (edu.wright.cs.raiderplanner.controller.MenuController.Window) + */ + @Override + public void open(Window current) { + // TODO Auto-generated method stub + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/Event.java b/src/edu/wright/cs/raiderplanner/model/Event.java new file mode 100644 index 00000000..0f250c37 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Event.java @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar, Eric Sweet, Roberto C. Sánchez + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MenuController.Window; +import org.apache.commons.lang3.time.FastDateFormat; + +import java.text.ParseException; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.regex.Pattern; + +/** + * A basic event with a calendar date and duration. + * Using FastDateFormat because it is threadsafe. + * @author Andrew Odintsov + */ +public class Event extends VersionControlEntity { + + private static final long serialVersionUID = 4940549364156632405L; + + private static final int DEFAULT_DURATION = 0; + private static Pattern dateRegex = + Pattern.compile("(\\d\\d)/(\\d\\d)/(\\d\\d\\d\\d)T(\\d\\d):(\\d\\d):(\\d\\d)Z"); + private static FastDateFormat formatter = + FastDateFormat.getInstance("MM/dd/yyyy'T'hh:mm:ss'Z'"); + + protected GregorianCalendar date = null; + protected int duration = DEFAULT_DURATION; + + /** + * Create an Event with the given date. + * + * @param date date of the event + */ + public Event(String date) { + setDate(date); + } + + /** + * Create a blank Event. + */ + public Event() { + } + + /** + * @return this Event's date. + */ + public Date getDate() { + return this.date.getTime(); + } + + /** + * @return the Event's duration. + */ + public int getDuration() { + return duration; + } + + /** + * @return this Event's calendar object. + */ + public GregorianCalendar getCalendar() { + return date; + } + + /** + * Sets the date using the date regex. + * + * @param dateString input of the date + */ + public void setDate(String dateString) { + // 09/04/2017T15:00:00Z + if (validDateString(dateString)) { + try { + Date parsedDate = formatter.parse(dateString); + this.date = new GregorianCalendar(); + this.date.setTime(parsedDate); + } catch (ParseException e) { + e.printStackTrace(); + } + } + } + + @Override + protected void replace(VersionControlEntity receivedVce) { + if (receivedVce instanceof Event) { + Event castedVce = (Event) receivedVce; + if (castedVce.getCalendar() != null) { + this.date = castedVce.getCalendar(); + } + } + super.replace(receivedVce); + } + + /** + * Validates the given String. + * + * @param dateString a String containing a Date + * @return whether the given String is a valid date + */ + public static boolean validDateString(String dateString) { + return dateRegex.matcher(dateString).matches(); + } + + @Override + public String toString() { + return this.date.getTime().toString(); + } + + /* (non-Javadoc) + * @see edu.wright.cs.raiderplanner.model.ModelEntity#open + * (edu.wright.cs.raiderplanner.controller.MenuController.Window) + */ + @Override + public void open(Window current) { + // TODO Auto-generated method stub + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/Exam.java b/src/edu/wright/cs/raiderplanner/model/Exam.java new file mode 100644 index 00000000..c46d7e57 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Exam.java @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class Exam extends Assignment { + // private data + private Exam resit = null; + private ExamEvent timeSlot = null; + + @Override + protected void replace(VersionControlEntity receivedVce) { + if (receivedVce instanceof Exam) { + Exam castedVce = (Exam) receivedVce; + if (castedVce.getResit() != null) { + this.resit = castedVce.getResit(); + } + if (castedVce.getTimeSlot() != null) { + this.timeSlot = castedVce.getTimeSlot(); + } + } + + super.replace(receivedVce); + } + + /** + * This Getter method gets the Resit value. + * @return Exam for variable resit. + */ + public Exam getResit() { + return resit; + } + + /** + * This Getter method gets the Exam event time slot. + * @return ExamEvent for variable timeSlot. + */ + public ExamEvent getTimeSlot() { + return timeSlot; + } + + + /** + * Class Constructors. + * @param cweighting : given value of type int + * @param csetBy : given value of type Person + * @param cmarkedBy : given value of type Person + * @param creviewedBy : given value of type Person + * @param cmarks : given value of type int + * @param ctimeSlot : given value of type ExamEvent + * @param cresit : given value of type Exam + */ + public Exam(int cweighting, Person csetBy, + Person cmarkedBy, Person creviewedBy, int cmarks, ExamEvent ctimeSlot, Exam cresit) { + super(cweighting, csetBy, cmarkedBy, creviewedBy, cmarks); + timeSlot = ctimeSlot; + resit = cresit; + } + + /** + * Class Constructors. + * @param cweighting : given value of type int + * @param csetBy : given value of type Person + * @param cmarkedBy : given value of type Person + * @param creviewedBy : given value of type Person + * @param cmarks : given value of type int + * @param ctimeSlot : given value of type ExamEvent + */ + public Exam(int cweighting, Person csetBy, Person cmarkedBy, + Person creviewedBy, int cmarks, ExamEvent ctimeSlot) { + super(cweighting, csetBy, cmarkedBy, creviewedBy, cmarks); + timeSlot = ctimeSlot; + resit = null; + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/ExamEvent.java b/src/edu/wright/cs/raiderplanner/model/ExamEvent.java new file mode 100644 index 00000000..f54db820 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/ExamEvent.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MenuController.Window; + +import java.text.SimpleDateFormat; + +/** + * An exam event with a location/room and a duration. + * + * @author Andrew Odintsov + */ +public class ExamEvent extends Event { + + private static final long serialVersionUID = 1127426583437715164L; + + private Room room; + + /** + * Create a new exam event from the given parameters. + * + * @param date date of the exam + * @param room room exam is in + * @param duration duration of the exam + */ + public ExamEvent(String date, Room room, int duration) { + super(date); + this.room = room; + this.duration = duration; + } + + @Override + protected void replace(VersionControlEntity receivedVce) { + if (receivedVce instanceof ExamEvent) { + ExamEvent castedVce = (ExamEvent) receivedVce; + if (castedVce.getRoom() != null) { + this.room = castedVce.getRoom(); + } + this.duration = castedVce.getDuration(); + } + super.replace(receivedVce); + } + + /** + * Returns a String representing the event. + * Used in JavaFX. + * + * @return a string representation of this exam event's date + */ + public String getDateString() { + // TODO: we should not create a new SimpleDateFormat every time + return new SimpleDateFormat("dd/MM/yyyy HH:MM").format(this.date.getTime()); + + } + + /** + * @return the room associated with this exam event. + */ + public Room getRoom() { + return room; + } + + /* (non-Javadoc) + * @see edu.wright.cs.raiderplanner.model.ModelEntity#open + * (edu.wright.cs.raiderplanner.controller.MenuController.Window) + */ + @Override + public void open(Window current) { + // TODO Auto-generated method stub + } + +} diff --git a/src/edu/wright/cs/raiderplanner/model/Extension.java b/src/edu/wright/cs/raiderplanner/model/Extension.java new file mode 100644 index 00000000..dfb1c628 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Extension.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MenuController.Window; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class Extension extends VersionControlEntity { + // private data + private Deadline newDeadline; + private MultilineString circumstances; + private ApprovalStatus approvalStatus; + + @Override + protected void replace(VersionControlEntity receivedVce) { + if (receivedVce instanceof Extension) { + Extension castedVce = (Extension) receivedVce; + this.newDeadline = castedVce.getNewDeadline(); + this.circumstances = castedVce.getCircumstances(); + this.approvalStatus = castedVce.getApprovalStatus(); + } + + super.replace(receivedVce); + } + + /** + * Enumerated values for approval statuses. + * @author Hayden + * + */ + public enum ApprovalStatus { + PENDING, APPROVED, DECLINED + } + + // public methods + + // getters + /** + * Returns the new deadline for the extension. + * @return the new deadline for the extension. + */ + public Deadline getNewDeadline() { + // initial set up code below - check if this needs updating + return newDeadline; + } + + /** + * Returns the current extension circumstances as a MultilineString. + * @return current circumstances of the extension. + */ + public MultilineString getCircumstances() { + // initial set up code below - check if this needs updating + return circumstances; + } + + /** + * Returns the current status of approval for the extension. + * @return the current status of approval for the extension. + */ + public ApprovalStatus getApprovalStatus() { + // initial set up code below - check if this needs updating + return approvalStatus; + } + + // setters + /** + * Sets the current circumstances of the extension. + * @param newCircumstances + * The new circumstances to set. + */ + public void setCircumstances(MultilineString newCircumstances) { + // initial set up code below - check if this needs updating + circumstances = newCircumstances; + } + + /** + * Sets the new deadline for the extension. + * @param newNewDeadline + * The new deadline for the extension. + */ + public void setNewDeadline(Deadline newNewDeadline) { + // initial set up code below - check if this needs updating + newDeadline = newNewDeadline; + } + + /** + * Sets the current extension approval status. + * @param newApprovalStatus + * The new extension approval status + */ + public void setApprovalStatus(ApprovalStatus newApprovalStatus) { + // initial set up code below - check if this needs updating + approvalStatus = newApprovalStatus; + } + + /* (non-Javadoc) + * @see edu.wright.cs.raiderplanner.model.ModelEntity#open + * (edu.wright.cs.raiderplanner.controller.MenuController.Window) + */ + @Override + public void open(Window current) { + // TODO Auto-generated method stub + } + + // constructor + +} diff --git a/src/edu/wright/cs/raiderplanner/model/ExtensionApplication.java b/src/edu/wright/cs/raiderplanner/model/ExtensionApplication.java new file mode 100644 index 00000000..c277e716 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/ExtensionApplication.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import java.io.Serializable; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class ExtensionApplication implements Serializable { + // private data + private Extension extension; + private String moduleCode; + private String assignmentUId; + private Account account; + + // public methods + + /** + * Getter for the Extension. + * @return Extension for variable extension + */ + public Extension getExtension() { + // initial set up code below - check if this needs updating + return extension; + } + + /** + * Getter for the Module Code. + * @return String for variable moduleCode + */ + public String getModuleCode() { + // initial set up code below - check if this needs updating + return moduleCode; + } + + /** + * Getter for the AssignmentUIds. + * @return String for variable assignmentUId + */ + public String getAssignmentUIds() { + // initial set up code below - check if this needs updating + return assignmentUId; + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/HubFile.java b/src/edu/wright/cs/raiderplanner/model/HubFile.java new file mode 100644 index 00000000..9ce7bc51 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/HubFile.java @@ -0,0 +1,731 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * Copyright (C) 2018 - Roberto C. Sánchez + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.DataController; +import edu.wright.cs.raiderplanner.controller.XmlController; + +import org.w3c.dom.NodeList; + +import java.io.IOException; +import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class HubFile implements Serializable { + + private ArrayList assets = new ArrayList<>(); + private ArrayList modules = new ArrayList<>(); + private ArrayList extensions = new ArrayList<>(); + private ArrayList updates = new ArrayList<>(); + private ArrayList calendarList = new ArrayList<>(); + private int version; + private int semester; + private int year; + private boolean updateFile; + private String semesterName; + private String semesterUId; + private MultilineString semesterDetails; + + // schemas + // note, for the time being these are hard coded into the code + // long term, these would be imported from a settings file + + // special SCHEMA for update file + public static HashMap SCHEMA_VCE; + + static { + SCHEMA_VCE = new HashMap<>(); + SCHEMA_VCE.put("name", XmlController.ImportAs.STRING); + SCHEMA_VCE.put("details", XmlController.ImportAs.MULTILINESTRING); + SCHEMA_VCE.put("uid", XmlController.ImportAs.STRING); + SCHEMA_VCE.put("version", XmlController.ImportAs.INTEGER); + } + + public static HashMap SCHEMA_ROOT; + + static { + SCHEMA_ROOT = new HashMap<>(); + SCHEMA_ROOT.put("hubfile", XmlController.ImportAs.NODELIST); + } + + public static HashMap SCHEMA_NEW_STUDYPROFILE; + + static { + SCHEMA_NEW_STUDYPROFILE = new HashMap<>(); + SCHEMA_NEW_STUDYPROFILE.put("version", XmlController.ImportAs.INTEGER); + SCHEMA_NEW_STUDYPROFILE.put("assets", XmlController.ImportAs.NODELIST); + SCHEMA_NEW_STUDYPROFILE.put("studyProfile", XmlController.ImportAs.NODELIST); + } + + public static HashMap SCHEMA_UPDATE_FILE; + + static { + SCHEMA_UPDATE_FILE = new HashMap<>(); + SCHEMA_UPDATE_FILE.put("version", XmlController.ImportAs.INTEGER); + SCHEMA_UPDATE_FILE.put("extensions", XmlController.ImportAs.NODELIST); + SCHEMA_UPDATE_FILE.put("updates", XmlController.ImportAs.NODELIST); + SCHEMA_UPDATE_FILE.put("new", XmlController.ImportAs.NODELIST); + } + + public static HashMap SCHEMA_ASSETS; + + static { + SCHEMA_ASSETS = new HashMap<>(); + SCHEMA_ASSETS.put("persons", XmlController.ImportAs.NODELIST); + SCHEMA_ASSETS.put("buildings", XmlController.ImportAs.NODELIST); + SCHEMA_ASSETS.put("rooms", XmlController.ImportAs.NODELIST); + SCHEMA_ASSETS.put("timetableEventTypes", XmlController.ImportAs.NODELIST); + } + + public static HashMap SCHEMA_STUDYPROFILE; + + static { + SCHEMA_STUDYPROFILE = new HashMap<>(); + SCHEMA_STUDYPROFILE.put("name", XmlController.ImportAs.STRING); + SCHEMA_STUDYPROFILE.put("details", XmlController.ImportAs.MULTILINESTRING); + SCHEMA_STUDYPROFILE.put("uid", XmlController.ImportAs.STRING); + SCHEMA_STUDYPROFILE.put("year", XmlController.ImportAs.INTEGER); + SCHEMA_STUDYPROFILE.put("semester", XmlController.ImportAs.INTEGER); + SCHEMA_STUDYPROFILE.put("modules", XmlController.ImportAs.NODELIST); + } + + public static HashMap SCHEMA_PERSON; + + static { + SCHEMA_PERSON = new HashMap<>(); + SCHEMA_PERSON.put("name", XmlController.ImportAs.STRING); + SCHEMA_PERSON.put("details", XmlController.ImportAs.MULTILINESTRING); + SCHEMA_PERSON.put("version", XmlController.ImportAs.INTEGER); + SCHEMA_PERSON.put("uid", XmlController.ImportAs.STRING); + SCHEMA_PERSON.put("givenNames", XmlController.ImportAs.STRING); + SCHEMA_PERSON.put("familyName", XmlController.ImportAs.STRING); + SCHEMA_PERSON.put("salutation", XmlController.ImportAs.STRING); + SCHEMA_PERSON.put("email", XmlController.ImportAs.STRING); + SCHEMA_PERSON.put("familyNameLast", XmlController.ImportAs.BOOLEAN); + } + + public static HashMap SCHEMA_BUILDING; + + static { + SCHEMA_BUILDING = new HashMap<>(); + SCHEMA_BUILDING.put("name", XmlController.ImportAs.STRING); + SCHEMA_BUILDING.put("details", XmlController.ImportAs.MULTILINESTRING); + SCHEMA_BUILDING.put("version", XmlController.ImportAs.INTEGER); + SCHEMA_BUILDING.put("uid", XmlController.ImportAs.STRING); + SCHEMA_BUILDING.put("code", XmlController.ImportAs.STRING); + SCHEMA_BUILDING.put("latitude", XmlController.ImportAs.DOUBLE); + SCHEMA_BUILDING.put("longitude", XmlController.ImportAs.DOUBLE); + } + + public static HashMap SCHEMA_ROOM; + + static { + SCHEMA_ROOM = new HashMap<>(); + SCHEMA_ROOM.put("name", XmlController.ImportAs.STRING); + SCHEMA_ROOM.put("details", XmlController.ImportAs.MULTILINESTRING); + SCHEMA_ROOM.put("version", XmlController.ImportAs.INTEGER); + SCHEMA_ROOM.put("uid", XmlController.ImportAs.STRING); + SCHEMA_ROOM.put("building", XmlController.ImportAs.STRING); + SCHEMA_ROOM.put("roomNumber", XmlController.ImportAs.STRING); + } + + public static HashMap SCHEMA_TIMETABLE_EVENT_TYPE; + + static { + SCHEMA_TIMETABLE_EVENT_TYPE = new HashMap<>(); + SCHEMA_TIMETABLE_EVENT_TYPE.put("name", XmlController.ImportAs.STRING); + SCHEMA_TIMETABLE_EVENT_TYPE.put("details", XmlController.ImportAs.MULTILINESTRING); + SCHEMA_TIMETABLE_EVENT_TYPE.put("version", XmlController.ImportAs.INTEGER); + SCHEMA_TIMETABLE_EVENT_TYPE.put("uid", XmlController.ImportAs.STRING); + } + + public static HashMap SCHEMA_MODULE; + + static { + SCHEMA_MODULE = new HashMap<>(); + SCHEMA_MODULE.put("name", XmlController.ImportAs.STRING); + SCHEMA_MODULE.put("details", XmlController.ImportAs.MULTILINESTRING); + SCHEMA_MODULE.put("version", XmlController.ImportAs.INTEGER); + SCHEMA_MODULE.put("uid", XmlController.ImportAs.STRING); + SCHEMA_MODULE.put("organiser", XmlController.ImportAs.STRING); + SCHEMA_MODULE.put("moduleCode", XmlController.ImportAs.STRING); + SCHEMA_MODULE.put("timetable", XmlController.ImportAs.NODELIST); + SCHEMA_MODULE.put("assignments", XmlController.ImportAs.NODELIST); + } + + public static HashMap SCHEMA_COURSEWORK; + + static { + SCHEMA_COURSEWORK = new HashMap<>(); + SCHEMA_COURSEWORK.put("name", XmlController.ImportAs.STRING); + SCHEMA_COURSEWORK.put("details", XmlController.ImportAs.MULTILINESTRING); + SCHEMA_COURSEWORK.put("version", XmlController.ImportAs.INTEGER); + SCHEMA_COURSEWORK.put("uid", XmlController.ImportAs.STRING); + + SCHEMA_COURSEWORK.put("weighting", XmlController.ImportAs.INTEGER); + SCHEMA_COURSEWORK.put("setBy", XmlController.ImportAs.STRING); + SCHEMA_COURSEWORK.put("markedBy", XmlController.ImportAs.STRING); + SCHEMA_COURSEWORK.put("reviewedBy", XmlController.ImportAs.STRING); + SCHEMA_COURSEWORK.put("marks", XmlController.ImportAs.INTEGER); + SCHEMA_COURSEWORK.put("startDate", XmlController.ImportAs.NODELIST); + SCHEMA_COURSEWORK.put("deadline", XmlController.ImportAs.NODELIST); + SCHEMA_COURSEWORK.put("extensions", XmlController.ImportAs.NODELIST); + } + + public static HashMap SCHEMA_EXAM; + + static { + SCHEMA_EXAM = new HashMap<>(); + SCHEMA_EXAM.put("name", XmlController.ImportAs.STRING); + SCHEMA_EXAM.put("details", XmlController.ImportAs.MULTILINESTRING); + SCHEMA_EXAM.put("version", XmlController.ImportAs.INTEGER); + SCHEMA_EXAM.put("uid", XmlController.ImportAs.STRING); + + SCHEMA_EXAM.put("resit", XmlController.ImportAs.STRING); + SCHEMA_EXAM.put("timeslot", XmlController.ImportAs.NODELIST); + SCHEMA_EXAM.put("weighting", XmlController.ImportAs.INTEGER); + SCHEMA_EXAM.put("setBy", XmlController.ImportAs.STRING); + SCHEMA_EXAM.put("markedBy", XmlController.ImportAs.STRING); + SCHEMA_EXAM.put("reviewedBy", XmlController.ImportAs.STRING); + SCHEMA_EXAM.put("marks", XmlController.ImportAs.INTEGER); + } + + public static HashMap SCHEMA_TIMETABLE_EVENT; + + static { + SCHEMA_TIMETABLE_EVENT = new HashMap<>(); + SCHEMA_TIMETABLE_EVENT.put("name", XmlController.ImportAs.STRING); + SCHEMA_TIMETABLE_EVENT.put("details", XmlController.ImportAs.MULTILINESTRING); + SCHEMA_TIMETABLE_EVENT.put("version", XmlController.ImportAs.INTEGER); + SCHEMA_TIMETABLE_EVENT.put("uid", XmlController.ImportAs.STRING); + + SCHEMA_TIMETABLE_EVENT.put("date", XmlController.ImportAs.STRING); + SCHEMA_TIMETABLE_EVENT.put("room", XmlController.ImportAs.STRING); + SCHEMA_TIMETABLE_EVENT.put("lecturer", XmlController.ImportAs.STRING); + SCHEMA_TIMETABLE_EVENT.put("timetableEventType", XmlController.ImportAs.STRING); + SCHEMA_TIMETABLE_EVENT.put("duration", XmlController.ImportAs.INTEGER); + } + + public static HashMap SCHEMA_EVENT; + + static { + SCHEMA_EVENT = new HashMap<>(); + SCHEMA_EVENT.put("name", XmlController.ImportAs.STRING); + SCHEMA_EVENT.put("details", XmlController.ImportAs.MULTILINESTRING); + SCHEMA_EVENT.put("version", XmlController.ImportAs.INTEGER); + SCHEMA_EVENT.put("uid", XmlController.ImportAs.STRING); + + SCHEMA_EVENT.put("date", XmlController.ImportAs.STRING); + } + + public static HashMap SCHEMA_EXAMEVENT; + + static { + SCHEMA_EXAMEVENT = new HashMap<>(); + SCHEMA_EXAMEVENT.put("name", XmlController.ImportAs.STRING); + SCHEMA_EXAMEVENT.put("details", XmlController.ImportAs.MULTILINESTRING); + SCHEMA_EXAMEVENT.put("version", XmlController.ImportAs.INTEGER); + SCHEMA_EXAMEVENT.put("uid", XmlController.ImportAs.STRING); + + SCHEMA_EXAMEVENT.put("date", XmlController.ImportAs.STRING); + SCHEMA_EXAMEVENT.put("room", XmlController.ImportAs.STRING); + SCHEMA_EXAMEVENT.put("duration", XmlController.ImportAs.INTEGER); + } + + public static HashMap> schemaList = + new HashMap<>(); + + static { + schemaList.put("person",SCHEMA_PERSON); + schemaList.put("examEvent",SCHEMA_EXAMEVENT); + schemaList.put("event",SCHEMA_EVENT); + schemaList.put("deadline",SCHEMA_EVENT); + schemaList.put("timetableEvent",SCHEMA_TIMETABLE_EVENT); + schemaList.put("exam",SCHEMA_EXAM); + schemaList.put("coursework",SCHEMA_COURSEWORK); + schemaList.put("module",SCHEMA_MODULE); + schemaList.put("timetableEventType",SCHEMA_TIMETABLE_EVENT_TYPE); + schemaList.put("building",SCHEMA_BUILDING); + schemaList.put("room",SCHEMA_ROOM); + } + + private static XmlController xmlTools = new XmlController(); + + /** + * Constructor for new Study Profile. + * + * @param v1 version + * @param y1 year + * @param s1 semester + * @param m1 Module list + * @param a1 VersionControlEntity list + * @param cal1 CALENDAR events list + */ + public HubFile(int v1, int y1, int s1, List m1, + List a1, List cal1) { + version = v1; + year = y1; + semester = s1; + modules = new ArrayList<>(m1); + assets = new ArrayList<>(a1); + calendarList = new ArrayList<>(cal1); + updateFile = false; + } + + /** + * Constructor for new Study Profile. + * + * @param v1 version + * @param y1 year + * @param s1 semester + * @param m1 Module list + */ + public HubFile(int v1, int y1, int s1, List m1, + List a1, List cal1, String n1, + MultilineString d1, String u1) { + this(v1, y1, s1, m1, a1, cal1); + semesterName = n1; + semesterDetails = d1; + semesterUId = u1; + } + + /** + * Constructor for update. + * + * @param v2 version + * @param e2 ExtensionApplication list + * @param u2 VersionControlEntity list + */ + public HubFile(int v2, List e2, + List u2) { + version = v2; + extensions = new ArrayList<>(e2); + updates = new ArrayList<>(u2); + updateFile = true; + } + + /** + * Returns the modules of the file. + * + * @return The modules of the file + */ + public ArrayList getModules() { + return modules; + } + + /** + * Returns the extensions of the file. + * + * @return The extensions of the file + */ + public ArrayList getExtensions() { + return extensions; + } + + /** + * Returns the calendar list of the file. + * + * @return The calendar list of the file + */ + public ArrayList getCalendarList() { + return calendarList; + } + + /** + * Returns the updates of the file. + * + * @return The updates of the file + */ + public ArrayList getUpdates() { + return updates; + } + + /** + * Returns the version of the file. + * + * @return the version of the file + */ + public int getVersion() { + return version; + } + + /** + * Returns the semester of the file. + * + * @return The semester of the file + */ + public int getSemester() { + return semester; + } + + /** + * Returns the year of the file. + * + * @return The year of the file + */ + public int getYear() { + return year; + } + + /** + * Returns the semester name of the file. + * + * @return The semester name of the file + */ + public String getSemesterName() { + return semesterName; + } + + /** + * Returns the semester UID of the file. + * + * @return The semester UID of the file + */ + public String getSemesterUId() { + return semesterUId; + } + + /** + * Returns the semester details of the file. + * + * @return The semester details of the file + */ + public MultilineString getSemesterDetails() { + return semesterDetails; + } + + /** + * Returns whether the file is an update file. + * + * @return True for an update file, false for any other file + */ + public boolean isUpdate() { + return updateFile; + } + + @Override + public String toString() { + return "HubFile for " + Integer.toString(year) + " semester: " + + Integer.toString(semester) + " | Module Count: " + + Integer.toString(modules.size()); + } + + /** + * An additional toString() method that allows for more verbose output. + * + * @param verbose True for verbose output, false for standard output + * @return A string representation of the object with the requested + * verbosity level + */ + public String toString(boolean verbose) { + if (verbose) { + StringBuilder verboseString = new StringBuilder(); + + verboseString.append(toString()); + int num = -1; + int ii = modules.size(); + while (++num < ii) { + verboseString.append(modules.get(num).toString(true)); + } + + return verboseString.toString(); + } else { + return toString(); + } + } + + /** + * Returns true if the given node name is recognized and can therefore be + * updated. Returns false otherwise. + * + * @param name The node name to check + * @return true if the given node name is recognized and can therefore be + * updated, false otherwise + */ + public static boolean updateableNode(String name) { + return schemaList.containsKey(name); + } + + /** + * Create a Person object from the specified node list. + * + * @param nc The node list containing the Person attributes + * @return a Person object + */ + public static Person createPerson(NodeList nc) { + HashMap personValues = + xmlTools.getSchemaValues(nc, HubFile.SCHEMA_PERSON); + + Person person = new Person(personValues.get("salutation").getString(), + personValues.get("givenNames").getString(), + personValues.get("familyName").getString(), + personValues.get("familyNameLast").getBoolean(), + personValues.get("email").getString()); + + DataController.addVceProperties(person, personValues); + return person; + } + + /** + * Create a Building object from the specified node list. + * + * @param nc The node list containing the Building attributes + * @return a Building object + */ + public static Building createBuilding(NodeList nc) { + HashMap pvalues = + xmlTools.getSchemaValues(nc, HubFile.SCHEMA_BUILDING); + + Building build = new Building(pvalues.get("code").getString(), + pvalues.get("latitude").getDouble(), + pvalues.get("longitude").getDouble()); + + DataController.addVceProperties(build, pvalues); + return build; + } + + /** + * Create a Room object from the specified node list. + * + * @param nc The node list containing the Room attributes + * @return a Room object + */ + public static Room createRoom(NodeList nc, HashMap assetList) { + HashMap pvalues = + xmlTools.getSchemaValues(nc, HubFile.SCHEMA_ROOM); + + Room room; + String linkedBuilding = pvalues.get("building").getString(); + if (assetList.containsKey(linkedBuilding) + && assetList.get(linkedBuilding) instanceof Building) { + room = new Room(pvalues.get("roomNumber").getString(), + (Building) assetList.get(linkedBuilding)); + } else { + room = new Room(pvalues.get("roomNumber").getString()); + } + DataController.addVceProperties(room, pvalues); + return room; + } + + /** + * Create a TimeTableEventType object from the specified node list. + * + * @param nc The node list containing the TimeTableEventType attributes + * @return a TimeTableEventType object + */ + public static TimeTableEventType createTimetableEventType(NodeList nc) { + HashMap pvalues = + xmlTools.getSchemaValues(nc, HubFile.SCHEMA_TIMETABLE_EVENT_TYPE); + + TimeTableEventType table = new TimeTableEventType(); + + DataController.addVceProperties(table, pvalues); + return table; + } + + /** + * Create a Coursework object from the specified node list and asset list. + * + * @param nc The node list containing the Coursework attributes + * @param assetList The assets belonging to the Coursework + * @return a Coursework object + * @throws IOException if requested item is not found in the list + */ + public static Coursework createCoursework(NodeList nc, + Map assetList) throws IOException { + + HashMap courseworkValues = + xmlTools.getSchemaValues(nc, HubFile.SCHEMA_COURSEWORK); + + Person cwSetBy; + Person cwMarkedBy; + Person cwReviewedBy; + Event cwStartDate; + Deadline cwDeadline; + + // extensions to be added later + + String linkedSetBy = courseworkValues.get("setBy").getString(); + String linkedMarkedBy = courseworkValues.get("markedBy").getString(); + String linkedReviewedBy = courseworkValues.get("reviewedBy").getString(); + + cwSetBy = DataController.inList(assetList, linkedSetBy); + cwMarkedBy = DataController.inList(assetList, linkedMarkedBy); + cwReviewedBy = DataController.inList(assetList, linkedReviewedBy); + + ArrayList cwExtensions = new ArrayList<>(); + + if (courseworkValues.containsKey("startDate") + && XmlController.matchesSchema(courseworkValues.get("startDate").getNodeList(), + HubFile.SCHEMA_EVENT)) { + HashMap eventValues = + xmlTools.getSchemaValues(courseworkValues.get("startDate").getNodeList(), + HubFile.SCHEMA_EVENT); + cwStartDate = new Event(eventValues.get("date").getString()); + + + DataController.addVceProperties(cwStartDate, eventValues); + assetList.put(eventValues.get("uid").getString(), cwStartDate); + + } else { + cwStartDate = null; + } + if (courseworkValues.containsKey("deadline") + && XmlController.matchesSchema(courseworkValues.get("deadline").getNodeList(), + HubFile.SCHEMA_EVENT)) { + HashMap eventValues = + xmlTools.getSchemaValues(courseworkValues.get("deadline").getNodeList(), + HubFile.SCHEMA_EVENT); + + cwDeadline = new Deadline(eventValues.get("date").getString()); + + DataController.addVceProperties(cwDeadline, eventValues); + assetList.put(eventValues.get("uid").getString(), cwDeadline); + + } else { + cwDeadline = null; + } + + Coursework coursework = new Coursework(courseworkValues.get("weighting").getInt(), + cwSetBy, cwMarkedBy, cwReviewedBy, courseworkValues.get("marks").getInt(), + cwStartDate, cwDeadline, cwExtensions); + + DataController.addVceProperties(coursework, courseworkValues); + return coursework; + + } + + /** + * Create an Exam object from the specified node list and asset list. + * + * @param nc The node list containing the Exam attributes + * @param assetList The assets belonging to the Exam + * @return an Exam object + * @throws IOException if requested item is not found in the list + */ + public static Exam createExam(NodeList nc, + Map assetList) throws IOException { + + HashMap examValues = + xmlTools.getSchemaValues(nc, HubFile.SCHEMA_EXAM); + + Person exSetBy; + Person exMarkedBy; + Person exReviewedBy; + ExamEvent exTimeSlot; + + String linkedSetBy = examValues.get("setBy").getString(); + String linkedMarkedBy = examValues.get("markedBy").getString(); + String linkedReviewedBy = examValues.get("reviewedBy").getString(); + + exSetBy = DataController.inList(assetList, linkedSetBy); + exMarkedBy = DataController.inList(assetList, linkedMarkedBy); + exReviewedBy = DataController.inList(assetList, linkedReviewedBy); + + if (examValues.containsKey("timeslot") + && XmlController.matchesSchema(examValues.get("timeslot").getNodeList(), + HubFile.SCHEMA_EXAMEVENT)) { + HashMap eventValues = + xmlTools.getSchemaValues(examValues.get("timeslot").getNodeList(), + HubFile.SCHEMA_EXAMEVENT); + //Room exRoom; + String linkedRoom = eventValues.get("room").getString(); + Room exRoom = DataController.inList(assetList, linkedRoom); + + + exTimeSlot = new ExamEvent(eventValues.get("date").getString(), exRoom, + eventValues.get("duration").getInt()); + + + DataController.addVceProperties(exTimeSlot, eventValues); + assetList.put(eventValues.get("uid").getString(), exTimeSlot); + + } else { + exTimeSlot = null; + } + + Exam exExamResit = null; + if (examValues.containsKey("resit")) { + String linkedResit = examValues.get("resit").getString(); + try { + exExamResit = DataController.inList(assetList, linkedResit); + } catch (Exception e) { // TODO maybe delete + // do nothing! + //exExamResit = null; + } + } + + Exam newExam; + if (exExamResit == null) { + newExam = new Exam(examValues.get("weighting").getInt(), + exSetBy, exMarkedBy, exReviewedBy, examValues.get("marks").getInt(), + exTimeSlot); + } else { + newExam = new Exam(examValues.get("weighting").getInt(), + exSetBy, exMarkedBy, exReviewedBy, examValues.get("marks").getInt(), + exTimeSlot, exExamResit); + } + + DataController.addVceProperties(newExam, examValues); + + return newExam; + } + + /** + * Create a TimetableEvent object from the specified node list and asset list. + * + * @param nc The node list containing the TimetableEvent attributes + * @param assetList The assets belonging to the TimetableEvent + * @return a TimetableEvent object + * @throws IOException if requested item is not found in the list + */ + public static TimetableEvent createTimetableEvent(NodeList nc, + Map assetList) throws IOException { + + TimetableEvent newTte; + + HashMap tteValues = xmlTools.getSchemaValues(nc, + HubFile.SCHEMA_TIMETABLE_EVENT); + + String linkedRoom = tteValues.get("room").getString(); + String linkedLecturer = tteValues.get("lecturer").getString(); + String linkedTtet = tteValues.get("timetableEventType").getString(); + + Room troom = DataController.inList(assetList, linkedRoom); + Person tlecturer = DataController.inList(assetList, linkedLecturer); + TimeTableEventType tttet = DataController.inList(assetList, linkedTtet); + + newTte = new TimetableEvent(tteValues.get("date").getString(), troom, tlecturer, + tttet, tteValues.get("duration").getInt()); + DataController.addVceProperties(newTte, tteValues); + + return newTte; + } + +} diff --git a/src/edu/wright/cs/raiderplanner/model/ICalExport.java b/src/edu/wright/cs/raiderplanner/model/ICalExport.java new file mode 100644 index 00000000..0be5a42b --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/ICalExport.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2017 - Amila Dias + * + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import biweekly.Biweekly; +import biweekly.ICalendar; +import biweekly.component.VEvent; +import biweekly.property.Summary; +import biweekly.util.Duration; + +import edu.wright.cs.raiderplanner.view.UiManager; + +import java.io.File; +import java.io.IOException; +import java.util.Date; +import java.util.Locale; + +/**Class used to create ICS files for export. + * @author Amila Dias + * CEG 3120 - RaiderPlanner + */ + +public class ICalExport { + private Date eventStart; + private String title; + private MultilineString description; + private int hours; + private int minutes; + private ICalendar ical = new ICalendar(); + + /** + * Take a user-created Event and add it to the ICalender for later export. + * to an ICS file. + * + * @param event User created event + */ + public void createExportEvent(Event event) { + setEventStart(event.getDate()); + setTitle(event.getName()); + setDescription(event.getDetails()); + + VEvent calEvent = new VEvent(); + Summary summary = calEvent.setSummary(title); + summary.setLanguage(getLang()); + + //Default hard coded duration + hours = 1; + minutes = 0; + + int intDuration = event.getDuration(); + hours = intDuration / 60; + minutes = intDuration % 60; + + //Set duration within event + Duration duration = new Duration.Builder().hours(hours).minutes(minutes).build(); + calEvent.setDuration(duration); + //Set start date + calEvent.setDateStart(eventStart); + //Add new event to ICS File + ical.addEvent(calEvent); + } + + /** + * Merge all existing iCal events and export the prepared ical events to an ICS file. + */ + public void exportToFile(File file) { + createIcs(ical, file); + } + + /** + * Generate the ICS file for export. + * + * @param ical ICalender that contains items for new ICS file + * @param file File object to be created by ICalendar information + */ + private void createIcs(ICalendar ical, File file) { + try { + Biweekly.write(ical).go(file); + } catch (IOException e) { + UiManager.reportError("File does not exist: " + e.getMessage()); + } + } + + /** + * Gets the current language of user based on the environment and locale. + * + * @return String formatted to provide user location and language + */ + private String getLang() { + Locale currentLocale = Locale.getDefault(); + String langCode = currentLocale.getDisplayLanguage() + + "-" + currentLocale.getDisplayCountry(); + return langCode; + } + + /** + * Sets the event start date. + * + * @param eventStart the event start date to set + */ + public void setEventStart(Date eventStart) { + this.eventStart = eventStart; + } + + /** + * Sets the title of the event. + * + * @param title the event title to set + */ + public void setTitle(String title) { + this.title = title; + } + + /** + * Sets the description of the event. + * + * @param description the event description to set + */ + public void setDescription(MultilineString description) { + this.description = description; + } + +} diff --git a/src/edu/wright/cs/raiderplanner/model/Milestone.java b/src/edu/wright/cs/raiderplanner/model/Milestone.java new file mode 100644 index 00000000..1038654a --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Milestone.java @@ -0,0 +1,248 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MenuController.Window; + +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class Milestone extends ModelEntity { + + private ArrayList tasks = new ArrayList<>(); + private Deadline deadline; + + // public methods + + // Constructors: + /** + * Milestone class constructor. + * @param name name of deadline + * @param details details of deadline + * @param deadline time of deadline + */ + public Milestone(String name, String details, LocalDate deadline) { + super(name, details); + this.deadline = new Deadline(deadline.format(DateTimeFormatter.ofPattern("MM/dd/yyyy")) + + "T00:00:01Z"); + } + + // Getters: + + /** + * Check whether this Milestone is complete. + * + * @return true if completed, false otherwise. + */ + public boolean isComplete() { + for (Task t : this.tasks) { + if (!t.isCheckedComplete()) { + return false; + } + } + return true; + } + + /** + * Computes progress based on Task weighting and Tasks completed. + * Used for JavaFX + * + * @return String representation of progress in percentage. + */ + public String getProgressPercentage() { + double completed = 0; + for (Task t : this.tasks) { + if (t.isCheckedComplete()) { + completed += t.getWeighting(); + } + } + + int result = (int) (completed / this.totalWeighting() * 100); + + return Integer.toString(result) + '%'; + } + + /** + * Returns an array of Tasks associated with this Milestone. + * + * @return an array of Tasks. + */ + public Task[] getTasks() { + return this.tasks.toArray(new Task[this.tasks.size()]); + } + + /** + * Returns the number of Tasks completed. + * + * @return integer representation of completed Tasks. + */ + public int tasksCompleted() { + int completed = 0; + for (Task t : this.tasks) { + if (t.isCheckedComplete()) { + completed++; + } + } + return completed; + } + + /** + * Returns the number of Tasks associated with this Milestone. + * + * @return number of Tasks. + */ + public int size() { + return this.tasks.size(); + } + + /** + * Return the sum of all Tasks weighting. + * + * @return integer representation of weightings sum. + */ + public int totalWeighting() { + int sum = 0; + for (Task t : this.tasks) { + sum += t.getWeighting(); + } + return sum; + } + + /** + * Returns a String representation of completed Tasks (e.g. 3/4). + * Used in JavaFX. + * + * @return String represenation of completed Tasks. + */ + public String getTaskCompletedAsString() { + return this.tasksCompleted() + "/" + this.tasks.size(); + } + + /** + * Returns a String representation of the Deadline for this Milestone. + * Used in JavaFX. + * + * @return String representation of a Deadline. + */ + public String getDeadline() { + return new SimpleDateFormat("MM/dd/yyyy").format(this.deadline.getDate()); + } + + /** + * Returns a Date object representing the Deadline of this Milestone. + * + * @return Date object. + */ + public Date getDeadlineDate() { + return this.deadline.getDate(); + } + + // Setters: + + /** + * Add the given Task to this Milestone. + * + * @param task Task to be added. + * @return Whether the Task was successfully added. + */ + public boolean addTask(Task task) { + if (this.tasks.contains(task)) { + return false; + } + this.tasks.add(task); + return true; + } + + /** + * Checks if the Milestone contains the requested task. + * + * @param task Task to be checked for + * @return Whether the task is contained. + */ + public boolean containsTask(Task task) { + return this.tasks.contains(task); + } + + /** + * Add all given Tasks to this Milestone. + * + * @param tasks + * a Collection of Tasks to be added. + * @return whether the provided Tasks were added successfully. + */ + public boolean addTasks(Collection tasks) { + for (Task curr : tasks) { + if (this.tasks.contains(curr)) { + return false; + } + } + this.tasks.addAll(tasks); + return true; + } + + /** + * Replace the current list of Tasks with the provided Tasks. + * + * @param tasks Collection of Tasks. + */ + public void replaceTasks(Collection tasks) { + this.tasks.clear(); + this.tasks.addAll(tasks); + } + + /** + * Remove a given Task from this Milestone. + * + * @param task Task to be removed. + */ + public void removeTask(Task task) { + this.tasks.remove(task); + } + + /** + * Set a new deadline. + * + * @param date date to be set as a new deadline + */ + public void setDeadline(LocalDate date) { + this.deadline.setDate(date.format(DateTimeFormatter.ofPattern("MM/dd/yyyy")) + + "T00:00:01Z"); + } + + /* (non-Javadoc) + * @see edu.wright.cs.raiderplanner.model.ModelEntity#open + * (edu.wright.cs.raiderplanner.controller.MenuController.Window) + */ + @Override + public void open(Window current) { + // TODO Auto-generated method stub + } + +} diff --git a/src/edu/wright/cs/raiderplanner/model/ModelEntity.java b/src/edu/wright/cs/raiderplanner/model/ModelEntity.java new file mode 100644 index 00000000..0cc195eb --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/ModelEntity.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * Copyright (C) 2018 - Roberto C. Sánchez + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MenuController; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public abstract class ModelEntity implements Serializable { + protected String name = ""; + protected MultilineString details = null; + protected ArrayList notes; + + /** + * Default constructor. + */ + public ModelEntity() { + this(""); + } + + /** + * Construct a ModelEntity with the given name and no details or notes. + * + * @param cname The name of the entity + */ + public ModelEntity(String cname) { + this(cname, ""); + } + + /** + * Construct a ModelEntity with the given name and white space-separated + * list of details and no notes. + * + * @param cname The name of the entity + * @param cdetails The entity details (white space-separated list) + */ + public ModelEntity(String cname, String cdetails) { + this(cname, cdetails.split("\n")); + } + + /** + * Construct a ModelEntity with the given name and array of details and no + * notes. + * + * @param cname The name of the entity + * @param cdetails The entity details (array) + */ + public ModelEntity(String cname, String[] cdetails) { + setName(cname); + setDetails(cdetails); + notes = new ArrayList<>(); + } + + /** + * Construct a ModelEntity with the given name and white space-separated + * list of details and list of notes. + * + * @param cname The name of the entity + * @param cdetails The entity details (array) + * @param cnotes The entity notes + */ + public ModelEntity(String cname, String[] cdetails, List cnotes) { + this(cname, cdetails); + notes = new ArrayList<>(cnotes); + } + + /** + * Returns the name of the entity. + * + * @return The name of the entity + */ + public String getName() { + return name; + } + + /** + * Returns the details of the entity. + * + * @return The details of the entity + */ + public MultilineString getDetails() { + return details; + } + + /** + * Set a new name for the entity. + * + * @param newName The new name for the entity + */ + public void setName(String newName) { + name = newName; + } + + /** + * Set new details for the entity. + * + * @param newDetails The new details for the entity + */ + public void setDetails(String newDetails) { + details = new MultilineString(newDetails); + } + + /** + * Set new details for the entity. + * + * @param newDetails The new details for the entity + */ + public void setDetails(String[] newDetails) { + details = new MultilineString(newDetails); + } + + /** + * Set new details for the entity. + * + * @param newDetails The new details for the entity + */ + public void setDetails(List newDetails) { + details = new MultilineString(newDetails.toArray(new String[newDetails.size()])); + } + + /** + * Set new details for the entity. + * + * @param newDetails The new details for the entity + */ + public void setDetails(MultilineString newDetails) { + details = newDetails; + } + + /** + * Set new name and details for the entity. + * + * @param aname The new entity name + * @param adetails The new details for the entity + */ + public void addProperties(String aname, MultilineString adetails) { + setName(aname); + setDetails(adetails.clone()); + } + + /** + * Set new name and details for the entity. + * + * @param aname The new entity name + * @param adetails The new details for the entity + */ + public void addProperties(String aname, String adetails) { + setName(aname); + setDetails(adetails); + } + + /** + * Open the appropriate UI window for this class + * To be overridden by children. + */ + public abstract void open(MenuController.Window current); + +} diff --git a/src/edu/wright/cs/raiderplanner/model/Module.java b/src/edu/wright/cs/raiderplanner/model/Module.java new file mode 100644 index 00000000..6ad00c2e --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Module.java @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MainController; +import edu.wright.cs.raiderplanner.controller.MenuController; + +import java.util.ArrayList; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 at 20:59 + */ +public class Module extends VersionControlEntity { + // private data + private ArrayList assignments = new ArrayList<>(); + private Person organizer; + private String moduleCode; + private ArrayList timetable = new ArrayList<>(); + + @Override + protected void replace(VersionControlEntity receivedVce) { + if (receivedVce instanceof Module) { + Module castedVce = (Module) receivedVce; + if (castedVce.getOrganiser() != null) { + this.organizer = castedVce.getOrganiser(); + } + if (castedVce.getModuleCode() != null) { + this.moduleCode = castedVce.getModuleCode(); + } + if (castedVce.getAssignments() != null) { + this.assignments = castedVce.getAssignments(); + } + if (castedVce.getAssignments() != null) { + this.timetable = castedVce.getTimetable(); + } + } + + super.replace(receivedVce); + } + + + /** + * ToString() method with boolean parameter. + * @param verbose True returns formatted string with Organizer, total assignments, + * and assignment details. False returns standard toString(). + * @return Formatted String. + */ + public String toString(boolean verbose) { + if (verbose) { + StringBuilder verboseString = new StringBuilder(); + verboseString.append(toString()); + verboseString.append("\n"); + verboseString.append("Organizer: " + organizer.toString()); + verboseString.append("\n"); + verboseString.append("Total Assignments: " + Integer.toString(assignments.size())); + verboseString.append("\n"); + + int num = -1; + int ii = assignments.size(); + + while (++num < ii) { + verboseString.append("\n"); + verboseString.append(assignments.get(num).toString(true)); + } + + return verboseString.toString(); + + } else { + return toString(); + } + } + + @Override + public String toString() { + return "Module: " + this.name + " ( " + this.moduleCode + " )"; + } + + /** + * Getter for the assignments variable. + * @return ArrayList for variable assignments. + */ + public ArrayList getAssignments() { + return assignments; + } + + /** + * Getter for the organizer variable. + * @return Person for variable organizer. + */ + public Person getOrganiser() { + return organizer; + } + + /** + * Getter for the moduleCode variable. + * @return String for variable moduleCode. + */ + public String getModuleCode() { + return moduleCode; + } + + /** + * Getter for retrieving the Timetable. + * @return ArrayList for variable timetable. + */ + public ArrayList getTimetable() { + return timetable; + } + + /** + * Getter for the number of assignments. + * @return Integer of the number of assignments. + */ + public int getNoOfAssignments() { + return this.assignments.size(); + } + + /** + * Calculates how much of this Module has been completed in percentage. + * + * @return int (0-100) + */ + public int calculateProgress() { + if (this.assignments.size() == 0) { + return 0; + } + + int sum = 0; + for (Assignment assignment : this.assignments) { + sum += assignment.calculateProgress(); + } + return sum / this.assignments.size(); + } + + /** + * Method to add an assignment. + * @param newAssignment Assignment variable to be added. + */ + public void addAssignment(Assignment newAssignment) { + // initial set up code below - check if this needs updating + if (!assignments.contains(newAssignment)) { + assignments.add(newAssignment); + } + } + + /** + * Method to remove an assignment. + * @param newAssignment Assignment variable to be removed. + */ + public void removeAssignment(Assignment newAssignment) { + // initial set up code below - check if this needs updating + if (assignments.contains(newAssignment)) { + assignments.remove(newAssignment); + } + } + + /** + * Method to set the organizer variable. + * @param newOrganizer Person variable. + */ + public void setOrganiser(Person newOrganizer) { + organizer = newOrganizer; + } + + /** + * Method to set the moduleCode variable. + * @param newModuleCode String variable. + */ + public void setModuleCode(String newModuleCode) { + moduleCode = newModuleCode; + } + + /** + * Method to add a Timetable Event. + * @param newTimetableEvent TimetableEvent variable that will be added. + */ + public void addTimetableEvent(TimetableEvent newTimetableEvent) { + if (!timetable.contains(newTimetableEvent)) { + timetable.add(newTimetableEvent); + } + } + + /** + * Method to remove a Time table event. + * @param newTimetableEvent TimetableEvent variable that will be removed. + */ + public void removeTimetableEvent(TimetableEvent newTimetableEvent) { + if (timetable.contains(newTimetableEvent)) { + timetable.remove(newTimetableEvent); + } + } + + @Override + public void open(MenuController.Window current) { + MainController.ui.moduleDetails(this, current); + } + + /** + * Constructor. + * @param corganizer Person variable used to set the organizer variable. + * @param cmoduleCode String variable used to set the moduleCode variable. + */ + public Module(Person corganizer, String cmoduleCode) { + setOrganiser(corganizer); + setModuleCode(cmoduleCode); + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/MultilineString.java b/src/edu/wright/cs/raiderplanner/model/MultilineString.java new file mode 100644 index 00000000..c4c354bb --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/MultilineString.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Arrays; + +/** + * Created by bendickson on 4/27/17. + */ +public class MultilineString implements Serializable, Cloneable { + // private Data; + private ArrayList lines; + + // public methods + + /** + * gets the multi-string clone from the array. + */ + public MultilineString clone() { + return new MultilineString(this.getAsArray()); + } + + /** + * Returns the number of lines in this MultilineString. + * + * @return number of lines + */ + public int getLines() { + return lines.size(); + } + + /** + * Gets the arrayList. + * @return arrayList of type string + */ + public ArrayList getAsArrayList() { + return lines; + } + + /** + * get the lines as an array. + * @return an array of lines + */ + public String[] getAsArray() { + String[] line = new String [(lines.size())]; + line = lines.toArray(line); + return line; + } + + /** + * get the arrayList as a string. + * @return the string joiner + */ + public String getAsString() { + return String.join("\n", getAsArray()); + } + + //Constructors + + /** + * Constructor with no arguments. + */ + public MultilineString() { + lines = new ArrayList<>(); + } + + /** + * Constructor with a String. + * @param string string to construct the multilineString + */ + public MultilineString(String string) { + lines = new ArrayList<>(Arrays.asList(string.split("\n"))); + } + + /** + * Constructor with an Array of type String. + * @param string array of strings to construct the multilineString + */ + public MultilineString(String[] string) { + lines = new ArrayList<>(Arrays.asList(string)); + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/Note.java b/src/edu/wright/cs/raiderplanner/model/Note.java new file mode 100644 index 00000000..e869c2c9 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Note.java @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import java.io.Serializable; +import java.util.GregorianCalendar; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class Note implements Serializable { + // private data + private String title; + private GregorianCalendar timeStamp; + private MultilineString text; + + // public methods + + /** + * This getter gets the title of the note. + * @return the title + */ + public String getTitle() { + // initial set up code below - check if this needs updating + return title; + } + + /** + * This getter gets the time stamp of the note. + * @return the time stamp + */ + public GregorianCalendar getTimeStamp() { + // initial set up code below - check if this needs updating + return timeStamp; + } + + /** + * This getter gets the text of the note. + * @return the text + */ + public MultilineString getText() { + return text; + } + + /** + * This setter sets the title of the note. + * @param newTitle this is the new title to be set + */ + public void setTitle(String newTitle) { + // initial set up code below - check if this needs updating + title = newTitle; + } + + /** + * This setter sets the time stamp of the note. + * @param newTimeStamp This is the new time stamp to be set + */ + public void setTimeStamp(GregorianCalendar newTimeStamp) { + // initial set up code below - check if this needs updating + timeStamp = newTimeStamp; + } + + /** + * This setter sets the time stamp of the note. + * @param yr for year + * @param month for month + * @param day for day + * @param hr for hour + * @param min for minute + * @param sec for second + */ + public void setTimeStamp(int yr, int month, int day, int hr, int min, int sec) { + // initial set up code below - check if this needs updating + timeStamp = new GregorianCalendar(yr, month, day, hr, min, sec); + } + + /** + * This setter sets the text of the note. + * @param newText This is the new text to be set + */ + public void setText(MultilineString newText) { + // initial set up code below - check if this needs updating + text = newText; + } + + /** + * This is the constructor for the class. + * @param title This is the title + * @param timeStamp This is the time stamp + * @param text This is the text + */ + public Note(String title, GregorianCalendar timeStamp, MultilineString text) { + this.title = title; + this.timeStamp = timeStamp; + this.text = text; + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/Notification.java b/src/edu/wright/cs/raiderplanner/model/Notification.java new file mode 100644 index 00000000..0dfd9ce1 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Notification.java @@ -0,0 +1,161 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import java.io.Serializable; +import java.util.GregorianCalendar; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class Notification implements Serializable { + // private data + private String title; + private GregorianCalendar dateTime; + private MultilineString details; + private boolean read; + private ModelEntity link; + + // public methods + + /** + * Getter for the title. + * @return String for variable title + */ + public String getTitle() { + return title; + } + + /** + * Getter for the date. + * @return GregorianCalendar for variable dateTime + */ + public GregorianCalendar getDateTime() { + return dateTime; + } + + /** + * Getter for the details. + * @return MultilineString for variable details + */ + public MultilineString getDetails() { + return details; + } + + /** + * Getter for the details as a String. + * @return String for variable details + */ + public String getDetailsAsString() { + return this.details.getAsString(); + } + + /** + * Getter for value of read. + * @return Boolean for variable read + */ + public boolean isRead() { + return read; + } + + /** + * Getter for link. + * @return ModelEntity for variable link + */ + public ModelEntity getLink() { + return link; + } + + /** + * toString() method. + * @return Formatted String of title and details + */ + public String toString() { + return this.title + ": " + this.getDetailsAsString(); + } + + // setters + + /** + * Setter for variable read. Sets read to true. + */ + public void read() { + this.read = true; + } + + /** + * Setter for variable read. Sets read to false. + */ + public void unread() { + this.read = false; + } + + /** + * Setter for variable read. Will toggle the value or read. + */ + public void toggle() { + this.read = !read; + } + + // constructors + + /** + * Constructor when all parameters are passed. + * @param title String + * @param dateTime GregorianCalendar + * @param details String + * @param link ModelEntity + */ + public Notification(String title, GregorianCalendar dateTime, String details, + ModelEntity link) { + this.title = title; + this.dateTime = dateTime; + this.details = new MultilineString(details); + this.read = false; + this.link = link; + } + + /** + * Constructor when the title, dateTime, and details parameters are passed. + * @param title String + * @param dateTime GregorianCalendar + * @param details String + */ + public Notification(String title, GregorianCalendar dateTime, String details) { + this.title = title; + this.dateTime = dateTime; + this.details = new MultilineString(details); + this.read = false; + } + + /** + * Constructor when the title and dateTime parameters are passed. + * @param title String + * @param dateTime GregorianCalendar + */ + public Notification(String title, GregorianCalendar dateTime) { + this.title = title; + this.dateTime = dateTime; + this.read = false; + } +} \ No newline at end of file diff --git a/src/edu/wright/cs/raiderplanner/model/Person.java b/src/edu/wright/cs/raiderplanner/model/Person.java new file mode 100644 index 00000000..b583778f --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Person.java @@ -0,0 +1,359 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar, Eric Sweet, Roberto C. Sánchez + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MenuController.Window; +import org.apache.commons.validator.routines.EmailValidator; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.regex.Pattern; + +/** + * This class represents a person and associated details (names, salutation, + * email, etc.). + * + * @author Team BRONZE on 4/27/17 + */ +public class Person extends VersionControlEntity { + + private static Pattern salutationRegex = Pattern.compile("[a-zA-Z]*"); + private static Pattern nameRegex = Pattern.compile("[a-zA-z\\s]*"); + + private ArrayList givenNames; + private String familyName; + private String salutation; + private String email; + private boolean familyNameLast = true; + + /** + * Create a person from the provided parameters. The name parameter + * is split to separate the family name from the given name(s). + * + * @param salutation The person's salutation, e.g., Mr., Mrs., Dr., etc. + * @param name The person's names, both given and family ("NAME1 NAME2 NAME3 + * .... NAMEn") + * @param famNameLast true to indicate that family comes last in the + * name parameter; false to indicate it comes first + */ + public Person(String salutation, String name, Boolean famNameLast) { + + this(salutation, name, famNameLast, ""); + + } + + /** + * Create a person from the provided parameters. + * + * @param salutation The person's salutation, e.g., Mr., Mrs., Dr., etc. + * @param givenNames The person's given name(s) + * @param famName The person's family name + * @param famNameLast true to indicate that family comes last in the + * name parameter; false to indicate it comes first + */ + public Person(String salutation, ArrayList givenNames, + String famName, Boolean famNameLast) { + + super(true); + setFamilyName(famName); + this.givenNames = new ArrayList(givenNames); + setSalutation(salutation); + familyNameLast = famNameLast; + email = ""; + + } + + /** + * Create a person from the provided parameters. The name parameter + * is split to separate the family name from the given name(s). + * + * @param salutation The person's salutation, e.g., Mr., Mrs., Dr., etc. + * @param name The person's given name(s) + * @param famNameLast true to indicate that family comes last in the + * name parameter; false to indicate it comes first + * @param newEmail The person's email address + */ + public Person(String salutation, String name, Boolean famNameLast, String newEmail) { + + setSalutation(salutation); + setName(name, famNameLast); + familyNameLast = famNameLast; + email = newEmail; + + } + + /** + * Create a person from the provided parameters. The givenNames + * parameter is split to separate multiple given names. + * + * @param salutation The person's salutation, e.g., Mr., Mrs., Dr., etc. + * @param givenNames The person's given name(s) + * @param famName The person's family name + * @param famNameLast true to indicate that family comes last; false to + * indicate it comes first + * @param newEmail The person's email address + */ + public Person(String salutation, String givenNames, String famName, + Boolean famNameLast, String newEmail) { + + setSalutation(salutation); + String personName; + if (famNameLast) { + personName = givenNames + " " + famName; + } else { + personName = famName + " " + givenNames; + } + setName(personName, famNameLast); + email = newEmail; + + } + + /** + * Create a person from the provided parameters. + * + * @param salutation The person's salutation, e.g., Mr., Mrs., Dr., etc. + * @param givenNames The person's given name(s) + * @param famName The person's family name + * @param famNameLast true to indicate that family comes last; false to + * indicate it comes first + * @param newEmail The person's email address + */ + public Person(String salutation, ArrayList givenNames, String famName, + Boolean famNameLast, String newEmail) { + + setFamilyName(famName); + this.givenNames = new ArrayList(givenNames); + setSalutation(salutation); + familyNameLast = famNameLast; + email = newEmail; + + } + + @Override + protected void replace(VersionControlEntity receivedVce) { + if (receivedVce instanceof Person) { + Person castedVce = (Person) receivedVce; + this.givenNames = castedVce.getGivenNames(); + this.familyName = castedVce.getFamilyName(); + this.salutation = castedVce.getSalutation(); + this.email = castedVce.getEmail(); + this.familyNameLast = castedVce.getFamilyNameLast(); + } + super.replace(receivedVce); + } + + /** + * Returns this person's full name. + * + * @return this person's full name + */ + public String getFullName() { + if (familyNameLast) { + return (salutation.length() > 0 ? salutation + " " : "") + + String.join(" ", givenNames) + " " + familyName; + } else { + return (salutation.length() > 0 ? salutation + " " : "") + familyName + " " + + String.join(" ", givenNames); + } + } + + /** + * Returns this person's family name. + * + * @return this person's family name + */ + public String getFamilyName() { + return familyName; + } + + /** + * Sets this person's family name. + * + * @param newFamilyName this person's family name + */ + public void setFamilyName(String newFamilyName) { + familyName = newFamilyName; + } + + /** + * Returns this person's email address. + * + * @return this person's email address + */ + public String getEmail() { + return email; + } + + /** + * Sets this person's email address. + * + * @param newEmail this person's email address + */ + public void setEmail(String newEmail) { + email = newEmail; + } + + /** + * Returns a list of given names for this person. + * + * @return a list of given names for this person + */ + public ArrayList getGivenNames() { + return new ArrayList(givenNames); + } + + /** + * Sets the given names from a string with space separated names. + * + * @param nameStr a space separated String containing names + */ + public void setGivenNames(String nameStr) { + String[] nameSplit = nameStr.split(" "); + givenNames = new ArrayList<>(Arrays.asList(nameSplit)); + } + + /** + * Checker whether this Person specified his family name as last name. + * + * @return true if family name comes last, false otherwise + */ + public boolean getFamilyNameLast() { + return familyNameLast; + } + + /** + * Returns this person's salutation. + * + * @return this person's salutation + */ + public String getSalutation() { + return salutation; + } + + /** + * Sets this person's salutation. + * + * @param newSalutation this person's salutation + */ + public void setSalutation(String newSalutation) { + salutation = newSalutation; + } + + /** + * Returns true if this person has a salutation, false otherwise. + * + * @return true if this person has a salutation, false otherwise + */ + public boolean hasSalutation() { + return salutation.length() > 0; + } + + /** + * Returns this person's preferred name. + * + * @return this person's preferred name + */ + public String getPreferredName() { + return name.length() > 0 ? name : (givenNames.size() > 0 ? givenNames.get(0) : familyName); + } + + /** + * Sets this person's preferred name. + * + * @param newPreferredName this person's preferred name + */ + public void setPreferredName(String newPreferredName) { + name = newPreferredName; + } + + /** + * Sets this person's name from a string with space separated names. The + * family name position at the start or end of the name is indicated by + * boolean value. + * + * @param name The person's names, both given and family ("NAME1 NAME2 NAME3 + * .... NAMEn") + * @param famNameLast true to indicate that family comes last in the + * name parameter; false to indicate it comes first + */ + public void setName(String name, boolean famNameLast) { + String[] nameSplit = name.split(" "); + familyNameLast = famNameLast; + givenNames = new ArrayList<>(); + int number = -1; + int ii = nameSplit.length; + if (familyNameLast) { + familyName = nameSplit[--ii]; + } else { + familyName = nameSplit[++number]; + } + while (++number < ii) { + givenNames.add(nameSplit[number]); + } + } + + /** + * Checks whether the given String is a valid email. + * + * @param email The email address to validate + * @return true if the email is valid, false otherwise + */ + public static boolean validEmail(String email) { + EmailValidator validator = EmailValidator.getInstance(); + return validator.isValid(email); + } + + /** + * Checks whether the given String is a valid name. + * + * @param name The name to validate + * @return true if the name is valid, false otherwise + */ + public static boolean validName(String name) { + return nameRegex.matcher(name).matches(); + } + + /** + * Checks whether the given String is a valid salutation. + * + * @param salutation The salutation to validate + * @return true if the salutation is valid, false otherwise + */ + public static boolean validSalutation(String salutation) { + return salutationRegex.matcher(salutation).matches(); + } + + @Override + public String toString() { + return getFullName() + " ( " + getEmail() + " )"; + } + + /* (non-Javadoc) + * @see edu.wright.cs.raiderplanner.model.ModelEntity#open + * (edu.wright.cs.raiderplanner.controller.MenuController.Window) + */ + @Override + public void open(Window current) { + // TODO Auto-generated method stub + } + +} diff --git a/src/edu/wright/cs/raiderplanner/model/QuantityType.java b/src/edu/wright/cs/raiderplanner/model/QuantityType.java new file mode 100644 index 00000000..1f402a05 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/QuantityType.java @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MainController; +import edu.wright.cs.raiderplanner.controller.MenuController.Window; + +import java.util.ArrayList; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class QuantityType extends ModelEntity { + private static ArrayList quantityDatabase = new ArrayList<>(); + + /** + * gets a list of names inside the database. + * @return array of names + */ + public static String[] listOfNames() { + String[] rr = new String[quantityDatabase.size()]; + int ii = quantityDatabase.size(); + int jj = -1; + while (++jj < ii) { + rr[jj] = quantityDatabase.get(jj).getName(); + } + return rr; + } + + /** + * gets an array of quantityTypes. + * @return array of quantityTypes + */ + public static QuantityType[] listOfQuantityTypes() { + QuantityType[] rr = new QuantityType[quantityDatabase.size()]; + int jj = -1; + int ii = quantityDatabase.size(); + while (++jj < ii) { + rr[jj] = quantityDatabase.get(jj); + } + return rr; + } + + /** + * gets the quantityType. + * @param tt string of the quantity + * @return the quantity type + */ + public static QuantityType get(String tt) { + int jj = -1; + int ii = quantityDatabase.size(); + while (++jj < ii) { + if (quantityDatabase.get(jj).equals(tt)) { + return quantityDatabase.get(jj); + } + } + return DEFAULT; + } + + /** + * checks if the quantityType exists. + * @param qt quantityType + * @return true or false if it exists + */ + public static boolean exists(QuantityType qt) { + int jj = -1; + int ii = quantityDatabase.size(); + while (++jj < ii) { + if (quantityDatabase.get(jj).equals(qt)) { + return true; + } + } + return false; + } + + /** + * checks to see if the string exists in the database. + * @param tt name of the quantity + * @return true or false depending on if it is in database + */ + public static boolean exists(String tt) { + int jj = -1; + int ii = quantityDatabase.size(); + while (++jj < ii) { + if (quantityDatabase.get(jj).equals(tt)) { + return true; + } + } + return false; + } + + /** + * Create a new QuantityType. + * + * @param name Name of the quantity. + * @param details Details of the quantity. + * @return quantityType + */ + public static QuantityType create(String name, String details) { + QuantityType type = new QuantityType(name, details); + if (MainController.getSpc() != null) { + MainController.getSpc().addQuantityType(type); + } + return type; + } + + /** + * Create a new QuantityType. + * + * @param name Name of the quantity. + * @return quantityType + */ + public static QuantityType create(String name) { + QuantityType type = new QuantityType(name); + if (MainController.getSpc() != null) { + MainController.getSpc().addQuantityType(type); + } + return type; + } + + /** + * Create a new QuantityType from an existing one. + * + * @param type QuantityType object + */ + public static void create(QuantityType type) { + if (!QuantityType.quantityDatabase.contains(type)) { + QuantityType.quantityDatabase.add(type); + if (MainController.getSpc() != null) { + MainController.getSpc().addQuantityType(type); + } + } + } + + /** + * A toString method used in TableView. + * @return name + */ + public String toString() { + return this.name; + } + + // a temporary way to populate the array until we later replace from reading a set up file + static { + /** + * Class to populate the quantityType until it is better implemented. + * @author Eric Sweet + * + */ + class Pair { + public String one; + public String two; + + /** + * Constructor of the pair. + * @param name name of the type + * @param details detail of the type + */ + Pair(String name, String details) { + one = name; + two = details; + } + } + + Pair[] staticTypes = { + new Pair("Other", "Other"), + new Pair("Hours", "Work in hours"), + new Pair("Books read", "Read this number of books"), + new Pair("Videos watched", "Watched this number of videos"), + new Pair("thousand words written", "Number of thousand words written"), + new Pair("questions answered", "Number of questions answered") + }; + int jj = -1; + int ii = staticTypes.length; + while (++jj < ii) { + QuantityType type = new QuantityType(staticTypes[jj].one, staticTypes[jj].two); + } + } + + /** + * adds the quantity to the database if it does not exist. + * @param name name of the quantity + */ + private QuantityType(String name) { + super(name); + if (!exists(this)) { + quantityDatabase.add(this); + } + } + + /** + * adds the quantity to database if it does not exist. + * @param name name of quantity + * @param details details of quantity + */ + private QuantityType(String name, String details) { + super(name, details); + if (!exists(this)) { + quantityDatabase.add(this); + } + } + + /** + * Test to see if the object name is equal to type. + * @return if they are equal + */ + @Override + public boolean equals(Object obj) { + QuantityType that = (QuantityType) obj; + try { + return that.getName().equals(getName()); + } catch (NullPointerException e) { + System.out.println("QuantityType name was null"); + return false; + } + } + + /** + * Sees if the name is equal to string c. + * @param name name to check against + * @return returns if they are equal + */ + public boolean equals(String name) { + return getName().equals(name); + } + + /** + * Only used to Override has code. + */ + @Override + public int hashCode() { + assert false : "hashCode not designed"; + return 42; // any arbitrary constant will do + } + + public static QuantityType DEFAULT = quantityDatabase.get(0); + + /* (non-Javadoc) + * @see edu.wright.cs.raiderplanner.model.ModelEntity#open + * (edu.wright.cs.raiderplanner.controller.MenuController.Window) + */ + @Override + public void open(Window current) { + // TODO Auto-generated method stub + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/Requirement.java b/src/edu/wright/cs/raiderplanner/model/Requirement.java new file mode 100644 index 00000000..aa859b8d --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Requirement.java @@ -0,0 +1,252 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MainController; +import edu.wright.cs.raiderplanner.controller.MenuController; +import edu.wright.cs.raiderplanner.view.UiManager; + +import java.io.IOException; +import java.util.ArrayList; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class Requirement extends ModelEntity { + protected boolean checkedCompleted; + protected double estimatedTimeInHours; + protected ArrayList activityLog = new ArrayList<>(); + protected int initialQuantity; + protected int remainingQuantity; + protected QuantityType quantityType; + + // public methods + + /** + * checks if it is complete. + * @return boolean true or false + */ + public boolean isComplete() { + return this.checkedCompleted; + } + + /** + * Returns the QuantityType of this Requirement. + * + * @return. + */ + public QuantityType getQuantityType() { + return this.quantityType; + } + + /** + * Returns the estimated time of this Requirement (in hours). + * + * @return. + */ + public double getEstimatedTimeInHours() { + return estimatedTimeInHours; + } + + /** + * Returns the initial quantity of this Requirement. + * + * @return. + */ + public int getInitialQuantity() { + return initialQuantity; + } + + /** + * Returns the remaining quantity of this Requirement. + */ + public int getRemainingQuantity() { + return remainingQuantity; + } + + /** + * Returns an array of ActivityEvents that are associated with this Requirement. + * + * @return. + */ + public Activity[] getActivityLog() { + return this.activityLog.toArray(new Activity[this.activityLog.size()]); + } + + /** + * Returns a double value representing the progress of this Requirement. + * + * @return value between 0.0 and 0.1 + */ + public double requirementProgress() { + return (double) (this.initialQuantity - this.remainingQuantity) / this.initialQuantity; + } + + /** + * sets the estimated time in hours. + * @param estimatedTimeInHours estimated time + */ + public void setEstimatedTimeInHours(double estimatedTimeInHours) { + this.estimatedTimeInHours = estimatedTimeInHours; + } + + /** + * Change the initial quantity. This will update the progress of this + * Requirement to reflect the change. + * + * @param initialQuantity + * The initial quantity to set of type int. + */ + public void setInitialQuantity(int initialQuantity) { + if (this.initialQuantity == this.remainingQuantity) { + this.initialQuantity = this.remainingQuantity = initialQuantity; + } else { + this.initialQuantity = initialQuantity; + this.update(); + } + } + + /** + * This method set the quantity type. + * @param quantityType The quantity type to set of type String. + */ + public void setQuantityType(String quantityType) { + this.quantityType = QuantityType.get(quantityType); + } + + /** + * Add an Activity to the current Requirement and update the progress + * of this Requirement accordingly. + * + * @param activity Activity to be added. + */ + public void addActivity(Activity activity) { + this.activityLog.add(activity); + this.remainingQuantity -= activity.getActivityQuantity(); + if (remainingQuantity <= 0) { + this.remainingQuantity = 0; + this.checkedCompleted = true; + } + } + + /** + * Update the current Requirement to reflect changes. + * + * @return whether any changes were made + */ + public boolean update() { + + + this.remainingQuantity = this.initialQuantity; + this.checkedCompleted = false; + for (Activity activity : this.activityLog) { + this.remainingQuantity -= activity.getActivityQuantity(); + } + int tempQuantity = this.remainingQuantity; + + if (this.remainingQuantity <= 0) { + this.remainingQuantity = 0; + this.checkedCompleted = true; + } + + return tempQuantity == this.remainingQuantity; + } + + /** + * Returns the Name of the Requirement (used for JavaFX). + * + * @return Name of the task + */ + @Override + public String toString() { + return this.name; + } + + /** + * Only used to Override has code. + */ + @Override + public int hashCode() { + assert false : "hashCode not designed"; + return 42; // any arbitrary constant will do + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object == null || this.getClass() != object.getClass()) { + return false; + } + + Requirement that = (Requirement) object; + + if (checkedCompleted != that.checkedCompleted) { + return false; + } + if (Double.compare(that.estimatedTimeInHours, estimatedTimeInHours) != 0) { + return false; + } + if (initialQuantity != that.initialQuantity) { + return false; + } + if (remainingQuantity != that.remainingQuantity) { + return false; + } + if (!activityLog.equals(that.activityLog)) { + return false; + } + return quantityType.equals(that.quantityType); + } + + @Override + public void open(MenuController.Window current) { + try { + MainController.ui.requirementDetails(this); + } catch (IOException e) { + UiManager.reportError("Unable to open view file"); + } + } + + // Constructors: + /** + * Class Constructor for the requirement containing the name, details, time, quantity, and type. + * @param name + * The name of the requirement to set of type String. + * @param details + * The details of the requirement to set of type String. + * @param time + * The time of the requirement to set of type double. + * @param quantity + * The quantity of the requirement to set of type int. + * @param type + * The type of the requirement to set of type String. + */ + public Requirement(String name, String details, double time, int quantity, String type) { + super(name, details); + this.estimatedTimeInHours = time; + this.initialQuantity = this.remainingQuantity = quantity; + this.quantityType = QuantityType.get(type); + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/Room.java b/src/edu/wright/cs/raiderplanner/model/Room.java new file mode 100644 index 00000000..1e67bca7 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Room.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar, Ian Smith + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MenuController.Window; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class Room extends VersionControlEntity { + static final long serialVersionUID = 1L; + // private data + private Building building = null; + private String roomNumber; + + @Override + protected void replace(VersionControlEntity receivedVce) { + if (receivedVce instanceof Room) { + Room castedVce = (Room) receivedVce; + if (castedVce.getBuilding() != null) { + this.building = castedVce.getBuilding(); + } + this.roomNumber = castedVce.getRoomNumber(); + } + super.replace(receivedVce); + } + + // public methods + + // getters + /** + * Default method to return the building this room resides in. + * @return this room's building. + */ + public Building getBuilding() { + return building; + } + + /** + * Default method to return the room number. + * @return this room's number. + */ + public String getRoomNumber() { + return roomNumber; + } + + /** + * Method to output the name and room number of a person. If the room does + * not have a building number, the output message displays "name + * − Unknown Building". If the building is not null, the output + * message displays "name ( room ) located in building", where + * {@code room} represents the room number and {@code building} represents + * the building object. + * + * @return the location of this room. + */ + public String getLocation() { + if (building == null) { + return name + " - Unknown Building"; + } else { + return name + " ( " + roomNumber + " ) located in " + building.toString(); + } + } + + /** + * Delegates to {@code getLocation()}. + */ + @Override + public String toString() { + return this.getLocation(); + } + + // setters + /** + * Sets a value for a building. + * + * @param newBuilding set this room's building to the specified value + */ + public void setBuilding(Building newBuilding) { + building = newBuilding; + } + + /** + * Sets the value for a roomNumber. + * + * @param newRoomNumber set this room's number to the specified value + */ + public void setRoomNumber(String newRoomNumber) { + roomNumber = newRoomNumber; + } + + // Constructors: + /** + * Constructor to create a room with a building and room number. + * + * @param constructRoomNumber value of the number of the room + * @param constructBuilding building in which the room is located + */ + public Room(String constructRoomNumber, Building constructBuilding) { + setRoomNumber(constructRoomNumber); + setBuilding(constructBuilding); + } + + /** + * Constructor that creates a room with a room number, but without a + * building. This allows the building to be added at a later time. Could be + * useful if a structure is being built, but has not been named, or is being + * renamed. + * + * @param constructRoomNumber value of the number of the room + */ + public Room(String constructRoomNumber) { + setRoomNumber(constructRoomNumber); + } + + /* (non-Javadoc) + * @see edu.wright.cs.raiderplanner.model.ModelEntity#open + * (edu.wright.cs.raiderplanner.controller.MenuController.Window) + */ + @Override + public void open(Window current) { + // TODO Auto-generated method stub + } + +} diff --git a/src/edu/wright/cs/raiderplanner/model/Settings.java b/src/edu/wright/cs/raiderplanner/model/Settings.java new file mode 100644 index 00000000..cc9034ee --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Settings.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2018 - Clayton D. Terrill + * + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.view.UiManager; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Properties; +import java.util.regex.Pattern; + +/** + * Class to load and save settings to a + * config.properties file. + * @author Clayton D. Terrill + * + */ +public class Settings { + + // Object Classes + Properties prop = new Properties(); + + // Global Parameters with default values + private boolean isAccountStartup = false; + private String accountFilePath = ""; + private String toolBarColor = "026937FF"; + private String toolBarTextColor = "FFFFFFFF"; + private String toolBarIconColor = "FFFFFFFF"; + + /** + * Constructor loads the settings from + * the properties file. + */ + public Settings() { + //createConfig(); + loadSettings(); + } + + /** + * Creates the config.properties file to + * store the setting's properties. + */ + public void createConfig() { + OutputStream output = null; + try { + // Set output stream to config.properties + output = new FileOutputStream("config.properties"); + // Insert default values into prop + this.prop.setProperty("isAccountStartup", String.valueOf(this.isAccountStartup)); + this.prop.setProperty("filePath", this.accountFilePath); + this.prop.setProperty("toolBarColor", this.toolBarColor); + this.prop.setProperty("toolBarTextColor", this.toolBarTextColor); + this.prop.setProperty("toolBarIconColor", this.toolBarIconColor); + // Save config.properties to project root folder + this.prop.store(output, null); + } catch (IOException io) { + UiManager.reportError("Error, unable to create config.properties."); + } finally { + if (output != null) { + try { + output.close(); + } catch (IOException e) { + UiManager.reportError("Error, unable to close config.properties."); + } + } + } + } + + /** + * Loads the settings from the config.properties file + * located within the root folder. The class variables + * are assigned these values. + */ + public void loadSettings() { + InputStream input = null; + try { + File file = new File("config.properties"); + if (!file.exists()) { + createConfig(); + } + // Set input stream to config.properties + input = new FileInputStream("config.properties"); // Looks in root directory + // Load the config.properties file + this.prop.load(input); + // Get the property value and assign it to variable + this.isAccountStartup = Boolean.parseBoolean(prop.getProperty("isAccountStartup")); + this.accountFilePath = prop.getProperty("filePath"); + this.toolBarColor = prop.getProperty("toolBarColor"); + this.toolBarTextColor = prop.getProperty("toolBarTextColor"); + this.toolBarIconColor = prop.getProperty("toolBarIconColor"); + } catch (IOException ex) { + UiManager.reportError("Error, unable to load config.properties."); + } finally { + if (input != null) { + try { + input.close(); + } catch (IOException e) { + UiManager.reportError("Error, unable to close config.properties."); + } + } + } + } + + /** + * Saves the settings to the config.properties file + * located within the root folder. Values being saved + * are from the class variables. + */ + public void saveSettings() { + OutputStream output = null; + try { + File file = new File(this.accountFilePath); + if (file.exists()) { + // Assign property files + this.prop.setProperty("isAccountStartup", String.valueOf(this.isAccountStartup)); + this.prop.setProperty("filePath", this.accountFilePath); + this.prop.setProperty("toolBarColor", this.toolBarColor); + this.prop.setProperty("toolBarTextColor", this.toolBarTextColor); + this.prop.setProperty("toolBarIconColor", this.toolBarIconColor); + } else { + // File did not exists so do not use. + this.prop.setProperty("isAccountStartup", "False"); + this.prop.setProperty("filePath", ""); + this.prop.setProperty("toolBarColor", "026937FF"); + this.prop.setProperty("toolBarTextColor", "FFFFFFFF"); + this.prop.setProperty("toolBarIconColor", "FFFFFFFF"); + } + // Set output stream to config.properties + output = new FileOutputStream("config.properties"); + this.prop.store(output, null); //Stores in root directory + } catch (FileNotFoundException e) { + UiManager.reportError("Error, config.properties does not exist."); + } catch (IOException e) { + UiManager.reportError("Error, unable to save to config.properties."); + } finally { + if (output != null) { + try { + output.close(); + } catch (IOException e) { + UiManager.reportError("Error, unable to close config.properties."); + } + } + } + } + + /** + * Determines if a string is a valid hex color. + * @param colorHexString - String with the color represented by hex. + * @return boolean - Returns true if the colorHexString matches the hex regex pattern. + */ + public boolean isColorHex(String colorHexString) { + Pattern colorPattern = Pattern.compile("([0-9a-fA-F]{8})"); + return colorPattern.matcher(colorHexString).matches(); + } + + /** + * Sets the isAccountStartup variable. + * @param isAccountStartupTemp - Value for isAccountStartup. + */ + public void setAccountStartup(boolean isAccountStartupTemp) { + this.isAccountStartup = isAccountStartupTemp; + } + + /** + * Sets the accountFilePath variable. + * @param accountFilePathTemp - Value for accountFilePath. + */ + public void setAccountFilePath(String accountFilePathTemp) { + this.accountFilePath = accountFilePathTemp; + } + + /** + * Sets the toolBarColor variable. + * @param toolBarColorTemp - Value for toolBarColor. + */ + public void setToolBarColor(String toolBarColorTemp) { + this.toolBarColor = toolBarColorTemp; + } + + /** + * Sets the toolBarTextColor variable. + * @param toolBarColorTextTemp - Value for toolBarTextColor. + */ + public void setToolBarTextColor(String toolBarColorTextTemp) { + this.toolBarTextColor = toolBarColorTextTemp; + } + + /** + * Sets the toolBarIconColor variable. + * @param toolBarIconColorTemp - Value for toolBarIconColor. + */ + public void setToolBarIconColor(String toolBarIconColorTemp) { + this.toolBarIconColor = toolBarIconColorTemp; + } + + /** + * Returns whether the account startup is used or not. + * @return boolean isAccountStartup + */ + public boolean getAccountStartup() { + return this.isAccountStartup; + } + + /** + * Returns the account file path. + * @return String accountFilePath + */ + public String getDefaultFilePath() { + return this.accountFilePath; + } + + /** + * Returns the color of the ToolBar. + * @return String toolBarColor + */ + public String getToolBarColor() { + return this.toolBarColor; + } + + /** + * Returns the color of the ToolBar Text. + * @return String toolBarColor + */ + public String getToolBarTextColor() { + return this.toolBarTextColor; + } + + /** + * Returns the color of the ToolBar Icon. + * @return String toolBarColor + */ + public String getToolBarIconColor() { + return this.toolBarIconColor; + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/StudyPlanner.java b/src/edu/wright/cs/raiderplanner/model/StudyPlanner.java new file mode 100644 index 00000000..1ae72689 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/StudyPlanner.java @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.view.UiManager; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class StudyPlanner implements Serializable { + // private data + private static final long serialVersionUID = 101L; + + private int version = -1; + private Account account; + private ArrayList quantityTypes = new ArrayList<>(); + private ArrayList taskTypes = new ArrayList<>(); + private ArrayList studyProfiles = new ArrayList<>(); + private ArrayList activityList = new ArrayList<>(); + private ArrayList calendar = new ArrayList<>(); + private ArrayList notifications = new ArrayList<>(); + private HashMap deadlineNotifications = new HashMap<>(); + private ArrayList versionControlLibrary = new ArrayList<>(); + + private StudyProfile currentStudyProfile = new StudyProfile(new HubFile(0, + 0,0,new ArrayList(),new ArrayList(), + new ArrayList(),"No semester",new MultilineString("No details"),"No UId")); + + // public methods + /** + * Removes the specified profile. + * + * @param profile The profile to remove + */ + public void removeProfile(StudyProfile profile) { + studyProfiles.remove(profile); + activityList.clear(); + calendar.clear(); + deadlineNotifications.clear(); + } + + /** + * Getter for the variable calendar. + * @return ArrayList for the variable calendar. + */ + public ArrayList getCalendar() { + return calendar; + } + + /** + * Return a String array of studyProfile names. + * + * @return a String array of studyProfile names. + */ + public String[] getListOfStudyProfileNames() { + int num = -1; + String[] studyProfileSize = new String[studyProfiles.size()]; + while (++num < studyProfiles.size()) { + studyProfileSize[num] = studyProfiles.get(num).getName(); + } + return studyProfileSize; + } + + /** + * Return an array of study profiles. + * + * @return an array of study profiles. + */ + public StudyProfile[] getStudyProfiles() { + StudyProfile[] sp = new StudyProfile[this.studyProfiles.size()]; + sp = this.studyProfiles.toArray(sp); + return sp; + } + + /** + * This was added at the last minute before releasing and should be in the Module class, however + * if we had done that our Java Serialized file, which took 3 hours prepare, + * would no longer have worked.This is temporary solution so you can still use + * our prepared Study Planner save file, but in the next iteration + * it will be moved to the correct place and would not consist of 4 nested loops. + * Also, for public release, we would create a file format for saving in so + * Java Serialization issues would not + * occur for the end user. + * + * @param module : given value is a Module type + * @return. + */ + public int getTimeSpent(Module module) { + int time = 0; + for (Assignment assignment : module.getAssignments()) { + for (Task task : assignment.getTasks()) { + for (Requirement requirement : task.getRequirements()) { + for (Activity activity : requirement.getActivityLog()) { + time += activity.getDuration(); + } + } + } + } + return time; + } + + /** + * Check whether this StudyPlanner contains a StudyProfile with the given parameters. + * + * @param syear year + * @param ssem semester number + * @return whether this StudyProfile exists + */ + public boolean containsStudyProfile(int syear, int ssem) { + int num = -1; + int ii = studyProfiles.size(); + while (++num < ii) { + if (studyProfiles.get(num).matches(syear, ssem)) { + return true; + } + } + return false; + } + + /** + * Add a given event to the global calendar. + * + * @param event Event to be added to the calendar. + */ + public void addEventToCalendar(Event event) { + if (!calendar.contains(event)) { + calendar.add(event); + } + } + + /** + * Returns the current StudyProfile. + * + * @return current StudyProfile + */ + public StudyProfile getCurrentStudyProfile() { + return this.currentStudyProfile; + } + + /** + * Get the preferred name of the user using this StudyPlanner. + * + * @return String containing the users name. + */ + public String getUserName() { + return this.account.getStudentDetails().getPreferredName(); + } + + /** + * Get all notifications in this StudyPlanner. + * + * @return an array of notifications. + */ + public Notification[] getNotifications() { + Notification[] notification = new Notification[this.notifications.size()]; + notification = this.notifications.toArray(notification); + return notification; + } + + /** + * Get all unread notifications in this StudyPlanner. + * + * @return an array of unread notifications. + */ + public Notification[] getUnreadNotifications() { + Notification[] notification = this.notifications.stream().filter(e -> + !e.isRead()).toArray(Notification[]::new); + return notification; + } + + /** + * Returns a HashMap that contains information about Deadline notifications. + * + * @return a HashMap that contains information about Deadline notifications. + */ + public HashMap getDeadlineNotifications() { + return deadlineNotifications; + } + + /** + * Returns an ArrayList of QuantityTypes. + * @return : returns a list of QuantityType + */ + public ArrayList getQuantityTypes() { + return this.quantityTypes; + } + + /** + * Returns an ArrayList of TaskTypes. + * + * @return returns a list of TaskType + */ + public ArrayList getTaskTypes() { + return this.taskTypes; + } + + // setters + + /** + * Change the current Study Profile to a given one. + * + * @param profile a StudyProfile to be marked as current. + * @return whether changed successfully. + */ + public boolean setCurrentStudyProfile(StudyProfile profile) { + if (this.studyProfiles.contains(profile)) { + if (this.currentStudyProfile != null) { + this.currentStudyProfile.setCurrent(false); + } + this.currentStudyProfile = profile; + profile.setCurrent(true); + return true; + } + return false; + } + + /** + * Change the current Study Profile to a Study Profile with the given ID. + * @param profileId : ID of a Study Profile. + * @return whether changed successfully. + */ + public boolean setCurrentStudyProfile(String profileId) { + this.studyProfiles.forEach(e -> { + if (e.getUid().equals(profileId)) { + this.setCurrentStudyProfile(e); + } + }); + + return this.currentStudyProfile.getUid().equals(profileId); + } + + /** + * Adds a new StudyProfile to the StudyPlanner. + * + * @param profile StudyProfile to be added. + */ + public void addStudyProfile(StudyProfile profile) { + this.studyProfiles.add(profile); + } + + /** + * Add a new notification to this StudyPlanner. + * + * @param notification Notification to be added. + */ + public void addNotification(Notification notification) { + this.notifications.add(notification); + } + + /** + * Add an Activity to this Study Planner and update appropriate fields. + * + * @param activity Activity to be added. + */ + public void addActivity(Activity activity) { + this.activityList.add(activity); + ArrayList assignments = new ArrayList<>(); + // Loop through all Tasks: + for (Task t : activity.getTasks()) { + // Distribute Activity Quantity to available Requirements of a Task: + int quantity = activity.getActivityQuantity(); + for (Requirement r : t.getRequirements()) { + if (r.getQuantityType().equals(activity.getType()) && !r.checkedCompleted) { + quantity -= r.getRemainingQuantity(); + Activity extracted = new Activity(activity); + + if (quantity > 0) { + extracted.setActivityQuantity(r.getRemainingQuantity()); + r.addActivity(extracted); + } else { + extracted.setActivityQuantity(quantity + r.getRemainingQuantity()); + r.addActivity(extracted); + break; + } + } + } + // ================= + for (Assignment assignment : t.getAssignmentReferences()) { + if (!assignments.contains(assignment)) { + assignments.add(assignment); + } + } + } + // ================= + + // Distribute quantity to Assignment requirements: + for (Assignment a : assignments) { + int quantity = activity.getActivityQuantity(); + for (Requirement r : a.getRequirements()) { + if (r.getQuantityType().equals(activity.getType()) && !r.checkedCompleted) { + quantity -= r.getRemainingQuantity(); + Activity extracted = new Activity(activity); + + if (quantity > 0) { + extracted.setActivityQuantity(r.getRemainingQuantity()); + r.addActivity(extracted); + } else { + extracted.setActivityQuantity(quantity + r.getRemainingQuantity()); + r.addActivity(extracted); + break; + } + } + } + } + // ================= + } + + /** + * Add a VersionControlEntity to the library. + * + * @param vce VersionControlEntity to be added. + * @return whether added successfully. + */ + public boolean addToVersionControlLibrary(VersionControlEntity vce) { + if (versionControlLibrary.contains(vce)) { + return false; + } else { + versionControlLibrary.add(vce); + return true; + } + } + + /** + * Check whether the current VCE library is empty. + * + * @return true if empty, false otherwise. + */ + public boolean emptyVersionControlLibrary() { + return versionControlLibrary.isEmpty(); + } + + /** + * Rebuild the VCE library (used when the app is reloaded). + */ + public void rebuildVersionControlLibrary() { + versionControlLibrary.forEach(e -> e.reload()); + } + + /** + * Update the version of this StudyPlanner. + * + * @param newVersion new version number + * @return whether updated successfully. + */ + public boolean setVersion(int newVersion) { + if (newVersion > version) { + version = newVersion; + return true; + } else { + return false; + } + } + + /** + * Getter for the variable version. + * @return Integer for variable version + */ + public int getVersion() { + return version; + } + + /** + * Constructor. + * @param newAccount Account type for variable account + */ + public StudyPlanner(Account newAccount) { + this.account = newAccount; + try { + // Add Default Quantity types: + Collections.addAll(this.quantityTypes, QuantityType.listOfQuantityTypes()); + // Add Default Task types: + Collections.addAll(this.taskTypes, TaskType.listOfTaskTypes()); + } catch (UnsupportedOperationException e) { + UiManager.reportError("Error, Unsupported Operation Exception."); + System.exit(1); + } catch (NullPointerException e) { + UiManager.reportError("Error, Null Pointer Exception."); + System.exit(1); + } catch (IllegalArgumentException e) { + UiManager.reportError("Error, Illegal Argument Exception."); + System.exit(1); + } + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/StudyProfile.java b/src/edu/wright/cs/raiderplanner/model/StudyProfile.java new file mode 100644 index 00000000..18480501 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/StudyProfile.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MainController; +import edu.wright.cs.raiderplanner.controller.MenuController; +import edu.wright.cs.raiderplanner.view.UiManager; + +import java.io.IOException; +import java.util.ArrayList; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class StudyProfile extends VersionControlEntity { + private ArrayList modules; + private ArrayList milestones; + private ArrayList extensions; + private ArrayList calendar = new ArrayList<>(); + private int year; + private int semesterNo; + private boolean current; + + // public methods + /** + * Clears the profile so that PlannerViewer can update. + */ + public void clearProfile() { + modules.clear(); + milestones.clear(); + extensions.clear(); + calendar.clear(); + } + + // getters: + /** + * Returns an array of the Modules of this StudyProfile. + * @return an array of the Modules + */ + public Module[] getModules() { + Module[] module = new Module[this.modules.size()]; + module = this.modules.toArray(module); + return module; + } + + /** + * Returns an array of the Milestones of this StudyProfile. + * @return an array of the Milestones + */ + public Milestone[] getMilestones() { + Milestone[] milestone = new Milestone[this.milestones.size()]; + milestone = this.milestones.toArray(milestone); + return milestone; + } + + /** + * Returns an array of the extensions of this StudyProfile. + * @return an array of the extensions + */ + public ExtensionApplication[] getExtensions() { + ExtensionApplication[] extension = new ExtensionApplication[this.extensions.size()]; + extension = this.extensions.toArray(extension); + return extension; + } + + /** + * Returns a calendar containing all the Events of this Study Profile. + * + * @return ArrayList of Events + */ + public ArrayList getCalendar() { + return calendar; + } + + /** + * Returns an ArrayList of the Tasks of this StudyProfile. + * These Task themselves can contain a list of their own Task. + * @return an ArrayList of the Tasks + */ + public ArrayList getTasks() { + ArrayList tasks = new ArrayList<>(); + this.modules.forEach(e -> e.getAssignments().forEach(ee -> tasks.addAll(ee.getTasks()))); + return tasks; + } + + /** + * Whether this StudyProfile is set as current. + * + * @return true if current, else otherwise. + */ + public boolean isCurrent() { + return current; + } + + /** + * Set/unset this StudyProfile as the current profile of the StudyPlanner. + * + * @param current true if this is the current profile, false otherwise + */ + public void setCurrent(boolean current) { + this.current = current; + } + + /** + * Add an Event to the calendar of this Study Profile. + * + * @param event Event to be added. + */ + public void addEventToCalendar(Event event) { + if (!calendar.contains(event)) { + calendar.add(event); + } + } + + /** + * Returns the name of this StudyProfile as a String. + * @return the name of this StudyProfile + */ + @Override + public String getName() { + return name; + } + + /** + * Returns the year of this StudyProfile as an int. + * @return the year of this StudyProfile + */ + public int getYear() { + return year; + } + + /** + * Returns the semester number of this StudyProfile as an int. + * @return the semester number of this StudyProfile + */ + public int getSemesterNo() { + return semesterNo; + } + + /** + * Whether this StudyProfile matches the given details. + * + * @param myear year + * @param msemesterNo semester number + * @return true if matches, false otherwise. + */ + public boolean matches(int myear, int msemesterNo) { + return myear == year && msemesterNo == semesterNo; + } + + // Setters: + + /** + * Adds a Milestone to this StudyProfile. + * + * @param milestone Milestone to be added. + */ + public void addMilestone(Milestone milestone) { + this.milestones.add(milestone); + } + + /** + * Removes a Milestone from this StudyProfile. + * + * @param milestone Milestone to be removed. + * @return whether the Milestone was removed successfully. + */ + public boolean removeMilestone(Milestone milestone) { + return this.milestones.remove(milestone); + } + + @Override + public void open(MenuController.Window current) { + try { + MainController.ui.studyProfileDetails(this); + } catch (IOException e) { + UiManager.reportError("Unable to open view file"); + } + } + + // constructors + /** + * Class Constructor for a StudyProfile that gets made from an initialHubFile. + * @param initialHubFile - the file containing the initial modules, extensions, + * year, semester number, version, name, and details. + */ + public StudyProfile(HubFile initialHubFile) { + this.milestones = new ArrayList<>(); + + this.modules = initialHubFile.getModules(); + this.extensions = initialHubFile.getExtensions(); + + this.year = initialHubFile.getYear(); + this.semesterNo = initialHubFile.getSemester(); + this.version = initialHubFile.getVersion(); + this.name = initialHubFile.getSemesterName(); + this.details = initialHubFile.getSemesterDetails(); + this.current = true; + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/Task.java b/src/edu/wright/cs/raiderplanner/model/Task.java new file mode 100644 index 00000000..a8eacfc3 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Task.java @@ -0,0 +1,402 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MainController; +import edu.wright.cs.raiderplanner.controller.MenuController; +import edu.wright.cs.raiderplanner.view.UiManager; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.time.LocalDate; +import java.time.format.DateTimeFormatter; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class Task extends ModelEntity { + // private data + private static final long serialVersionUID = 5768237547422537188L; + private ArrayList dependencies = new ArrayList<>(); + private Deadline deadline; + private ArrayList requirements = new ArrayList<>(); + private boolean checkedComplete; + private int weighting; + private TaskType type; + private ArrayList assignments = new ArrayList<>(); + + // public methods + + /** + * Getter for deadline. + * @return Formatted String for the variable deadline + */ + public String getDeadline() { + return new SimpleDateFormat("MM/dd/yyyy").format(this.deadline.getDate()); + } + + /** + * Getter for the deadline date. + * @return Date for variable deadline + */ + public Date getDeadlineDate() { + return this.deadline.getDate(); + } + + /** + * Getter for the weighting. + * @return Integer for variable weighting + */ + public int getWeighting() { + return this.weighting; + } + + /** + * Wrapper for JavaFX TableView. + * + * @return. + */ + public boolean isCheckedComplete() { + return canCheckComplete() && checkedComplete; + } + + /** + * Getter for the type. + * @return TaskType for variable type + */ + public TaskType getType() { + return this.type; + } + + /** + * Getter for the dependencies. + * @return Task[] for variable dependencies + */ + public Task[] getDependencies() { + return this.dependencies.toArray(new Task[this.dependencies.size()]); + } + + /** + * Getter for the requirements. + * @return Requirement[] for variable requirements + */ + public Requirement[] getRequirements() { + return this.requirements.toArray(new Requirement[this.requirements.size()]); + } + + /** + * Getter for the number of requirements. + * @return Integer of the size of variable requirements + */ + public int requirementCount() { + return requirements.size(); + } + + /** + * Returns the number of complete in Requirements in this Task. + * + * @return integer + */ + public int requirementsComplete() { + int r1 = 0; + int i1 = -1; + int ii = requirements.size(); + while (++i1 < ii) { + if (requirements.get(i1).isComplete()) { + r1++; + } + } + return r1; + } + + /** + * Calculates how much of this Task has been completed in percentage. + * + * @return int (0-100) + */ + public int calculateProgress() { + if (this.requirementCount() == 0) { + return 0; + } else { + return (this.requirementsComplete() * 100) / this.requirementCount(); + } + } + + /** + * Returns an array of Assignments to which this Task relates. + * + * @return array of Assignments. + */ + public Assignment[] getAssignmentReferences() { + return this.assignments.toArray(new Assignment[this.assignments.size()]); + } + + /** + * Checks whether all dependencies of this Task are complete. + * + * @return true if complete, false otherwise. + */ + public boolean dependenciesComplete() { + int indexValue = -1; + int ii = dependencies.size(); + while (++indexValue < ii) { + if (!dependencies.get(indexValue).isCheckedComplete()) { + return false; + } + } + return true; + } + + /** + * Checks whether this Task has any dependencies. + * + * @return true if does, false otherwise. + */ + public boolean hasDependencies() { + return dependencies.size() > 0; + } + + /** + * Same as canCheckComplete(), wrapper for TableView. + * + * @return. + */ + public boolean isPossibleToComplete() { + return canCheckComplete(); + } + + /** + * Checks whether this Task can be checked as complete. If it cannot, makes sure it is marked as + * incomplete. + * + * @return. + */ + public boolean canCheckComplete() { + int i1 = -1; + int ii = requirements.size(); + while (++i1 < ii) { + if (!requirements.get(i1).isComplete()) { + this.checkedComplete = false; + return false; + } + } + if (this.dependenciesComplete()) { + return true; + } else { + this.checkedComplete = false; + return false; + } + } + + /** + * Checks whether this Task already contains a given dependency. + * + * @param dep dependency to be checked for + * @return true or false + */ + public boolean containsDependency(Task dep) { + return this.dependencies.contains(dep); + } + + /** + * Checks whether this Task already contains a given Requirement. + * + * @param requirement requirement to be checked for + * @return true or false + */ + public boolean containsRequirement(Requirement requirement) { + return this.requirements.contains(requirement); + } + + /** + * Returns the Name of the Task (used for JavaFX). + * + * @return Name of the task + */ + @Override + public String toString() { + return this.name; + } + + // Setters: + + /** + * Add a Requirement to the current Task. + * + * @param req requirement to be added + */ + public void addRequirement(Requirement req) { + this.requirements.add(req); + this.canCheckComplete(); + } + + /** + * Add a Task to the list of dependencies for the current Task. + * + * @param task Task to be added + */ + public void addDependency(Task task) { + this.dependencies.add(task); + this.canCheckComplete(); + } + + /** + * Replaces the current Requirements with the given ones. + * + * @param requirements list of requirements + */ + public void replaceRequirements(Collection requirements) { + this.requirements.clear(); + this.requirements.addAll(requirements); + this.canCheckComplete(); + } + + /** + * Replaces the current Dependencies with the given ones. + * + * @param dependencies list of Tasks + */ + public void replaceDependencies(Collection dependencies) { + this.dependencies.clear(); + this.dependencies.addAll(dependencies); + this.canCheckComplete(); + } + + /** + * Removes a given Task from the dependencies list. + * + * @param dependency Task to be removed + * @return whether the dependency has been removed successfully + */ + public boolean removeDependency(Task dependency) { + return this.dependencies.remove(dependency); + } + + /** + * Removes a given Requirement from the requirements list. + * + * @param requirement Requirement to be removed + * @return whether the Requirement has been removed successfully + */ + public boolean removeRequirement(Requirement requirement) { + return this.requirements.remove(requirement); + } + + /** + * Toggle complete. + */ + public void toggleComplete() { + if (this.isCheckedComplete()) { + this.checkedComplete = false; + } else if (this.canCheckComplete()) { + this.checkedComplete = true; + } + } + + /** + * Mark as complete/incomplete. + * + * @param c1 boolean value. + */ + public void setComplete(boolean c1) { + this.checkedComplete = c1; + } + + /** + * Set a new deadline. + * + * @param date date to be set as a new deadline + */ + public void setDeadline(LocalDate date) { + this.deadline.setDate(date.format(DateTimeFormatter.ofPattern("MM/dd/yyyy")) + + "T00:00:01Z"); + } + + /** + * Set a new weighting for this Task. + * + * @param weighting . + */ + public void setWeighting(int weighting) { + this.weighting = weighting; + } + + /** + * Set a new type for this Task. + * + * @param type String representation of a type + */ + public void setType(String type) { + if (TaskType.exists(type)) { + this.type = TaskType.get(type); + } + } + + /** + * Add a reference to an Assignment to this task. (Used for completing Assignment Requirements). + * + * @param assignment Assignment which should be linked with this Task. + */ + public void addAssignmentReference(Assignment assignment) { + if (!this.assignments.contains(assignment)) { + this.assignments.add(assignment); + } + } + + /** + * Removes a reference from the list of Assignments this Task relates to. + * + * @param assignment Assignment to be removed. + */ + public void removeAssignmentReference(Assignment assignment) { + this.assignments.remove(assignment); + } + + @Override + public void open(MenuController.Window current) { + try { + MainController.ui.taskDetails(this); + } catch (IOException e) { + UiManager.reportError("Unable to open view file"); + } + } + + /** + * Class Constructors. + * @param name : given value is String + * @param details : given value is String + * @param deadline : given value is LocalDate + * @param weighting : given value is int + * @param type : given value is String + */ + public Task(String name, String details, LocalDate deadline, int weighting, String type) { + super(name, details); + this.deadline = new Deadline(deadline.format(DateTimeFormatter.ofPattern("MM/dd/yyyy")) + + "T00:00:01Z"); + this.weighting = weighting; + this.type = TaskType.get(type); + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/TaskType.java b/src/edu/wright/cs/raiderplanner/model/TaskType.java new file mode 100644 index 00000000..ef0f6fc7 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/TaskType.java @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MainController; +import edu.wright.cs.raiderplanner.controller.MenuController.Window; + +import java.util.ArrayList; + +/** + * PearPlanner/RaiderPlanner Created by Team BRONZE on 4/27/17. + */ +public class TaskType extends ModelEntity { + /** + * the taskDatabase. + */ + + private static ArrayList taskDatabase = new ArrayList<>(); + + /** + * Returns a array of names of the tasks in the taskDatabase ArrayList. + */ + public static String[] listOfNames() { + String[] str = new String[taskDatabase.size()]; + int i1 = -1; + int i2 = taskDatabase.size(); + while (++i1 < i2) { + str[i1] = taskDatabase.get(i1).getName(); + } + return str; + } + + /** + * Returns a array of task types of the tasks in the taskDatabase ArrayList. + */ + public static TaskType[] listOfTaskTypes() { + TaskType[] taskTypes = new TaskType[taskDatabase.size()]; + int i1 = -1; + int i2 = taskDatabase.size(); + while (++i1 < i2) { + taskTypes[i1] = taskDatabase.get(i1); + } + return taskTypes; + } + + /** + * @param tt Name of the task you want the type of. + * @return Returns a TaskType if it finds a task of that name, or DEFAULT otherwise. + */ + public static TaskType get(String tt) { + int i1 = -1; + int i2 = taskDatabase.size(); + while (++i1 < i2) { + if (taskDatabase.get(i1).equals(tt)) { + return taskDatabase.get(i1); + } + } + return DEFAULT; + } + + /** + * @param tt TaskType being checked. + * @return true if the TaskType exists in taskDatabase. + */ + public static boolean exists(TaskType tt) { + int i1 = -1; + int i2 = taskDatabase.size(); + while (++i1 < i2) { + if (taskDatabase.get(i1).equals(tt)) { + return true; + } + } + return false; + } + + /** + * @param tt string name of TaskType being checked. + * @return true if the TaskType with name tt exists in taskDatabase. + */ + public static boolean exists(String tt) { + int i1 = -1; + int i2 = taskDatabase.size(); + while (++i1 < i2) { + if (taskDatabase.get(i1).equals(tt)) { + return true; + } + } + return false; + } + + /** + * Create a new TaskType. + * + * @param name + * Name of the TaskType. + * @param details + * Details of the TaskType. + * @return Returns new TaskType. + */ + public static TaskType create(String name, String details) { + TaskType tt = new TaskType(name, details); + if (MainController.getSpc() != null) { + MainController.getSpc().addTaskType(tt); + } + return tt; + } + + /** + * Create a new TaskType. + * @param name + * Name of the TaskType. + * @return Returns created TaskType. + */ + public static TaskType create(String name) { + TaskType tt = new TaskType(name); + if (MainController.getSpc() != null) { + MainController.getSpc().addTaskType(tt); + } + return tt; + } + + /** + * Create a new TaskType from an existing one. + * @param type + * TaskType object + */ + public static void create(TaskType type) { + if (!TaskType.taskDatabase.contains(type)) { + TaskType.taskDatabase.add(type); + if (MainController.getSpc() != null) { + MainController.getSpc().addTaskType(type); + } + } + } + + // this is a temporary way to populate the array until we later replace from reading a set up + // file + static { + /** + * PearPlanner/RaiderPlanner Created by Team BRONZE on 4/27/17. + */ + class Pair { + public String str1; + public String str2; + + /** + * Pair Constructor. + * @param name Name of TaskType. + * @param details Details of TaskType. + */ + Pair(String name, String details) { + str1 = name; + str2 = details; + } + } + + Pair[] staticTypes = { new Pair("Other", "Other type of task"), + new Pair("Reading", "Read some required text"), + new Pair("Exercises", "Did some assigned exercises"), + new Pair("Listening", "Listened to a podcast"), + new Pair("Coursework", "Worked towards coursework"), + new Pair("Revision", "Revised towards exam"), + new Pair("Meeting", "Meet with other course members") + + }; + int i1 = -1; + int i2 = staticTypes.length; + while (++i1 < i2) { + TaskType tt = new TaskType(staticTypes[i1].str1, staticTypes[i1].str2); + } + } + + /** + * Constructor for TaskType. + * @param name Name of TaskType + */ + private TaskType(String name) { + super(name); + if (!exists(this)) { + taskDatabase.add(this); + } + } + + /** + * Constructor for TaskType. + * @param name Name of TaskType. + * @param details Details of TaskType. + */ + private TaskType(String name, String details) { + super(name, details); + if (!exists(this)) { + taskDatabase.add(this); + } + } + + /** + * Constructor for TaskType. + */ + public static ArrayList getTaskDatabase() { + return taskDatabase; + } + + /** + * taskDatabase the taskDatabase to set. + */ + public static void setTaskDatabase(ArrayList taskDatabase) { + TaskType.taskDatabase = taskDatabase; + } + + @Override + public boolean equals(Object obj) { + TaskType that = (TaskType) obj; + if (getName() != null && that.getName() != null) { + return getName().equals(that.getName()); + } else { + return false; + } + } + + + /** + * Equals method that takes a String instead of an object. Only checks against name + * @param name Name of TaskType to check against. + * @return Returns true if names are equal. + */ + public boolean equals(String name) { + return getName().equals(name); + } + + /** + * Only used to Override has code. + */ + @Override + public int hashCode() { + assert false : "hashCode not designed"; + return 42; // any arbitrary constant will do + } + + + + public static TaskType DEFAULT = taskDatabase.get(0); + + /* (non-Javadoc) + * @see edu.wright.cs.raiderplanner.model.ModelEntity#open + * (edu.wright.cs.raiderplanner.controller.MenuController.Window) + */ + @Override + public void open(Window current) { + // TODO Auto-generated method stub + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/TimeTableEventType.java b/src/edu/wright/cs/raiderplanner/model/TimeTableEventType.java new file mode 100644 index 00000000..90b1c7a3 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/TimeTableEventType.java @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MenuController.Window; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class TimeTableEventType extends VersionControlEntity { + + /** + * toString() method. + * @return String for variable name. + */ + public String toString() { + return name; + } + + /* (non-Javadoc) + * @see edu.wright.cs.raiderplanner.model.ModelEntity#open + * (edu.wright.cs.raiderplanner.controller.MenuController.Window) + */ + @Override + public void open(Window current) { + // TODO Auto-generated method stub + } +} diff --git a/src/edu/wright/cs/raiderplanner/model/TimetableEvent.java b/src/edu/wright/cs/raiderplanner/model/TimetableEvent.java new file mode 100644 index 00000000..406bcced --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/TimetableEvent.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MenuController.Window; + +/** + * PearPlanner/RaiderPlanner. + * + * @author Andrew Odintsov + */ +public class TimetableEvent extends Event { + + private static final long serialVersionUID = 939793921119553866L; + + private Room room; + private Person lecturer; + private TimeTableEventType timeTableEventType; + + /** + * Create a new Timetable event from the given parameters. + * + * @param date date of the event + * @param room room the event will be in + * @param lecturer teacher of the event + * @param timeTableEventType creating timetable event type + * @param duration how long the event will be + */ + public TimetableEvent(String date, Room room, Person lecturer, + TimeTableEventType timeTableEventType, int duration) { + + super(date); + setRoom(room); + setLecturer(lecturer); + setTimeTableEventType(timeTableEventType); + setDuration(duration); + + } + + @Override + protected void replace(VersionControlEntity receivedVce) { + if (receivedVce instanceof TimetableEvent) { + TimetableEvent castedVce = (TimetableEvent) receivedVce; + this.duration = castedVce.getDuration(); + if (castedVce.getLecturer() != null) { + this.lecturer = castedVce.getLecturer(); + } + if (castedVce.getRoom() != null) { + this.room = castedVce.getRoom(); + } + if (castedVce.getTimeTableEventType() != null) { + this.timeTableEventType = castedVce.getTimeTableEventType(); + } + } + super.replace(receivedVce); + } + + /** + * @return the details of the room the event is in. + */ + public Room getRoom() { + return room; + } + + /** + * @return the details of the lecturer holding the event. + */ + public Person getLecturer() { + return lecturer; + } + + /** + * @return the eventType of the timetable event. + */ + public TimeTableEventType getTimeTableEventType() { + return timeTableEventType; + } + + /** + * Sets the room the event will be in. + * + * @param newRoom room event will be in. + */ + public void setRoom(Room newRoom) { + room = newRoom; + } + + /** + * Sets the lecturer of the event. + * + * @param newLecturer person holding the event. + */ + public void setLecturer(Person newLecturer) { + lecturer = newLecturer; + } + + /** + * Creates the event type of the timeTableEvent. + * + * @param newTimeTableEventType type of event + */ + public void setTimeTableEventType(TimeTableEventType newTimeTableEventType) { + timeTableEventType = newTimeTableEventType; + } + + /** + * Sets the duration of the event. + * + * @param newDuration duration of the event. + */ + public void setDuration(int newDuration) { + duration = newDuration; + } + + @Override + public String toString() { + return name + " in " + room.toString() + " at " + date.getTime(); + } + + /* (non-Javadoc) + * @see edu.wright.cs.raiderplanner.model.ModelEntity#open + * (edu.wright.cs.raiderplanner.controller.MenuController.Window) + */ + @Override + public void open(Window current) { + // TODO Auto-generated method stub + } + +} diff --git a/src/edu/wright/cs/raiderplanner/model/VersionControlEntity.java b/src/edu/wright/cs/raiderplanner/model/VersionControlEntity.java new file mode 100644 index 00000000..2b1ca5ff --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/VersionControlEntity.java @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import edu.wright.cs.raiderplanner.controller.MainController; + +import java.util.HashMap; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public abstract class VersionControlEntity extends ModelEntity { + protected int version; + protected String uid; + protected boolean sealed; + private static HashMap library = new HashMap<>(); + protected boolean importer = false; // used for VCEs created during XML import + // private methods + + /** + * This method overwrites the data in the received object with that received. + * This method will need to overrode in every class that extends it + * + * @param receivedVce VersionControlEntity to override + */ + protected void replace(VersionControlEntity receivedVce) { + name = receivedVce.getName(); + details = receivedVce.getDetails(); + version = receivedVce.getVersion(); + // super.replace(receivedVce); + } + + // public methods + + /** + * Update this VCE with a given one. + * + * @param receivedVce received VCE for updating the current one. + * @return whether updated successfully. + */ + public boolean update(VersionControlEntity receivedVce) { + if (uid.equals(receivedVce.getUid()) && version < receivedVce.getVersion()) { + replace(receivedVce); + return true; + } else { + return false; + } + } + + /** + * Find the given VCE in the library and then update it. + * + * @param receivedVce a VCE to be looked for and updated. + * @return whether found and updated successfully. + */ + public static boolean findAndUpdate(VersionControlEntity receivedVce) { + String uid = receivedVce.getUid(); + if (inLibrary(uid)) { + library.get(uid).update(receivedVce); + return true; + } else { + return false; + } + } + + /** + * Creates a importer if not sealed. + * @return boolean + */ + public boolean makeImporter() { + if (!sealed) { + importer = true; + return true; + } else { + return false; + } + } + + /** + * Checks if it is an importer. + * @return boolean + */ + public boolean isImporter() { + return importer; + } + + /** + * Add this VCE to the library. + * + * @return whether added successfully. + */ + public boolean addToLibrary() { + if (importer) { + if (inLibrary(uid)) { + return false; + } else { + importer = false; + sealed = true; + library.put(uid, this); + MainController.getSpc().getPlanner().addToVersionControlLibrary(this); + return true; + } + } else { + return false; + } + } + + /** + * Get a VCE from the library by it's UID. + * + * @param uid UID to be looked for. + * @return a valid VCE if found, null otherwise. + */ + public static VersionControlEntity get(String uid) { + if (inLibrary(uid)) { + return library.get(uid); + } else { + return null; + } + } + + /** + * Check whether a VCE with the given UID exists in the library. + * + * @param uid UID to be checked for. + * @return true if found, false otherwise. + */ + public static boolean inLibrary(String uid) { + return library.containsKey(uid); + } + + // getters + + /** + * gets the version of the entity. + * @return version + */ + public int getVersion() { + return version; + } + + /** + * gets the UID of user. + * @return boolean + */ + public String getUid() { + return uid; + } + + /** + * Returns the VCE library. + * + * @return HashMap containing all VCEs. + */ + public static HashMap getLibrary() { + return library; + } + + /** + * Returns a summary of the VCEs in the library. + * + * @return String + */ + public static String libraryReport() { + return "Total Entries: " + library.size(); + } + + /** + * Set a new UID and version for this VCE. + * + * @param newUid new UID + * @param newVersion new version + * @return whether changed successfully. + */ + public boolean setUid(String newUid, int newVersion) { +// setUid(newUID); + if (importer) { + setUid(newUid); + version = newVersion; + return true; + } else if (sealed || library.containsKey(newUid)) { + return false; + } else { + setUid(newUid); + version = newVersion; + return true; + } + } + + /** + * Set a new UID for this VCE. + * + * @param newUid new UID + * @return whether changed successfully. + */ + public boolean setUid(String newUid) { + if (importer) { + uid = newUid; + return true; + } else if (sealed || library.containsKey(newUid)) { + return false; + } else { + uid = newUid; + library.put(newUid, this); + MainController.getSpc().getPlanner().addToVersionControlLibrary(this); + return true; + } + } + + /** + * Called once the program is loaded, adds this VCE to the library if possible. + */ + public void reload() { + if (!inLibrary(this.uid) && !importer && sealed) { + library.put(this.uid, this); + } + } + + // Constructors + + /** + * Constructor with a boolean if it should stay sealed. + * @param leaveUnsealed if it should be sealed + */ + public VersionControlEntity(boolean leaveUnsealed) { + super(); + sealed = !leaveUnsealed; + } + + /** + * Constructor with no String UID. + */ + public VersionControlEntity() { + super(); + sealed = false; + } + + /** + * Constructor with a String UID. + * @param uid UID of user + */ + public VersionControlEntity(String uid) { + super(); + sealed = setUid(uid); + } + +} diff --git a/src/edu/wright/cs/raiderplanner/model/Video.java b/src/edu/wright/cs/raiderplanner/model/Video.java new file mode 100644 index 00000000..e7900e34 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/model/Video.java @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 4/27/17 + */ +public class Video /*extends Requirement*/ { + // private data + private String url; + + // public methods + + /** + * Getter for the url. + * @return String for variable url. + */ + public String getUrl() { + return url; + } + + /** + * Setter for the url. + * @param newUrl String for the variable url. + */ + public void setUrl(String newUrl) { + url = newUrl; + } +} diff --git a/src/edu/wright/cs/raiderplanner/util/DatabaseConfigurator.java b/src/edu/wright/cs/raiderplanner/util/DatabaseConfigurator.java new file mode 100644 index 00000000..8565ee99 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/util/DatabaseConfigurator.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2018 - Logan Krause, Corbin McGuire + * + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.util; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.SQLException; +import java.sql.Statement; + +/** + * Establishes connection to server, then executes DDL. + * @author LoganKrause + */ +public class DatabaseConfigurator { + private static final String DRIVER = "org.apache.derby.jdbc.EmbeddedDriver"; + private static final String JDBC_URL = "jdbc;derby;ConnectingCreatingJavaDB;create=true"; + + /** + * Establishes the Server and creates the database. + */ + private DatabaseConfigurator() {} + + /** + * Executes the DDL to create the database. + */ + public static void createDatabase() { + + try (Connection conn = DriverManager.getConnection(JDBC_URL)) { + if (conn != null) { + + String ddl = "CREATE TABLE ExceptionDB ( " + + " ExID INTEGER NOT NULL, " + + " Message VARCHAR(2000) NOT NULL, " + + " Type VARCHAR(200) NOT NULL, " + + " Date DATE NOT NULL, " + + " Time TIME NOT NULL, " + + " PRIMARY KEY (ExID) " + + ");"; + Statement statement = null; + statement = conn.createStatement(); + statement.executeQuery(ddl); + System.out.println("Connection Successful"); + } + + } catch (SQLException e) { + e.printStackTrace(); + System.out.println("Connection Failed. Exception: " + e.toString()); + } + } +} \ No newline at end of file diff --git a/src/edu/wright/cs/raiderplanner/util/RaiderException.java b/src/edu/wright/cs/raiderplanner/util/RaiderException.java new file mode 100644 index 00000000..fe5bbd69 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/util/RaiderException.java @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2018 - Logan Krause, Corbin McGuire, Daniel Howard + * + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.util; + +/** + * Custom exception class that displays more useful error messages to users and programmers. + * @author LoganKrause + * @author CorbinMcGuire + * @author DanielHoward + */ +@SuppressWarnings("serial") +public class RaiderException extends Exception { + /** + * Parameterless constructor. + */ + public RaiderException() { + getMessage(); + } + + /** + * Message that displays the custom message. + * @param message Helping message + */ + public RaiderException(String message) { + super(message); + message = "Error, Please try again or seek help."; + } + + /** + * Returns the message to the user. + * @see java.lang.Throwable#getMessage() + * Able to assign specific message + */ + @Override + public String getMessage() { + return super.getMessage(); + } + + /** + * Displays Error message and exception. + * @param message Receives the message passed by the thrown exception. + * @param throwable Thrown exception. + */ + public RaiderException(String message, Throwable throwable) { + super(message, throwable); + } +} \ No newline at end of file diff --git a/src/edu/wright/cs/raiderplanner/view/AccountStart.fxml b/src/edu/wright/cs/raiderplanner/view/AccountStart.fxml new file mode 100644 index 00000000..3c5bbe48 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/view/AccountStart.fxml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/edu/wright/cs/raiderplanner/view/Activity.fxml b/src/edu/wright/cs/raiderplanner/view/Activity.fxml new file mode 100644 index 00000000..fd4ae601 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/view/Activity.fxml @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/edu/wright/cs/raiderplanner/view/ConsoleIo.java b/src/edu/wright/cs/raiderplanner/view/ConsoleIo.java new file mode 100644 index 00000000..f0a8e951 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/view/ConsoleIo.java @@ -0,0 +1,260 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.view; + +import edu.wright.cs.raiderplanner.controller.DataController; +import edu.wright.cs.raiderplanner.controller.StudyPlannerController; +import edu.wright.cs.raiderplanner.model.HubFile; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.ArrayList; +import java.util.Scanner; + +/** + * Created by bendickson on 5/4/17. + */ +public class ConsoleIo { + static ArrayList logged = new ArrayList<>(); + + /** + * Retrieve user input. + * + * @param message Message to be shown + * @return retrieved String + */ + public static String getDataString(String message) { + try (Scanner scan = new Scanner(System.in)) { + String str = ""; + while ("".equals(str)) { + System.out.println(message); + str = scan.nextLine(); + } + return str; + } + } + + /** + * Get yes/no input. + * If the user enters "y" true, if the user enters "n" false. + * @param message Message to be shown. + * @return true for yes, false for no. + */ + public static boolean getDataBool(String message) { + try (Scanner scan = new Scanner(System.in)) { + String str = ""; + while (!("y".equals(str) || "n".equals(str))) { + System.out.println(message); + str = scan.next(); + } + return "y".equals(str); + } + } + + /** + * Calculate how many lines have been written to the log. + * + * @return number of lines. + */ + public static int getLogSize() { + return logged.size(); + } + + /** + * Save the full log to a file. + * + * @param filePath file path + */ + public static void saveLog(String filePath) { + saveLog(filePath, 0, logged.size()); + } + + /** + * Save specific lines to a file. + * + * @param filePath file path + * @param startLine starting line + * @param endLine end line + */ + public static void saveLog(String filePath, int startLine, int endLine) { + if (startLine < 0) { + startLine = 0; + } + if (endLine > logged.size()) { + endLine = logged.size(); + } + int itr = startLine - 1; + File logFile = new File(filePath); + try (FileOutputStream fos = new FileOutputStream(logFile); + BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(fos));) { + + while (++itr < endLine) { + if (itr != startLine) { + bw.newLine(); + } + bw.write(logged.get(itr)); + } + } catch (IOException e) { + setConsoleMessage("File not written", true); + } + } + + /** + * Display a message on a console. + * + * @param message message to be displayed. + */ + public static void setConsoleMessage(String message) { + System.out.println(message); + } + + /** + * Display a message with an option of saving it to a log. + * + * @param message message to be displayed (or logged) + * @param logMessage whether to log it + */ + public static void setConsoleMessage(String message, boolean logMessage) { + System.out.println(message); + if (logMessage) { + logged.add(message); + } + } + + /** + * Get user selection of a menu option. + * + * @param menuOptions available options + * @return user selection + */ + public static int getMenuOption(String[] menuOptions) { + try (Scanner scan = new Scanner(System.in)) { + int option = -1; + while (option < 0 || option >= menuOptions.length) { + System.out.println("Please select an option\n"); + option = -1; + while (++option < menuOptions.length) { + System.out.printf("%d - " + menuOptions[option] + "\n", option); + } + option = scan.nextInt(); + } + return option; + } + } + + // Console view below + /** + * Displays main menu. + * Creates the menu options. + * + * @return menu choice + */ + public static String view_main() { + setConsoleMessage("MAIN MENU"); + // list of options + String[] menuOptions = { + "Create Study Profile", + "View Study Profile", + "View Notifications", + "Quit Program" + }; + int choice = getMenuOption(menuOptions); + + return menuOptions[choice]; + + } + + /** + * Displays Study Profile creation. + * Creates menu options for study profile. + * @return Returns menu choice + */ + public static String view_createStudyP() { + setConsoleMessage("CREATE A STUDY PROFILE"); + // list of options + String[] menuOptions = { + "Load Study Profile File", + "Return to Main Menu" + }; + int choice = getMenuOption(menuOptions); + + return menuOptions[choice]; + + } + + /** + * Shows a study profile. + * + * @param spController input StudyPlannerController + * @return menu option + */ + public static String view_viewStudyP(StudyPlannerController spController) { + setConsoleMessage("VIEW A STUDY PROFILE"); + String[] studyProfiles = spController.getPlanner().getListOfStudyProfileNames(); + int i1 = -1; + int i2 = studyProfiles.length; + + if (i2 < 1) { + setConsoleMessage("No existing study profiles"); + } + + String[] menuOptions = new String[i2 + 1]; + while (++i1 < i2) { + menuOptions[i1] = studyProfiles[i1]; + } + + menuOptions[i2] = "Return to Main Menu"; + + int i3 = -1; + while (i3 < i2) { + i3 = getMenuOption(menuOptions); + } + + return menuOptions[i2]; + } + + /** + * Loads a Study Profile. + * + * @param spController input StudyPlannerController + * @return a static string + */ + public static String view_loadStudyP(StudyPlannerController spController) { + setConsoleMessage("LOAD A STUDY PROFILE"); + + String filename = getDataString("Enter filepath:"); + File tempFile = new File(filename); + HubFile fileData = DataController.loadHubFile(tempFile); + while (!filename.equals("") && fileData == null) { + filename = getDataString("File not valid, enter a different filepath:"); + tempFile = new File(filename); + fileData = DataController.loadHubFile(tempFile); + } + + System.out.println(fileData.toString(true)); + + return "Return to Main Menu"; + } +} diff --git a/src/View/CreateAccount.fxml b/src/edu/wright/cs/raiderplanner/view/CreateAccount.fxml similarity index 54% rename from src/View/CreateAccount.fxml rename to src/edu/wright/cs/raiderplanner/view/CreateAccount.fxml index 30d482ce..af5a7db6 100644 --- a/src/View/CreateAccount.fxml +++ b/src/edu/wright/cs/raiderplanner/view/CreateAccount.fxml @@ -1,91 +1,90 @@ + + + - + + + - + - - - - + + + + - + + + + + + + + - + - - + + - + - + - + - + - + - + - + - + - - - - + diff --git a/src/edu/wright/cs/raiderplanner/view/GanttishDiagram.java b/src/edu/wright/cs/raiderplanner/view/GanttishDiagram.java new file mode 100644 index 00000000..a00b45a6 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/view/GanttishDiagram.java @@ -0,0 +1,493 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.view; + +import edu.wright.cs.raiderplanner.model.Assignment; +import edu.wright.cs.raiderplanner.model.StudyPlanner; +import edu.wright.cs.raiderplanner.model.Task; + +import java.awt.BasicStroke; +import java.awt.Color; +import java.awt.Font; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Paint; +import java.awt.RenderingHints; +import java.awt.image.BufferedImage; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; + +import javax.imageio.ImageIO; + +/** + * Created by bendickson on 5/15/17. + */ +public class GanttishDiagram { + + static int width = 1920; + static int height = 1280; + + static int imageType = BufferedImage.TYPE_INT_ARGB; + + static int badgeSize = 128; + static int badgeRingSize = 112; + static int getBadgeRingThickness = 8; + static int fontSize = 24; + + static int GANTT_TITLE_SPACE = 80; + static int GANTT_TITLE_FONT_SIZE = 64; + static int GANTT_COLUMN_WIDTH = 400; + static int GANTT_COLUMN_PADDING = 80; + static int GANTT_COLUMN_SPACING = 16; + static int GANTT_ENTRY_FONT_SIZE = 18; + static Paint GANTT_ENTRY_FONT_COLOR = Color.black; + static int GANTT_ENTRY_HEIGHT = 64; + static Paint GANTT_ENTRY_DEFAULT_COLOR = BadgeColors.GREY.getPaint(); + static HashMap GANTT_ENTRY_COLOR = new HashMap<>(); + + static { + GANTT_ENTRY_COLOR.put("Reading", BadgeColors.PINK.getPaint()); + GANTT_ENTRY_COLOR.put("Exercises", BadgeColors.ORANGE.getPaint()); + GANTT_ENTRY_COLOR.put("Revision", BadgeColors.YELLOW.getPaint()); + GANTT_ENTRY_COLOR.put("Listening", BadgeColors.GREEN.getPaint()); + GANTT_ENTRY_COLOR.put("Coursework", BadgeColors.BLUE.getPaint()); + GANTT_ENTRY_COLOR.put("Meeting", BadgeColors.PURPLE.getPaint()); + } + + static Paint GANTT_ENTRY_DEFAULT_FCOLOR = BadgeColors.T_GREY.getPaint(); + static HashMap GANTT_ENTRY_FCOLOR = new HashMap<>(); + + static { + GANTT_ENTRY_FCOLOR.put("Reading", BadgeColors.T_PINK.getPaint()); + GANTT_ENTRY_FCOLOR.put("Exercises", BadgeColors.T_ORANGE.getPaint()); + GANTT_ENTRY_FCOLOR.put("Revision", BadgeColors.T_YELLOW.getPaint()); + GANTT_ENTRY_FCOLOR.put("Listening", BadgeColors.T_GREEN.getPaint()); + GANTT_ENTRY_FCOLOR.put("Coursework", BadgeColors.T_BLUE.getPaint()); + GANTT_ENTRY_FCOLOR.put("Meeting", BadgeColors.T_PURPLE.getPaint()); + } + + + /** + * creates the strokes and sizes. + * @author Eric Sweet + * + */ + enum Stroke { + DASHED(true, 1), + NONE(false, 0), + SOLID(false, 1); + + double widthMult = 1.0; + boolean dashed = false; + + /** + * gets the stroke and thickness. + * @param thickness thickness of the stroke + * @return the thickness times width + */ + BasicStroke getStroke(int thickness) { + if (widthMult == 0) { + return new BasicStroke(0); + } else if (dashed) { + float[] pat = {10.0f}; + return new BasicStroke((float) widthMult * 1.0f, + BasicStroke.CAP_BUTT, + BasicStroke.JOIN_MITER, + 10.0f, pat, 0.0f); + } else { + return new BasicStroke((float) widthMult * thickness); + } + } + + /** + * modifies the stroke and width. + * @param dashed boolean if dashed + * @param widthMult width size + */ + Stroke(boolean dashed, double widthMult) { + this.dashed = dashed; + this.widthMult = widthMult; + } + } + + /** + * Creates the colors and fill diagram colors. + * @author Eric Sweet + * + */ + enum BadgeColors { + FINISHED(0, 255, 0), + started(255, 255, 0), + CANSTART(140, 26, 26), + CANNOTSTART(64, 64, 64), + + FINISHED_FILL(255, 255, 255), + started_FILL(255, 255, 255), + CANSTART_FILL(255, 255, 255), + CANNOTSTART_FILL(128, 128, 128), + GREY(192, 192, 192), + PINK(255, 192, 192), + ORANGE(255, 128, 0), + BLUE(0, 128, 255), + GREEN(0, 255, 64), + PURPLE(64, 0, 255), + YELLOW(255, 64, 0), + T_GREY(192, 192, 192, 128), + T_PINK(255, 192, 192, 128), + T_ORANGE(255, 128, 0, 128), + T_BLUE(0, 128, 255, 128), + T_GREEN(0, 255, 64, 128), + T_PURPLE(64, 0, 255, 128), + T_YELLOW(255, 64, 0, 128); + + private int red; + private int green; + private int blue; + private int aa = 255; + + /** + * gets the color of the diagram. + * @return paint color + */ + private Paint getPaint() { + return new Color(red, green, blue, aa); + } + + /** + * Returns a color based on a progress out of 100. + * + * @param progress The progress of the module + * + * @return a color based on progress + */ + private Color getPaint(int progress) { + if (progress > 100) { + return new Color(0, 255, 0); + } + if (progress < 0) { + return new Color(255, 0, 137); + } + + red = 255 - (int)(2.5 * progress); + green = (int)(Math.log(progress + 1) * 29); + blue = 0; + + return new Color(red, green, blue, 255); + } + + /** + * Creation of badge color. + * @param cr red spectrum + * @param cg green spectrum + * @param cb blue spectrum + */ + BadgeColors(int cr, int cg, int cb) { + red = cr > 255 ? 255 : (cr < 0 ? 0 : cr); + green = cg > 255 ? 255 : (cg < 0 ? 0 : cg); + blue = cb > 255 ? 255 : (cb < 0 ? 0 : cb); + } + + /** + * Creation of badge color with aa included. + * @param cr red color + * @param cg green color + * @param cb blue color + * @param ca aa color + */ + BadgeColors(int cr, int cg, int cb, int ca) { + this(cr, cg, cb); + aa = ca > 255 ? 255 : (ca < 0 ? 0 : ca); + } + } + + /** + * Creates a GanttishDiagram from a given Assignment. + * + * @param fromStudyProfile StudyProfile + * @param fromAssignment Assignment for which to generate the GanttishDiagram. + * @return Generated diagram + */ + public static BufferedImage createGanttishDiagram(StudyPlanner fromStudyProfile, + Assignment fromAssignment) { + return createGanttishDiagram(fromStudyProfile, fromAssignment, ""); + } + + /** + * Creates a GanttishDiagram from a given Assignment and saves it to a file. + * + * @param fromStudyProfile StudyProfile + * @param fromAssignment Assignment for which to generate the GanttishDiagram. + * @param filePath file path + * @return Generated diagram + */ + public static BufferedImage createGanttishDiagram(StudyPlanner fromStudyProfile, + Assignment fromAssignment, String filePath) { + return createGanttishDiagram(fromStudyProfile, fromAssignment, filePath, false); + } + + /** + * Creates a GanttishDiagram from a given Assignment, + * with the option to set the background transparent. + * + * @param fromStudyProfile StudyProfile + * @param fromAssignment Assignment for which to generate the GanttishDiagram. + * @param filePath file path + * @param transparentBackground whether the background should be white or transparent + * @return Generated diagram + */ + public static BufferedImage createGanttishDiagram(StudyPlanner fromStudyProfile, + Assignment fromAssignment, String filePath, boolean transparentBackground) { + + HashMap> catTasks = new HashMap<>(); + + String completed = "completed tasks"; + String started = "Tasks started"; + String possible = "possible to start"; + String impossible = "Not possible to start"; + + /** + BadgeColors[][] badges = {{BadgeColors.FINISHED, BadgeColors.FINISHED_FILL}, + {BadgeColors.started, BadgeColors.started_FILL}, + {BadgeColors.CANSTART, BadgeColors.CANSTART_FILL}, + {BadgeColors.CANNOTSTART, BadgeColors.CANNOTSTART_FILL}}; + */ + + catTasks.put(completed, new ArrayList<>()); + catTasks.put(started, new ArrayList<>()); + catTasks.put(possible, new ArrayList<>()); + catTasks.put(impossible, new ArrayList<>()); + + ArrayList assignmentTasks = fromAssignment.getTasks(); + + Iterator iterator = assignmentTasks.iterator(); + int longest = 0; + while (iterator.hasNext()) { + Task taskT = iterator.next(); + String cat; + if (taskT.isCheckedComplete()) { + cat = completed; + } else if (taskT.requirementsComplete() > 0) { + cat = started; + } else if (taskT.dependenciesComplete()) { + cat = possible; + } else { + cat = impossible; + } + catTasks.get(cat).add(taskT); + if (catTasks.get(cat).size() > longest) { + longest = catTasks.get(cat).size(); + } + } + + String[] categoryList = {completed, started, possible, impossible}; + int jj = categoryList.length; + + int width = GANTT_COLUMN_PADDING + jj * (GANTT_COLUMN_PADDING + GANTT_COLUMN_WIDTH); + int height = 2 * GANTT_COLUMN_PADDING + GANTT_TITLE_SPACE + (1 + longest) + * (GANTT_COLUMN_SPACING + GANTT_ENTRY_HEIGHT); + + BufferedImage bufferedImageR = new BufferedImage(width, height, imageType); + Graphics2D g2d = bufferedImageR.createGraphics(); + + if (!transparentBackground) { + + g2d.setPaint(Color.white); + g2d.fillRect(0, 0, width, height); + } + + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + g2d.setPaint(GANTT_ENTRY_FONT_COLOR); + drawMessage(g2d, GANTT_TITLE_FONT_SIZE, fromAssignment.getName(), GANTT_COLUMN_PADDING, + GANTT_COLUMN_PADDING, width - (2 * GANTT_COLUMN_PADDING), GANTT_TITLE_SPACE); + + int ii = -1; + while (++ii < jj) { + int ox = GANTT_COLUMN_PADDING + ii * (GANTT_COLUMN_PADDING + GANTT_COLUMN_WIDTH); + int oy = GANTT_COLUMN_PADDING + GANTT_TITLE_SPACE; + + String name = categoryList[ii]; + int inc = 0; + int kk = catTasks.get(name).size() + 1; + + + g2d.setPaint(GANTT_ENTRY_FONT_COLOR); + drawMessage(g2d, GANTT_ENTRY_FONT_SIZE, name, ox, oy, GANTT_COLUMN_WIDTH, + GANTT_ENTRY_HEIGHT); + + while (++inc < kk) { + Task dt = catTasks.get(name).get(inc - 1); + String dtt = dt.getType().getName(); + Paint col; + Paint fcol; + if (GANTT_ENTRY_COLOR.containsKey(dtt)) { + fcol = (GANTT_ENTRY_FCOLOR.get(dtt)); + col = (GANTT_ENTRY_COLOR.get(dtt)); + } else { + fcol = (GANTT_ENTRY_DEFAULT_FCOLOR); + col = (GANTT_ENTRY_DEFAULT_COLOR); + } + + int oyk = oy + inc * (GANTT_COLUMN_SPACING + GANTT_ENTRY_HEIGHT); + + g2d.setPaint(fcol); + g2d.fillRoundRect(ox, oyk, GANTT_COLUMN_WIDTH, GANTT_ENTRY_HEIGHT, 15, 15); + + g2d.setPaint(col); + g2d.drawRoundRect(ox, oyk, GANTT_COLUMN_WIDTH, GANTT_ENTRY_HEIGHT, 15, 15); + + g2d.setPaint(GANTT_ENTRY_FONT_COLOR); + drawMessage(g2d, GANTT_ENTRY_FONT_SIZE, dt.getName(), ox, oyk, GANTT_COLUMN_WIDTH + - GANTT_ENTRY_HEIGHT, GANTT_ENTRY_HEIGHT); + int percentage; + if (dt.requirementCount() <= 0) { + percentage = dt.isCheckedComplete() ? 100 : 0; + } else { + percentage = (100 * dt.requirementsComplete()) / dt.requirementCount(); + } + BufferedImage badge = getBadge(percentage, dt.dependenciesComplete(), 0.5); + + g2d.drawImage(badge, ox + GANTT_COLUMN_WIDTH + - GANTT_ENTRY_HEIGHT, oyk, null); + } + } + + + if (!filePath.equals("")) { + try { + File outputfile = new File(filePath); + ImageIO.write(bufferedImageR, "png", outputfile); + } catch (IOException e) { + System.out.println(e.getMessage()); + } + } + return bufferedImageR; + } + + /** + * Generates a Badge with the given progress. + * + * @param progress integer representation of the progress. + * @param canStart true - default, false - grayed out. + * @return generated Badge + */ + public static BufferedImage getBadge(int progress, boolean canStart) { + return getBadge(progress, canStart, 1.0f); + } + + /** + * Generates a Badge with the given progress. + * + * @param progress integer representation of the progress. + * @param canStart true - default, false - greyed out. + * @param multiplier size multiplier. + * @return generated Badge + */ + public static BufferedImage getBadge(int progress, boolean canStart, double multiplier) { + int canvasSize = (int) (badgeSize * multiplier + .5); + int ringSize = (int) (badgeRingSize * multiplier + .5); + int badgeRingThickness = (int) (getBadgeRingThickness * multiplier + .5); + + BufferedImage imageBufferR = new BufferedImage(canvasSize, canvasSize, imageType); + Graphics2D g2d = imageBufferR.createGraphics(); + g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); + + Paint ringColor; + Paint fillColor; + + if (!canStart) { + g2d.setStroke(Stroke.DASHED.getStroke(badgeRingThickness)); + ringColor = new Color(140 ,26, 26); + fillColor = Color.white; + } else { + g2d.setStroke(Stroke.SOLID.getStroke(badgeRingThickness)); + ringColor = BadgeColors.FINISHED.getPaint(progress); + fillColor = BadgeColors.FINISHED_FILL.getPaint(); + } + + int ovalOffset = (canvasSize - ringSize) / 2; + g2d.setPaint(fillColor); + g2d.fillOval(ovalOffset, ovalOffset, ringSize, ringSize); + + int badgeFontSize = (int) (fontSize * multiplier + .5); + Font font = new Font("Helvetica", Font.PLAIN, badgeFontSize); + FontMetrics metrics = g2d.getFontMetrics(font); + + String msg = progress + "%"; + + int msgX = ovalOffset + (ringSize - metrics.stringWidth(msg)) / 2; + int msgY = ovalOffset + (ringSize - metrics.getHeight()) / 2 + metrics.getAscent(); + + int arc = 100; + if (arc == 100) { + g2d.setPaint(ringColor); + g2d.drawOval(ovalOffset, ovalOffset, ringSize, ringSize); + } else { + g2d.setPaint(ringColor); + int startAngle = 90; + int endAngle = -((int) ((arc / 100f) * 360)); + g2d.drawArc(ovalOffset, ovalOffset, ringSize, ringSize, startAngle, endAngle); + } + + + g2d.setFont(font); + g2d.drawString(msg, msgX, msgY); + + +/* try { + File outputfile = new File("badge_"+progress+".png"); + ImageIO.write(r, "png", outputfile); + } + catch(Exception e) + { + System.out.println(e.getMessage()); + }*/ + + return imageBufferR; + } + + /** + * Generates a rectangle with the given dimensions and the given message in the middle. + * @param g2d 2d graphics + * @param fontSize font size + * @param msg message + * @param cx x-axis + * @param cy y-axis + * @param width width + * @param height height + */ + private static void drawMessage(Graphics2D g2d, int fontSize, String msg, + int cx, int cy, int width, int height) { + + Font font = new Font("Helvetica", Font.PLAIN, fontSize); + FontMetrics metrics = g2d.getFontMetrics(font); + + int msgX = cx + (width - metrics.stringWidth(msg)) / 2; + int msgY = cy + (height - metrics.getHeight()) / 2 + metrics.getAscent(); + + g2d.setFont(font); + g2d.drawString(msg, msgX, msgY); + } +} diff --git a/src/edu/wright/cs/raiderplanner/view/GradPlanner.fxml b/src/edu/wright/cs/raiderplanner/view/GradPlanner.fxml new file mode 100644 index 00000000..850ab99c --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/view/GradPlanner.fxml @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/edu/wright/cs/raiderplanner/view/RightClickPopup.java b/src/edu/wright/cs/raiderplanner/view/RightClickPopup.java new file mode 100644 index 00000000..40f42091 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/view/RightClickPopup.java @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2018 + * + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.view; + +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.ItemEvent; +import java.awt.event.ItemListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; + +import javax.swing.JMenuItem; +import javax.swing.JPopupMenu; +import javax.swing.JTextArea; + +/** + * @author Dominick Hatton. Class that implements a right click pop up menu function. + */ +public class RightClickPopup implements ActionListener, ItemListener { + JMenuItem item; + JTextArea output; + + /** + * Initializes the popup menu. + */ + public void createPopupMenu() { + JPopupMenu popup = new JPopupMenu(); + item = new JMenuItem("Delete"); + item.addActionListener(this); + popup.add(item); + // Add listener to the text area so the popup menu can come up. + MouseListener popupListener = new PopupListener(popup); + output.addMouseListener(popupListener); + } + + /** + * Listens for a right click to create a popup menu. + */ + class PopupListener extends MouseAdapter { + JPopupMenu menu; + + /** + * @param popupMenu. + * + */ + PopupListener(JPopupMenu popupMenu) { + menu = popupMenu; + } + + /** + * Called when there is a right click pressed. + * + * @see java.awt.event.MouseAdapter#mousePressed(java.awt.event.MouseEvent) + */ + public void mousePressed(MouseEvent me) { + showPopup(me); + } + + /** + * Called when there is a right click release. + * + * @see java.awt.event.MouseAdapter#mouseReleased(java.awt.event.MouseEvent) + */ + public void mouseReleased(MouseEvent me) { + showPopup(me); + } + + /** + * When there is a right click event triggered, this method will begin to display the menu. + */ + private void showPopup(MouseEvent me) { + if (me.isPopupTrigger()) { + menu.show(me.getComponent(), me.getX(), me.getY()); + } + } + } + + /** + * (non-Javadoc) + * + * @see java.awt.event.ItemListener#itemStateChanged(java.awt.event.ItemEvent) + */ + @Override + public void itemStateChanged(ItemEvent me) { + // TODO Auto-generated method stub + + } + + /** + * (non-Javadoc) + * + * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent) + */ + @Override + public void actionPerformed(ActionEvent arg0) { + // TODO Auto-generated method stub + + } +} diff --git a/src/edu/wright/cs/raiderplanner/view/Settings.fxml b/src/edu/wright/cs/raiderplanner/view/Settings.fxml new file mode 100644 index 00000000..b1978750 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/view/Settings.fxml @@ -0,0 +1,178 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/View/StudyProfile.fxml b/src/edu/wright/cs/raiderplanner/view/StudyProfile.fxml similarity index 89% rename from src/View/StudyProfile.fxml rename to src/edu/wright/cs/raiderplanner/view/StudyProfile.fxml index d0db13b8..fbfde5e6 100644 --- a/src/View/StudyProfile.fxml +++ b/src/edu/wright/cs/raiderplanner/view/StudyProfile.fxml @@ -4,7 +4,7 @@ @@ -33,14 +33,14 @@ text="# extension applications."/> - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/edu/wright/cs/raiderplanner/view/UiManager.java b/src/edu/wright/cs/raiderplanner/view/UiManager.java new file mode 100644 index 00000000..af994707 --- /dev/null +++ b/src/edu/wright/cs/raiderplanner/view/UiManager.java @@ -0,0 +1,682 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * Copyright (C) 2018 - Clayton D. Terrill + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.view; + +import edu.wright.cs.raiderplanner.controller.AccountController; +import edu.wright.cs.raiderplanner.controller.AccountLoader; +import edu.wright.cs.raiderplanner.controller.ActivityController; +import edu.wright.cs.raiderplanner.controller.MenuController; +import edu.wright.cs.raiderplanner.controller.MilestoneController; +import edu.wright.cs.raiderplanner.controller.RequirementController; +import edu.wright.cs.raiderplanner.controller.SettingsController; +import edu.wright.cs.raiderplanner.controller.StartupController; +import edu.wright.cs.raiderplanner.controller.TaskController; +import edu.wright.cs.raiderplanner.model.Account; +import edu.wright.cs.raiderplanner.model.Activity; +import edu.wright.cs.raiderplanner.model.Assignment; +import edu.wright.cs.raiderplanner.model.Milestone; +import edu.wright.cs.raiderplanner.model.ModelEntity; +import edu.wright.cs.raiderplanner.model.Module; +import edu.wright.cs.raiderplanner.model.Requirement; +import edu.wright.cs.raiderplanner.model.StudyProfile; +import edu.wright.cs.raiderplanner.model.Task; +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.SceneAntialiasing; +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; +import javafx.scene.image.Image; +import javafx.stage.FileChooser; +import javafx.stage.Modality; +import javafx.stage.Screen; +import javafx.stage.Stage; + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.net.URL; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Calendar; + +/** + * Created by Zilvinas on 04/05/2017. + */ +public class UiManager { + private static final URL accountStartFxml = null; + private static Stage mainStage = new Stage(); + private static MenuController mc = new MenuController(); + private static SettingsController sc = new SettingsController(); + private static File savesFolder = new File("./saves"); + private static Image icon = new Image("file:icon.png"); + private static FileChooser.ExtensionFilter datExtension = + new FileChooser.ExtensionFilter("dat file", "*.dat"); + private static FileChooser.ExtensionFilter xmlExtension = + new FileChooser.ExtensionFilter("XML file", "*.xml"); + private static FileChooser.ExtensionFilter pngExtension = + new FileChooser.ExtensionFilter("PNG file", "*.png"); + private static FileChooser.ExtensionFilter icsExtension = + new FileChooser.ExtensionFilter("ICS file", "*.ics"); + private URL activityFxml = getClass().getResource( + "/edu/wright/cs/raiderplanner/view/Activity.fxml"); + private URL milestoneFxml = getClass().getResource( + "/edu/wright/cs/raiderplanner/view/Milestone.fxml"); + private URL taskFxml = getClass().getResource( + "/edu/wright/cs/raiderplanner/view/Task.fxml"); + private URL requirementFxml = getClass().getResource( + "/edu/wright/cs/raiderplanner/view/Requirement.fxml"); + private URL createAccountFxml = getClass().getResource( + "/edu/wright/cs/raiderplanner/view/CreateAccount.fxml"); + private URL mainMenuFxml = getClass().getResource( + "/edu/wright/cs/raiderplanner/view/MainMenu.fxml"); + private URL studyProfileFxml = getClass().getResource( + "/edu/wright/cs/raiderplanner/view/StudyProfile.fxml"); + private URL startupFxml = getClass().getResource( + "/edu/wright/cs/raiderplanner/view/Startup.fxml"); + private URL settingsFxml = getClass().getResource( + "/edu/wright/cs/raiderplanner/view/Settings.fxml"); + + private static int setupCount = 0; + /** + * Displays a window to allow the user to load an existing account or create a new one. + * @throws IOException for loader.load + */ + + public boolean accountStart() throws IOException { + AccountLoader accountControl = new AccountLoader(); + System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Hello World!"); + FXMLLoader loader = new FXMLLoader(accountStartFxml); + loader.setController(accountControl); + Parent root = loader.load(); + Stage stage = new Stage(); + stage.setScene(new Scene(root, 550, 232)); + stage.setTitle("Load Account"); + stage.resizableProperty().setValue(false); + stage.getIcons().add(icon); + stage.showAndWait(); + boolean newAccount = accountControl.handleSubmit(); + return newAccount; + } + + /** + * Displays a 'Create Account' window and handles the creation of a new Account object. + * Checks if the account was created successfully if not exit. + * @return newly created Account + * @throws IOException for loader.load() + */ + public Account createAccount(boolean first) throws IOException { + AccountController accountControl = new AccountController(); + System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Hello World!"); + // Load in the .fxml file: + FXMLLoader loader = new FXMLLoader(createAccountFxml); + loader.setController(accountControl); + Parent root = loader.load(); + // Set the scene: + Stage stage = new Stage(); + stage.setScene(new Scene(root, 565, 232)); + stage.setTitle("Create Account"); + stage.resizableProperty().setValue(false); + stage.getIcons().add(icon); + stage.showAndWait(); + // Handle creation of the Account object: + // If user exits before submitting information, program exits. + if (!accountControl.isSuccess() && first == true) { + System.exit(0); + } else if (!accountControl.isSuccess() && first == false) { + stage.close(); + return null; + } + + Account newAccount = accountControl.getAccount(); + return newAccount; + } + + /** + * Displays the main menu. + * + * @throws Exception when the FXMLLoader is unable to load + */ + public void mainMenu() throws Exception { + // Load in the .fxml file: + System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Hello World!"); + FXMLLoader loader = new FXMLLoader(mainMenuFxml); + loader.setController(UiManager.mc); + Parent root = loader.load(); + + // Set the scene: + mainStage.setScene(new Scene(root, + Screen.getPrimary().getVisualBounds().getWidth() * 0.77, + Screen.getPrimary().getVisualBounds().getHeight() * 0.9, + true, SceneAntialiasing.BALANCED)); + mainStage.setTitle("RaiderPlanner"); + + // Set minimum resolution to around 360p in 3:5 aspect ratio for small phones + mainStage.setMinHeight(555); + mainStage.setMinWidth(333); + mainStage.getIcons().add(icon); + mainStage.showAndWait(); + } + + /** + * Reloads the main page after a new profile is open. + * @throws Exception When reloading the menu. + */ + public void reloadMainMenu() throws Exception { + FXMLLoader loader = new FXMLLoader(mainMenuFxml); + loader.setController(UiManager.mc); + Parent root = loader.load(); + mainStage.getScene().setRoot(root); + } + + /** + * Displays the main menu in the current stage. + * Uses the mainStage set forth by the main menu on start up. + * + * @throws Exception when the FXMLLoader is unable to load + */ + public void showMain() throws Exception { + // Load in the .fxml file: + System.setProperty("com.apple.mrj.application.apple.menu.about.name", "Hello World!"); + FXMLLoader loader = new FXMLLoader(mainMenuFxml); + loader.setController(UiManager.mc); + Parent root = loader.load(); + // prevents saving file closing the program when the main menu has been opened + setupCount++; + // so if the cancel or exit are pressed in saving file, only closes the + // program during first account setup + + // Set the scene with the SettingsFxml: + mainStage.getScene().setRoot(root); + mainStage.setTitle("RaiderPlanner"); + } + + /** + * Displays the settings menu in the current stage. + * Uses the mainStage set forth by the main menu on start up. + * + * @throws Exception when the FXMLLoader is unable to load + */ + public void showSettings() throws Exception { + // Load in the .fxml file: + FXMLLoader loader = new FXMLLoader(settingsFxml); + loader.setController(sc); + Parent root = loader.load(); + + // Set the scene with the SettingsFxml: + mainStage.getScene().setRoot(root); + mainStage.setTitle("RaiderPlanner-Settings"); + } + + /** + * Display the 'Add Activity' window. + * Checks to see if the creation is successful. + * @return newly created Activity. If not successful return null. + * @throws IOException exception if IO error is triggered + */ + public Activity addActivity() throws IOException { + ActivityController ac = new ActivityController(); + // Load in the .fxml file: + FXMLLoader loader = new FXMLLoader(activityFxml); + loader.setController(ac); + Parent root = loader.load(); + // Set the scene: + Stage stage = new Stage(); + stage.initModality(Modality.APPLICATION_MODAL); + stage.setScene(new Scene(root, 550, 358)); + stage.setTitle("New Activity"); + stage.resizableProperty().setValue(false); + stage.getIcons().add(icon); + stage.showAndWait(); + // Add the Activity to the StudyPlanner + if (ac.isSuccess()) { + return ac.getActivity(); + } + return null; + } + + /** + * Displays the Activity details page. + * + * @param activity for which the details should be displayed + * @throws IOException if there is an error while loading the FXML GUI + */ + public void activityDetails(Activity activity) throws IOException { + ActivityController ac = new ActivityController(activity); + // Load in the .fxml file: + FXMLLoader loader = new FXMLLoader(activityFxml); + loader.setController(ac); + Parent root = loader.load(); + // Set the scene: + Stage stage = new Stage(); + stage.initModality(Modality.APPLICATION_MODAL); + stage.setScene(new Scene(root, 550, 358)); + stage.setTitle("Activity"); + stage.resizableProperty().setValue(false); + stage.getIcons().add(icon); + stage.showAndWait(); + } + + /** + * Displays the 'Add Milestone' window. + * Checks to see if the milestone was added. + * @return newly created Milestone object. if not return null. + * @throws IOException if there is an error while loading the FXML GUI + */ + public Milestone addMilestone() throws IOException { + MilestoneController mc = new MilestoneController(); + // Load in the .fxml file: + FXMLLoader loader = new FXMLLoader(milestoneFxml); + loader.setController(mc); + Parent root = loader.load(); + // Set the scene: + Stage stage = new Stage(); + stage.initModality(Modality.APPLICATION_MODAL); + stage.setScene(new Scene(root, 550, 355)); + stage.setTitle("Milestone"); + stage.resizableProperty().setValue(false); + stage.getIcons().add(icon); + stage.showAndWait(); + // Add the Milestone to the StudyPlanner + if (mc.isSuccess()) { + return mc.getMilestone(); + } + return null; + } + + /** + * Displays the Milestone details page. + * + * @param milestone for which the details should be shown + * @throws IOException if there is an error while loading the FXML GUI + */ + public void milestoneDetails(Milestone milestone) throws IOException { + MilestoneController mc = new MilestoneController(milestone); + // Load in the .fxml file: + FXMLLoader loader = new FXMLLoader(milestoneFxml); + loader.setController(mc); + Parent root = loader.load(); + // Set the scene: + Stage stage = new Stage(); + stage.initModality(Modality.APPLICATION_MODAL); + stage.setScene(new Scene(root, 550, 355)); + stage.setTitle("Milestone"); + stage.resizableProperty().setValue(false); + stage.getIcons().add(icon); + stage.showAndWait(); + } + + /** + * Displays the StudyProfile details page. + * + * @param profile StudyProfile for which the details should be shown + * @throws IOException if there is an error while loading the FXML GUI + */ + public void studyProfileDetails(StudyProfile profile) throws IOException { + UiManager.mc.main(MenuController.Window.PROFILES); + UiManager.mc.loadStudyProfile(profile); + } + + /** + * Displays the Module details page. + * + * @param module for which the details should be shown + * @param current Window from which this method is called + */ + public void moduleDetails(Module module, MenuController.Window current) { + UiManager.mc.loadModule(module, current, null); + } + + /** + * Displays the Module details page. + * + * @param module for which the details should be shown + * @param current Window from which this method is called + */ + public void moduleDetails(Module module, ModelEntity current) { + UiManager.mc.loadModule(module, MenuController.Window.EMPTY, current); + } + + /** + * Displays the Assignment details page. + * + * @param assignment for which the details should be shown + * @param current Window from which this method is called + */ + public void assignmentDetails(Assignment assignment, MenuController.Window current) { + UiManager.mc.loadAssignment(assignment, current, null); + } + + /** + * Displays the Assignment details page. + * + * @param assignment for which the details should be shown + * @param current Window from which this method is called + */ + public void assignmentDetails(Assignment assignment, ModelEntity current) { + UiManager.mc.loadAssignment(assignment, MenuController.Window.EMPTY, current); + } + + /** + * Creates a window for adding a new Task. + * Checks if window was made successfully. + * @return newly created Task. Returns null if failed + * @throws IOException if there is an error while loading the FXML GUI + */ + public Task addTask() throws IOException { + TaskController tc = new TaskController(); + // Load in the .fxml file: + FXMLLoader loader = new FXMLLoader(taskFxml); + loader.setController(tc); + Parent root = loader.load(); + // Set the scene: + Stage stage = new Stage(); + stage.initModality(Modality.APPLICATION_MODAL); + stage.setScene(new Scene(root, 550, 558)); + stage.setTitle("New Task"); + stage.resizableProperty().setValue(false); + stage.getIcons().add(icon); + stage.showAndWait(); + // Handle creation of the Account object: + if (tc.isSuccess()) { + return tc.getTask(); + } + return null; + } + + /** + * Displays the Task details page. + * + * @param task for which the details should be displayed + * @throws IOException if there is an error while loading the FXML GUI + */ + public void taskDetails(Task task) throws IOException { + TaskController tc = new TaskController(task); + // Load in the .fxml file: + FXMLLoader loader = new FXMLLoader(taskFxml); + loader.setController(tc); + Parent root = loader.load(); + // Set the scene: + Stage stage = new Stage(); + stage.initModality(Modality.APPLICATION_MODAL); + stage.setScene(new Scene(root, 550, 558)); + stage.setTitle("Task"); + stage.resizableProperty().setValue(false); + stage.getIcons().add(icon); + stage.showAndWait(); + } + + /** + * Creates a window for adding a new Requirement. + * Checks if window was created successfully. + * @return newly created Requirement. Return null if failed + * @throws IOException if there is an error while loading the FXML GUI + */ + public Requirement addRequirement() throws IOException { + RequirementController rc = new RequirementController(); + // Load in the .fxml file: + FXMLLoader loader = new FXMLLoader(requirementFxml); + loader.setController(rc); + Parent root = loader.load(); + // Set the scene: + Stage stage = new Stage(); + stage.initModality(Modality.APPLICATION_MODAL); + stage.setScene(new Scene(root, 550, 260)); + stage.setTitle("New Requirement"); + stage.resizableProperty().setValue(false); + stage.getIcons().add(icon); + stage.showAndWait(); + // Handle creation of the Account object: + if (rc.isSuccess()) { + return rc.getRequirement(); + } + return null; + } + + /** + * Displays the Requirement details page. + * + * @param requirement for which the details should be displayed + * @throws IOException if there is an error while loading the FXML GUI + */ + public void requirementDetails(Requirement requirement) throws IOException { + RequirementController rc = new RequirementController(requirement); + // Load in the .fxml file: + FXMLLoader loader = new FXMLLoader(requirementFxml); + loader.setController(rc); + Parent root = loader.load(); + // Set the scene: + Stage stage = new Stage(); + stage.initModality(Modality.APPLICATION_MODAL); + stage.setScene(new Scene(root, 550, 558)); + stage.setTitle("Requirement"); + stage.resizableProperty().setValue(false); + stage.getIcons().add(icon); + stage.showAndWait(); + } + + /** + * Display startup window. + * @throws IOException if there is an error while loading the FXML GUI + */ + public void showStartup() throws IOException { + StartupController sc = new StartupController(); + // Load in the .fxml file: + FXMLLoader loader = new FXMLLoader(startupFxml); + loader.setController(sc); + Parent root = loader.load(); + // Set the scene: + Stage stage = new Stage(); + stage.setScene(new Scene(root, 400, 500)); + stage.setTitle("RaiderPlanner"); + stage.resizableProperty().setValue(false); + stage.getIcons().add(icon); + // Replaces red x click with a System.exit(0); + stage.setOnCloseRequest(event -> { + event.consume(); + System.exit(0); + }); + stage.showAndWait(); + } + + /** + * Displays a file dialog for importing .xml files + * + * @return a File object + */ + public File loadFileDialog() { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Select a HUB file"); + fileChooser.getExtensionFilters().add(xmlExtension); + fileChooser.setInitialDirectory(new File("StudyProfiles")); + File file = fileChooser.showOpenDialog(mainStage); + return file; + } + + /** + * Displays a file dialog for saving a .png file + * + * @return String path + */ + public String saveFileDialog(Stage stage) { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Save Ganttish Diagram"); + fileChooser.getExtensionFilters().add(pngExtension); + File path = fileChooser.showSaveDialog(stage); + return path.getAbsolutePath(); + } + + /** + * Displays a file dialog for importing .dat planner files + * + * @return a File object + */ + public File loadPlannerFileDialog() { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Select a planner to load"); + fileChooser.getExtensionFilters().add(datExtension); + if (!savesFolder.exists()) { + savesFolder.mkdirs(); + } + fileChooser.setInitialDirectory(savesFolder); + File file = fileChooser.showOpenDialog(mainStage); + return file; + } + + /** + * Displays a file dialog for saving .ics export files + * + * @return a File object + */ + public File saveIcsFileDialog() { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Save your ICS export file"); + fileChooser.getExtensionFilters().add(icsExtension); + if (!savesFolder.exists()) { + savesFolder.mkdirs(); + } + fileChooser.setInitialDirectory(savesFolder); + File file = fileChooser.showSaveDialog(mainStage); + return file; + } + + /** + * Displays a file dialog for saving .dat planner files + * + * @return a File object + */ + public File savePlannerFileDialog() { + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Save your .dat file"); + fileChooser.getExtensionFilters().add(datExtension); + if (!savesFolder.exists()) { + savesFolder.mkdirs(); + } + fileChooser.setInitialDirectory(savesFolder); + File file = fileChooser.showSaveDialog(mainStage); + //allows program to close if cancel or exit are pressed + if (file == null && setupCount == 0) { + System.exit(0); + } + setupCount++;//prevents the cancel button from closing the program except for initial setup. + return file; + } + + /** + * Reporting errors without logging. + * + * @param displayMessage message to display to user + */ + public static void reportError(String displayMessage) { + displayError(displayMessage); + } + + /** + * Error reporting with stack trace. + * + * @param displayMessage message to display to user. + * @param stackTrace StackTrace from thrown exception + */ + public static void reportError(String displayMessage,StackTraceElement[] stackTrace) { + reportError(displayMessage,Arrays.toString(stackTrace)); + } + + /** + * Error reporting with string. + * + * @param displayMessage message to be displayed to user + * @param errorMessage error to log, can be string or stack trace + */ + public static void reportError(String displayMessage,String errorMessage) { + try (BufferedWriter bw = new BufferedWriter(new FileWriter("errorlog.txt", true));) { + // Time stamp code from + // https://stackoverflow.com/questions/5175728/how-to-get-the-current-date-time-in-java + String timeStamp = new SimpleDateFormat( + "yyyyMMdd_HHmmss").format(Calendar.getInstance().getTime()); + bw.write(timeStamp + " " + displayMessage + " " + errorMessage); + bw.newLine(); + bw.close(); + } catch (IOException e) { + e.printStackTrace(); + } + displayError(displayMessage); + } + + /** + * Displays an error to the user. + * + * @param message to be displayed to user + */ + public static void displayError(String message) { + Alert alert = new Alert(Alert.AlertType.ERROR, message); + alert.showAndWait(); + } + + /** + * Reports that an action was successful and displays a message. + * + * @param message to be displayed + */ + public static void reportSuccess(String message) { + Alert alert = new Alert(Alert.AlertType.INFORMATION, message); + alert.showAndWait(); + } + + /** + * Reports that an action was successful. + */ + public static void reportSuccess() { + Alert alert = new Alert(Alert.AlertType.INFORMATION, "Success!"); + alert.showAndWait(); + } + + /** + * Confirm box with 'Yes' or 'No' as available options. + * + * @param message to be displayed + * @return true for yes, false for no + */ + public static boolean confirm(String message) { + Alert alert = new Alert(Alert.AlertType.CONFIRMATION, message, ButtonType.YES, + ButtonType.NO); + alert.showAndWait(); + return alert.getResult().equals(ButtonType.YES); + } + + /** + * Please don't use. + */ + public static void areYouFeelingLucky() { + while (UiManager.confirm("Are you feeling lucky?") == (Math.random() < 0.5)) { + } + } + + /** + * The save folder location. + * @return The save folder location. + */ + public static File getSavesFolder() { + return savesFolder; + } + +} diff --git a/Test/LICENSE.md b/test/LICENSE.md similarity index 100% rename from Test/LICENSE.md rename to test/LICENSE.md diff --git a/test/edu/wright/cs/raiderplanner/controller/DataControllerTest.java b/test/edu/wright/cs/raiderplanner/controller/DataControllerTest.java new file mode 100644 index 00000000..82a4133c --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/controller/DataControllerTest.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Created by bijan on 08/05/2017. + */ +public class DataControllerTest { + /** + * WIP: This test class should set up required data and fields. + * @throws Exception to handle when the test setup fails for any given reason. + */ + @BeforeEach + public void setUp() throws Exception { + } + + /** + * WIP: This test case should verify that there is a settings file present. + * @throws Exception to handle when there is no settings file. + */ + @Test + public void existingSettingsFile() throws Exception { + } + + /** + * WIP: This test case should verify that the UI nodes are reachable. + * @throws Exception to handle when the nodes can not be retrieved. + */ + @Test + public void getNodes() throws Exception { + } + + /** + * WIP: This test case should verify that the nodes gathered are valid. + * @throws Exception to handle when an invalid node is found. + */ + @Test + public void validNodeList() throws Exception { + } + + /** + * WIP: This test case should attempt to load a sample hub file (a default, for instance). + * @throws Exception to handle when the hub file cannot be properly instantiated. + */ + @Test + public void loadHubFile() throws Exception { + } + +} \ No newline at end of file diff --git a/test/edu/wright/cs/raiderplanner/controller/MainControllerTest.java b/test/edu/wright/cs/raiderplanner/controller/MainControllerTest.java new file mode 100644 index 00000000..094897a5 --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/controller/MainControllerTest.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Created by bijan on 08/05/2017. + */ +public class MainControllerTest { + /** + * WIP: This test case should contain data and fields to set up further test cases. + * @throws Exception to handle anything that may cause this method to fail. + */ + @BeforeEach + public void setUp() throws Exception { + } + + /** + * WIP: This test case should set up the components of the main controller + * and ensure that all assets are available. + * @throws Exception to handle any initialization failures. + */ + @Test + public void initialise() throws Exception { + } + + /** + * WIP: This test case should be used to test error reporting handled by the + * main controller. + * @throws Exception to handle cases when the error reporting structure does not work + * as expected. + */ + @Test + public void reportError() throws Exception { + } + +} \ No newline at end of file diff --git a/test/edu/wright/cs/raiderplanner/controller/StudyPlannerControllerTest.java b/test/edu/wright/cs/raiderplanner/controller/StudyPlannerControllerTest.java new file mode 100644 index 00000000..ea2d8324 --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/controller/StudyPlannerControllerTest.java @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.controller; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import edu.wright.cs.raiderplanner.controller.StudyPlannerController; +import edu.wright.cs.raiderplanner.model.Account; +import edu.wright.cs.raiderplanner.model.Event; +import edu.wright.cs.raiderplanner.model.HubFile; +import edu.wright.cs.raiderplanner.model.Module; +import edu.wright.cs.raiderplanner.model.Person; +import edu.wright.cs.raiderplanner.model.Task; +import edu.wright.cs.raiderplanner.model.VersionControlEntity; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; + +/** + * Created by bijan on 08/05/2017. + */ +public class StudyPlannerControllerTest { + + StudyPlannerController studyPlannerController; + + /** + * This test cases sets up the Account and Controller necessary for this test suite. + * @throws Exception to handle errors when creating the objects. + */ + @BeforeEach + public void setUp() throws Exception { + Account account = new Account(new Person("Mr","Adrew",true),"100125464"); + studyPlannerController = new StudyPlannerController(account); + + int v1 = 2; + int y1 = 2017; + int s1 = 3; + ArrayList m1 = new ArrayList<>(); + ArrayList a1 = new ArrayList<>(); + ArrayList cal1 = new ArrayList<>(); + HubFile hubFile = new HubFile(v1, y1, s1, m1, a1, cal1); + studyPlannerController.createStudyProfile(hubFile); + } + + /** + * WIP: This test case should attempt to retrieve user study profiles. + * @throws Exception to handle when the study profiles cannot be found or loaded properly. + */ + + @Test + public void getStudyProfiles() throws Exception { + assertEquals(1, studyPlannerController.getPlanner().getStudyProfiles().length); + } + + /** + * WIP: This test case should verify that the study profile file is not corrupted. + * @throws Exception if the study profile does not contain a profile, or if it is corrupted. + */ + @Test + public void containsStudyProfile() throws Exception { + assertEquals(true, studyPlannerController.containsStudyProfile(2017, 3)); + } + + /** + * WIP: This test case should attempt to retrieve the current version. + * @throws Exception when the current version cannot be found. + */ + @Test + public void getCurrentVersion() throws Exception { + studyPlannerController.getPlanner().setVersion(5); + assertEquals(5, studyPlannerController.getPlanner().getVersion()); + } + + /** + * WIP: This test case should test the process for creating a new study profile. + * @throws Exception when a study profile cannot be created successfully. + */ + @Test + public void createStudyProfile() throws Exception { + int v1 = 5; + int y1 = 1997; + int s1 = 30; + ArrayList m1 = new ArrayList<>(); + ArrayList a1 = new ArrayList<>(); + ArrayList cal1 = new ArrayList<>(); + HubFile newHubFile = new HubFile(v1, y1, s1, m1, a1, cal1); + assertEquals(true, studyPlannerController.createStudyProfile(newHubFile)); + } + + /** + * WIP: This test case should test the process of updating the study profile with new data. + * @throws Exception when the study profile cannot be updated successfully. + */ + @Test + public void updateStudyProfile() throws Exception { + studyPlannerController.getPlanner().getStudyProfiles()[0].setName("John Doe"); + String profileName = studyPlannerController.getPlanner().getStudyProfiles()[0].getName(); + assertEquals("John Doe", profileName); + } + + /** + * WIP: This test case should attempt to return the current list of tasks in the study profile. + * @throws Exception if the list cannot be retrieved. + */ + @Test + public void getListOfTasks() throws Exception { + ArrayList task = studyPlannerController.getPlanner().getStudyProfiles()[0].getTasks(); + assertEquals(new ArrayList(), task); + } + + /** + * WIP: This test case should attempt to add a new activity to the study profile. + * @throws Exception when an activity is not able to be added. + */ + @Test + public void newActivity() throws Exception { + } + + /** + * This test case erases the data from the StudyPlannerController object to avoid any + * leftover data which could conflict with future tests. + * @throws Exception to handle when the StudyPlannerController object cannot be found. + */ + @AfterEach + public void tearDown() throws Exception { + studyPlannerController = null; + } +} diff --git a/test/edu/wright/cs/raiderplanner/model/AccountTest.java b/test/edu/wright/cs/raiderplanner/model/AccountTest.java new file mode 100644 index 00000000..8fec39e1 --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/model/AccountTest.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import edu.wright.cs.raiderplanner.model.Account; +import edu.wright.cs.raiderplanner.model.Person; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Created by bijan on 04/05/2017. + */ +public class AccountTest { + + Person person; + edu.wright.cs.raiderplanner.model.Account account; + + /** + * This test case creates Person and Account objects for use throughout this suite. + * @throws Exception to handle when a new Person or Account cannot be created successfully. + */ + @BeforeEach + public void setUp() throws Exception { + person = new edu.wright.cs.raiderplanner.model.Person("Mr","Andrew Odintsov", true); + account = new Account(person, "10012721-UG"); + } + + /** + * This test case verifies that the Account.getStudentDetails method returns correct info. + * @throws Exception to handle when the data returned does not match. + */ + @Test + public void getStudentDetails() throws Exception { + assertEquals(person, account.getStudentDetails()); + } + + /** + * This test case verifies that the Account.getStudentNumber method returns correct info. + * @throws Exception to handle when the returned number does not match what is expected. + */ + @Test + public void getStudentNumber() throws Exception { + assertEquals("10012721-UG", account.getStudentNumber()); + } + + /** + * This test case verifies that the Account details for a student can be set properly. + * @throws Exception to handle when a new Person cannot be created, when the student + * details cannot be set, or when the details returned are incorrect. + */ + @Test + public void setStudentDetails() throws Exception { + Person person2 = new Person("Dr","Zilvinas Ceikauskas", true, "zil.Cei@gmail.com"); + account.setStudentDetails(person2); + assertEquals(person2, account.getStudentDetails()); + } + + /** + * This test case verifies that the student number can be successfully retrieved from + * the Account object. + * @throws Exception to handle when the student number cannot be set, or when the + * number returned is incorrect. + */ + @Test + public void setStudentNumber() throws Exception { + account.setStudentNumber("99222213-UG"); + assertEquals("99222213-UG", account.getStudentNumber()); + } + + /** + * This test case erases the data from the Person and Account objects to avoid any + * leftover data which could conflict with future tests. + * @throws Exception to handle when the Person or Account objects cannot be found. + */ + @AfterEach + public void tearDown() throws Exception { + person = null; + account = null; + } +} diff --git a/test/edu/wright/cs/raiderplanner/model/ApacheDerbyDatabaseTest.java b/test/edu/wright/cs/raiderplanner/model/ApacheDerbyDatabaseTest.java new file mode 100644 index 00000000..4ba6cf0b --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/model/ApacheDerbyDatabaseTest.java @@ -0,0 +1,158 @@ +/* + * Copyright (C) 2018 - Nathaniel Crossman + * + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import org.apache.derby.jdbc.EmbeddedDriver; + +import org.junit.After; +import org.junit.Test; + +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Properties; + + +/** + * This is my test to see if SQl. + * @author Nathaniel Crossman + * + */ +public class ApacheDerbyDatabaseTest { + + private Connection conn = null; + private EmbeddedDriver driver = null; + private Statement sm = null; + + /** + * This method prints out the entire contents of the relational table. + * @throws SQLException get up + */ + public void checksData() throws SQLException { + ResultSet rs = this.sm.executeQuery("SELECT * FROM strNames"); + while (rs.next()) { + int stringD = rs.getInt("string_id"); + String randString = rs.getString("RandString"); + assertNotNull(stringD); + assertNotNull(randString); + } + } + + /** + * Creates a table the database and inserts for values. + * @throws SQLException get up + */ + public void addData() throws SQLException { + sm.execute("CREATE TABLE strNames( string_id int, RandString varchar(255))"); + sm.execute("INSERT INTO strNames values (1,'Webster St.')"); + sm.execute("INSERT INTO strNames values (1,'Webster St.')"); + sm.execute("INSERT INTO strNames values (2,'Union St.')"); + sm.execute("INSERT INTO strNames values (3,'Gold St.')"); + sm.execute("INSERT INTO strNames values (4,'Never St.')"); + } + + /** + * Setup the connection to the DB. + * @throws SQLException get up + */ + public void createConnection() throws SQLException { + String protocol = "jdbc:derby:"; + String driverOne = "org.apache.derby.jdbc.EmbeddedDriver"; + // Removes the derby.log file creations + if (System.getProperty("os.name").toLowerCase().contains("windows")) { + System.setProperty("derby.stream.error.file", "NUL"); + } else { + System.setProperty("derby.stream.error.file", "/dev/null"); + } + this.driver = new EmbeddedDriver(); + assertNotNull(driver); //Test Driver + conn = driver.connect("jdbc:derby:memory:MyDbTest;create=true", new Properties()); + assertNotNull(conn); //Test Connection + this.sm = conn.createStatement(); + assertNotNull(sm); //Test Statement + } + + /** + * Main Setup Method. + */ + @Test + public void before() { + try { + createConnection(); + addData(); + checksData(); + } catch (SQLException sqle) { + printErrors(sqle); + } + try { + this.sm.execute("DROP TABLE strNames"); + } catch (SQLException sqle) { + printErrors(sqle); + } + + } + + /** + * Main Setup Method. + + */ + @After + public void after() { + try { + if (conn != null) { + conn.close(); + DriverManager.getConnection("jdbc:derby:;shutdown=true;deregister=false"); + assertTrue(true); + } + } catch (SQLException sqle) { + printErrors(sqle); + } + } + + /** + * This method is used to print out error message. + * Additionally, if this method is ever executed it mean that the assert should fail. + * @param exce any SQL exception. + */ + public static void printErrors(SQLException exce) { + if (exce.getSQLState().equals("XJ015")) { + return; // Ignore + } + while (exce != null) { + System.err.println("\n----- SQLException -----"); + System.err.println(" SQL State: " + exce.getSQLState()); + System.err.println(" Error Code: " + exce.getErrorCode()); + System.err.println(" Message: " + exce.getMessage()); + exce.printStackTrace(System.err); + exce = exce.getNextException(); + } + assertTrue(false); + } +} + + + diff --git a/test/edu/wright/cs/raiderplanner/model/AssignmentTest.java b/test/edu/wright/cs/raiderplanner/model/AssignmentTest.java new file mode 100644 index 00000000..b35f7abb --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/model/AssignmentTest.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import edu.wright.cs.raiderplanner.model.Assignment; +import edu.wright.cs.raiderplanner.model.Person; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Created by bijan on 12/05/2017. + */ +public class AssignmentTest { + Assignment assignment; + Person person1; + Person person2; + ExamEvent examEvent1; + + /** + * This test case creates two Person objects and an Assignment object for use in + * this test suite. + * @throws Exception to handle when new Person or Assignment objects cannot be created. + */ + @BeforeEach + public void setUp() throws Exception { + person1 = new Person("Dr.", "Mark Fisher", true); + person2 = new Person("Dr.", "Steven Laycock", true); + assignment = new Exam(30, person1, person1, person2, 100, examEvent1); + } + + /** + * This test case removes all data from the Person and Assignment objects after each run + * of the test suite to avoid any orphan data interfering with further tests. + * @throws Exception to handle when the Person or Assignment objects cannot be accessed. + */ + @AfterEach + public void tearDown() throws Exception { + person1 = null; + person2 = null; + assignment = null; + } + + @Test + public void toStringTest() throws Exception { + String expected = "Assignment '" + assignment.getName() + "'"; + assertEquals(expected, assignment.toString()); + } + + @Test + public void toStringTest2() throws Exception { + // Testing with a true verbose + StringBuilder testString2 = new StringBuilder(); + testString2.append(assignment); + testString2.append("\n"); + testString2.append("Total marks: " + 100); + testString2.append("\n"); + testString2.append("Total weighting: " + 30); + + testString2.append("\n"); + testString2.append("Set By: " + person1.toString()); + testString2.append("\n"); + testString2.append("Marked By: " + person1.toString()); + testString2.append("\n"); + testString2.append("Reviewed By: " + person2.toString()); + + assertEquals(testString2.toString(), assignment.toString(true)); + + // Testing with a false verbose + assertEquals(assignment.toString(), assignment.toString(false)); + } + +} diff --git a/test/edu/wright/cs/raiderplanner/model/EventTest.java b/test/edu/wright/cs/raiderplanner/model/EventTest.java new file mode 100644 index 00000000..d910d194 --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/model/EventTest.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import edu.wright.cs.raiderplanner.controller.MainController; +import edu.wright.cs.raiderplanner.model.Event; + +import javafx.stage.Stage; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; + +import org.testfx.framework.junit.ApplicationTest; + +import java.util.GregorianCalendar; + +/** + * Created by bijan on 12/05/2017. + */ + +public class EventTest extends ApplicationTest { + + @Override + public void start(Stage primaryStage) throws Exception { + MainController.isNumeric("23"); + } + + Event event; + + /** + * This test case sets up a new Event object each time it is run. + * @throws Exception if a new Event cannot be created. + */ + @BeforeEach + public void setUp() throws Exception { + event = new Event("09/04/2017T15:00:00Z"); + } + + /** + * WIP: This test case should verify that the Event.validDateString method returns valid + * information. Currently, this test causes the build to crash. When resolved, + * the tag should be changed from @Disabled to @Test. + * This test case verifies that the Event.validDateString method returns valid information. + * @throws Exception if the Event.validDateString method returns incorrect information. + */ + @Disabled + public void validDateString() throws Exception { + // Testing a valid date format + assertEquals(true, Event.validDateString("02/05/2017T05:02:09Z")); + + // Testing an invalid date format + assertEquals(false, Event.validDateString("02/05/217T0:02:09Z")); + } + + /** + * WIP: This test case should verify that the GregorianCalendar object correctly returns + * data for a given date. Currently, this test causes the build to crash. When + * resolved, the tag should be changed from @Disabled to @Test. + * @throws Exception if the date/time string returned is not correct. + */ + @Disabled + public void toStringTest() throws Exception { + GregorianCalendar expectedDate = new GregorianCalendar(2017, 3, 9, 15, 0, 0); + assertEquals(expectedDate.getTime().toString(), event.toString()); + } + + /** + * WIP: This test case should verify that the Event.setDate method correctly sets/saves + * a given date/time. As long as the previous test cases pass successfully, + * then this test case should also pass if the Event.setDate method is working. + * @throws Exception to handle when the date cannot be set, or when the Event and the + * GregorianCalendar date do not match. + */ + @Disabled + public void setDate() throws Exception { + event.setDate("04/10/2017T09:08:13Z"); + GregorianCalendar expectedDate = new GregorianCalendar(2017, 9, 4, 9, 8, 13); + assertEquals(expectedDate.getTime().toString(), event.toString()); + } + +} diff --git a/test/edu/wright/cs/raiderplanner/model/HubFileTest.java b/test/edu/wright/cs/raiderplanner/model/HubFileTest.java new file mode 100644 index 00000000..8c3d7042 --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/model/HubFileTest.java @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import org.junit.jupiter.api.Test; + +/** + * Created by bijan on 06/05/2017. + */ +public class HubFileTest { + @Test + public void getModules() throws Exception { + } + + @Test + public void getExtensions() throws Exception { + } + + @Test + public void getUpdates() throws Exception { + } + +} \ No newline at end of file diff --git a/test/edu/wright/cs/raiderplanner/model/ModelEntityTest.java b/test/edu/wright/cs/raiderplanner/model/ModelEntityTest.java new file mode 100644 index 00000000..f1199a94 --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/model/ModelEntityTest.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import edu.wright.cs.raiderplanner.model.ModelEntity; +import edu.wright.cs.raiderplanner.model.MultilineString; +import edu.wright.cs.raiderplanner.model.Note; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.GregorianCalendar; + +/** + * Created by bijan on 08/05/2017. + */ +@Disabled +public class ModelEntityTest { + + private ModelEntity modelEntity; + private GregorianCalendar gregorianCalendar; + private MultilineString multilineString; + private Note note; + private ArrayList notes; + + /** + * This test case should set up all of the objects necessary for the following tests in + * this suite. + * @throws Exception to handle when any of the required objects cannot be instantiated. + */ + @BeforeEach + public void setUp() throws Exception { + gregorianCalendar = new GregorianCalendar(2017, 06, 02, 3, + 31, 30); + multilineString = new MultilineString("This is some note for testing purposes"); + note = new Note("Note1", gregorianCalendar, multilineString); + notes = new ArrayList<>(); + notes.add(note); + modelEntity = new Milestone("Milestone", "Test", LocalDate.of(2018, 2, 4)); + } + + /** + * After each run, this case removes all data from the ModelEntity object to avoid + * interference with other test runs. + * @throws Exception to handle when the ModelEntity cannot be accessed. + */ + @AfterEach + public void tearDown() throws Exception { + modelEntity = null; + } + + @Test + public void getDetails() throws Exception { + assertEquals("detail", modelEntity.getDetails().getAsString()); + } + + @Test + public void setName1() throws Exception { + modelEntity.setName("Andrew"); + assertEquals("Andrew", modelEntity.getName()); + } + + @Test + public void setDetails() throws Exception { + // Testing setDetails with String argument + modelEntity.setDetails("Some details to be added"); + assertEquals("Some details to be added", modelEntity.getDetails().getAsString()); + } + + @Test + public void setDetails1() throws Exception { + // Testing setDetails with String array argument + String[] detailArray = {"Some details to be added", "more details to be added"}; + modelEntity.setDetails(detailArray); + assertArrayEquals(detailArray, modelEntity.getDetails().getAsArray()); + } + + @Test + public void setDetails2() throws Exception { + // Testing setDetails with String ArrayList argument + ArrayList detailArrrayList = new ArrayList<>(); + detailArrrayList.add("New details to be added "); + detailArrrayList.add("And some more details to be added "); + modelEntity.setDetails(detailArrrayList); + assertArrayEquals(detailArrrayList.toArray(), modelEntity.getDetails().getAsArray()); + } + + @Test + public void setDetails3() throws Exception { + // Testing setDetails with multiline String argument + MultilineString multilineStr = new MultilineString("New details to be added "); + modelEntity.setDetails(multilineStr); + assertArrayEquals(multilineStr.getAsArray(), modelEntity.getDetails().getAsArray()); + } + + @Test + public void addProperties() throws Exception { + modelEntity.addProperties("name2", new MultilineString("Added details")); + assertEquals("name2", modelEntity.getName()); + assertEquals("Added details", modelEntity.getDetails().getAsString()); + } + + @Test + public void addProperties1() throws Exception { + modelEntity.addProperties("name3", "Added more details"); + assertEquals("name3", modelEntity.getName()); + assertEquals("Added more details", modelEntity.getDetails().getAsString()); + } + +} diff --git a/test/edu/wright/cs/raiderplanner/model/ModuleTest.java b/test/edu/wright/cs/raiderplanner/model/ModuleTest.java new file mode 100644 index 00000000..0253df77 --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/model/ModuleTest.java @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import org.junit.jupiter.api.Test; + +/** + * Created by bijan on 06/05/2017. + */ +public class ModuleTest { + + @Test + public void replace() throws Exception { + } + + @Test + public void addAssignment() throws Exception { + } + + @Test + public void removeAssignment() throws Exception { + } + + @Test + public void setOrganiser() throws Exception { + } + + @Test + public void setModuleCode() throws Exception { + } + + @Test + public void addTimetableEvent() throws Exception { + } + + @Test + public void removeTimetableEvent() throws Exception { + } + +} \ No newline at end of file diff --git a/test/edu/wright/cs/raiderplanner/model/MultilineStringTest.java b/test/edu/wright/cs/raiderplanner/model/MultilineStringTest.java new file mode 100644 index 00000000..d26b8b10 --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/model/MultilineStringTest.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import edu.wright.cs.raiderplanner.model.MultilineString; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; + +/** + * Created by bijan on 06/05/2017. + */ +public class MultilineStringTest { + + private MultilineString multilineStringEmpty; + private MultilineString multilineStringNormal; + private MultilineString multilineStringArray; + + /** + * This test case creates the various MultilineString objects that are necessary for + * the other tests in this suite. + * @throws Exception if any of the MultilineString objects cannot be created. + */ + @BeforeEach + public void setUp() throws Exception { + multilineStringEmpty = new MultilineString(); + + String text = "Much did had call new drew that kept.\n" + + "Limits expect wonder law she. Now has you views woman noisy match money rooms.\n" + + "To up remark it eldest length oh passed. Off because yet mistake " + + "feeling has men."; + multilineStringNormal = new MultilineString(text); + + String[] textArray = {"Much did had call new drew that kept.", + "Limits expect wonder law she. Now has you views woman noisy match money rooms.", + "To up remark it eldest length oh passed. Off because yet mistake " + + "feeling has men."}; + multilineStringArray = new MultilineString(textArray); + } + + @Test + public void getLines() throws Exception { + // Checking an empty MultilineString + assertEquals(0, multilineStringEmpty.getLines()); + + // Checking a MultilineString with 3 lines + assertEquals(3, multilineStringNormal.getLines()); + + // Checking a MultilineString with 3 lines (Created by an array of Strings) + assertEquals(3, multilineStringArray.getLines()); + } + + @Test + public void getAsArrayList() throws Exception { + // Checking an empty MultilineString + ArrayList empty = new ArrayList<>(); + assertEquals(empty, multilineStringEmpty.getAsArrayList()); + + // Checking a MultilineString with 3 lines + ArrayList text = new ArrayList<>(); + text.add("Much did had call new drew that kept."); + text.add("Limits expect wonder law she. Now has you views woman noisy match money rooms."); + text.add("To up remark it eldest length oh passed. " + + "Off because yet mistake feeling has men."); + for (int i = 0; i < multilineStringNormal.getLines(); i++) { + assertEquals(text.get(i), multilineStringNormal.getAsArrayList().get(i)); + } + + // Checking a MultilineString with 3 lines (Created by an array of Strings) + for (int i = 0; i < multilineStringArray.getLines(); i++) { + assertEquals(text.get(i), multilineStringArray.getAsArrayList().get(i)); + } + } + + @Test + public void getAsArray() throws Exception { + // Checking an empty MultilineString + String[] emptyText = {}; + assertArrayEquals(emptyText, multilineStringEmpty.getAsArray()); + + // Checking a MultilineString with 3 lines + String[] textWithThreeLines = {"Much did had call new drew that kept.", + "Limits expect wonder law she. Now has you views woman noisy match money rooms.", + "To up remark it eldest length oh passed. Off because yet mistake " + + "feeling has men."}; + assertArrayEquals(textWithThreeLines, multilineStringNormal.getAsArray()); + + + // Checking a MultilineString with 3 lines (Created by an array of Strings) + String[] textWithThreeLinesArray = {"Much did had call new drew that kept.", + "Limits expect wonder law she. Now has you views woman noisy match money rooms.", + "To up remark it eldest length oh passed. Off because yet mistake " + + "feeling has men."}; + assertArrayEquals(textWithThreeLinesArray, multilineStringArray.getAsArray()); + + } + + @Test + public void getAsString() throws Exception { + // Checking an empty MultilineString + String empty = ""; + assertEquals(empty, multilineStringEmpty.getAsString()); + + // Checking a MultilineString with 3 lines + String text = "Much did had call new drew that kept.\n" + + "Limits expect wonder law she. Now has you views " + + "woman noisy match money rooms.\n" + + "To up remark it eldest length oh passed. Off because yet mistake " + + "feeling has men."; + assertEquals(text, multilineStringNormal.getAsString()); + + // Checking a MultilineString with 3 lines (Created by an array of Strings) + assertEquals(text, multilineStringArray.getAsString()); + } + + /** + * This test case should set all MultilineString objects to null after each run so + * as to avoid interference with future test runs. + * @throws Exception if any of the MultilineString objects cannot be accessed. + */ + @AfterEach + public void tearDown() throws Exception { + multilineStringEmpty = null; + multilineStringNormal = null; + multilineStringArray = null; + } + +} diff --git a/test/edu/wright/cs/raiderplanner/model/NoteTest.java b/test/edu/wright/cs/raiderplanner/model/NoteTest.java new file mode 100644 index 00000000..9bb9e788 --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/model/NoteTest.java @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2018 - Adam Cone + * + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.GregorianCalendar; + +/** + * Created by Adam Cone 10/16/2018. + */ +public class NoteTest { + String title; + GregorianCalendar timeStamp; + MultilineString text; + Note testNote; + + /** + * This test case sets up the objects necessary for each tests. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @BeforeEach + public void setUp() throws Exception { + title = "title"; + timeStamp = new GregorianCalendar(2018, 10, 21, 00, 00, 00); + text = new MultilineString("information"); + testNote = new Note(title, timeStamp, text); + } + + /** + * This test case attempts to retrieve Note title object information. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @Test + public void getTitle() throws Exception { + assertEquals(this.title, testNote.getTitle()); + } + + /** + * This test case attempts to retrieve Note timeStamp object information. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @Test + public void getTimeStamp() throws Exception { + assertEquals(this.timeStamp, testNote.getTimeStamp()); + } + + /** + * This test case attempts to retrieve Note text object information. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @Test + public void getText() throws Exception { + assertEquals(this.text, testNote.getText()); + } + + /** + * This test case attempts to set Note title object information. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @Test + public void setTitle() throws Exception { + this.title = "newTitle"; + testNote.setTitle(this.title); + assertEquals(this.title, testNote.getTitle()); + } + + /** + * This test case attempts to set Note timeStamp object information. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @Test + public void setTimeStamp1() throws Exception { + this.timeStamp = new GregorianCalendar(2018, 10, 22, 00, 00, 00); + testNote.setTimeStamp(this.timeStamp); + assertEquals(this.timeStamp, testNote.getTimeStamp()); + } + + /** + * This test case attempts to set Note timeStamp object information. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @Test + public void setTimeStamp2() throws Exception { + this.timeStamp = new GregorianCalendar(2018, 10, 23, 00, 00, 00); + testNote.setTimeStamp(2018, 10, 23, 00, 00, 00); + assertEquals(this.timeStamp, testNote.getTimeStamp()); + } + + /** + * This test case attempts to set Note text object information. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @Test + public void setText() throws Exception { + this.text = new MultilineString("newInformation"); + testNote.setText(this.text); + assertEquals(this.text, testNote.getText()); + } + + /** + * After each run, this removes all data to avoid interference with other tests. + * + * @throws Exception + * to handle when the Task objects cannot be accessed. + */ + @AfterEach + public void tearDown() throws Exception { + title = null; + timeStamp = null; + text = null; + testNote = null; + } +} \ No newline at end of file diff --git a/test/edu/wright/cs/raiderplanner/model/PersonTest.java b/test/edu/wright/cs/raiderplanner/model/PersonTest.java new file mode 100644 index 00000000..5ebaafd2 --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/model/PersonTest.java @@ -0,0 +1,297 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import edu.wright.cs.raiderplanner.model.Person; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; + +/** + * Created by bijan on 04/05/2017. + */ + +public class PersonTest { + + private ArrayList personName = new ArrayList<>(); + private Person person1; + private Person person2; + + /** + * set up for the test. + * Ran before the actual test. + * @throws Exception if person cannot be loaded properly + */ + @BeforeEach + public void setUp() throws Exception { + personName.add("Andrew"); + person1 = new Person("Mr", personName, "Odintsov", true, "Andrew.odi@apple.com"); + person2 = new Person("Mr", "Zilvinas Ceikauskas", true, "Zill.cei@apple.com"); + } + + @Test + public void getFullName() throws Exception { + // Testing with separate given names and family name passed to the constructor + String expectedFullName = "Mr Andrew Odintsov"; + assertEquals(expectedFullName, person1.getFullName()); + + person1 = new Person("Mr", personName, "Odintsov", false, "Andrew.odi@apple.com"); + expectedFullName = "Mr Odintsov Andrew"; + assertEquals(expectedFullName, person1.getFullName()); + + personName.add("Ben"); + person1 = new Person("Mr", personName, "Odintsov", true, "Andrew.odi@apple.com"); + expectedFullName = "Mr Andrew Ben Odintsov"; + assertEquals(expectedFullName, person1.getFullName()); + + person1 = new Person("Mr", personName, "Odintsov", false, "Andrew.odi@apple.com"); + expectedFullName = "Mr Odintsov Andrew Ben"; + assertEquals(expectedFullName, person1.getFullName()); + + // Testing with full name passed to the constructor + expectedFullName = "Mr Zilvinas Ceikauskas"; + assertEquals(expectedFullName, person2.getFullName()); + + person2 = new Person("Mr", "Ceikauskas Zilvinas", false, "Zill.cei@apple.com"); + expectedFullName = "Mr Ceikauskas Zilvinas"; + assertEquals(expectedFullName, person2.getFullName()); + } + + @Test + public void getFamilyName() throws Exception { + // Testing with separate given names and family name passed to the constructor + String expectedFamilyName = "Odintsov"; + assertEquals(expectedFamilyName, person1.getFamilyName()); + + // Testing with full name passed to the constructor + expectedFamilyName = "Ceikauskas"; + assertEquals(expectedFamilyName, person2.getFamilyName()); + + } + + @Test + public void getEmail() throws Exception { + // Testing with separate given names and family name passed to the constructor + String expectedFullName = "Andrew.odi@apple.com"; + assertEquals(expectedFullName, person1.getEmail()); + + // Testing with full name passed to the constructor + expectedFullName = "Zill.cei@apple.com"; + assertEquals(expectedFullName, person2.getEmail()); + } + + @Test + public void getGivenNames() throws Exception { + // Testing with separate given names and family name passed to the constructor + assertEquals(personName, person1.getGivenNames()); + + personName.add("Ben"); + person1 = new Person("Mr", personName, "Odintsov", true, "Andrew.odi@apple.com"); + assertEquals(personName, person1.getGivenNames()); + + // Testing with full name passed to the constructor + ArrayList person2GivenNames = new ArrayList<>(); + person2GivenNames.add("Zilvinas"); + assertEquals(person2GivenNames, person2.getGivenNames()); + + person2 = new Person("Mr", "Zilvinas Ben Ceikauskas", true, "Zill.cei@apple.com"); + person2GivenNames.add("Ben"); + assertEquals(person2GivenNames, person2.getGivenNames()); + } + + @Test + public void getFamilyNameLast() throws Exception { + // Testing with TRUE value for familyNameLast + assertTrue(person1.getFamilyNameLast()); + + // Testing with FALSE value for familyNameLast + person2 = new Person("Mr", "Zilvinas Ceikauskas", false, "Zill.cei@apple.com"); + assertFalse(person2.getFamilyNameLast()); + } + + @Test + public void getSalutation() throws Exception { + String expectedSalutation = "Mr"; + assertEquals(expectedSalutation, person1.getSalutation()); + + expectedSalutation = "Mr"; + assertEquals(expectedSalutation, person2.getSalutation()); + } + + @Test + public void hasSalutation() throws Exception { + // Testing with salutation + assertEquals(true, person1.hasSalutation()); + + // Testing without salutation + person2 = new Person("", "Zilvinas Ceikauskas", true, "Zill.cei@apple.com"); + assertEquals(false, person2.hasSalutation()); + } + + @Test + public void getPreferredName() throws Exception { + // Testing with one name + assertEquals("Andrew", person1.getPreferredName()); + assertEquals("Zilvinas", person2.getPreferredName()); + + // Testing with multiple names + personName.add("Ben"); + person1 = new Person("Mr", personName, "Odintsov", true, "Andrew.odi@apple.com"); + assertEquals(personName.get(0), person1.getPreferredName()); + + person2 = new Person("Mr", "Zilvinas Ben Ceikauskas", true, "Zill.cei@apple.com"); + assertEquals("Zilvinas", person2.getPreferredName()); + + // Testing with no names + personName.clear(); + person1 = new Person("Mr", personName, "Odintsov", true, "Andrew.odi@apple.com"); + assertEquals("Odintsov", person1.getPreferredName()); + + person2 = new Person("Mr", "Ceikauskas", true, "Zill.cei@apple.com"); + assertEquals("Ceikauskas", person2.getPreferredName()); + + // Testing with Preferred Name set + person1.setPreferredName("Andy"); + assertEquals("Andy", person1.getPreferredName()); + + person2.setPreferredName("Zil"); + assertEquals("Zil", person2.getPreferredName()); + } + + @Test + public void setFamilyName() throws Exception { + person1.setFamilyName("Williams"); + assertEquals("Williams", person1.getFamilyName()); + + person2.setFamilyName("Dickson"); + assertEquals("Dickson", person2.getFamilyName()); + } + + @Test + public void setGivenNames() throws Exception { + person1.setGivenNames("Zil Ceikauskas"); + ArrayList names = new ArrayList<>(); + names.add("Zil"); + names.add("Ceikauskas"); + for (int i = 0; i < 2; i++) { + assertEquals(names.get(i), person1.getGivenNames().get(i)); + } + + person2.setGivenNames("Andrew Odintsov"); + names.clear(); + names.add("Andrew"); + names.add("Odintsov"); + for (int i = 0; i < 2; i++) { + assertEquals(names.get(i), person2.getGivenNames().get(i)); + } + } + + @Test + public void setName() throws Exception { + person1.setName("Zil Ceikauskas", true); + assertEquals("Zil", person1.getPreferredName()); + assertEquals("Ceikauskas", person1.getFamilyName()); + + person1.setName("Ceikauskas Zil", false); + assertEquals("Zil", person1.getPreferredName()); + assertEquals("Ceikauskas", person1.getFamilyName()); + } + + @Test + public void setSalutation() throws Exception { + person1.setSalutation("Dr"); + assertEquals("Dr", person1.getSalutation()); + + person2.setSalutation("Ms"); + assertEquals("Ms", person2.getSalutation()); + } + + @Test + public void validSalutation() throws Exception { + // Testing a valid salutation + boolean result = Person.validSalutation("Mr"); + assertEquals(true, result); + + // Testing an invalid salutation + result = Person.validSalutation("7483mfd"); + assertEquals(false, result); + + result = Person.validSalutation("Mr mr"); + assertEquals(false, result); + } + + @Test + public void validName() throws Exception { + // Testing a valid name + boolean result = Person.validName("Andrew Odintsov"); + assertEquals(true, result); + + // Testing an invalid name + result = Person.validName("Andrew35 Odintsov"); + assertEquals(false, result); + } + + @Test + public void validEmail() throws Exception { + // Testing valid emails + // Valid emails from: https://blogs.msdn.microsoft.com/testing123/2009/02/06/email-address-test-cases/ + String[] validEmails = {"email@domain.com", "firstname.lastname@domain.com", + "email@subdomain.domain.com", "firstname+lastname@domain.com", + "email@[123.123.123.123]", "“email”@domain.com", "1234567890@domain.com", + "email@domain-one.com", "_______@domain.com", "email@domain.name", + "email@domain.co.jp", "firstname-lastname@domain.com"}; + + for (String validEmail : validEmails) { + assertTrue(Person.validEmail(validEmail)); + } + + // Testing invalid emails + // Invalid emails from: https://blogs.msdn.microsoft.com/testing123/2009/02/06/email-address-test-cases/ + String[] invalidEmails = {"plainaddress", "#@%^%#$@#$@#.com", "@domain.com", + "Joe Smith /", "email.domain.com", "email@domain@domain.com", + ".email@domain.com", "email.@domain.com", "email..email@domain.com", + "email@domain.com (Joe Smith)", "email@domain..com"}; + + for (String invalidEmail : invalidEmails) { + assertFalse(Person.validEmail(invalidEmail)); + } + + } + + /** + * Completed after the test. + * @throws Exception exception if person cannot be nullified + */ + @AfterEach + public void tearDown() throws Exception { + personName = null; + person1 = null; + person2 = null; + } +} diff --git a/test/edu/wright/cs/raiderplanner/model/RequirementTest.java b/test/edu/wright/cs/raiderplanner/model/RequirementTest.java new file mode 100644 index 00000000..1eae0c27 --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/model/RequirementTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import org.junit.jupiter.api.Test; + +/** + * Created by bijan on 06/05/2017. + */ +public class RequirementTest { + + @Test + public void isComplete() throws Exception { + } + + @Test + public void requirementProgress() throws Exception { + } + +} \ No newline at end of file diff --git a/test/edu/wright/cs/raiderplanner/model/RoomTest.java b/test/edu/wright/cs/raiderplanner/model/RoomTest.java new file mode 100644 index 00000000..656491b4 --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/model/RoomTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2018 - Adam Cone + * + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Created by Adam Cone 09/22/2018. + */ +public class RoomTest { + Building bd1; + Building bd2; + Room rm1; + Room rm2; + + /** + * This test case sets up the objects necessary for each tests. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @BeforeEach + public void setUp() throws Exception { + bd1 = new Building("CEG", 0.0, 0.0); + bd2 = new Building("MTH", 0.0, 0.0); + rm1 = new Room("321", bd1); + rm2 = new Room("123", bd2); + } + + /** + * This test case attempts to retrieve building object information. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @Test + public void getBuilding() throws Exception { + assertEquals(bd1, rm1.getBuilding()); + } + + /** + * This test case attempts to retrieve room object information. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @Test + public void getRoomNumber() throws Exception { + assertEquals("321", rm1.getRoomNumber()); + } + + /** + * This test case attempts to set building object information. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @Test + public void setBuilding() throws Exception { + rm1.setBuilding(bd2); + assertEquals(bd2, rm1.getBuilding()); + } + + /** + * This test case attempts to set room object information. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @Test + public void setRoomNumber() throws Exception { + rm1.setRoomNumber("foo"); + assertEquals("foo", rm1.getRoomNumber()); + } + + /** + * After each run, this removes all data to avoid interference with other tests. + * + * @throws Exception + * to handle when the Task objects cannot be accessed. + */ + @AfterEach + public void tearDown() throws Exception { + bd1 = null; + bd2 = null; + rm1 = null; + rm2 = null; + } +} \ No newline at end of file diff --git a/test/edu/wright/cs/raiderplanner/model/StudyPlannerTest.java b/test/edu/wright/cs/raiderplanner/model/StudyPlannerTest.java new file mode 100644 index 00000000..e9d7f163 --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/model/StudyPlannerTest.java @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import org.junit.jupiter.api.Test; + +/** + * Created by bijan on 06/05/2017. + */ +public class StudyPlannerTest { + + @Test + public void getListOfStudyProfiles() throws Exception { + } + + @Test + public void loadFile() throws Exception { + } + + @Test + public void processHubFile() throws Exception { + } + +} \ No newline at end of file diff --git a/test/edu/wright/cs/raiderplanner/model/StudyProfileTest.java b/test/edu/wright/cs/raiderplanner/model/StudyProfileTest.java new file mode 100644 index 00000000..9990f2a2 --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/model/StudyProfileTest.java @@ -0,0 +1,39 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import org.junit.jupiter.api.Test; + +/** + * Created by bijan on 06/05/2017. + */ +public class StudyProfileTest { + + @Test + public void milestonesCompleted() throws Exception { + } + + @Test + public void milestonesProgress() throws Exception { + } + +} \ No newline at end of file diff --git a/test/edu/wright/cs/raiderplanner/model/TaskTest.java b/test/edu/wright/cs/raiderplanner/model/TaskTest.java new file mode 100644 index 00000000..5a0140f7 --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/model/TaskTest.java @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import edu.wright.cs.raiderplanner.model.Task; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.time.LocalDate; + + +/** + * Created by bijan on 06/05/2017. + */ +public class TaskTest { + Task task; + Task task2; + + /** + * This test case should set up all of the objects necessary for the following tests in + * this suite. + * @throws Exception to handle when any of the required objects cannot be instantiated. + */ + @BeforeEach + public void setUp() throws Exception { + String name = "Do stuff"; + String details = "Do lots of stuff"; + LocalDate deadline = LocalDate.of(2017, 12, 12); + int weighting = 5; + String type = "Stuff"; + task = new Task(name, details, deadline, weighting, type); + + String name2 = "Do stuff 2"; + String details2 = "Do lots of stuff 2"; + LocalDate deadline2 = LocalDate.of(2017, 12, 13); + int weighting2 = 6; + String type2 = "Stuff"; + task2 = new Task(name2, details2, deadline2, weighting2, type2); + task.addDependency(task2); + } + + @Test + public void dependenciesComplete() throws Exception { + task2.setComplete(false); + assertEquals(false, task.dependenciesComplete()); + + task2.setComplete(true); + assertEquals(true, task.dependenciesComplete()); + } + + @Test + public void hasDependencies() throws Exception { + assertEquals(true, task.hasDependencies()); + + task.removeDependency(task2); + assertEquals(false, task.hasDependencies()); + } + + @Test + public void isComplete() throws Exception { + assertEquals(false, task.isCheckedComplete()); + + task.setComplete(true); + assertEquals(false, task.isCheckedComplete()); + + task2.setComplete(true); + task.setComplete(true); + assertEquals(true, task.isCheckedComplete()); + } + + @Test + public void canCheckComplete() throws Exception { + assertEquals(false, task.canCheckComplete()); + + task2.setComplete(true); + assertEquals(true, task.canCheckComplete()); + } + + /** + * After each run, this case removes all data from the Task objects to avoid + * interference with other test runs. + * @throws Exception to handle when the Task objects cannot be accessed. + */ + @AfterEach + public void tearDown() throws Exception { + task = null; + task2 = null; + } +} diff --git a/test/edu/wright/cs/raiderplanner/model/TimetableEventTest.java b/test/edu/wright/cs/raiderplanner/model/TimetableEventTest.java new file mode 100644 index 00000000..9bb1e85a --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/model/TimetableEventTest.java @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2018 - Adam Cone + * + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +/** + * Created by Adam Cone 11/14/2018. + */ +public class TimetableEventTest { + + private String date; + private Room room; + private Person lecturer; + private TimeTableEventType timeTableEventType; + private int duration; + private TimetableEvent timetableEventTest; + + /** + * This test case sets up the objects necessary for each tests. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @BeforeEach + public void setUp() throws Exception { + date = "11/14/2018"; + room = new Room("152"); + lecturer = new Person("Mr.", "Adam Cone", true, "adam@wright.edu"); + timeTableEventType = new TimeTableEventType(); + duration = 60; + timetableEventTest = new TimetableEvent(date, room, lecturer, timeTableEventType, duration); + } + + /** + * This test case attempts to retrieve room information. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @Test + public void testGetRoom() throws Exception { + assertEquals(this.room, timetableEventTest.getRoom()); + } + + /** + * This test case attempts to retrieve lecturer information. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @Test + public void testGetLecturer() throws Exception { + assertEquals(this.lecturer, timetableEventTest.getLecturer()); + } + + /** + * This test case attempts to retrieve timeTableEventType information. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @Test + public void testGetTimeTableEventType() throws Exception { + assertEquals(this.timeTableEventType, timetableEventTest.getTimeTableEventType()); + } + + /** + * This test case attempts to set room information. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @Test + public void testSetRoom() throws Exception { + Room newRoom = new Room("300"); + timetableEventTest.setRoom(newRoom); + assertEquals(newRoom, timetableEventTest.getRoom()); + } + + /** + * This test case attempts to set lecturer information. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @Test + public void testSetLecturer() throws Exception { + Person newLecturer = new Person("Mr.", "Dam Done", true, "dam@wright.edu"); + timetableEventTest.setLecturer(newLecturer); + assertEquals(newLecturer, timetableEventTest.getLecturer()); + } + + /** + * This test case attempts to set timeTableEventType information. + * + * @throws Exception + * to handle when any of the required objects cannot be instantiated. + */ + @Test + public void testSetTimeTableEventType() throws Exception { + TimeTableEventType newTimeTableEventType = new TimeTableEventType(); + timetableEventTest.setTimeTableEventType(newTimeTableEventType); + assertEquals(newTimeTableEventType, timetableEventTest.getTimeTableEventType()); + } + + /** + * After each run, this removes all data to avoid interference with other tests. + * + * @throws Exception + * to handle when the Task objects cannot be accessed. + */ + @AfterEach + public void tearDown() throws Exception { + date = null; + room = null; + lecturer = null; + timeTableEventType = null; + duration = 0; + timetableEventTest = null; + } +} \ No newline at end of file diff --git a/test/edu/wright/cs/raiderplanner/model/VersionControlEntityTest.java b/test/edu/wright/cs/raiderplanner/model/VersionControlEntityTest.java new file mode 100644 index 00000000..dfa7a8ca --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/model/VersionControlEntityTest.java @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.model; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import edu.wright.cs.raiderplanner.controller.MainController; +import edu.wright.cs.raiderplanner.model.VersionControlEntity; +import javafx.stage.Stage; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Disabled; +import org.junit.jupiter.api.Test; + +import org.testfx.framework.junit.ApplicationTest; + +/** + * Created by bijan on 06/05/2017. + */ +@Disabled +public class VersionControlEntityTest extends ApplicationTest { + + VersionControlEntity versionControlEntity; + ExamEvent examEvent; + + /** + * This test case create a new instance of the VersionControlEntity. + * @throws Exception if the VersionControlEntity cannot be created. + */ + @BeforeEach + public void setUp() throws Exception { + Person person = new Person("Mr.", "Greene", true); + versionControlEntity = new Exam(4, person, person, person, 4, examEvent); + } + + + @Override + public void start(Stage stage) throws Exception { + MainController.initialise(); + MainController.isNumeric("23"); + } + + @Test + public void findAndUpdate() throws Exception { + assertFalse(VersionControlEntity.findAndUpdate(versionControlEntity)); + + versionControlEntity.setUid("99856-ID"); + assertTrue(VersionControlEntity.findAndUpdate(versionControlEntity)); + } + + @Test + public void inLibrary() throws Exception { + versionControlEntity.setUid("1234-ID"); + assertTrue(VersionControlEntity.inLibrary("1234-ID")); + + assertFalse(VersionControlEntity.inLibrary("10132-ID")); + versionControlEntity.setUid("10132-ID"); + assertTrue(VersionControlEntity.inLibrary("10132-ID")); + } + + @Test + public void getVersion() throws Exception { + assertEquals(0, versionControlEntity.getVersion()); + } + + @Test + public void getUid() throws Exception { + assertEquals(null, versionControlEntity.getUid()); + } + + @Test + public void setUid() throws Exception { + // Testing setUid with one argument + versionControlEntity.setUid("1234-ID"); + assertEquals("1234-ID", versionControlEntity.getUid()); + + // Testing the duplication + versionControlEntity.setUid("1234-ID"); + + // Testing setUid with two argument + assertEquals(true, versionControlEntity.setUid("95657-ID",1)); + assertEquals("95657-ID", versionControlEntity.getUid()); + assertEquals(1, versionControlEntity.getVersion()); + + // Testing the duplication +// assertFalse(versionControlEntity.setUid("5678-ID", 2)); + } + + /** + * This test case removes the data from the VersionControlEntity object to avoid + * interference with future test runs. + * @throws Exception if the VersionControlEntity cannot be accessed. + */ + @AfterEach + public void tearDown() throws Exception { + versionControlEntity = null; + } +} diff --git a/test/edu/wright/cs/raiderplanner/view/CreateAccountUiTest.java b/test/edu/wright/cs/raiderplanner/view/CreateAccountUiTest.java new file mode 100644 index 00000000..1d71a437 --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/view/CreateAccountUiTest.java @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.view; + +import static org.testfx.api.FxAssert.verifyThat; +import static org.testfx.matcher.base.NodeMatchers.isDisabled; +import static org.testfx.matcher.base.NodeMatchers.isEnabled; + +import javafx.scene.control.Button; +import javafx.scene.control.TextField; + +import org.junit.jupiter.api.Disabled; +// import org.junit.jupiter.api.Test; + +/** + * Unit test that exercises the GUI window for creating new accounts. + * + * @author Team BRONZE + */ +public class CreateAccountUiTest extends FxBase { + /** + * WIP: This test case should enter sample data into the CreateAccountUi dialog and + * submit it. Currently, this test causes the build to crash, so it is + * disabled. Once resolved, change the tag from @Disabled to @Test. + */ + @Disabled + public void testTextField() { + final TextField t1 = find("#salutation"); + final TextField t2 = find("#fullName"); + final TextField t3 = find("#accountNo"); + final TextField t4 = find("#email"); + final Button b1 = find("#submit"); + + clickOn(t1).write("Mr"); + clickOn(t2).write("Andrei Odintsov"); + clickOn(t3).write("100125464"); + clickOn(t4).write("a1covker@gmail.com"); + + verifyThat(b1, isEnabled()); + + } + + /** + * WIP: This test case should verify that the regular expressions for each of the input + * fields works correctly. If so, the form should not allow the user to submit. In + * other words, the submit button should be disabled. Currently, this test causes + * the build to crash. When resolved, replace the @Disabled tag with @Test. + */ + @Disabled + public void testTextFieldRegex() { + final TextField t1 = find("#salutation"); + final TextField t2 = find("#fullName"); + final TextField t3 = find("#accountNo"); + final TextField t4 = find("#email"); + final Button b1 = find("#submit"); + + clickOn(t1).write("Mr"); + clickOn(t2).write("Andrei Odintsov"); + clickOn(t3).write("100125464"); + clickOn(t4).write("somethingnotemail"); + + verifyThat(b1, isDisabled()); + + } + +} \ No newline at end of file diff --git a/test/edu/wright/cs/raiderplanner/view/FxBase.java b/test/edu/wright/cs/raiderplanner/view/FxBase.java new file mode 100644 index 00000000..982e4822 --- /dev/null +++ b/test/edu/wright/cs/raiderplanner/view/FxBase.java @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2017 - Benjamin Dickson, Andrew Odintsov, Zilvinas Ceikauskas, + * Bijan Ghasemi Afshar + * + * + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +package edu.wright.cs.raiderplanner.view; + +import edu.wright.cs.raiderplanner.controller.AccountController; + +import javafx.application.Platform; +import javafx.fxml.FXMLLoader; +import javafx.scene.Node; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.input.KeyCode; +import javafx.scene.input.MouseButton; +import javafx.stage.Stage; + +import org.junit.jupiter.api.AfterEach; +import org.testfx.api.FxToolkit; +import org.testfx.framework.junit.ApplicationTest; + +import java.util.concurrent.TimeoutException; + +/** + * PearPlanner/RaiderPlanner. + * Created by Team BRONZE on 08/05/2017 + */ +public abstract class FxBase extends ApplicationTest { + + @Override + public void start(Stage stage) throws Exception { + AccountController accountControl = new AccountController(); + FXMLLoader loader = new FXMLLoader(getClass().getResource("CreateAccount.fxml")); + loader.setController(accountControl); + Parent root = loader.load(); + + stage.setOnCloseRequest(e -> { + Platform.exit(); + System.exit(0); + }); + + Scene scene = new Scene(root, 550, 232); + + stage.setScene(scene); + + stage.show(); + } + + /** + * This test should handle the cleanup of the CreateAccountUi dialog after each run. + * @throws TimeoutException if the operation takes too long or hangs. + */ + @AfterEach + public void afterEachTest() throws TimeoutException { + FxToolkit.hideStage(); + release(new KeyCode[]{}); + release(new MouseButton[]{}); + } + + /** + * This method attempts to retrieve UI Node objects from the CreateAccountUi dialog. + * @param query the Node we want to retrieve. + * @return the requested Node. + */ + // Suppressing because converting to type T should work with all objects of Node. + @SuppressWarnings("unchecked") + public T find(final String query) { + return (T) lookup(query).queryAll().iterator().next(); + } + +} diff --git a/user-guide/images/0.jpg b/user-guide/images/0.jpg new file mode 100644 index 00000000..ebedba30 Binary files /dev/null and b/user-guide/images/0.jpg differ diff --git a/user-guide/images/1.jpg b/user-guide/images/1.jpg new file mode 100644 index 00000000..5c76557c Binary files /dev/null and b/user-guide/images/1.jpg differ diff --git a/user-guide/images/10.jpg b/user-guide/images/10.jpg new file mode 100644 index 00000000..6655e2e8 Binary files /dev/null and b/user-guide/images/10.jpg differ diff --git a/user-guide/images/11.jpg b/user-guide/images/11.jpg new file mode 100644 index 00000000..08b031bf Binary files /dev/null and b/user-guide/images/11.jpg differ diff --git a/user-guide/images/12.jpg b/user-guide/images/12.jpg new file mode 100644 index 00000000..07d46dc3 Binary files /dev/null and b/user-guide/images/12.jpg differ diff --git a/user-guide/images/13.jpg b/user-guide/images/13.jpg new file mode 100644 index 00000000..cccf59e2 Binary files /dev/null and b/user-guide/images/13.jpg differ diff --git a/user-guide/images/14.jpg b/user-guide/images/14.jpg new file mode 100644 index 00000000..1c2ddcc0 Binary files /dev/null and b/user-guide/images/14.jpg differ diff --git a/user-guide/images/15.jpg b/user-guide/images/15.jpg new file mode 100644 index 00000000..1cd545c4 Binary files /dev/null and b/user-guide/images/15.jpg differ diff --git a/user-guide/images/16.jpg b/user-guide/images/16.jpg new file mode 100644 index 00000000..11938b23 Binary files /dev/null and b/user-guide/images/16.jpg differ diff --git a/user-guide/images/17.jpg b/user-guide/images/17.jpg new file mode 100644 index 00000000..7f05e2f4 Binary files /dev/null and b/user-guide/images/17.jpg differ diff --git a/user-guide/images/18.jpg b/user-guide/images/18.jpg new file mode 100644 index 00000000..48d85223 Binary files /dev/null and b/user-guide/images/18.jpg differ diff --git a/user-guide/images/19.jpg b/user-guide/images/19.jpg new file mode 100644 index 00000000..f5ef5a19 Binary files /dev/null and b/user-guide/images/19.jpg differ diff --git a/user-guide/images/2.jpg b/user-guide/images/2.jpg new file mode 100644 index 00000000..3ebb9509 Binary files /dev/null and b/user-guide/images/2.jpg differ diff --git a/user-guide/images/20.jpg b/user-guide/images/20.jpg new file mode 100644 index 00000000..955577a2 Binary files /dev/null and b/user-guide/images/20.jpg differ diff --git a/user-guide/images/3.jpg b/user-guide/images/3.jpg new file mode 100644 index 00000000..fdb704f9 Binary files /dev/null and b/user-guide/images/3.jpg differ diff --git a/user-guide/images/4.jpg b/user-guide/images/4.jpg new file mode 100644 index 00000000..70d5790a Binary files /dev/null and b/user-guide/images/4.jpg differ diff --git a/user-guide/images/5.jpg b/user-guide/images/5.jpg new file mode 100644 index 00000000..640d5841 Binary files /dev/null and b/user-guide/images/5.jpg differ diff --git a/user-guide/images/6.jpg b/user-guide/images/6.jpg new file mode 100644 index 00000000..8d352a44 Binary files /dev/null and b/user-guide/images/6.jpg differ diff --git a/user-guide/images/7.jpg b/user-guide/images/7.jpg new file mode 100644 index 00000000..92e57625 Binary files /dev/null and b/user-guide/images/7.jpg differ diff --git a/user-guide/images/8.jpg b/user-guide/images/8.jpg new file mode 100644 index 00000000..a014c95f Binary files /dev/null and b/user-guide/images/8.jpg differ diff --git a/user-guide/images/9.jpg b/user-guide/images/9.jpg new file mode 100644 index 00000000..51ae0e4d Binary files /dev/null and b/user-guide/images/9.jpg differ diff --git a/user-guide/images/RaiderPlannerUML.png b/user-guide/images/RaiderPlannerUML.png new file mode 100644 index 00000000..4ecbf5a1 Binary files /dev/null and b/user-guide/images/RaiderPlannerUML.png differ diff --git a/user-guide/images/RaiderPlanner_ControllerPackageUML.png b/user-guide/images/RaiderPlanner_ControllerPackageUML.png new file mode 100644 index 00000000..5777dfd6 Binary files /dev/null and b/user-guide/images/RaiderPlanner_ControllerPackageUML.png differ diff --git a/user-guide/images/RaiderPlanner_ModelPackageUML.png b/user-guide/images/RaiderPlanner_ModelPackageUML.png new file mode 100644 index 00000000..b528dde8 Binary files /dev/null and b/user-guide/images/RaiderPlanner_ModelPackageUML.png differ diff --git a/user-guide/images/RaiderPlanner_ViewPackageUML.png b/user-guide/images/RaiderPlanner_ViewPackageUML.png new file mode 100644 index 00000000..a67ddfcc Binary files /dev/null and b/user-guide/images/RaiderPlanner_ViewPackageUML.png differ diff --git a/user-guide/userguide.rst b/user-guide/userguide.rst new file mode 100644 index 00000000..ff10a71e --- /dev/null +++ b/user-guide/userguide.rst @@ -0,0 +1,73 @@ +RaiderPlanner User Guide +======================== + +Getting Started +--------------- +When first opening the application, you will see the following dialogue box – select “New File” +..image:: 1.jpg + +You will be asked to create an account – select “OK” when you’ve filled out the information +..image:: 2.jpg + +The following dialogue box will appear - insert a name for your planner file +..image:: 3.jpg + + +Importing a HUB file +-------------------- +Next, you will need to import a HUB file. Click on the icon in the upper left-hand corner that looks like three white bars. Select “Import HUB file” +..image:: 4.jpg + +Select the appropriate .xml file, then “Open” +..image:: 5.jpg + +If your file was imported successfully, you will receive the following message +..image:: 6.jpg + +Congratulations, you’re now ready to begin using the RaiderPlanner! + +Navigating the RaiderPlanner - Left Side +---------------------------------------- +To see the different options for using the RaiderPlanner, click on the 3 white bars in the upper left corner +..image:: 7.jpg + +To return to the main menu, select the Student Dashboard +..image:: 8.jpg + +To edit student goals, open Milestones +..image:: 9.jpg + +In Milestones, users can see how they are progressing +..image:: 10.jpg + +To edit student course information, open Modules +..image:: 11.jpg + +In Modules, users can enter relevant course information +..image:: 12.jpg + +Under Study Profile, you can see information about your profile +..image:: 13.jpg + +Here you can see information related to name, year and semester +..image:: 14.jpg + +Selecting Help will open the RaiderPlanner website, where you can find more information about the RaiderPlanner +..image:: 15.jpg + +To chat with another user, select chat +..image:: 16.jpg + +To chat, you'll have to enter the name of the person and the host +..image:: 17.jpg + +Navigating the RaiderPlanner - Right Side +----------------------------------------- +To see notifications, select the mail icon in the upper right hand corner of the RaiderPlanner +..image:: 18.jpg + +Select the calendar icon to open the calendar +..image:: 19.jpg + +To add an activity, select the + icon +..image:: 20.jpg \ No newline at end of file