diff options
560 files changed, 69392 insertions, 21267 deletions
diff --git a/.classpath b/.classpath new file mode 100644 index 0000000000..381f0d280d --- /dev/null +++ b/.classpath @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="server/tests/src"/> + <classpathentry kind="src" path="client/tests/src"/> + <classpathentry kind="src" path="theme-compiler/tests/src"/> + <classpathentry kind="src" path="theme-compiler/src"/> + <classpathentry kind="src" path="theme-compiler/tests/resources"/> + <classpathentry kind="src" path="client/src"/> + <classpathentry kind="src" path="server/src"/> + <classpathentry kind="src" path="client-compiler/src"/> + <classpathentry kind="src" path="uitest/src"/> + <classpathentry kind="src" path="buildhelpers/src"/> + <classpathentry kind="src" path="shared/src"/> + <classpathentry kind="src" path="push/src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"> + <attributes> + <attribute name="owner.project.facets" value="java"/> + </attributes> + </classpathentry> + <classpathentry exported="true" kind="con" path="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=vaadin&ivyXmlPath=client%2Fivy.xml&confs=ide&ivySettingsPath=%24%7Bworkspace_loc%3Avaadin%2Fivysettings.xml%7D&loadSettingsOnDemand=false&propertyFiles="/> + <classpathentry exported="true" kind="con" path="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=vaadin&ivyXmlPath=server%2Fivy.xml&confs=ide&ivySettingsPath=%24%7Bworkspace_loc%3Avaadin%2Fivysettings.xml%7D&loadSettingsOnDemand=false&propertyFiles="/> + <classpathentry exported="true" kind="con" path="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=vaadin&ivyXmlPath=shared%2Fivy.xml&confs=ide&ivySettingsPath=%24%7Bworkspace_loc%3Avaadin%2Fivysettings.xml%7D&loadSettingsOnDemand=false&propertyFiles="/> + <classpathentry exported="true" kind="con" path="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=vaadin&ivyXmlPath=client-compiler%2Fivy.xml&confs=ide&ivySettingsPath=%24%7Bworkspace_loc%3Avaadin%2Fivysettings.xml%7D&loadSettingsOnDemand=false&propertyFiles="/> + <classpathentry exported="true" kind="con" path="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=vaadin&ivyXmlPath=theme-compiler%2Fivy.xml&confs=ide&ivySettingsPath=%24%7Bworkspace_loc%3Avaadin%2Fivysettings.xml%7D&loadSettingsOnDemand=false&propertyFiles="/> + <classpathentry exported="true" kind="con" path="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=vaadin&ivyXmlPath=uitest%2Fivy.xml&confs=ide&ivySettingsPath=%24%7Bworkspace_loc%3Avaadin%2Fivysettings.xml%7D&loadSettingsOnDemand=false&propertyFiles="/> + <classpathentry exported="true" kind="con" path="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=vaadin&ivyXmlPath=push%2Fivy.xml&confs=ide&ivySettingsPath=%24%7Bworkspace_loc%3Avaadin%2Fivysettings.xml%7D&loadSettingsOnDemand=false&propertyFiles="/> + <classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.web.container"/> + <classpathentry kind="con" path="org.eclipse.jst.j2ee.internal.module.container"/> + <classpathentry combineaccessrules="false" kind="src" path="/gwt-dev"/> + <classpathentry combineaccessrules="false" kind="src" path="/gwt-user"/> + <classpathentry kind="output" path="build/classes"/> +</classpath> diff --git a/.gitignore b/.gitignore index d2236fafb0..d5899b3d58 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,8 @@ /WebContent/VAADIN/widgetsets /WebContent/VAADIN/gwt-unitCache* +WebContent/VAADIN/vaadinPush.js + # /WebContent/WEB-INF/ /WebContent/WEB-INF/classes diff --git a/.project b/.project new file mode 100644 index 0000000000..a0a79fbcec --- /dev/null +++ b/.project @@ -0,0 +1,31 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>vaadin</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.wst.common.project.facet.core.builder</name> + <arguments> + </arguments> + </buildCommand> + <buildCommand> + <name>org.eclipse.wst.validation.validationbuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jem.workbench.JavaEMFNature</nature> + <nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature> + <nature>org.eclipse.wst.common.project.facet.core.nature</nature> + <nature>org.eclipse.jdt.core.javanature</nature> + <nature>org.apache.ivyde.eclipse.ivynature</nature> + </natures> +</projectDescription> diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 0000000000..99f26c0203 --- /dev/null +++ b/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding/<project>=UTF-8 diff --git a/.settings/org.eclipse.core.runtime.prefs b/.settings/org.eclipse.core.runtime.prefs new file mode 100644 index 0000000000..5a0ad22d2a --- /dev/null +++ b/.settings/org.eclipse.core.runtime.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +line.separator=\n diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000000..ecafda3a05 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,291 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.6 +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.6 +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_method_declaration=0 +org.eclipse.jdt.core.formatter.alignment_for_multiple_fields=16 +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_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_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.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=80 +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=8 +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=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_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=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_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_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=80 +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.put_empty_statement_on_new_line=true +org.eclipse.jdt.core.formatter.tabulation.char=space +org.eclipse.jdt.core.formatter.tabulation.size=4 +org.eclipse.jdt.core.formatter.use_on_off_tags=true +org.eclipse.jdt.core.formatter.use_tabs_only_for_leading_indentations=false +org.eclipse.jdt.core.formatter.wrap_before_binary_operator=true +org.eclipse.jdt.core.formatter.wrap_before_or_operator_multicatch=true +org.eclipse.jdt.core.formatter.wrap_outer_expressions_when_nested=true diff --git a/.settings/org.eclipse.jdt.ui.prefs b/.settings/org.eclipse.jdt.ui.prefs new file mode 100644 index 0000000000..31240a63bd --- /dev/null +++ b/.settings/org.eclipse.jdt.ui.prefs @@ -0,0 +1,58 @@ +eclipse.preferences.version=1 +editor_save_participant_org.eclipse.jdt.ui.postsavelistener.cleanup=true +formatter_profile=_Vaadin Java Conventions 20110923 +formatter_settings_version=12 +org.eclipse.jdt.ui.javadoc=true +org.eclipse.jdt.ui.text.custom_code_templates=<?xml version\="1.0" encoding\="UTF-8" standalone\="no"?><templates><template autoinsert\="true" context\="gettercomment_context" deleted\="false" description\="Comment for getter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.gettercomment" name\="gettercomment">/**\n * @return the ${bare_field_name}\n */</template><template autoinsert\="true" context\="settercomment_context" deleted\="false" description\="Comment for setter method" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.settercomment" name\="settercomment">/**\n * @param ${param} the ${bare_field_name} to set\n */</template><template autoinsert\="true" context\="constructorcomment_context" deleted\="false" description\="Comment for created constructors" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorcomment" name\="constructorcomment">/**\n * ${tags}\n */</template><template autoinsert\="false" context\="filecomment_context" deleted\="false" description\="Comment for created Java files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.filecomment" name\="filecomment">/**\n * \n */</template><template autoinsert\="false" context\="typecomment_context" deleted\="false" description\="Comment for created types" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.typecomment" name\="typecomment">/**\n * \n * @since \n * @author Vaadin Ltd\n * ${tags}\n */</template><template autoinsert\="true" context\="fieldcomment_context" deleted\="false" description\="Comment for fields" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.fieldcomment" name\="fieldcomment">/**\n * \n */</template><template autoinsert\="false" context\="methodcomment_context" deleted\="false" description\="Comment for non-overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodcomment" name\="methodcomment">/**\n * @since\n * ${tags}\n */</template><template autoinsert\="true" context\="overridecomment_context" deleted\="false" description\="Comment for overriding methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.overridecomment" name\="overridecomment">/* (non-Javadoc)\n * ${see_to_overridden}\n */</template><template autoinsert\="true" context\="delegatecomment_context" deleted\="false" description\="Comment for delegate methods" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.delegatecomment" name\="delegatecomment">/**\n * ${tags}\n * ${see_to_target}\n */</template><template autoinsert\="false" context\="newtype_context" deleted\="false" description\="Newly created files" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.newtype" name\="newtype">/*\n * Copyright 2000-2013 Vaadin Ltd.\n * \n * Licensed under the Apache License, Version 2.0 (the "License"); you may not\n * use this file except in compliance with the License. You may obtain a copy of\n * the License at\n * \n * http\://www.apache.org/licenses/LICENSE-2.0\n * \n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT\n * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n * License for the specific language governing permissions and limitations under\n * the License.\n */\n\n${filecomment}\n${package_declaration}\n\n${typecomment}\n${type_declaration}</template><template autoinsert\="true" context\="classbody_context" deleted\="false" description\="Code in new class type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.classbody" name\="classbody">\n</template><template autoinsert\="true" context\="interfacebody_context" deleted\="false" description\="Code in new interface type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.interfacebody" name\="interfacebody">\n</template><template autoinsert\="true" context\="enumbody_context" deleted\="false" description\="Code in new enum type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.enumbody" name\="enumbody">\n</template><template autoinsert\="true" context\="annotationbody_context" deleted\="false" description\="Code in new annotation type bodies" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.annotationbody" name\="annotationbody">\n</template><template autoinsert\="true" context\="catchblock_context" deleted\="false" description\="Code in new catch blocks" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.catchblock" name\="catchblock">// ${todo} Auto-generated catch block\n${exception_var}.printStackTrace();</template><template autoinsert\="true" context\="methodbody_context" deleted\="false" description\="Code in created method stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.methodbody" name\="methodbody">// ${todo} Auto-generated method stub\n${body_statement}</template><template autoinsert\="true" context\="constructorbody_context" deleted\="false" description\="Code in created constructor stubs" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.constructorbody" name\="constructorbody">${body_statement}\n// ${todo} Auto-generated constructor stub</template><template autoinsert\="true" context\="getterbody_context" deleted\="false" description\="Code in created getters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.getterbody" name\="getterbody">return ${field};</template><template autoinsert\="true" context\="setterbody_context" deleted\="false" description\="Code in created setters" enabled\="true" id\="org.eclipse.jdt.ui.text.codetemplates.setterbody" name\="setterbody">${field} \= ${param};</template></templates> +sp_cleanup.add_default_serial_version_id=true +sp_cleanup.add_generated_serial_version_id=false +sp_cleanup.add_missing_annotations=true +sp_cleanup.add_missing_deprecated_annotations=true +sp_cleanup.add_missing_methods=false +sp_cleanup.add_missing_nls_tags=false +sp_cleanup.add_missing_override_annotations=true +sp_cleanup.add_missing_override_annotations_interface_methods=true +sp_cleanup.add_serial_version_id=false +sp_cleanup.always_use_blocks=true +sp_cleanup.always_use_parentheses_in_expressions=false +sp_cleanup.always_use_this_for_non_static_field_access=false +sp_cleanup.always_use_this_for_non_static_method_access=false +sp_cleanup.convert_to_enhanced_for_loop=false +sp_cleanup.correct_indentation=false +sp_cleanup.format_source_code=true +sp_cleanup.format_source_code_changes_only=false +sp_cleanup.make_local_variable_final=false +sp_cleanup.make_parameters_final=false +sp_cleanup.make_private_fields_final=true +sp_cleanup.make_type_abstract_if_missing_method=false +sp_cleanup.make_variable_declarations_final=false +sp_cleanup.never_use_blocks=false +sp_cleanup.never_use_parentheses_in_expressions=true +sp_cleanup.on_save_use_additional_actions=true +sp_cleanup.organize_imports=true +sp_cleanup.qualify_static_field_accesses_with_declaring_class=false +sp_cleanup.qualify_static_member_accesses_through_instances_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_through_subtypes_with_declaring_class=true +sp_cleanup.qualify_static_member_accesses_with_declaring_class=false +sp_cleanup.qualify_static_method_accesses_with_declaring_class=false +sp_cleanup.remove_private_constructors=true +sp_cleanup.remove_trailing_whitespaces=true +sp_cleanup.remove_trailing_whitespaces_all=true +sp_cleanup.remove_trailing_whitespaces_ignore_empty=false +sp_cleanup.remove_unnecessary_casts=true +sp_cleanup.remove_unnecessary_nls_tags=false +sp_cleanup.remove_unused_imports=true +sp_cleanup.remove_unused_local_variables=false +sp_cleanup.remove_unused_private_fields=true +sp_cleanup.remove_unused_private_members=false +sp_cleanup.remove_unused_private_methods=true +sp_cleanup.remove_unused_private_types=true +sp_cleanup.sort_members=false +sp_cleanup.sort_members_all=false +sp_cleanup.use_blocks=true +sp_cleanup.use_blocks_only_for_return_and_throw=false +sp_cleanup.use_parentheses_in_expressions=false +sp_cleanup.use_this_for_non_static_field_access=true +sp_cleanup.use_this_for_non_static_field_access_only_if_necessary=true +sp_cleanup.use_this_for_non_static_method_access=true +sp_cleanup.use_this_for_non_static_method_access_only_if_necessary=true diff --git a/.settings/org.eclipse.wst.validation.prefs b/.settings/org.eclipse.wst.validation.prefs new file mode 100644 index 0000000000..9e3833bbc9 --- /dev/null +++ b/.settings/org.eclipse.wst.validation.prefs @@ -0,0 +1,10 @@ +DELEGATES_PREFERENCE=delegateValidatorList +USER_BUILD_PREFERENCE=enabledBuildValidatorList +USER_MANUAL_PREFERENCE=enabledManualValidatorList +USER_PREFERENCE=overrideGlobalPreferencestruedisableAllValidationfalseversion1.2.401.v201209052200 +eclipse.preferences.version=1 +override=true +suspend=false +vals/org.eclipse.wst.html.core.HTMLValidator/global=TF01 +vals/org.eclipse.wst.jsdt.web.core.JsBatchValidator/global=TF02 +vf.version=3 diff --git a/README.md b/README.md new file mode 100644 index 0000000000..5b56333cdb --- /dev/null +++ b/README.md @@ -0,0 +1,100 @@ +Cloning the project repositories +====== + +Vaadin 7 consists of three separate repositories +* https://github.com/vaadin/vaadin.git +* https://github.com/vaadin/gwt.git +* https://github.com/vaadin/gwt-tools.git + +Start by cloning these repositories into the same folder: +<pre><code>git clone https://github.com/vaadin/vaadin.git +git clone https://github.com/vaadin/gwt.git +git clone https://github.com/vaadin/gwt-tools.git</code></pre> + +The *vaadin* and *gwt* repositories contain project code. The *gwt-tools* project only contain dependency jars used by the other projects. + +Do not rename the repositories as the rest of this document relies on using the standard naming. + +Setting up Eclipse to Develop Vaadin 7 +========= +Assuming you have cloned the repositories as described in “Cloning the project repositories” above, you can import the *vaadin* and *gwt* projects into Eclipse as follows: + +Start Eclipse +------------- +Start Eclipse and use the root checkout folder (the one containing the *vaadin*, *gwt* and *gwt-tools* folders) as the workspace folder + +Define Required Variables for the GWT Eclipse Projects +-------- +To be able to find all files, the GWT project requires you to define a couple of variables: + +1. Open *Window* -> *Preferences* (Windows) or *Eclipse* -> *Preferences* (Mac) +1. Go to *General* -> *Workspace* -> *Linked Resources* +1. Add a new Path Variable **GWT_ROOT** referring to the gwt folder containing the gwt project + +1. Go to *Java* -> *Build Path* -> *Classpath Variables* +1. Add two new variables + 1. GWT_TOOLS referring to the gwt-tools folder containing the dependency jars + 1. JDK_HOME referring to your jdk installation directory +  +1. Go to Java -> Compiler + 1. Check that the compliance level has been set to 1.6 (or higher) + +Import the Projects into the Workspace +------------ +1. Do *File* -> *Import* -> *General* -> *Existing Projects into Workspace* + +1. Select the workspace folder as root directory +1. Click “deselect all” and select + 1. gwt-dev + 2. gwt-user +1. Click “finish” to complete the import of GWT +1. Then repeat by doing *File* -> *Import* -> *General* -> *Existing Projects into Workspace* +1. Select the workspace folder as root directory +1. Click “deselect all” and select + 1. vaadin +1. Click “finish” to complete the import of Vaadin Framework + + + +You should now have three projects in your workspace. If the vaadin project does not compile without errors, choose *Ivy* -> *Resolve* from the vaadin project popup menu. Now all projects should compile without errors (there might be warnings). + +Note that the first compilation takes a while to finish as Ivy downloads dependencies used in the projects. + +Compiling the Default Widget Set and Themes +-------- +Compile the default widget set by executing the default target in build/ide.xml in the vaadin project. +In Eclipse this is done by opening build/ide.xml, right clicking on it and choosing *Run As* -> *Ant Build*. + + +Running a UI test +------ +The *vaadin* project includes an embedded Jetty which is used for running the UI tests. +It is a standard Java application: *com.vaadin.launcher.DevelopmentServerLauncher*. +Launch it in debug mode in Eclipse by right clicking on it and selecting *Debug As* -> *Java Application*. + +This launches a Jetty on port 8888 which allows you to run any UI class in the project by opening http://localhost:8888/run/<UI class name>?restartApplication in your browser, e.g. [http://localhost:8888/run/com.vaadin.tests.components.label.LabelModes?restartApplication](http://localhost:8888/run/com.vaadin.tests.components.label.LabelModes?restartApplication) (Add ?restartApplication to ensure). + +Running JUnit tests +===== +The JUnit tests for the projects can be run using +<pre><code>ant test</code></pre> + +Running this in the *gwt* directory will run the GWT JUnit tests. +Running it in the *vaadin* directory will run the Vaadin JUnit tests. + +Running the Vaadin TestBench tests currently requires access to a correctly configured TestBench 2 cluster, only available inside Vaadin. + +Building a package +===== +The distribution files can be built in a few steps. First build the *gwt* project by running +<pre><code>ant</code></pre> +in the *gwt* directory. The elemental package needs to be built separately: +<pre><code>ant elemental</code></pre> +Building the elemental package is not possible on Windows as it requires gcc. + +Move to the *vaadin* project directory and unpack the previously built gwt jars +<pre><code>ant -f gwt-files.xml unpack.gwt</code></pre> +Then build the *vaadin* project by running +<pre><code>ant</code></pre> +in the *vaadin* directory. + diff --git a/WebContent/VAADIN/jquery-1.7.2.js b/WebContent/VAADIN/jquery-1.7.2.js new file mode 100644 index 0000000000..3774ff9861 --- /dev/null +++ b/WebContent/VAADIN/jquery-1.7.2.js @@ -0,0 +1,9404 @@ +/*! + * jQuery JavaScript Library v1.7.2 + * http://jquery.com/ + * + * Copyright 2011, John Resig + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * + * Date: Wed Mar 21 12:46:34 2012 -0700 + */ +(function( window, undefined ) { + +// Use the correct document accordingly with window argument (sandbox) +var document = window.document, + navigator = window.navigator, + location = window.location; +var jQuery = (function() { + +// Define a local copy of jQuery +var jQuery = function( selector, context ) { + // The jQuery object is actually just the init constructor 'enhanced' + return new jQuery.fn.init( selector, context, rootjQuery ); + }, + + // Map over jQuery in case of overwrite + _jQuery = window.jQuery, + + // Map over the $ in case of overwrite + _$ = window.$, + + // A central reference to the root jQuery(document) + rootjQuery, + + // A simple way to check for HTML strings or ID strings + // Prioritize #id over <tag> to avoid XSS via location.hash (#9521) + quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/, + + // Check if a string has a non-whitespace character in it + rnotwhite = /\S/, + + // Used for trimming whitespace + trimLeft = /^\s+/, + trimRight = /\s+$/, + + // Match a standalone tag + rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/, + + // JSON RegExp + rvalidchars = /^[\],:{}\s]*$/, + rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, + rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, + rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g, + + // Useragent RegExp + rwebkit = /(webkit)[ \/]([\w.]+)/, + ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/, + rmsie = /(msie) ([\w.]+)/, + rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/, + + // Matches dashed string for camelizing + rdashAlpha = /-([a-z]|[0-9])/ig, + rmsPrefix = /^-ms-/, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return ( letter + "" ).toUpperCase(); + }, + + // Keep a UserAgent string for use with jQuery.browser + userAgent = navigator.userAgent, + + // For matching the engine and version of the browser + browserMatch, + + // The deferred used on DOM ready + readyList, + + // The ready event handler + DOMContentLoaded, + + // Save a reference to some core methods + toString = Object.prototype.toString, + hasOwn = Object.prototype.hasOwnProperty, + push = Array.prototype.push, + slice = Array.prototype.slice, + trim = String.prototype.trim, + indexOf = Array.prototype.indexOf, + + // [[Class]] -> type pairs + class2type = {}; + +jQuery.fn = jQuery.prototype = { + constructor: jQuery, + init: function( selector, context, rootjQuery ) { + var match, elem, ret, doc; + + // Handle $(""), $(null), or $(undefined) + if ( !selector ) { + return this; + } + + // Handle $(DOMElement) + if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + } + + // The body element only exists once, optimize finding it + if ( selector === "body" && !context && document.body ) { + this.context = document; + this[0] = document.body; + this.selector = selector; + this.length = 1; + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + // Are we dealing with HTML string or an ID? + if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = quickExpr.exec( selector ); + } + + // Verify a match, and that no context was specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + doc = ( context ? context.ownerDocument || context : document ); + + // If a single string is passed in and it's a single tag + // just do a createElement and skip the rest + ret = rsingleTag.exec( selector ); + + if ( ret ) { + if ( jQuery.isPlainObject( context ) ) { + selector = [ document.createElement( ret[1] ) ]; + jQuery.fn.attr.call( selector, context, true ); + + } else { + selector = [ doc.createElement( ret[1] ) ]; + } + + } else { + ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); + selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes; + } + + return jQuery.merge( this, selector ); + + // HANDLE: $("#id") + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[2] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return rootjQuery.ready( selector ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }, + + // Start with an empty selector + selector: "", + + // The current version of jQuery being used + jquery: "1.7.2", + + // The default length of a jQuery object is 0 + length: 0, + + // The number of elements contained in the matched element set + size: function() { + return this.length; + }, + + toArray: function() { + return slice.call( this, 0 ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num == null ? + + // Return a 'clean' array + this.toArray() : + + // Return just the object + ( num < 0 ? this[ this.length + num ] : this[ num ] ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems, name, selector ) { + // Build a new jQuery matched element set + var ret = this.constructor(); + + if ( jQuery.isArray( elems ) ) { + push.apply( ret, elems ); + + } else { + jQuery.merge( ret, elems ); + } + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + + ret.context = this.context; + + if ( name === "find" ) { + ret.selector = this.selector + ( this.selector ? " " : "" ) + selector; + } else if ( name ) { + ret.selector = this.selector + "." + name + "(" + selector + ")"; + } + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + // (You can seed the arguments with an array of args, but this is + // only used internally.) + each: function( callback, args ) { + return jQuery.each( this, callback, args ); + }, + + ready: function( fn ) { + // Attach the listeners + jQuery.bindReady(); + + // Add the callback + readyList.add( fn ); + + return this; + }, + + eq: function( i ) { + i = +i; + return i === -1 ? + this.slice( i ) : + this.slice( i, i + 1 ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ), + "slice", slice.call(arguments).join(",") ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); + }, + + end: function() { + return this.prevObject || this.constructor(null); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: [].sort, + splice: [].splice +}; + +// Give the init function the jQuery prototype for later instantiation +jQuery.fn.init.prototype = jQuery.fn; + +jQuery.extend = jQuery.fn.extend = function() { + var options, name, src, copy, copyIsArray, clone, + target = arguments[0] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + target = arguments[1] || {}; + // skip the boolean and the target + i = 2; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction(target) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( length === i ) { + target = this; + --i; + } + + for ( ; i < length; i++ ) { + // Only deal with non-null/undefined values + if ( (options = arguments[ i ]) != null ) { + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) { + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray(src) ? src : []; + + } else { + clone = src && jQuery.isPlainObject(src) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend({ + noConflict: function( deep ) { + if ( window.$ === jQuery ) { + window.$ = _$; + } + + if ( deep && window.jQuery === jQuery ) { + window.jQuery = _jQuery; + } + + return jQuery; + }, + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + // Either a released hold or an DOMready/load event and not yet ready + if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( !document.body ) { + return setTimeout( jQuery.ready, 1 ); + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.fireWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.trigger ) { + jQuery( document ).trigger( "ready" ).off( "ready" ); + } + } + }, + + bindReady: function() { + if ( readyList ) { + return; + } + + readyList = jQuery.Callbacks( "once memory" ); + + // Catch cases where $(document).ready() is called after the + // browser event has already occurred. + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + return setTimeout( jQuery.ready, 1 ); + } + + // Mozilla, Opera and webkit nightlies currently support this event + if ( document.addEventListener ) { + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", jQuery.ready, false ); + + // If IE event model is used + } else if ( document.attachEvent ) { + // ensure firing before onload, + // maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", DOMContentLoaded ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", jQuery.ready ); + + // If IE and not a frame + // continually check to see if the document is ready + var toplevel = false; + + try { + toplevel = window.frameElement == null; + } catch(e) {} + + if ( document.documentElement.doScroll && toplevel ) { + doScrollCheck(); + } + } + }, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type(obj) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type(obj) === "array"; + }, + + isWindow: function( obj ) { + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + return !isNaN( parseFloat(obj) ) && isFinite( obj ); + }, + + type: function( obj ) { + return obj == null ? + String( obj ) : + class2type[ toString.call(obj) ] || "object"; + }, + + isPlainObject: function( obj ) { + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call(obj, "constructor") && + !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) { + return false; + } + } catch ( e ) { + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + + var key; + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + isEmptyObject: function( obj ) { + for ( var name in obj ) { + return false; + } + return true; + }, + + error: function( msg ) { + throw new Error( msg ); + }, + + parseJSON: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + + // Make sure leading/trailing whitespace is removed (IE can't handle it) + data = jQuery.trim( data ); + + // Attempt to parse using the native JSON parser first + if ( window.JSON && window.JSON.parse ) { + return window.JSON.parse( data ); + } + + // Make sure the incoming data is actual JSON + // Logic borrowed from http://json.org/json2.js + if ( rvalidchars.test( data.replace( rvalidescape, "@" ) + .replace( rvalidtokens, "]" ) + .replace( rvalidbraces, "")) ) { + + return ( new Function( "return " + data ) )(); + + } + jQuery.error( "Invalid JSON: " + data ); + }, + + // Cross-browser xml parsing + parseXML: function( data ) { + if ( typeof data !== "string" || !data ) { + return null; + } + var xml, tmp; + try { + if ( window.DOMParser ) { // Standard + tmp = new DOMParser(); + xml = tmp.parseFromString( data , "text/xml" ); + } else { // IE + xml = new ActiveXObject( "Microsoft.XMLDOM" ); + xml.async = "false"; + xml.loadXML( data ); + } + } catch( e ) { + xml = undefined; + } + if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) { + jQuery.error( "Invalid XML: " + data ); + } + return xml; + }, + + noop: function() {}, + + // Evaluates a script in a global context + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && rnotwhite.test( data ) ) { + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); + }, + + // args is for internal usage only + each: function( object, callback, args ) { + var name, i = 0, + length = object.length, + isObj = length === undefined || jQuery.isFunction( object ); + + if ( args ) { + if ( isObj ) { + for ( name in object ) { + if ( callback.apply( object[ name ], args ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.apply( object[ i++ ], args ) === false ) { + break; + } + } + } + + // A special, fast, case for the most common use of each + } else { + if ( isObj ) { + for ( name in object ) { + if ( callback.call( object[ name ], name, object[ name ] ) === false ) { + break; + } + } + } else { + for ( ; i < length; ) { + if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) { + break; + } + } + } + } + + return object; + }, + + // Use native String.trim function wherever possible + trim: trim ? + function( text ) { + return text == null ? + "" : + trim.call( text ); + } : + + // Otherwise use our own trimming functionality + function( text ) { + return text == null ? + "" : + text.toString().replace( trimLeft, "" ).replace( trimRight, "" ); + }, + + // results is for internal usage only + makeArray: function( array, results ) { + var ret = results || []; + + if ( array != null ) { + // The window, strings (and functions) also have 'length' + // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930 + var type = jQuery.type( array ); + + if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) { + push.call( ret, array ); + } else { + jQuery.merge( ret, array ); + } + } + + return ret; + }, + + inArray: function( elem, array, i ) { + var len; + + if ( array ) { + if ( indexOf ) { + return indexOf.call( array, elem, i ); + } + + len = array.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + // Skip accessing in sparse arrays + if ( i in array && array[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var i = first.length, + j = 0; + + if ( typeof second.length === "number" ) { + for ( var l = second.length; j < l; j++ ) { + first[ i++ ] = second[ j ]; + } + + } else { + while ( second[j] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, inv ) { + var ret = [], retVal; + inv = !!inv; + + // Go through the array, only saving the items + // that pass the validator function + for ( var i = 0, length = elems.length; i < length; i++ ) { + retVal = !!callback( elems[ i ], i ); + if ( inv !== retVal ) { + ret.push( elems[ i ] ); + } + } + + return ret; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var value, key, ret = [], + i = 0, + length = elems.length, + // jquery objects are treated as arrays + isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ; + + // Go through the array, translating each of the items to their + if ( isArray ) { + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + + // Go through every key on the object, + } else { + for ( key in elems ) { + value = callback( elems[ key ], key, arg ); + + if ( value != null ) { + ret[ ret.length ] = value; + } + } + } + + // Flatten any nested arrays + return ret.concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + if ( typeof context === "string" ) { + var tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + var args = slice.call( arguments, 2 ), + proxy = function() { + return fn.apply( context, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++; + + return proxy; + }, + + // Mutifunctional method to get and set values to a collection + // The value/s can optionally be executed if it's a function + access: function( elems, fn, key, value, chainable, emptyGet, pass ) { + var exec, + bulk = key == null, + i = 0, + length = elems.length; + + // Sets many values + if ( key && typeof key === "object" ) { + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], 1, emptyGet, value ); + } + chainable = 1; + + // Sets one value + } else if ( value !== undefined ) { + // Optionally, function values get executed if exec is true + exec = pass === undefined && jQuery.isFunction( value ); + + if ( bulk ) { + // Bulk operations only iterate when executing function values + if ( exec ) { + exec = fn; + fn = function( elem, key, value ) { + return exec.call( jQuery( elem ), value ); + }; + + // Otherwise they run against the entire set + } else { + fn.call( elems, value ); + fn = null; + } + } + + if ( fn ) { + for (; i < length; i++ ) { + fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass ); + } + } + + chainable = 1; + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[0], key ) : emptyGet; + }, + + now: function() { + return ( new Date() ).getTime(); + }, + + // Use of jQuery.browser is frowned upon. + // More details: http://docs.jquery.com/Utilities/jQuery.browser + uaMatch: function( ua ) { + ua = ua.toLowerCase(); + + var match = rwebkit.exec( ua ) || + ropera.exec( ua ) || + rmsie.exec( ua ) || + ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) || + []; + + return { browser: match[1] || "", version: match[2] || "0" }; + }, + + sub: function() { + function jQuerySub( selector, context ) { + return new jQuerySub.fn.init( selector, context ); + } + jQuery.extend( true, jQuerySub, this ); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init( selector, context ) { + if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) { + context = jQuerySub( context ); + } + + return jQuery.fn.init.call( this, selector, context, rootjQuerySub ); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; + }, + + browser: {} +}); + +// Populate the class2type map +jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +}); + +browserMatch = jQuery.uaMatch( userAgent ); +if ( browserMatch.browser ) { + jQuery.browser[ browserMatch.browser ] = true; + jQuery.browser.version = browserMatch.version; +} + +// Deprecated, use jQuery.browser.webkit instead +if ( jQuery.browser.webkit ) { + jQuery.browser.safari = true; +} + +// IE doesn't match non-breaking spaces with \s +if ( rnotwhite.test( "\xA0" ) ) { + trimLeft = /^[\s\xA0]+/; + trimRight = /[\s\xA0]+$/; +} + +// All jQuery objects should point back to these +rootjQuery = jQuery(document); + +// Cleanup functions for the document ready method +if ( document.addEventListener ) { + DOMContentLoaded = function() { + document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false ); + jQuery.ready(); + }; + +} else if ( document.attachEvent ) { + DOMContentLoaded = function() { + // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443). + if ( document.readyState === "complete" ) { + document.detachEvent( "onreadystatechange", DOMContentLoaded ); + jQuery.ready(); + } + }; +} + +// The DOM ready check for Internet Explorer +function doScrollCheck() { + if ( jQuery.isReady ) { + return; + } + + try { + // If IE is used, use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + document.documentElement.doScroll("left"); + } catch(e) { + setTimeout( doScrollCheck, 1 ); + return; + } + + // and execute any waiting functions + jQuery.ready(); +} + +return jQuery; + +})(); + + +// String to Object flags format cache +var flagsCache = {}; + +// Convert String-formatted flags into Object-formatted ones and store in cache +function createFlags( flags ) { + var object = flagsCache[ flags ] = {}, + i, length; + flags = flags.split( /\s+/ ); + for ( i = 0, length = flags.length; i < length; i++ ) { + object[ flags[i] ] = true; + } + return object; +} + +/* + * Create a callback list using the following parameters: + * + * flags: an optional list of space-separated flags that will change how + * the callback list behaves + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible flags: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( flags ) { + + // Convert flags from String-formatted to Object-formatted + // (we check in cache first) + flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; + + var // Actual callback list + list = [], + // Stack of fire calls for repeatable lists + stack = [], + // Last fire value (for non-forgettable lists) + memory, + // Flag to know if list was already fired + fired, + // Flag to know if list is currently firing + firing, + // First callback to fire (used internally by add and fireWith) + firingStart, + // End of the loop when firing + firingLength, + // Index of currently firing callback (modified by remove if needed) + firingIndex, + // Add one or several callbacks to the list + add = function( args ) { + var i, + length, + elem, + type, + actual; + for ( i = 0, length = args.length; i < length; i++ ) { + elem = args[ i ]; + type = jQuery.type( elem ); + if ( type === "array" ) { + // Inspect recursively + add( elem ); + } else if ( type === "function" ) { + // Add if not in unique mode and callback is not in + if ( !flags.unique || !self.has( elem ) ) { + list.push( elem ); + } + } + } + }, + // Fire callbacks + fire = function( context, args ) { + args = args || []; + memory = !flags.memory || [ context, args ]; + fired = true; + firing = true; + firingIndex = firingStart || 0; + firingStart = 0; + firingLength = list.length; + for ( ; list && firingIndex < firingLength; firingIndex++ ) { + if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { + memory = true; // Mark as halted + break; + } + } + firing = false; + if ( list ) { + if ( !flags.once ) { + if ( stack && stack.length ) { + memory = stack.shift(); + self.fireWith( memory[ 0 ], memory[ 1 ] ); + } + } else if ( memory === true ) { + self.disable(); + } else { + list = []; + } + } + }, + // Actual Callbacks object + self = { + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + var length = list.length; + add( arguments ); + // Do we need to add the callbacks to the + // current firing batch? + if ( firing ) { + firingLength = list.length; + // With memory, if we're not firing then + // we should call right away, unless previous + // firing was halted (stopOnFalse) + } else if ( memory && memory !== true ) { + firingStart = length; + fire( memory[ 0 ], memory[ 1 ] ); + } + } + return this; + }, + // Remove a callback from the list + remove: function() { + if ( list ) { + var args = arguments, + argIndex = 0, + argLength = args.length; + for ( ; argIndex < argLength ; argIndex++ ) { + for ( var i = 0; i < list.length; i++ ) { + if ( args[ argIndex ] === list[ i ] ) { + // Handle firingIndex and firingLength + if ( firing ) { + if ( i <= firingLength ) { + firingLength--; + if ( i <= firingIndex ) { + firingIndex--; + } + } + } + // Remove the element + list.splice( i--, 1 ); + // If we have some unicity property then + // we only need to do this once + if ( flags.unique ) { + break; + } + } + } + } + } + return this; + }, + // Control if a given callback is in the list + has: function( fn ) { + if ( list ) { + var i = 0, + length = list.length; + for ( ; i < length; i++ ) { + if ( fn === list[ i ] ) { + return true; + } + } + } + return false; + }, + // Remove all callbacks from the list + empty: function() { + list = []; + return this; + }, + // Have the list do nothing anymore + disable: function() { + list = stack = memory = undefined; + return this; + }, + // Is it disabled? + disabled: function() { + return !list; + }, + // Lock the list in its current state + lock: function() { + stack = undefined; + if ( !memory || memory === true ) { + self.disable(); + } + return this; + }, + // Is it locked? + locked: function() { + return !stack; + }, + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( stack ) { + if ( firing ) { + if ( !flags.once ) { + stack.push( [ context, args ] ); + } + } else if ( !( flags.once && memory ) ) { + fire( context, args ); + } + } + return this; + }, + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + + + +var // Static reference to slice + sliceDeferred = [].slice; + +jQuery.extend({ + + Deferred: function( func ) { + var doneList = jQuery.Callbacks( "once memory" ), + failList = jQuery.Callbacks( "once memory" ), + progressList = jQuery.Callbacks( "memory" ), + state = "pending", + lists = { + resolve: doneList, + reject: failList, + notify: progressList + }, + promise = { + done: doneList.add, + fail: failList.add, + progress: progressList.add, + + state: function() { + return state; + }, + + // Deprecated + isResolved: doneList.fired, + isRejected: failList.fired, + + then: function( doneCallbacks, failCallbacks, progressCallbacks ) { + deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks ); + return this; + }, + always: function() { + deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments ); + return this; + }, + pipe: function( fnDone, fnFail, fnProgress ) { + return jQuery.Deferred(function( newDefer ) { + jQuery.each( { + done: [ fnDone, "resolve" ], + fail: [ fnFail, "reject" ], + progress: [ fnProgress, "notify" ] + }, function( handler, data ) { + var fn = data[ 0 ], + action = data[ 1 ], + returned; + if ( jQuery.isFunction( fn ) ) { + deferred[ handler ](function() { + returned = fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify ); + } else { + newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] ); + } + }); + } else { + deferred[ handler ]( newDefer[ action ] ); + } + }); + }).promise(); + }, + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + if ( obj == null ) { + obj = promise; + } else { + for ( var key in promise ) { + obj[ key ] = promise[ key ]; + } + } + return obj; + } + }, + deferred = promise.promise({}), + key; + + for ( key in lists ) { + deferred[ key ] = lists[ key ].fire; + deferred[ key + "With" ] = lists[ key ].fireWith; + } + + // Handle state + deferred.done( function() { + state = "resolved"; + }, failList.disable, progressList.lock ).fail( function() { + state = "rejected"; + }, doneList.disable, progressList.lock ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( firstParam ) { + var args = sliceDeferred.call( arguments, 0 ), + i = 0, + length = args.length, + pValues = new Array( length ), + count = length, + pCount = length, + deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ? + firstParam : + jQuery.Deferred(), + promise = deferred.promise(); + function resolveFunc( i ) { + return function( value ) { + args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + if ( !( --count ) ) { + deferred.resolveWith( deferred, args ); + } + }; + } + function progressFunc( i ) { + return function( value ) { + pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value; + deferred.notifyWith( promise, pValues ); + }; + } + if ( length > 1 ) { + for ( ; i < length; i++ ) { + if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) { + args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) ); + } else { + --count; + } + } + if ( !count ) { + deferred.resolveWith( deferred, args ); + } + } else if ( deferred !== firstParam ) { + deferred.resolveWith( deferred, length ? [ firstParam ] : [] ); + } + return promise; + } +}); + + + + +jQuery.support = (function() { + + var support, + all, + a, + select, + opt, + input, + fragment, + tds, + events, + eventName, + i, + isSupported, + div = document.createElement( "div" ), + documentElement = document.documentElement; + + // Preliminary tests + div.setAttribute("className", "t"); + div.innerHTML = " <link/><table></table><a href='/a' style='top:1px;float:left;opacity:.55;'>a</a><input type='checkbox'/>"; + + all = div.getElementsByTagName( "*" ); + a = div.getElementsByTagName( "a" )[ 0 ]; + + // Can't get basic test support + if ( !all || !all.length || !a ) { + return {}; + } + + // First batch of supports tests + select = document.createElement( "select" ); + opt = select.appendChild( document.createElement("option") ); + input = div.getElementsByTagName( "input" )[ 0 ]; + + support = { + // IE strips leading whitespace when .innerHTML is used + leadingWhitespace: ( div.firstChild.nodeType === 3 ), + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + tbody: !div.getElementsByTagName("tbody").length, + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + htmlSerialize: !!div.getElementsByTagName("link").length, + + // Get the style information from getAttribute + // (IE uses .cssText instead) + style: /top/.test( a.getAttribute("style") ), + + // Make sure that URLs aren't manipulated + // (IE normalizes it by default) + hrefNormalized: ( a.getAttribute("href") === "/a" ), + + // Make sure that element opacity exists + // (IE uses filter instead) + // Use a regex to work around a WebKit issue. See #5145 + opacity: /^0.55/.test( a.style.opacity ), + + // Verify style float existence + // (IE uses styleFloat instead of cssFloat) + cssFloat: !!a.style.cssFloat, + + // Make sure that if no value is specified for a checkbox + // that it defaults to "on". + // (WebKit defaults to "" instead) + checkOn: ( input.value === "on" ), + + // Make sure that a selected-by-default option has a working selected property. + // (WebKit defaults to false instead of true, IE too, if it's in an optgroup) + optSelected: opt.selected, + + // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7) + getSetAttribute: div.className !== "t", + + // Tests for enctype support on a form(#6743) + enctype: !!document.createElement("form").enctype, + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav></:nav>", + + // Will be defined later + submitBubbles: true, + changeBubbles: true, + focusinBubbles: false, + deleteExpando: true, + noCloneEvent: true, + inlineBlockNeedsLayout: false, + shrinkWrapBlocks: false, + reliableMarginRight: true, + pixelMargin: true + }; + + // jQuery.boxModel DEPRECATED in 1.3, use jQuery.support.boxModel instead + jQuery.boxModel = support.boxModel = (document.compatMode === "CSS1Compat"); + + // Make sure checked status is properly cloned + input.checked = true; + support.noCloneChecked = input.cloneNode( true ).checked; + + // Make sure that the options inside disabled selects aren't marked as disabled + // (WebKit marks them as disabled) + select.disabled = true; + support.optDisabled = !opt.disabled; + + // Test to see if it's possible to delete an expando from an element + // Fails in Internet Explorer + try { + delete div.test; + } catch( e ) { + support.deleteExpando = false; + } + + if ( !div.addEventListener && div.attachEvent && div.fireEvent ) { + div.attachEvent( "onclick", function() { + // Cloning a node shouldn't copy over any + // bound event handlers (IE does this) + support.noCloneEvent = false; + }); + div.cloneNode( true ).fireEvent( "onclick" ); + } + + // Check if a radio maintains its value + // after being appended to the DOM + input = document.createElement("input"); + input.value = "t"; + input.setAttribute("type", "radio"); + support.radioValue = input.value === "t"; + + input.setAttribute("checked", "checked"); + + // #11217 - WebKit loses check when the name is after the checked attribute + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + fragment = document.createDocumentFragment(); + fragment.appendChild( div.lastChild ); + + // WebKit doesn't clone checked state correctly in fragments + support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + support.appendChecked = input.checked; + + fragment.removeChild( input ); + fragment.appendChild( div ); + + // Technique from Juriy Zaytsev + // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/ + // We only care about the case where non-standard event systems + // are used, namely in IE. Short-circuiting here helps us to + // avoid an eval call (in setAttribute) which can cause CSP + // to go haywire. See: https://developer.mozilla.org/en/Security/CSP + if ( div.attachEvent ) { + for ( i in { + submit: 1, + change: 1, + focusin: 1 + }) { + eventName = "on" + i; + isSupported = ( eventName in div ); + if ( !isSupported ) { + div.setAttribute( eventName, "return;" ); + isSupported = ( typeof div[ eventName ] === "function" ); + } + support[ i + "Bubbles" ] = isSupported; + } + } + + fragment.removeChild( div ); + + // Null elements to avoid leaks in IE + fragment = select = opt = div = input = null; + + // Run tests that need a body at doc ready + jQuery(function() { + var container, outer, inner, table, td, offsetSupport, + marginDiv, conMarginTop, style, html, positionTopLeftWidthHeight, + paddingMarginBorderVisibility, paddingMarginBorder, + body = document.getElementsByTagName("body")[0]; + + if ( !body ) { + // Return for frameset docs that don't have a body + return; + } + + conMarginTop = 1; + paddingMarginBorder = "padding:0;margin:0;border:"; + positionTopLeftWidthHeight = "position:absolute;top:0;left:0;width:1px;height:1px;"; + paddingMarginBorderVisibility = paddingMarginBorder + "0;visibility:hidden;"; + style = "style='" + positionTopLeftWidthHeight + paddingMarginBorder + "5px solid #000;"; + html = "<div " + style + "display:block;'><div style='" + paddingMarginBorder + "0;display:block;overflow:hidden;'></div></div>" + + "<table " + style + "' cellpadding='0' cellspacing='0'>" + + "<tr><td></td></tr></table>"; + + container = document.createElement("div"); + container.style.cssText = paddingMarginBorderVisibility + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px"; + body.insertBefore( container, body.firstChild ); + + // Construct the test element + div = document.createElement("div"); + container.appendChild( div ); + + // Check if table cells still have offsetWidth/Height when they are set + // to display:none and there are still other visible table cells in a + // table row; if so, offsetWidth/Height are not reliable for use when + // determining if an element has been hidden directly using + // display:none (it is still safe to use offsets if a parent element is + // hidden; don safety goggles and see bug #4512 for more information). + // (only IE 8 fails this test) + div.innerHTML = "<table><tr><td style='" + paddingMarginBorder + "0;display:none'></td><td>t</td></tr></table>"; + tds = div.getElementsByTagName( "td" ); + isSupported = ( tds[ 0 ].offsetHeight === 0 ); + + tds[ 0 ].style.display = ""; + tds[ 1 ].style.display = "none"; + + // Check if empty table cells still have offsetWidth/Height + // (IE <= 8 fail this test) + support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 ); + + // Check if div with explicit width and no margin-right incorrectly + // gets computed margin-right based on width of container. For more + // info see bug #3333 + // Fails in WebKit before Feb 2011 nightlies + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + if ( window.getComputedStyle ) { + div.innerHTML = ""; + marginDiv = document.createElement( "div" ); + marginDiv.style.width = "0"; + marginDiv.style.marginRight = "0"; + div.style.width = "2px"; + div.appendChild( marginDiv ); + support.reliableMarginRight = + ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0; + } + + if ( typeof div.style.zoom !== "undefined" ) { + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + // (IE < 8 does this) + div.innerHTML = ""; + div.style.width = div.style.padding = "1px"; + div.style.border = 0; + div.style.overflow = "hidden"; + div.style.display = "inline"; + div.style.zoom = 1; + support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 ); + + // Check if elements with layout shrink-wrap their children + // (IE 6 does this) + div.style.display = "block"; + div.style.overflow = "visible"; + div.innerHTML = "<div style='width:5px;'></div>"; + support.shrinkWrapBlocks = ( div.offsetWidth !== 3 ); + } + + div.style.cssText = positionTopLeftWidthHeight + paddingMarginBorderVisibility; + div.innerHTML = html; + + outer = div.firstChild; + inner = outer.firstChild; + td = outer.nextSibling.firstChild.firstChild; + + offsetSupport = { + doesNotAddBorder: ( inner.offsetTop !== 5 ), + doesAddBorderForTableAndCells: ( td.offsetTop === 5 ) + }; + + inner.style.position = "fixed"; + inner.style.top = "20px"; + + // safari subtracts parent border width here which is 5px + offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 ); + inner.style.position = inner.style.top = ""; + + outer.style.overflow = "hidden"; + outer.style.position = "relative"; + + offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 ); + offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop ); + + if ( window.getComputedStyle ) { + div.style.marginTop = "1%"; + support.pixelMargin = ( window.getComputedStyle( div, null ) || { marginTop: 0 } ).marginTop !== "1%"; + } + + if ( typeof container.style.zoom !== "undefined" ) { + container.style.zoom = 1; + } + + body.removeChild( container ); + marginDiv = div = container = null; + + jQuery.extend( support, offsetSupport ); + }); + + return support; +})(); + + + + +var rbrace = /^(?:\{.*\}|\[.*\])$/, + rmultiDash = /([A-Z])/g; + +jQuery.extend({ + cache: {}, + + // Please use with caution + uuid: 0, + + // Unique for each copy of jQuery on the page + // Non-digits removed to match rinlinejQuery + expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), + + // The following elements throw uncatchable exceptions if you + // attempt to add expando properties to them. + noData: { + "embed": true, + // Ban all objects except for Flash (which handle expandos) + "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000", + "applet": true + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var privateCache, thisCache, ret, + internalKey = jQuery.expando, + getByName = typeof name === "string", + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey, + isEvents = name === "events"; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { + return; + } + + if ( !id ) { + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + elem[ internalKey ] = id = ++jQuery.uuid; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + cache[ id ] = {}; + + // Avoids exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + if ( !isNode ) { + cache[ id ].toJSON = jQuery.noop; + } + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + privateCache = thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Users should not attempt to inspect the internal events object using jQuery.data, + // it is undocumented and subject to change. But does anyone listen? No. + if ( isEvents && !thisCache[ name ] ) { + return privateCache.events; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( getByName ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; + }, + + removeData: function( elem, name, pvt /* Internal Use Only */ ) { + if ( !jQuery.acceptData( elem ) ) { + return; + } + + var thisCache, i, l, + + // Reference to internal data cache key + internalKey = jQuery.expando, + + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + + // See jQuery.data for more information + id = isNode ? elem[ internalKey ] : internalKey; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split( " " ); + } + } + } + + for ( i = 0, l = name.length; i < l; i++ ) { + delete thisCache[ name[i] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject(cache[ id ]) ) { + return; + } + } + + // Browsers that fail expando deletion also refuse to delete expandos on + // the window, but it will allow it on all other JS objects; other browsers + // don't care + // Ensure that `cache` is not a window object #10080 + if ( jQuery.support.deleteExpando || !cache.setInterval ) { + delete cache[ id ]; + } else { + cache[ id ] = null; + } + + // We destroyed the cache and need to eliminate the expando on the node to avoid + // false lookups in the cache for entries that no longer exist + if ( isNode ) { + // IE does not allow us to delete expando properties from nodes, + // nor does it have a removeAttribute function on Document nodes; + // we must handle all of these cases + if ( jQuery.support.deleteExpando ) { + delete elem[ internalKey ]; + } else if ( elem.removeAttribute ) { + elem.removeAttribute( internalKey ); + } else { + elem[ internalKey ] = null; + } + } + }, + + // For internal use only. + _data: function( elem, name, data ) { + return jQuery.data( elem, name, data, true ); + }, + + // A method for determining if a DOM node can handle the data expando + acceptData: function( elem ) { + if ( elem.nodeName ) { + var match = jQuery.noData[ elem.nodeName.toLowerCase() ]; + + if ( match ) { + return !(match === true || elem.getAttribute("classid") !== match); + } + } + + return true; + } +}); + +jQuery.fn.extend({ + data: function( key, value ) { + var parts, part, attr, name, l, + elem = this[0], + i = 0, + data = null; + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + attr = elem.attributes; + for ( l = attr.length; i < l; i++ ) { + name = attr[i].name; + + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.substring(5) ); + + dataAttr( elem, name, data[ name ] ); + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each(function() { + jQuery.data( this, key ); + }); + } + + parts = key.split( ".", 2 ); + parts[1] = parts[1] ? "." + parts[1] : ""; + part = parts[1] + "!"; + + return jQuery.access( this, function( value ) { + + if ( value === undefined ) { + data = this.triggerHandler( "getData" + part, [ parts[0] ] ); + + // Try to fetch any internally stored data first + if ( data === undefined && elem ) { + data = jQuery.data( elem, key ); + data = dataAttr( elem, key, data ); + } + + return data === undefined && parts[1] ? + this.data( parts[0] ) : + data; + } + + parts[1] = value; + this.each(function() { + var self = jQuery( this ); + + self.triggerHandler( "setData" + part, parts ); + jQuery.data( this, key, value ); + self.triggerHandler( "changeData" + part, parts ); + }); + }, null, value, arguments.length > 1, null, false ); + }, + + removeData: function( key ) { + return this.each(function() { + jQuery.removeData( this, key ); + }); + } +}); + +function dataAttr( elem, key, data ) { + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + jQuery.isNumeric( data ) ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + for ( var name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + + + + +function handleQueueMarkDefer( elem, type, src ) { + var deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + defer = jQuery._data( elem, deferDataKey ); + if ( defer && + ( src === "queue" || !jQuery._data(elem, queueDataKey) ) && + ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) { + // Give room for hard-coded callbacks to fire first + // and eventually mark/queue something else on the element + setTimeout( function() { + if ( !jQuery._data( elem, queueDataKey ) && + !jQuery._data( elem, markDataKey ) ) { + jQuery.removeData( elem, deferDataKey, true ); + defer.fire(); + } + }, 0 ); + } +} + +jQuery.extend({ + + _mark: function( elem, type ) { + if ( elem ) { + type = ( type || "fx" ) + "mark"; + jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 ); + } + }, + + _unmark: function( force, elem, type ) { + if ( force !== true ) { + type = elem; + elem = force; + force = false; + } + if ( elem ) { + type = type || "fx"; + var key = type + "mark", + count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 ); + if ( count ) { + jQuery._data( elem, key, count ); + } else { + jQuery.removeData( elem, key, true ); + handleQueueMarkDefer( elem, type, "mark" ); + } + } + }, + + queue: function( elem, type, data ) { + var q; + if ( elem ) { + type = ( type || "fx" ) + "queue"; + q = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !q || jQuery.isArray(data) ) { + q = jQuery._data( elem, type, jQuery.makeArray(data) ); + } else { + q.push( data ); + } + } + return q || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + fn = queue.shift(), + hooks = {}; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + } + + if ( fn ) { + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + jQuery._data( elem, type + ".run", hooks ); + fn.call( elem, function() { + jQuery.dequeue( elem, type ); + }, hooks ); + } + + if ( !queue.length ) { + jQuery.removeData( elem, type + "queue " + type + ".run", true ); + handleQueueMarkDefer( elem, type, "queue" ); + } + } +}); + +jQuery.fn.extend({ + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[0], type ); + } + + return data === undefined ? + this : + this.each(function() { + var queue = jQuery.queue( this, type, data ); + + if ( type === "fx" && queue[0] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + }); + }, + dequeue: function( type ) { + return this.each(function() { + jQuery.dequeue( this, type ); + }); + }, + // Based off of the plugin by Clint Helfers, with permission. + // http://blindsignals.com/index.php/2009/07/jquery-delay/ + delay: function( time, type ) { + time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; + type = type || "fx"; + + return this.queue( type, function( next, hooks ) { + var timeout = setTimeout( next, time ); + hooks.stop = function() { + clearTimeout( timeout ); + }; + }); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, object ) { + if ( typeof type !== "string" ) { + object = type; + type = undefined; + } + type = type || "fx"; + var defer = jQuery.Deferred(), + elements = this, + i = elements.length, + count = 1, + deferDataKey = type + "defer", + queueDataKey = type + "queue", + markDataKey = type + "mark", + tmp; + function resolve() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + } + while( i-- ) { + if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) || + ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) || + jQuery.data( elements[ i ], markDataKey, undefined, true ) ) && + jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) { + count++; + tmp.add( resolve ); + } + } + resolve(); + return defer.promise( object ); + } +}); + + + + +var rclass = /[\n\t\r]/g, + rspace = /\s+/, + rreturn = /\r/g, + rtype = /^(?:button|input)$/i, + rfocusable = /^(?:button|input|object|select|textarea)$/i, + rclickable = /^a(?:rea)?$/i, + rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i, + getSetAttribute = jQuery.support.getSetAttribute, + nodeHook, boolHook, fixSpecified; + +jQuery.fn.extend({ + attr: function( name, value ) { + return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); + }, + + removeAttr: function( name ) { + return this.each(function() { + jQuery.removeAttr( this, name ); + }); + }, + + prop: function( name, value ) { + return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); + }, + + removeProp: function( name ) { + name = jQuery.propFix[ name ] || name; + return this.each(function() { + // try/catch handles cases where IE balks (such as removing a property on window) + try { + this[ name ] = undefined; + delete this[ name ]; + } catch( e ) {} + }); + }, + + addClass: function( value ) { + var classNames, i, l, elem, + setClass, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).addClass( value.call(this, j, this.className) ); + }); + } + + if ( value && typeof value === "string" ) { + classNames = value.split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 ) { + if ( !elem.className && classNames.length === 1 ) { + elem.className = value; + + } else { + setClass = " " + elem.className + " "; + + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) { + setClass += classNames[ c ] + " "; + } + } + elem.className = jQuery.trim( setClass ); + } + } + } + } + + return this; + }, + + removeClass: function( value ) { + var classNames, i, l, elem, className, c, cl; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( j ) { + jQuery( this ).removeClass( value.call(this, j, this.className) ); + }); + } + + if ( (value && typeof value === "string") || value === undefined ) { + classNames = ( value || "" ).split( rspace ); + + for ( i = 0, l = this.length; i < l; i++ ) { + elem = this[ i ]; + + if ( elem.nodeType === 1 && elem.className ) { + if ( value ) { + className = (" " + elem.className + " ").replace( rclass, " " ); + for ( c = 0, cl = classNames.length; c < cl; c++ ) { + className = className.replace(" " + classNames[ c ] + " ", " "); + } + elem.className = jQuery.trim( className ); + + } else { + elem.className = ""; + } + } + } + } + + return this; + }, + + toggleClass: function( value, stateVal ) { + var type = typeof value, + isBool = typeof stateVal === "boolean"; + + if ( jQuery.isFunction( value ) ) { + return this.each(function( i ) { + jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); + }); + } + + return this.each(function() { + if ( type === "string" ) { + // toggle individual class names + var className, + i = 0, + self = jQuery( this ), + state = stateVal, + classNames = value.split( rspace ); + + while ( (className = classNames[ i++ ]) ) { + // check each className given, space seperated list + state = isBool ? state : !self.hasClass( className ); + self[ state ? "addClass" : "removeClass" ]( className ); + } + + } else if ( type === "undefined" || type === "boolean" ) { + if ( this.className ) { + // store className if set + jQuery._data( this, "__className__", this.className ); + } + + // toggle whole className + this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || ""; + } + }); + }, + + hasClass: function( selector ) { + var className = " " + selector + " ", + i = 0, + l = this.length; + for ( ; i < l; i++ ) { + if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) { + return true; + } + } + + return false; + }, + + val: function( value ) { + var hooks, ret, isFunction, + elem = this[0]; + + if ( !arguments.length ) { + if ( elem ) { + hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; + + if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { + return ret; + } + + ret = elem.value; + + return typeof ret === "string" ? + // handle most common string cases + ret.replace(rreturn, "") : + // handle cases where value is null/undef or number + ret == null ? "" : ret; + } + + return; + } + + isFunction = jQuery.isFunction( value ); + + return this.each(function( i ) { + var self = jQuery(this), val; + + if ( this.nodeType !== 1 ) { + return; + } + + if ( isFunction ) { + val = value.call( this, i, self.val() ); + } else { + val = value; + } + + // Treat null/undefined as ""; convert numbers to string + if ( val == null ) { + val = ""; + } else if ( typeof val === "number" ) { + val += ""; + } else if ( jQuery.isArray( val ) ) { + val = jQuery.map(val, function ( value ) { + return value == null ? "" : value + ""; + }); + } + + hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; + + // If set returns undefined, fall back to normal setting + if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { + this.value = val; + } + }); + } +}); + +jQuery.extend({ + valHooks: { + option: { + get: function( elem ) { + // attributes.value is undefined in Blackberry 4.7 but + // uses .value. See #6932 + var val = elem.attributes.value; + return !val || val.specified ? elem.value : elem.text; + } + }, + select: { + get: function( elem ) { + var value, i, max, option, + index = elem.selectedIndex, + values = [], + options = elem.options, + one = elem.type === "select-one"; + + // Nothing was selected + if ( index < 0 ) { + return null; + } + + // Loop through all the selected options + i = one ? index : 0; + max = one ? index + 1 : options.length; + for ( ; i < max; i++ ) { + option = options[ i ]; + + // Don't return options that are disabled or in a disabled optgroup + if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && + (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) { + + // Get the specific value for the option + value = jQuery( option ).val(); + + // We don't need an array for one selects + if ( one ) { + return value; + } + + // Multi-Selects return an array + values.push( value ); + } + } + + // Fixes Bug #2551 -- select.val() broken in IE after form.reset() + if ( one && !values.length && options.length ) { + return jQuery( options[ index ] ).val(); + } + + return values; + }, + + set: function( elem, value ) { + var values = jQuery.makeArray( value ); + + jQuery(elem).find("option").each(function() { + this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0; + }); + + if ( !values.length ) { + elem.selectedIndex = -1; + } + return values; + } + } + }, + + attrFn: { + val: true, + css: true, + html: true, + text: true, + data: true, + width: true, + height: true, + offset: true + }, + + attr: function( elem, name, value, pass ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set attributes on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + if ( pass && name in jQuery.attrFn ) { + return jQuery( elem )[ name ]( value ); + } + + // Fallback to prop when attributes are not supported + if ( typeof elem.getAttribute === "undefined" ) { + return jQuery.prop( elem, name, value ); + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + // All attributes are lowercase + // Grab necessary hook if one is defined + if ( notxml ) { + name = name.toLowerCase(); + hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook ); + } + + if ( value !== undefined ) { + + if ( value === null ) { + jQuery.removeAttr( elem, name ); + return; + + } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + elem.setAttribute( name, "" + value ); + return value; + } + + } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + + ret = elem.getAttribute( name ); + + // Non-existent attributes return null, we normalize to undefined + return ret === null ? + undefined : + ret; + } + }, + + removeAttr: function( elem, value ) { + var propName, attrNames, name, l, isBool, + i = 0; + + if ( value && elem.nodeType === 1 ) { + attrNames = value.toLowerCase().split( rspace ); + l = attrNames.length; + + for ( ; i < l; i++ ) { + name = attrNames[ i ]; + + if ( name ) { + propName = jQuery.propFix[ name ] || name; + isBool = rboolean.test( name ); + + // See #9699 for explanation of this approach (setting first, then removal) + // Do not do this for boolean attributes (see #10870) + if ( !isBool ) { + jQuery.attr( elem, name, "" ); + } + elem.removeAttribute( getSetAttribute ? name : propName ); + + // Set corresponding property to false for boolean attributes + if ( isBool && propName in elem ) { + elem[ propName ] = false; + } + } + } + } + }, + + attrHooks: { + type: { + set: function( elem, value ) { + // We can't allow the type property to be changed (since it causes problems in IE) + if ( rtype.test( elem.nodeName ) && elem.parentNode ) { + jQuery.error( "type property can't be changed" ); + } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { + // Setting the type on a radio button after the value resets the value in IE6-9 + // Reset value to it's default in case type is set after value + // This is for element creation + var val = elem.value; + elem.setAttribute( "type", value ); + if ( val ) { + elem.value = val; + } + return value; + } + } + }, + // Use the value property for back compat + // Use the nodeHook for button elements in IE6/7 (#1954) + value: { + get: function( elem, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.get( elem, name ); + } + return name in elem ? + elem.value : + null; + }, + set: function( elem, value, name ) { + if ( nodeHook && jQuery.nodeName( elem, "button" ) ) { + return nodeHook.set( elem, value, name ); + } + // Does not return so that setAttribute is also used + elem.value = value; + } + } + }, + + propFix: { + tabindex: "tabIndex", + readonly: "readOnly", + "for": "htmlFor", + "class": "className", + maxlength: "maxLength", + cellspacing: "cellSpacing", + cellpadding: "cellPadding", + rowspan: "rowSpan", + colspan: "colSpan", + usemap: "useMap", + frameborder: "frameBorder", + contenteditable: "contentEditable" + }, + + prop: function( elem, name, value ) { + var ret, hooks, notxml, + nType = elem.nodeType; + + // don't get/set properties on text, comment and attribute nodes + if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { + return; + } + + notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); + + if ( notxml ) { + // Fix name and attach hooks + name = jQuery.propFix[ name ] || name; + hooks = jQuery.propHooks[ name ]; + } + + if ( value !== undefined ) { + if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { + return ret; + + } else { + return ( elem[ name ] = value ); + } + + } else { + if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { + return ret; + + } else { + return elem[ name ]; + } + } + }, + + propHooks: { + tabIndex: { + get: function( elem ) { + // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set + // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/ + var attributeNode = elem.getAttributeNode("tabindex"); + + return attributeNode && attributeNode.specified ? + parseInt( attributeNode.value, 10 ) : + rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ? + 0 : + undefined; + } + } + } +}); + +// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional) +jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex; + +// Hook for boolean attributes +boolHook = { + get: function( elem, name ) { + // Align boolean attributes with corresponding properties + // Fall back to attribute presence where some booleans are not supported + var attrNode, + property = jQuery.prop( elem, name ); + return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ? + name.toLowerCase() : + undefined; + }, + set: function( elem, value, name ) { + var propName; + if ( value === false ) { + // Remove boolean attributes when set to false + jQuery.removeAttr( elem, name ); + } else { + // value is true since we know at this point it's type boolean and not false + // Set boolean attributes to the same name and set the DOM property + propName = jQuery.propFix[ name ] || name; + if ( propName in elem ) { + // Only set the IDL specifically if it already exists on the element + elem[ propName ] = true; + } + + elem.setAttribute( name, name.toLowerCase() ); + } + return name; + } +}; + +// IE6/7 do not support getting/setting some attributes with get/setAttribute +if ( !getSetAttribute ) { + + fixSpecified = { + name: true, + id: true, + coords: true + }; + + // Use this for any attribute in IE6/7 + // This fixes almost every IE6/7 issue + nodeHook = jQuery.valHooks.button = { + get: function( elem, name ) { + var ret; + ret = elem.getAttributeNode( name ); + return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ? + ret.nodeValue : + undefined; + }, + set: function( elem, value, name ) { + // Set the existing or create a new attribute node + var ret = elem.getAttributeNode( name ); + if ( !ret ) { + ret = document.createAttribute( name ); + elem.setAttributeNode( ret ); + } + return ( ret.nodeValue = value + "" ); + } + }; + + // Apply the nodeHook to tabindex + jQuery.attrHooks.tabindex.set = nodeHook.set; + + // Set width and height to auto instead of 0 on empty string( Bug #8150 ) + // This is for removals + jQuery.each([ "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + set: function( elem, value ) { + if ( value === "" ) { + elem.setAttribute( name, "auto" ); + return value; + } + } + }); + }); + + // Set contenteditable to false on removals(#10429) + // Setting to empty string throws an error as an invalid value + jQuery.attrHooks.contenteditable = { + get: nodeHook.get, + set: function( elem, value, name ) { + if ( value === "" ) { + value = "false"; + } + nodeHook.set( elem, value, name ); + } + }; +} + + +// Some attributes require a special call on IE +if ( !jQuery.support.hrefNormalized ) { + jQuery.each([ "href", "src", "width", "height" ], function( i, name ) { + jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], { + get: function( elem ) { + var ret = elem.getAttribute( name, 2 ); + return ret === null ? undefined : ret; + } + }); + }); +} + +if ( !jQuery.support.style ) { + jQuery.attrHooks.style = { + get: function( elem ) { + // Return undefined in the case of empty string + // Normalize to lowercase since IE uppercases css property names + return elem.style.cssText.toLowerCase() || undefined; + }, + set: function( elem, value ) { + return ( elem.style.cssText = "" + value ); + } + }; +} + +// Safari mis-reports the default selected property of an option +// Accessing the parent's selectedIndex property fixes it +if ( !jQuery.support.optSelected ) { + jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, { + get: function( elem ) { + var parent = elem.parentNode; + + if ( parent ) { + parent.selectedIndex; + + // Make sure that it also works with optgroups, see #5701 + if ( parent.parentNode ) { + parent.parentNode.selectedIndex; + } + } + return null; + } + }); +} + +// IE6/7 call enctype encoding +if ( !jQuery.support.enctype ) { + jQuery.propFix.enctype = "encoding"; +} + +// Radios and checkboxes getter/setter +if ( !jQuery.support.checkOn ) { + jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = { + get: function( elem ) { + // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified + return elem.getAttribute("value") === null ? "on" : elem.value; + } + }; + }); +} +jQuery.each([ "radio", "checkbox" ], function() { + jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], { + set: function( elem, value ) { + if ( jQuery.isArray( value ) ) { + return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); + } + } + }); +}); + + + + +var rformElems = /^(?:textarea|input|select)$/i, + rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/, + rhoverHack = /(?:^|\s)hover(\.\S+)?\b/, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|contextmenu)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/, + quickParse = function( selector ) { + var quick = rquickIs.exec( selector ); + if ( quick ) { + // 0 1 2 3 + // [ _, tag, id, class ] + quick[1] = ( quick[1] || "" ).toLowerCase(); + quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" ); + } + return quick; + }, + quickIs = function( elem, m ) { + var attrs = elem.attributes || {}; + return ( + (!m[1] || elem.nodeName.toLowerCase() === m[1]) && + (!m[2] || (attrs.id || {}).value === m[2]) && + (!m[3] || m[3].test( (attrs[ "class" ] || {}).value )) + ); + }, + hoverHack = function( events ) { + return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" ); + }; + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + add: function( elem, types, handler, data, selector ) { + + var elemData, eventHandle, events, + t, tns, type, namespaces, handleObj, + handleObjIn, quick, handlers, special; + + // Don't attach events to noData or text/comment nodes (allow plain objects tho) + if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + events = elemData.events; + if ( !events ) { + elemData.events = events = {}; + } + eventHandle = elemData.handle; + if ( !eventHandle ) { + elemData.handle = eventHandle = function( e ) { + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + // jQuery(...).bind("mouseover mouseout", fn); + types = jQuery.trim( hoverHack(types) ).split( " " ); + for ( t = 0; t < types.length; t++ ) { + + tns = rtypenamespace.exec( types[t] ) || []; + type = tns[1]; + namespaces = ( tns[2] || "" ).split( "." ).sort(); + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend({ + type: type, + origType: tns[1], + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + quick: selector && quickParse( selector ), + namespace: namespaces.join(".") + }, handleObjIn ); + + // Init the event handler queue if we're the first + handlers = events[ type ]; + if ( !handlers ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + global: {}, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + + var elemData = jQuery.hasData( elem ) && jQuery._data( elem ), + t, tns, type, origType, namespaces, origCount, + j, events, special, handle, eventType, handleObj; + + if ( !elemData || !(events = elemData.events) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = jQuery.trim( hoverHack( types || "" ) ).split(" "); + for ( t = 0; t < types.length; t++ ) { + tns = rtypenamespace.exec( types[t] ) || []; + type = origType = tns[1]; + namespaces = tns[2]; + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector? special.delegateType : special.bindType ) || type; + eventType = events[ type ] || []; + origCount = eventType.length; + namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null; + + // Remove matching events + for ( j = 0; j < eventType.length; j++ ) { + handleObj = eventType[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !namespaces || namespaces.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) { + eventType.splice( j--, 1 ); + + if ( handleObj.selector ) { + eventType.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( eventType.length === 0 && origCount !== eventType.length ) { + if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) { + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + handle = elemData.handle; + if ( handle ) { + handle.elem = null; + } + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery.removeData( elem, [ "events", "handle" ], true ); + } + }, + + // Events that are safe to short-circuit if no handlers are attached. + // Native DOM events should not be added, they may have inline handlers. + customEvent: { + "getData": true, + "setData": true, + "changeData": true + }, + + trigger: function( event, data, elem, onlyHandlers ) { + // Don't do events on text and comment nodes + if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) { + return; + } + + // Event object or event type + var type = event.type || event, + namespaces = [], + cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType; + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "!" ) >= 0 ) { + // Exclusive events trigger only for the exact event (no namespaces) + type = type.slice(0, -1); + exclusive = true; + } + + if ( type.indexOf( "." ) >= 0 ) { + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split("."); + type = namespaces.shift(); + namespaces.sort(); + } + + if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) { + // No jQuery handlers for this event type, and it can't have inline handlers + return; + } + + // Caller can pass in an Event, Object, or just an event type string + event = typeof event === "object" ? + // jQuery.Event object + event[ jQuery.expando ] ? event : + // Object literal + new jQuery.Event( type, event ) : + // Just the event type (string) + new jQuery.Event( type ); + + event.type = type; + event.isTrigger = true; + event.exclusive = exclusive; + event.namespace = namespaces.join( "." ); + event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null; + ontype = type.indexOf( ":" ) < 0 ? "on" + type : ""; + + // Handle a global trigger + if ( !elem ) { + + // TODO: Stop taunting the data cache; remove global events and always attach to document + cache = jQuery.cache; + for ( i in cache ) { + if ( cache[ i ].events && cache[ i ].events[ type ] ) { + jQuery.event.trigger( event, data, cache[ i ].handle.elem, true ); + } + } + return; + } + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data != null ? jQuery.makeArray( data ) : []; + data.unshift( event ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + eventPath = [[ elem, special.bindType || type ]]; + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode; + old = null; + for ( ; cur; cur = cur.parentNode ) { + eventPath.push([ cur, bubbleType ]); + old = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( old && old === elem.ownerDocument ) { + eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]); + } + } + + // Fire handlers on the event path + for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) { + + cur = eventPath[i][0]; + event.type = eventPath[i][1]; + + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" ); + if ( handle ) { + handle.apply( cur, data ); + } + // Note that this is a bare JS function and not a jQuery handler + handle = ontype && cur[ ontype ]; + if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) { + event.preventDefault(); + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) && + !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + // IE<9 dies on focus/blur to hidden element (#1486) + if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + old = elem[ ontype ]; + + if ( old ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + elem[ type ](); + jQuery.event.triggered = undefined; + + if ( old ) { + elem[ ontype ] = old; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event || window.event ); + + var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []), + delegateCount = handlers.delegateCount, + args = [].slice.call( arguments, 0 ), + run_all = !event.exclusive && !event.namespace, + special = jQuery.event.special[ event.type ] || {}, + handlerQueue = [], + i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[0] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers that should run if there are delegated events + // Avoid non-left-click bubbling in Firefox (#3861) + if ( delegateCount && !(event.button && event.type === "click") ) { + + // Pregenerate a single jQuery object for reuse with .is() + jqcur = jQuery(this); + jqcur.context = this.ownerDocument || this; + + for ( cur = event.target; cur != this; cur = cur.parentNode || this ) { + + // Don't process events on disabled elements (#6911, #8165) + if ( cur.disabled !== true ) { + selMatch = {}; + matches = []; + jqcur[0] = cur; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + sel = handleObj.selector; + + if ( selMatch[ sel ] === undefined ) { + selMatch[ sel ] = ( + handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel ) + ); + } + if ( selMatch[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push({ elem: cur, matches: matches }); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( handlers.length > delegateCount ) { + handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) }); + } + + // Run delegates first; they may want to stop propagation beneath us + for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) { + matched = handlerQueue[ i ]; + event.currentTarget = matched.elem; + + for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) { + handleObj = matched.matches[ j ]; + + // Triggered event must either 1) be non-exclusive and have no namespace, or + // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace). + if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) { + + event.data = handleObj.data; + event.handleObj = handleObj; + + ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler ) + .apply( matched.elem, args ); + + if ( ret !== undefined ) { + event.result = ret; + if ( ret === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 *** + props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split(" "), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "), + filter: function( event, original ) { + var eventDoc, doc, body, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? original.toElement : fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, + originalEvent = event, + fixHook = jQuery.event.fixHooks[ event.type ] || {}, + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = jQuery.Event( originalEvent ); + + for ( i = copy.length; i; ) { + prop = copy[ --i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Target should not be a text node (#504, Safari) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8) + if ( event.metaKey === undefined ) { + event.metaKey = event.ctrlKey; + } + + return fixHook.filter? fixHook.filter( event, originalEvent ) : event; + }, + + special: { + ready: { + // Make sure the ready event is setup + setup: jQuery.bindReady + }, + + load: { + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + + focus: { + delegateType: "focusin" + }, + blur: { + delegateType: "focusout" + }, + + beforeunload: { + setup: function( data, namespaces, eventHandle ) { + // We only want to do this special case on windows + if ( jQuery.isWindow( this ) ) { + this.onbeforeunload = eventHandle; + } + }, + + teardown: function( namespaces, eventHandle ) { + if ( this.onbeforeunload === eventHandle ) { + this.onbeforeunload = null; + } + } + } + }, + + simulate: function( type, elem, event, bubble ) { + // Piggyback on a donor event to simulate a different one. + // Fake originalEvent to avoid donor's stopPropagation, but if the + // simulated event prevents default then we do the same on the donor. + var e = jQuery.extend( + new jQuery.Event(), + event, + { type: type, + isSimulated: true, + originalEvent: {} + } + ); + if ( bubble ) { + jQuery.event.trigger( e, null, elem ); + } else { + jQuery.event.dispatch.call( elem, e ); + } + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +// Some plugins are using, but it's undocumented/deprecated and will be removed. +// The 1.7 special event interface should provide all the hooks needed now. +jQuery.event.handle = jQuery.event.dispatch; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle, false ); + } + } : + function( elem, type, handle ) { + if ( elem.detachEvent ) { + elem.detachEvent( "on" + type, handle ); + } + }; + +jQuery.Event = function( src, props ) { + // Allow instantiation without the 'new' keyword + if ( !(this instanceof jQuery.Event) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false || + src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +function returnFalse() { + return false; +} +function returnTrue() { + return true; +} + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + preventDefault: function() { + this.isDefaultPrevented = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + + // if preventDefault exists run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // otherwise set the returnValue property of the original event to false (IE) + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + this.isPropagationStopped = returnTrue; + + var e = this.originalEvent; + if ( !e ) { + return; + } + // if stopPropagation exists run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + // otherwise set the cancelBubble property of the original event to true (IE) + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + this.isImmediatePropagationStopped = returnTrue; + this.stopPropagation(); + }, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +jQuery.each({ + mouseenter: "mouseover", + mouseleave: "mouseout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var target = this, + related = event.relatedTarget, + handleObj = event.handleObj, + selector = handleObj.selector, + ret; + + // For mousenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || (related !== target && !jQuery.contains( target, related )) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +}); + +// IE submit delegation +if ( !jQuery.support.submitBubbles ) { + + jQuery.event.special.submit = { + setup: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined; + if ( form && !form._submit_attached ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submit_bubble = true; + }); + form._submit_attached = true; + } + }); + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + // If form was submitted by the user, bubble the event up the tree + if ( event._submit_bubble ) { + delete event._submit_bubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event, true ); + } + } + }, + + teardown: function() { + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !jQuery.support.changeBubbles ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._just_changed = true; + } + }); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._just_changed && !event.isTrigger ) { + this._just_changed = false; + jQuery.event.simulate( "change", this, event, true ); + } + }); + } + return false; + } + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event, true ); + } + }); + elem._change_attached = true; + } + }); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) { + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return rformElems.test( this.nodeName ); + } + }; +} + +// Create "bubbling" focus and blur events +if ( !jQuery.support.focusinBubbles ) { + jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler while someone wants focusin/focusout + var attaches = 0, + handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + if ( attaches++ === 0 ) { + document.addEventListener( orig, handler, true ); + } + }, + teardown: function() { + if ( --attaches === 0 ) { + document.removeEventListener( orig, handler, true ); + } + } + }; + }); +} + +jQuery.fn.extend({ + + on: function( types, selector, data, fn, /*INTERNAL*/ one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { // && selector != null + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + this.on( type, selector, data, types[ type ], one ); + } + return this; + } + + if ( data == null && fn == null ) { + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return this; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return this.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + }); + }, + one: function( types, selector, data, fn ) { + return this.on( types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + if ( types && types.preventDefault && types.handleObj ) { + // ( event ) dispatched jQuery.Event + var handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + // ( types-object [, selector] ) + for ( var type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each(function() { + jQuery.event.remove( this, types, fn, selector ); + }); + }, + + bind: function( types, data, fn ) { + return this.on( types, null, data, fn ); + }, + unbind: function( types, fn ) { + return this.off( types, null, fn ); + }, + + live: function( types, data, fn ) { + jQuery( this.context ).on( types, this.selector, data, fn ); + return this; + }, + die: function( types, fn ) { + jQuery( this.context ).off( types, this.selector || "**", fn ); + return this; + }, + + delegate: function( selector, types, data, fn ) { + return this.on( types, selector, data, fn ); + }, + undelegate: function( selector, types, fn ) { + // ( namespace ) or ( selector, types [, fn] ) + return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn ); + }, + + trigger: function( type, data ) { + return this.each(function() { + jQuery.event.trigger( type, data, this ); + }); + }, + triggerHandler: function( type, data ) { + if ( this[0] ) { + return jQuery.event.trigger( type, data, this[0], true ); + } + }, + + toggle: function( fn ) { + // Save reference to arguments for access in closure + var args = arguments, + guid = fn.guid || jQuery.guid++, + i = 0, + toggler = function( event ) { + // Figure out which function to execute + var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i; + jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 ); + + // Make sure that clicks stop + event.preventDefault(); + + // and execute the function + return args[ lastToggle ].apply( this, arguments ) || false; + }; + + // link all the functions, so any of them can unbind this click handler + toggler.guid = guid; + while ( i < args.length ) { + args[ i++ ].guid = guid; + } + + return this.click( toggler ); + }, + + hover: function( fnOver, fnOut ) { + return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver ); + } +}); + +jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " + + "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " + + "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) { + + // Handle event binding + jQuery.fn[ name ] = function( data, fn ) { + if ( fn == null ) { + fn = data; + data = null; + } + + return arguments.length > 0 ? + this.on( name, null, data, fn ) : + this.trigger( name ); + }; + + if ( jQuery.attrFn ) { + jQuery.attrFn[ name ] = true; + } + + if ( rkeyEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks; + } + + if ( rmouseEvent.test( name ) ) { + jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks; + } +}); + + + +/*! + * Sizzle CSS Selector Engine + * Copyright 2011, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + expando = "sizcache" + (Math.random() + '').replace('.', ''), + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true, + rBackslash = /\\/g, + rReturn = /\r\n/g, + rNonWord = /\W/; + +// Here we check if the JavaScript engine is using some sort of +// optimization where it does not always call our comparision +// function. If that is the case, discard the hasDuplicate value. +// Thus far that includes Google Chrome. +[0, 0].sort(function() { + baseHasDuplicate = false; + return 0; +}); + +var Sizzle = function( selector, context, results, seed ) { + results = results || []; + context = context || document; + + var origContext = context; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var m, set, checkSet, extra, ret, cur, pop, i, + prune = true, + contextXML = Sizzle.isXML( context ), + parts = [], + soFar = selector; + + // Reset the position of the chunker regexp (start from head) + do { + chunker.exec( "" ); + m = chunker.exec( soFar ); + + if ( m ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + } while ( m ); + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context, seed ); + + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) { + selector += parts.shift(); + } + + set = posProcess( selector, set, seed ); + } + } + + } else { + // Take a shortcut and set the context if the root selector is an ID + // (but not if it'll be faster if the inner selector is an ID) + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + + ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? + Sizzle.filter( ret.expr, ret.set )[0] : + ret.set[0]; + } + + if ( context ) { + ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + + set = ret.expr ? + Sizzle.filter( ret.expr, ret.set ) : + ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray( set ); + + } else { + prune = false; + } + + while ( parts.length ) { + cur = parts.pop(); + pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + Sizzle.error( cur || selector ); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + + } else if ( context && context.nodeType === 1 ) { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + + } else { + for ( i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function( results ) { + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort( sortOrder ); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[ i - 1 ] ) { + results.splice( i--, 1 ); + } + } + } + } + + return results; +}; + +Sizzle.matches = function( expr, set ) { + return Sizzle( expr, null, null, set ); +}; + +Sizzle.matchesSelector = function( node, expr ) { + return Sizzle( expr, null, null, [node] ).length > 0; +}; + +Sizzle.find = function( expr, context, isXML ) { + var set, i, len, match, type, left; + + if ( !expr ) { + return []; + } + + for ( i = 0, len = Expr.order.length; i < len; i++ ) { + type = Expr.order[i]; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + left = match[1]; + match.splice( 1, 1 ); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace( rBackslash, "" ); + set = Expr.find[ type ]( match, context, isXML ); + + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( "*" ) : + []; + } + + return { set: set, expr: expr }; +}; + +Sizzle.filter = function( expr, set, inplace, not ) { + var match, anyFound, + type, found, item, filter, left, + i, pass, + old = expr, + result = [], + curLoop = set, + isXMLFilter = set && set[0] && Sizzle.isXML( set[0] ); + + while ( expr && set.length ) { + for ( type in Expr.filter ) { + if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) { + filter = Expr.filter[ type ]; + left = match[1]; + + anyFound = false; + + match.splice(1,1); + + if ( left.substr( left.length - 1 ) === "\\" ) { + continue; + } + + if ( curLoop === result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + pass = not ^ found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + + } else { + curLoop[i] = false; + } + + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + // Improper expression + if ( expr === old ) { + if ( anyFound == null ) { + Sizzle.error( expr ); + + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Utility function for retreiving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +var getText = Sizzle.getText = function( elem ) { + var i, node, + nodeType = elem.nodeType, + ret = ""; + + if ( nodeType ) { + if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent || innerText for elements + if ( typeof elem.textContent === 'string' ) { + return elem.textContent; + } else if ( typeof elem.innerText === 'string' ) { + // Replace IE's carriage returns + return elem.innerText.replace( rReturn, '' ); + } else { + // Traverse it's children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + } else { + + // If no nodeType, this is expected to be an array + for ( i = 0; (node = elem[i]); i++ ) { + // Do not traverse comment nodes + if ( node.nodeType !== 8 ) { + ret += getText( node ); + } + } + } + return ret; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + + match: { + ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/ + }, + + leftMatch: {}, + + attrMap: { + "class": "className", + "for": "htmlFor" + }, + + attrHandle: { + href: function( elem ) { + return elem.getAttribute( "href" ); + }, + type: function( elem ) { + return elem.getAttribute( "type" ); + } + }, + + relative: { + "+": function(checkSet, part){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !rNonWord.test( part ), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag ) { + part = part.toLowerCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + + ">": function( checkSet, part ) { + var elem, + isPartStr = typeof part === "string", + i = 0, + l = checkSet.length; + + if ( isPartStr && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false; + } + } + + } else { + for ( ; i < l; i++ ) { + elem = checkSet[i]; + + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + + "": function(checkSet, part, isXML){ + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML ); + }, + + "~": function( checkSet, part, isXML ) { + var nodeCheck, + doneName = done++, + checkFn = dirCheck; + + if ( typeof part === "string" && !rNonWord.test( part ) ) { + part = part.toLowerCase(); + nodeCheck = part; + checkFn = dirNodeCheck; + } + + checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML ); + } + }, + + find: { + ID: function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + return m && m.parentNode ? [m] : []; + } + }, + + NAME: function( match, context ) { + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], + results = context.getElementsByName( match[1] ); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + + TAG: function( match, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( match[1] ); + } + } + }, + preFilter: { + CLASS: function( match, curLoop, inplace, result, not, isXML ) { + match = " " + match[1].replace( rBackslash, "" ) + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) { + if ( !inplace ) { + result.push( elem ); + } + + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + + ID: function( match ) { + return match[1].replace( rBackslash, "" ); + }, + + TAG: function( match, curLoop ) { + return match[1].replace( rBackslash, "" ).toLowerCase(); + }, + + CHILD: function( match ) { + if ( match[1] === "nth" ) { + if ( !match[2] ) { + Sizzle.error( match[0] ); + } + + match[2] = match[2].replace(/^\+|\s*/g, ''); + + // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6' + var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec( + match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + // calculate the numbers (first)n+(last) including if they are negative + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + else if ( match[2] ) { + Sizzle.error( match[0] ); + } + + // TODO: Move to normal caching system + match[0] = done++; + + return match; + }, + + ATTR: function( match, curLoop, inplace, result, not, isXML ) { + var name = match[1] = match[1].replace( rBackslash, "" ); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + // Handle if an un-quoted value was used + match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" ); + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + + PSEUDO: function( match, curLoop, inplace, result, not ) { + if ( match[1] === "not" ) { + // If we're dealing with a complex expression, or a simple one + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + + if ( !inplace ) { + result.push.apply( result, ret ); + } + + return false; + } + + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + + POS: function( match ) { + match.unshift( true ); + + return match; + } + }, + + filters: { + enabled: function( elem ) { + return elem.disabled === false && elem.type !== "hidden"; + }, + + disabled: function( elem ) { + return elem.disabled === true; + }, + + checked: function( elem ) { + return elem.checked === true; + }, + + selected: function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + parent: function( elem ) { + return !!elem.firstChild; + }, + + empty: function( elem ) { + return !elem.firstChild; + }, + + has: function( elem, i, match ) { + return !!Sizzle( match[3], elem ).length; + }, + + header: function( elem ) { + return (/h\d/i).test( elem.nodeName ); + }, + + text: function( elem ) { + var attr = elem.getAttribute( "type" ), type = elem.type; + // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) + // use getAttribute instead to test this case + return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null ); + }, + + radio: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type; + }, + + checkbox: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type; + }, + + file: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "file" === elem.type; + }, + + password: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "password" === elem.type; + }, + + submit: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "submit" === elem.type; + }, + + image: function( elem ) { + return elem.nodeName.toLowerCase() === "input" && "image" === elem.type; + }, + + reset: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && "reset" === elem.type; + }, + + button: function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && "button" === elem.type || name === "button"; + }, + + input: function( elem ) { + return (/input|select|textarea|button/i).test( elem.nodeName ); + }, + + focus: function( elem ) { + return elem === elem.ownerDocument.activeElement; + } + }, + setFilters: { + first: function( elem, i ) { + return i === 0; + }, + + last: function( elem, i, match, array ) { + return i === array.length - 1; + }, + + even: function( elem, i ) { + return i % 2 === 0; + }, + + odd: function( elem, i ) { + return i % 2 === 1; + }, + + lt: function( elem, i, match ) { + return i < match[3] - 0; + }, + + gt: function( elem, i, match ) { + return i > match[3] - 0; + }, + + nth: function( elem, i, match ) { + return match[3] - 0 === i; + }, + + eq: function( elem, i, match ) { + return match[3] - 0 === i; + } + }, + filter: { + PSEUDO: function( elem, match, i, array ) { + var name = match[1], + filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0; + + } else if ( name === "not" ) { + var not = match[3]; + + for ( var j = 0, l = not.length; j < l; j++ ) { + if ( not[j] === elem ) { + return false; + } + } + + return true; + + } else { + Sizzle.error( name ); + } + }, + + CHILD: function( elem, match ) { + var first, last, + doneName, parent, cache, + count, diff, + type = match[1], + node = elem; + + switch ( type ) { + case "only": + case "first": + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + if ( type === "first" ) { + return true; + } + + node = elem; + + /* falls through */ + case "last": + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) { + return false; + } + } + + return true; + + case "nth": + first = match[2]; + last = match[3]; + + if ( first === 1 && last === 0 ) { + return true; + } + + doneName = match[0]; + parent = elem.parentNode; + + if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) { + count = 0; + + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + + parent[ expando ] = doneName; + } + + diff = elem.nodeIndex - last; + + if ( first === 0 ) { + return diff === 0; + + } else { + return ( diff % first === 0 && diff / first >= 0 ); + } + } + }, + + ID: function( elem, match ) { + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + + TAG: function( elem, match ) { + return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match; + }, + + CLASS: function( elem, match ) { + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + + ATTR: function( elem, match ) { + var name = match[1], + result = Sizzle.attr ? + Sizzle.attr( elem, name ) : + Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + !type && Sizzle.attr ? + result != null : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value !== check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + + POS: function( elem, match, i, array ) { + var name = match[2], + filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS, + fescape = function(all, num){ + return "\\" + (num - 0 + 1); + }; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) ); +} +// Expose origPOS +// "global" as in regardless of relation to brackets/parens +Expr.match.globalPOS = origPOS; + +var makeArray = function( array, results ) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +// Perform a simple check to determine if the browser is capable of +// converting a NodeList to an array using builtin methods. +// Also verifies that the returned array holds DOM nodes +// (which is not the case in the Blackberry browser) +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType; + +// Provide a fallback method if it does not work +} catch( e ) { + makeArray = function( array, results ) { + var i = 0, + ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + + } else { + if ( typeof array.length === "number" ) { + for ( var l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + + } else { + for ( ; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; +} + +var sortOrder, siblingCheck; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + return a.compareDocumentPosition ? -1 : 1; + } + + return a.compareDocumentPosition(b) & 4 ? -1 : 1; + }; + +} else { + sortOrder = function( a, b ) { + // The nodes are identical, we can exit early + if ( a === b ) { + hasDuplicate = true; + return 0; + + // Fallback to using sourceIndex (in IE) if it's available on both nodes + } else if ( a.sourceIndex && b.sourceIndex ) { + return a.sourceIndex - b.sourceIndex; + } + + var al, bl, + ap = [], + bp = [], + aup = a.parentNode, + bup = b.parentNode, + cur = aup; + + // If the nodes are siblings (or identical) we can do a quick check + if ( aup === bup ) { + return siblingCheck( a, b ); + + // If no parents were found then the nodes are disconnected + } else if ( !aup ) { + return -1; + + } else if ( !bup ) { + return 1; + } + + // Otherwise they're somewhere else in the tree so we need + // to build up a full list of the parentNodes for comparison + while ( cur ) { + ap.unshift( cur ); + cur = cur.parentNode; + } + + cur = bup; + + while ( cur ) { + bp.unshift( cur ); + cur = cur.parentNode; + } + + al = ap.length; + bl = bp.length; + + // Start walking down the tree looking for a discrepancy + for ( var i = 0; i < al && i < bl; i++ ) { + if ( ap[i] !== bp[i] ) { + return siblingCheck( ap[i], bp[i] ); + } + } + + // We ended someplace up the tree so do a sibling check + return i === al ? + siblingCheck( a, bp[i], -1 ) : + siblingCheck( ap[i], b, 1 ); + }; + + siblingCheck = function( a, b, ret ) { + if ( a === b ) { + return ret; + } + + var cur = a.nextSibling; + + while ( cur ) { + if ( cur === b ) { + return -1; + } + + cur = cur.nextSibling; + } + + return 1; + }; +} + +// Check to see if the browser returns elements by name when +// querying by getElementById (and provide a workaround) +(function(){ + // We're going to inject a fake input element with a specified name + var form = document.createElement("div"), + id = "script" + (new Date()).getTime(), + root = document.documentElement; + + form.innerHTML = "<a name='" + id + "'/>"; + + // Inject it into the root element, check its status, and remove it quickly + root.insertBefore( form, root.firstChild ); + + // The workaround has to do additional checks after a getElementById + // Which slows things down for other browsers (hence the branching) + if ( document.getElementById( id ) ) { + Expr.find.ID = function( match, context, isXML ) { + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + + return m ? + m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? + [m] : + undefined : + []; + } + }; + + Expr.filter.ID = function( elem, match ) { + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + + // release memory in IE + root = form = null; +})(); + +(function(){ + // Check to see if the browser returns only elements + // when doing getElementsByTagName("*") + + // Create a fake element + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + // Make sure no comments are found + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function( match, context ) { + var results = context.getElementsByTagName( match[1] ); + + // Filter out possible comments + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + // Check to see if an attribute returns normalized href attributes + div.innerHTML = "<a href='#'></a>"; + + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + + Expr.attrHandle.href = function( elem ) { + return elem.getAttribute( "href", 2 ); + }; + } + + // release memory in IE + div = null; +})(); + +if ( document.querySelectorAll ) { + (function(){ + var oldSizzle = Sizzle, + div = document.createElement("div"), + id = "__sizzle__"; + + div.innerHTML = "<p class='TEST'></p>"; + + // Safari can't handle uppercase or unicode characters when + // in quirks mode. + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function( query, context, extra, seed ) { + context = context || document; + + // Only use querySelectorAll on non-XML documents + // (ID selectors don't work in non-HTML documents) + if ( !seed && !Sizzle.isXML(context) ) { + // See if we find a selector to speed up + var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query ); + + if ( match && (context.nodeType === 1 || context.nodeType === 9) ) { + // Speed-up: Sizzle("TAG") + if ( match[1] ) { + return makeArray( context.getElementsByTagName( query ), extra ); + + // Speed-up: Sizzle(".CLASS") + } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) { + return makeArray( context.getElementsByClassName( match[2] ), extra ); + } + } + + if ( context.nodeType === 9 ) { + // Speed-up: Sizzle("body") + // The body element only exists once, optimize finding it + if ( query === "body" && context.body ) { + return makeArray( [ context.body ], extra ); + + // Speed-up: Sizzle("#ID") + } else if ( match && match[3] ) { + var elem = context.getElementById( match[3] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id === match[3] ) { + return makeArray( [ elem ], extra ); + } + + } else { + return makeArray( [], extra ); + } + } + + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(qsaError) {} + + // qSA works strangely on Element-rooted queries + // We can work around this by specifying an extra ID on the root + // and working up from there (Thanks to Andrew Dupont for the technique) + // IE 8 doesn't work on object elements + } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { + var oldContext = context, + old = context.getAttribute( "id" ), + nid = old || id, + hasParent = context.parentNode, + relativeHierarchySelector = /^\s*[+~]/.test( query ); + + if ( !old ) { + context.setAttribute( "id", nid ); + } else { + nid = nid.replace( /'/g, "\\$&" ); + } + if ( relativeHierarchySelector && hasParent ) { + context = context.parentNode; + } + + try { + if ( !relativeHierarchySelector || hasParent ) { + return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra ); + } + + } catch(pseudoError) { + } finally { + if ( !old ) { + oldContext.removeAttribute( "id" ); + } + } + } + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + // release memory in IE + div = null; + })(); +} + +(function(){ + var html = document.documentElement, + matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector; + + if ( matches ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9 fails this) + var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ), + pseudoWorks = false; + + try { + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( document.documentElement, "[test!='']:sizzle" ); + + } catch( pseudoError ) { + pseudoWorks = true; + } + + Sizzle.matchesSelector = function( node, expr ) { + // Make sure that attribute selectors are quoted + expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']"); + + if ( !Sizzle.isXML( node ) ) { + try { + if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) { + var ret = matches.call( node, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || !disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9, so check for that + node.document && node.document.nodeType !== 11 ) { + return ret; + } + } + } catch(e) {} + } + + return Sizzle(expr, null, null, [node]).length > 0; + }; + } +})(); + +(function(){ + var div = document.createElement("div"); + + div.innerHTML = "<div class='test e'></div><div class='test'></div>"; + + // Opera can't find a second classname (in 9.6) + // Also, make sure that getElementsByClassName actually exists + if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) { + return; + } + + // Safari caches class attributes, doesn't catch changes (in 3.2) + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) { + return; + } + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function( match, context, isXML ) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + // release memory in IE + div = null; +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem[ expando ] === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem[ expando ] = doneName; + elem.sizset = i; + } + + if ( elem.nodeName.toLowerCase() === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + + if ( elem ) { + var match = false; + + elem = elem[dir]; + + while ( elem ) { + if ( elem[ expando ] === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem[ expando ] = doneName; + elem.sizset = i; + } + + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +if ( document.documentElement.contains ) { + Sizzle.contains = function( a, b ) { + return a !== b && (a.contains ? a.contains(b) : true); + }; + +} else if ( document.documentElement.compareDocumentPosition ) { + Sizzle.contains = function( a, b ) { + return !!(a.compareDocumentPosition(b) & 16); + }; + +} else { + Sizzle.contains = function() { + return false; + }; +} + +Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement; + + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +var posProcess = function( selector, context, seed ) { + var match, + tmpSet = [], + later = "", + root = context.nodeType ? [context] : context; + + // Position selectors must be done after the filter + // And so must :not(positional) so we move all PSEUDOs to the end + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet, seed ); + } + + return Sizzle.filter( later, tmpSet ); +}; + +// EXPOSE +// Override sizzle attribute retrieval +Sizzle.attr = jQuery.attr; +Sizzle.selectors.attrMap = {}; +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[":"] = jQuery.expr.filters; +jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + +})(); + + +var runtil = /Until$/, + rparentsprev = /^(?:parents|prevUntil|prevAll)/, + // Note: This RegExp should be improved, or likely pulled from Sizzle + rmultiselector = /,/, + isSimple = /^.[^:#\[\.,]*$/, + slice = Array.prototype.slice, + POS = jQuery.expr.match.globalPOS, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend({ + find: function( selector ) { + var self = this, + i, l; + + if ( typeof selector !== "string" ) { + return jQuery( selector ).filter(function() { + for ( i = 0, l = self.length; i < l; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + }); + } + + var ret = this.pushStack( "", "find", selector ), + length, n, r; + + for ( i = 0, l = this.length; i < l; i++ ) { + length = ret.length; + jQuery.find( selector, this[i], ret ); + + if ( i > 0 ) { + // Make sure that the results are unique + for ( n = length; n < ret.length; n++ ) { + for ( r = 0; r < length; r++ ) { + if ( ret[r] === ret[n] ) { + ret.splice(n--, 1); + break; + } + } + } + } + } + + return ret; + }, + + has: function( target ) { + var targets = jQuery( target ); + return this.filter(function() { + for ( var i = 0, l = targets.length; i < l; i++ ) { + if ( jQuery.contains( this, targets[i] ) ) { + return true; + } + } + }); + }, + + not: function( selector ) { + return this.pushStack( winnow(this, selector, false), "not", selector); + }, + + filter: function( selector ) { + return this.pushStack( winnow(this, selector, true), "filter", selector ); + }, + + is: function( selector ) { + return !!selector && ( + typeof selector === "string" ? + // If this is a positional selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + POS.test( selector ) ? + jQuery( selector, this.context ).index( this[0] ) >= 0 : + jQuery.filter( selector, this ).length > 0 : + this.filter( selector ).length > 0 ); + }, + + closest: function( selectors, context ) { + var ret = [], i, l, cur = this[0]; + + // Array (deprecated as of jQuery 1.7) + if ( jQuery.isArray( selectors ) ) { + var level = 1; + + while ( cur && cur.ownerDocument && cur !== context ) { + for ( i = 0; i < selectors.length; i++ ) { + + if ( jQuery( cur ).is( selectors[ i ] ) ) { + ret.push({ selector: selectors[ i ], elem: cur, level: level }); + } + } + + cur = cur.parentNode; + level++; + } + + return ret; + } + + // String + var pos = POS.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( i = 0, l = this.length; i < l; i++ ) { + cur = this[i]; + + while ( cur ) { + if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) { + ret.push( cur ); + break; + + } else { + cur = cur.parentNode; + if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) { + break; + } + } + } + } + + ret = ret.length > 1 ? jQuery.unique( ret ) : ret; + + return this.pushStack( ret, "closest", selectors ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[0], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[0] : elem, this ); + }, + + add: function( selector, context ) { + var set = typeof selector === "string" ? + jQuery( selector, context ) : + jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), + all = jQuery.merge( this.get(), set ); + + return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ? + all : + jQuery.unique( all ) ); + }, + + andSelf: function() { + return this.add( this.prevObject ); + } +}); + +// A painfully simple check to see if an element is disconnected +// from a document (should be improved, where feasible). +function isDisconnected( node ) { + return !node || !node.parentNode || node.parentNode.nodeType === 11; +} + +jQuery.each({ + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return jQuery.nth( elem, 2, "nextSibling" ); + }, + prev: function( elem ) { + return jQuery.nth( elem, 2, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.makeArray( elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( !runtil.test( name ) ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret; + + if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + + return this.pushStack( ret, name, slice.call( arguments ).join(",") ); + }; +}); + +jQuery.extend({ + filter: function( expr, elems, not ) { + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 ? + jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] : + jQuery.find.matches(expr, elems); + }, + + dir: function( elem, dir, until ) { + var matched = [], + cur = elem[ dir ]; + + while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) { + if ( cur.nodeType === 1 ) { + matched.push( cur ); + } + cur = cur[dir]; + } + return matched; + }, + + nth: function( cur, result, dir, elem ) { + result = result || 1; + var num = 0; + + for ( ; cur; cur = cur[dir] ) { + if ( cur.nodeType === 1 && ++num === result ) { + break; + } + } + + return cur; + }, + + sibling: function( n, elem ) { + var r = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + r.push( n ); + } + } + + return r; + } +}); + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, keep ) { + + // Can't pass null or undefined to indexOf in Firefox 4 + // Set to 0 to skip string check + qualifier = qualifier || 0; + + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep(elements, function( elem, i ) { + var retVal = !!qualifier.call( elem, i, elem ); + return retVal === keep; + }); + + } else if ( qualifier.nodeType ) { + return jQuery.grep(elements, function( elem, i ) { + return ( elem === qualifier ) === keep; + }); + + } else if ( typeof qualifier === "string" ) { + var filtered = jQuery.grep(elements, function( elem ) { + return elem.nodeType === 1; + }); + + if ( isSimple.test( qualifier ) ) { + return jQuery.filter(qualifier, filtered, !keep); + } else { + qualifier = jQuery.filter( qualifier, filtered ); + } + } + + return jQuery.grep(elements, function( elem, i ) { + return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep; + }); +} + + + + +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" + + "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video", + rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g, + rleadingWhitespace = /^\s+/, + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig, + rtagName = /<([\w:]+)/, + rtbody = /<tbody/i, + rhtml = /<|&#?\w+;/, + rnoInnerhtml = /<(?:script|style)/i, + rnocache = /<(?:script|object|embed|option|style)/i, + rnoshimcache = new RegExp("<(?:" + nodeNames + ")[\\s/>]", "i"), + // checked="checked" or checked + rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, + rscriptType = /\/(java|ecma)script/i, + rcleanScript = /^\s*<!(?:\[CDATA\[|\-\-)/, + wrapMap = { + option: [ 1, "<select multiple='multiple'>", "</select>" ], + legend: [ 1, "<fieldset>", "</fieldset>" ], + thead: [ 1, "<table>", "</table>" ], + tr: [ 2, "<table><tbody>", "</tbody></table>" ], + td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ], + col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ], + area: [ 1, "<map>", "</map>" ], + _default: [ 0, "", "" ] + }, + safeFragment = createSafeFragment( document ); + +wrapMap.optgroup = wrapMap.option; +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + +// IE can't serialize <link> and <script> tags normally +if ( !jQuery.support.htmlSerialize ) { + wrapMap._default = [ 1, "div<div>", "</div>" ]; +} + +jQuery.fn.extend({ + text: function( value ) { + return jQuery.access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( ( this[0] && this[0].ownerDocument || document ).createTextNode( value ) ); + }, null, value, arguments.length ); + }, + + wrapAll: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapAll( html.call(this, i) ); + }); + } + + if ( this[0] ) { + // The elements to wrap the target around + var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true); + + if ( this[0].parentNode ) { + wrap.insertBefore( this[0] ); + } + + wrap.map(function() { + var elem = this; + + while ( elem.firstChild && elem.firstChild.nodeType === 1 ) { + elem = elem.firstChild; + } + + return elem; + }).append( this ); + } + + return this; + }, + + wrapInner: function( html ) { + if ( jQuery.isFunction( html ) ) { + return this.each(function(i) { + jQuery(this).wrapInner( html.call(this, i) ); + }); + } + + return this.each(function() { + var self = jQuery( this ), + contents = self.contents(); + + if ( contents.length ) { + contents.wrapAll( html ); + + } else { + self.append( html ); + } + }); + }, + + wrap: function( html ) { + var isFunction = jQuery.isFunction( html ); + + return this.each(function(i) { + jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); + }); + }, + + unwrap: function() { + return this.parent().each(function() { + if ( !jQuery.nodeName( this, "body" ) ) { + jQuery( this ).replaceWith( this.childNodes ); + } + }).end(); + }, + + append: function() { + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 ) { + this.appendChild( elem ); + } + }); + }, + + prepend: function() { + return this.domManip(arguments, true, function( elem ) { + if ( this.nodeType === 1 ) { + this.insertBefore( elem, this.firstChild ); + } + }); + }, + + before: function() { + if ( this[0] && this[0].parentNode ) { + return this.domManip(arguments, false, function( elem ) { + this.parentNode.insertBefore( elem, this ); + }); + } else if ( arguments.length ) { + var set = jQuery.clean( arguments ); + set.push.apply( set, this.toArray() ); + return this.pushStack( set, "before", arguments ); + } + }, + + after: function() { + if ( this[0] && this[0].parentNode ) { + return this.domManip(arguments, false, function( elem ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + }); + } else if ( arguments.length ) { + var set = this.pushStack( this, "after", arguments ); + set.push.apply( set, jQuery.clean(arguments) ); + return set; + } + }, + + // keepData is for internal use only--do not document + remove: function( selector, keepData ) { + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + if ( !selector || jQuery.filter( selector, [ elem ] ).length ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName("*") ); + jQuery.cleanData( [ elem ] ); + } + + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } + } + } + + return this; + }, + + empty: function() { + for ( var i = 0, elem; (elem = this[i]) != null; i++ ) { + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName("*") ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function () { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return jQuery.access( this, function( value ) { + var elem = this[0] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + null; + } + + + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || ["", ""] )[1].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1></$2>" ); + + try { + for (; i < l; i++ ) { + // Remove element nodes and prevent memory leaks + elem = this[i] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( elem.getElementsByTagName( "*" ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch(e) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function( value ) { + if ( this[0] && this[0].parentNode ) { + // Make sure that the elements are removed from the DOM before they are inserted + // this can help fix replacing a parent with child elements + if ( jQuery.isFunction( value ) ) { + return this.each(function(i) { + var self = jQuery(this), old = self.html(); + self.replaceWith( value.call( this, i, old ) ); + }); + } + + if ( typeof value !== "string" ) { + value = jQuery( value ).detach(); + } + + return this.each(function() { + var next = this.nextSibling, + parent = this.parentNode; + + jQuery( this ).remove(); + + if ( next ) { + jQuery(next).before( value ); + } else { + jQuery(parent).append( value ); + } + }); + } else { + return this.length ? + this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value ) : + this; + } + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, table, callback ) { + var results, first, fragment, parent, + value = args[0], + scripts = []; + + // We can't cloneNode fragments that contain checked, in WebKit + if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) { + return this.each(function() { + jQuery(this).domManip( args, table, callback, true ); + }); + } + + if ( jQuery.isFunction(value) ) { + return this.each(function(i) { + var self = jQuery(this); + args[0] = value.call(this, i, table ? self.html() : undefined); + self.domManip( args, table, callback ); + }); + } + + if ( this[0] ) { + parent = value && value.parentNode; + + // If we're in a fragment, just use that instead of building a new one + if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) { + results = { fragment: parent }; + + } else { + results = jQuery.buildFragment( args, this, scripts ); + } + + fragment = results.fragment; + + if ( fragment.childNodes.length === 1 ) { + first = fragment = fragment.firstChild; + } else { + first = fragment.firstChild; + } + + if ( first ) { + table = table && jQuery.nodeName( first, "tr" ); + + for ( var i = 0, l = this.length, lastIndex = l - 1; i < l; i++ ) { + callback.call( + table ? + root(this[i], first) : + this[i], + // Make sure that we do not leak memory by inadvertently discarding + // the original fragment (which might have attached data) instead of + // using it; in addition, use the original fragment object for the last + // item instead of first because it can end up being emptied incorrectly + // in certain situations (Bug #8070). + // Fragments from the fragment cache must always be cloned and never used + // in place. + results.cacheable || ( l > 1 && i < lastIndex ) ? + jQuery.clone( fragment, true, true ) : + fragment + ); + } + } + + if ( scripts.length ) { + jQuery.each( scripts, function( i, elem ) { + if ( elem.src ) { + jQuery.ajax({ + type: "GET", + global: false, + url: elem.src, + async: false, + dataType: "script" + }); + } else { + jQuery.globalEval( ( elem.text || elem.textContent || elem.innerHTML || "" ).replace( rcleanScript, "/*$0*/" ) ); + } + + if ( elem.parentNode ) { + elem.parentNode.removeChild( elem ); + } + }); + } + } + + return this; + } +}); + +function root( elem, cur ) { + return jQuery.nodeName(elem, "table") ? + (elem.getElementsByTagName("tbody")[0] || + elem.appendChild(elem.ownerDocument.createElement("tbody"))) : + elem; +} + +function cloneCopyEvent( src, dest ) { + + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function cloneFixAttributes( src, dest ) { + var nodeName; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + // clearAttributes removes the attributes, which we don't want, + // but also removes the attachEvent events, which we *do* want + if ( dest.clearAttributes ) { + dest.clearAttributes(); + } + + // mergeAttributes, in contrast, only merges back on the + // original attributes, not the events + if ( dest.mergeAttributes ) { + dest.mergeAttributes( src ); + } + + nodeName = dest.nodeName.toLowerCase(); + + // IE6-8 fail to clone children inside object elements that use + // the proprietary classid attribute value (rather than the type + // attribute) to identify the type of content to display + if ( nodeName === "object" ) { + dest.outerHTML = src.outerHTML; + + } else if ( nodeName === "input" && (src.type === "checkbox" || src.type === "radio") ) { + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + if ( src.checked ) { + dest.defaultChecked = dest.checked = src.checked; + } + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + + // IE blanks contents when cloning scripts + } else if ( nodeName === "script" && dest.text !== src.text ) { + dest.text = src.text; + } + + // Event data gets referenced instead of copied if the expando + // gets copied too + dest.removeAttribute( jQuery.expando ); + + // Clear flags for bubbling special change/submit events, they must + // be reattached when the newly cloned events are first activated + dest.removeAttribute( "_submit_attached" ); + dest.removeAttribute( "_change_attached" ); +} + +jQuery.buildFragment = function( args, nodes, scripts ) { + var fragment, cacheable, cacheresults, doc, + first = args[ 0 ]; + + // nodes may contain either an explicit document object, + // a jQuery collection or context object. + // If nodes[0] contains a valid object to assign to doc + if ( nodes && nodes[0] ) { + doc = nodes[0].ownerDocument || nodes[0]; + } + + // Ensure that an attr object doesn't incorrectly stand in as a document object + // Chrome and Firefox seem to allow this to occur and will throw exception + // Fixes #8950 + if ( !doc.createDocumentFragment ) { + doc = document; + } + + // Only cache "small" (1/2 KB) HTML strings that are associated with the main document + // Cloning options loses the selected state, so don't cache them + // IE 6 doesn't like it when you put <object> or <embed> elements in a fragment + // Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache + // Lastly, IE6,7,8 will not correctly reuse cached fragments that were created from unknown elems #10501 + if ( args.length === 1 && typeof first === "string" && first.length < 512 && doc === document && + first.charAt(0) === "<" && !rnocache.test( first ) && + (jQuery.support.checkClone || !rchecked.test( first )) && + (jQuery.support.html5Clone || !rnoshimcache.test( first )) ) { + + cacheable = true; + + cacheresults = jQuery.fragments[ first ]; + if ( cacheresults && cacheresults !== 1 ) { + fragment = cacheresults; + } + } + + if ( !fragment ) { + fragment = doc.createDocumentFragment(); + jQuery.clean( args, doc, fragment, scripts ); + } + + if ( cacheable ) { + jQuery.fragments[ first ] = cacheresults ? fragment : 1; + } + + return { fragment: fragment, cacheable: cacheable }; +}; + +jQuery.fragments = {}; + +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var ret = [], + insert = jQuery( selector ), + parent = this.length === 1 && this[0].parentNode; + + if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) { + insert[ original ]( this[0] ); + return this; + + } else { + for ( var i = 0, l = insert.length; i < l; i++ ) { + var elems = ( i > 0 ? this.clone(true) : this ).get(); + jQuery( insert[i] )[ original ]( elems ); + ret = ret.concat( elems ); + } + + return this.pushStack( ret, name, insert.selector ); + } + }; +}); + +function getAll( elem ) { + if ( typeof elem.getElementsByTagName !== "undefined" ) { + return elem.getElementsByTagName( "*" ); + + } else if ( typeof elem.querySelectorAll !== "undefined" ) { + return elem.querySelectorAll( "*" ); + + } else { + return []; + } +} + +// Used in clean, fixes the defaultChecked property +function fixDefaultChecked( elem ) { + if ( elem.type === "checkbox" || elem.type === "radio" ) { + elem.defaultChecked = elem.checked; + } +} +// Finds all inputs and passes them to fixDefaultChecked +function findInputs( elem ) { + var nodeName = ( elem.nodeName || "" ).toLowerCase(); + if ( nodeName === "input" ) { + fixDefaultChecked( elem ); + // Skip scripts, get other children + } else if ( nodeName !== "script" && typeof elem.getElementsByTagName !== "undefined" ) { + jQuery.grep( elem.getElementsByTagName("input"), fixDefaultChecked ); + } +} + +// Derived From: http://www.iecss.com/shimprove/javascript/shimprove.1-0-1.js +function shimCloneNode( elem ) { + var div = document.createElement( "div" ); + safeFragment.appendChild( div ); + + div.innerHTML = elem.outerHTML; + return div.firstChild; +} + +jQuery.extend({ + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var srcElements, + destElements, + i, + // IE<=8 does not properly clone detached, unknown element nodes + clone = jQuery.support.html5Clone || jQuery.isXMLDoc(elem) || !rnoshimcache.test( "<" + elem.nodeName + ">" ) ? + elem.cloneNode( true ) : + shimCloneNode( elem ); + + if ( (!jQuery.support.noCloneEvent || !jQuery.support.noCloneChecked) && + (elem.nodeType === 1 || elem.nodeType === 11) && !jQuery.isXMLDoc(elem) ) { + // IE copies events bound via attachEvent when using cloneNode. + // Calling detachEvent on the clone will also remove the events + // from the original. In order to get around this, we use some + // proprietary methods to clear the events. Thanks to MooTools + // guys for this hotness. + + cloneFixAttributes( elem, clone ); + + // Using Sizzle here is crazy slow, so we use getElementsByTagName instead + srcElements = getAll( elem ); + destElements = getAll( clone ); + + // Weird iteration because IE will replace the length property + // with an element if you are cloning the body and one of the + // elements on the page has a name or id of "length" + for ( i = 0; srcElements[i]; ++i ) { + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[i] ) { + cloneFixAttributes( srcElements[i], destElements[i] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + cloneCopyEvent( elem, clone ); + + if ( deepDataAndEvents ) { + srcElements = getAll( elem ); + destElements = getAll( clone ); + + for ( i = 0; srcElements[i]; ++i ) { + cloneCopyEvent( srcElements[i], destElements[i] ); + } + } + } + + srcElements = destElements = null; + + // Return the cloned set + return clone; + }, + + clean: function( elems, context, fragment, scripts ) { + var checkScriptType, script, j, + ret = []; + + context = context || document; + + // !context.createElement fails in IE with an error but returns typeof 'object' + if ( typeof context.createElement === "undefined" ) { + context = context.ownerDocument || context[0] && context[0].ownerDocument || document; + } + + for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { + if ( typeof elem === "number" ) { + elem += ""; + } + + if ( !elem ) { + continue; + } + + // Convert html string into DOM nodes + if ( typeof elem === "string" ) { + if ( !rhtml.test( elem ) ) { + elem = context.createTextNode( elem ); + } else { + // Fix "XHTML"-style tags in all browsers + elem = elem.replace(rxhtmlTag, "<$1></$2>"); + + // Trim whitespace, otherwise indexOf won't work as expected + var tag = ( rtagName.exec( elem ) || ["", ""] )[1].toLowerCase(), + wrap = wrapMap[ tag ] || wrapMap._default, + depth = wrap[0], + div = context.createElement("div"), + safeChildNodes = safeFragment.childNodes, + remove; + + // Append wrapper element to unknown element safe doc fragment + if ( context === document ) { + // Use the fragment we've already created for this document + safeFragment.appendChild( div ); + } else { + // Use a fragment created with the owner document + createSafeFragment( context ).appendChild( div ); + } + + // Go to html and back, then peel off extra wrappers + div.innerHTML = wrap[1] + elem + wrap[2]; + + // Move to the right depth + while ( depth-- ) { + div = div.lastChild; + } + + // Remove IE's autoinserted <tbody> from table fragments + if ( !jQuery.support.tbody ) { + + // String was a <table>, *may* have spurious <tbody> + var hasBody = rtbody.test(elem), + tbody = tag === "table" && !hasBody ? + div.firstChild && div.firstChild.childNodes : + + // String was a bare <thead> or <tfoot> + wrap[1] === "<table>" && !hasBody ? + div.childNodes : + []; + + for ( j = tbody.length - 1; j >= 0 ; --j ) { + if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) { + tbody[ j ].parentNode.removeChild( tbody[ j ] ); + } + } + } + + // IE completely kills leading whitespace when innerHTML is used + if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) { + div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild ); + } + + elem = div.childNodes; + + // Clear elements from DocumentFragment (safeFragment or otherwise) + // to avoid hoarding elements. Fixes #11356 + if ( div ) { + div.parentNode.removeChild( div ); + + // Guard against -1 index exceptions in FF3.6 + if ( safeChildNodes.length > 0 ) { + remove = safeChildNodes[ safeChildNodes.length - 1 ]; + + if ( remove && remove.parentNode ) { + remove.parentNode.removeChild( remove ); + } + } + } + } + } + + // Resets defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + var len; + if ( !jQuery.support.appendChecked ) { + if ( elem[0] && typeof (len = elem.length) === "number" ) { + for ( j = 0; j < len; j++ ) { + findInputs( elem[j] ); + } + } else { + findInputs( elem ); + } + } + + if ( elem.nodeType ) { + ret.push( elem ); + } else { + ret = jQuery.merge( ret, elem ); + } + } + + if ( fragment ) { + checkScriptType = function( elem ) { + return !elem.type || rscriptType.test( elem.type ); + }; + for ( i = 0; ret[i]; i++ ) { + script = ret[i]; + if ( scripts && jQuery.nodeName( script, "script" ) && (!script.type || rscriptType.test( script.type )) ) { + scripts.push( script.parentNode ? script.parentNode.removeChild( script ) : script ); + + } else { + if ( script.nodeType === 1 ) { + var jsTags = jQuery.grep( script.getElementsByTagName( "script" ), checkScriptType ); + + ret.splice.apply( ret, [i + 1, 0].concat( jsTags ) ); + } + fragment.appendChild( script ); + } + } + } + + return ret; + }, + + cleanData: function( elems ) { + var data, id, + cache = jQuery.cache, + special = jQuery.event.special, + deleteExpando = jQuery.support.deleteExpando; + + for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) { + if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) { + continue; + } + + id = elem[ jQuery.expando ]; + + if ( id ) { + data = cache[ id ]; + + if ( data && data.events ) { + for ( var type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + + // Null the DOM reference to avoid IE6/7/8 leak (#7054) + if ( data.handle ) { + data.handle.elem = null; + } + } + + if ( deleteExpando ) { + delete elem[ jQuery.expando ]; + + } else if ( elem.removeAttribute ) { + elem.removeAttribute( jQuery.expando ); + } + + delete cache[ id ]; + } + } + } +}); + + + + +var ralpha = /alpha\([^)]*\)/i, + ropacity = /opacity=([^)]*)/, + // fixed for IE9, see #8346 + rupper = /([A-Z]|^ms)/g, + rnum = /^[\-+]?(?:\d*\.)?\d+$/i, + rnumnonpx = /^-?(?:\d*\.)?\d+(?!px)[^\d\s]+$/i, + rrelNum = /^([\-+])=([\-+.\de]+)/, + rmargin = /^margin/, + + cssShow = { position: "absolute", visibility: "hidden", display: "block" }, + + // order is important! + cssExpand = [ "Top", "Right", "Bottom", "Left" ], + + curCSS, + + getComputedStyle, + currentStyle; + +jQuery.fn.css = function( name, value ) { + return jQuery.access( this, function( elem, name, value ) { + return value !== undefined ? + jQuery.style( elem, name, value ) : + jQuery.css( elem, name ); + }, name, value, arguments.length > 1 ); +}; + +jQuery.extend({ + // Add in style property hooks for overriding the default + // behavior of getting and setting a style property + cssHooks: { + opacity: { + get: function( elem, computed ) { + if ( computed ) { + // We should always get a number back from opacity + var ret = curCSS( elem, "opacity" ); + return ret === "" ? "1" : ret; + + } else { + return elem.style.opacity; + } + } + } + }, + + // Exclude the following css properties to add px + cssNumber: { + "fillOpacity": true, + "fontWeight": true, + "lineHeight": true, + "opacity": true, + "orphans": true, + "widows": true, + "zIndex": true, + "zoom": true + }, + + // Add in properties whose names you wish to fix before + // setting or getting the value + cssProps: { + // normalize float css property + "float": jQuery.support.cssFloat ? "cssFloat" : "styleFloat" + }, + + // Get and set the style property on a DOM Node + style: function( elem, name, value, extra ) { + // Don't set styles on text and comment nodes + if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 || !elem.style ) { + return; + } + + // Make sure that we're working with the right name + var ret, type, origName = jQuery.camelCase( name ), + style = elem.style, hooks = jQuery.cssHooks[ origName ]; + + name = jQuery.cssProps[ origName ] || origName; + + // Check if we're setting a value + if ( value !== undefined ) { + type = typeof value; + + // convert relative number strings (+= or -=) to relative numbers. #7345 + if ( type === "string" && (ret = rrelNum.exec( value )) ) { + value = ( +( ret[1] + 1) * +ret[2] ) + parseFloat( jQuery.css( elem, name ) ); + // Fixes bug #9237 + type = "number"; + } + + // Make sure that NaN and null values aren't set. See: #7116 + if ( value == null || type === "number" && isNaN( value ) ) { + return; + } + + // If a number was passed in, add 'px' to the (except for certain CSS properties) + if ( type === "number" && !jQuery.cssNumber[ origName ] ) { + value += "px"; + } + + // If a hook was provided, use that value, otherwise just set the specified value + if ( !hooks || !("set" in hooks) || (value = hooks.set( elem, value )) !== undefined ) { + // Wrapped to prevent IE from throwing errors when 'invalid' values are provided + // Fixes bug #5509 + try { + style[ name ] = value; + } catch(e) {} + } + + } else { + // If a hook was provided get the non-computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, false, extra )) !== undefined ) { + return ret; + } + + // Otherwise just get the value from the style object + return style[ name ]; + } + }, + + css: function( elem, name, extra ) { + var ret, hooks; + + // Make sure that we're working with the right name + name = jQuery.camelCase( name ); + hooks = jQuery.cssHooks[ name ]; + name = jQuery.cssProps[ name ] || name; + + // cssFloat needs a special treatment + if ( name === "cssFloat" ) { + name = "float"; + } + + // If a hook was provided get the computed value from there + if ( hooks && "get" in hooks && (ret = hooks.get( elem, true, extra )) !== undefined ) { + return ret; + + // Otherwise, if a way to get the computed value exists, use that + } else if ( curCSS ) { + return curCSS( elem, name ); + } + }, + + // A method for quickly swapping in/out CSS properties to get correct calculations + swap: function( elem, options, callback ) { + var old = {}, + ret, name; + + // Remember the old values, and insert the new ones + for ( name in options ) { + old[ name ] = elem.style[ name ]; + elem.style[ name ] = options[ name ]; + } + + ret = callback.call( elem ); + + // Revert the old values + for ( name in options ) { + elem.style[ name ] = old[ name ]; + } + + return ret; + } +}); + +// DEPRECATED in 1.3, Use jQuery.css() instead +jQuery.curCSS = jQuery.css; + +if ( document.defaultView && document.defaultView.getComputedStyle ) { + getComputedStyle = function( elem, name ) { + var ret, defaultView, computedStyle, width, + style = elem.style; + + name = name.replace( rupper, "-$1" ).toLowerCase(); + + if ( (defaultView = elem.ownerDocument.defaultView) && + (computedStyle = defaultView.getComputedStyle( elem, null )) ) { + + ret = computedStyle.getPropertyValue( name ); + if ( ret === "" && !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) { + ret = jQuery.style( elem, name ); + } + } + + // A tribute to the "awesome hack by Dean Edwards" + // WebKit uses "computed value (percentage if specified)" instead of "used value" for margins + // which is against the CSSOM draft spec: http://dev.w3.org/csswg/cssom/#resolved-values + if ( !jQuery.support.pixelMargin && computedStyle && rmargin.test( name ) && rnumnonpx.test( ret ) ) { + width = style.width; + style.width = ret; + ret = computedStyle.width; + style.width = width; + } + + return ret; + }; +} + +if ( document.documentElement.currentStyle ) { + currentStyle = function( elem, name ) { + var left, rsLeft, uncomputed, + ret = elem.currentStyle && elem.currentStyle[ name ], + style = elem.style; + + // Avoid setting ret to empty string here + // so we don't default to auto + if ( ret == null && style && (uncomputed = style[ name ]) ) { + ret = uncomputed; + } + + // From the awesome hack by Dean Edwards + // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 + + // If we're not dealing with a regular pixel number + // but a number that has a weird ending, we need to convert it to pixels + if ( rnumnonpx.test( ret ) ) { + + // Remember the original values + left = style.left; + rsLeft = elem.runtimeStyle && elem.runtimeStyle.left; + + // Put in the new values to get a computed value out + if ( rsLeft ) { + elem.runtimeStyle.left = elem.currentStyle.left; + } + style.left = name === "fontSize" ? "1em" : ret; + ret = style.pixelLeft + "px"; + + // Revert the changed values + style.left = left; + if ( rsLeft ) { + elem.runtimeStyle.left = rsLeft; + } + } + + return ret === "" ? "auto" : ret; + }; +} + +curCSS = getComputedStyle || currentStyle; + +function getWidthOrHeight( elem, name, extra ) { + + // Start with offset property + var val = name === "width" ? elem.offsetWidth : elem.offsetHeight, + i = name === "width" ? 1 : 0, + len = 4; + + if ( val > 0 ) { + if ( extra !== "border" ) { + for ( ; i < len; i += 2 ) { + if ( !extra ) { + val -= parseFloat( jQuery.css( elem, "padding" + cssExpand[ i ] ) ) || 0; + } + if ( extra === "margin" ) { + val += parseFloat( jQuery.css( elem, extra + cssExpand[ i ] ) ) || 0; + } else { + val -= parseFloat( jQuery.css( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; + } + } + } + + return val + "px"; + } + + // Fall back to computed then uncomputed css if necessary + val = curCSS( elem, name ); + if ( val < 0 || val == null ) { + val = elem.style[ name ]; + } + + // Computed unit is not pixels. Stop here and return. + if ( rnumnonpx.test(val) ) { + return val; + } + + // Normalize "", auto, and prepare for extra + val = parseFloat( val ) || 0; + + // Add padding, border, margin + if ( extra ) { + for ( ; i < len; i += 2 ) { + val += parseFloat( jQuery.css( elem, "padding" + cssExpand[ i ] ) ) || 0; + if ( extra !== "padding" ) { + val += parseFloat( jQuery.css( elem, "border" + cssExpand[ i ] + "Width" ) ) || 0; + } + if ( extra === "margin" ) { + val += parseFloat( jQuery.css( elem, extra + cssExpand[ i ]) ) || 0; + } + } + } + + return val + "px"; +} + +jQuery.each([ "height", "width" ], function( i, name ) { + jQuery.cssHooks[ name ] = { + get: function( elem, computed, extra ) { + if ( computed ) { + if ( elem.offsetWidth !== 0 ) { + return getWidthOrHeight( elem, name, extra ); + } else { + return jQuery.swap( elem, cssShow, function() { + return getWidthOrHeight( elem, name, extra ); + }); + } + } + }, + + set: function( elem, value ) { + return rnum.test( value ) ? + value + "px" : + value; + } + }; +}); + +if ( !jQuery.support.opacity ) { + jQuery.cssHooks.opacity = { + get: function( elem, computed ) { + // IE uses filters for opacity + return ropacity.test( (computed && elem.currentStyle ? elem.currentStyle.filter : elem.style.filter) || "" ) ? + ( parseFloat( RegExp.$1 ) / 100 ) + "" : + computed ? "1" : ""; + }, + + set: function( elem, value ) { + var style = elem.style, + currentStyle = elem.currentStyle, + opacity = jQuery.isNumeric( value ) ? "alpha(opacity=" + value * 100 + ")" : "", + filter = currentStyle && currentStyle.filter || style.filter || ""; + + // IE has trouble with opacity if it does not have layout + // Force it by setting the zoom level + style.zoom = 1; + + // if setting opacity to 1, and no other filters exist - attempt to remove filter attribute #6652 + if ( value >= 1 && jQuery.trim( filter.replace( ralpha, "" ) ) === "" ) { + + // Setting style.filter to null, "" & " " still leave "filter:" in the cssText + // if "filter:" is present at all, clearType is disabled, we want to avoid this + // style.removeAttribute is IE Only, but so apparently is this code path... + style.removeAttribute( "filter" ); + + // if there there is no filter style applied in a css rule, we are done + if ( currentStyle && !currentStyle.filter ) { + return; + } + } + + // otherwise, set new filter values + style.filter = ralpha.test( filter ) ? + filter.replace( ralpha, opacity ) : + filter + " " + opacity; + } + }; +} + +jQuery(function() { + // This hook cannot be added until DOM ready because the support test + // for it is not run until after DOM ready + if ( !jQuery.support.reliableMarginRight ) { + jQuery.cssHooks.marginRight = { + get: function( elem, computed ) { + // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right + // Work around by temporarily setting element display to inline-block + return jQuery.swap( elem, { "display": "inline-block" }, function() { + if ( computed ) { + return curCSS( elem, "margin-right" ); + } else { + return elem.style.marginRight; + } + }); + } + }; + } +}); + +if ( jQuery.expr && jQuery.expr.filters ) { + jQuery.expr.filters.hidden = function( elem ) { + var width = elem.offsetWidth, + height = elem.offsetHeight; + + return ( width === 0 && height === 0 ) || (!jQuery.support.reliableHiddenOffsets && ((elem.style && elem.style.display) || jQuery.css( elem, "display" )) === "none"); + }; + + jQuery.expr.filters.visible = function( elem ) { + return !jQuery.expr.filters.hidden( elem ); + }; +} + +// These hooks are used by animate to expand properties +jQuery.each({ + margin: "", + padding: "", + border: "Width" +}, function( prefix, suffix ) { + + jQuery.cssHooks[ prefix + suffix ] = { + expand: function( value ) { + var i, + + // assumes a single number if not a string + parts = typeof value === "string" ? value.split(" ") : [ value ], + expanded = {}; + + for ( i = 0; i < 4; i++ ) { + expanded[ prefix + cssExpand[ i ] + suffix ] = + parts[ i ] || parts[ i - 2 ] || parts[ 0 ]; + } + + return expanded; + } + }; +}); + + + + +var r20 = /%20/g, + rbracket = /\[\]$/, + rCRLF = /\r?\n/g, + rhash = /#.*$/, + rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, // IE leaves an \r character at EOL + rinput = /^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i, + // #7653, #8125, #8152: local protocol detection + rlocalProtocol = /^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/, + rnoContent = /^(?:GET|HEAD)$/, + rprotocol = /^\/\//, + rquery = /\?/, + rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, + rselectTextarea = /^(?:select|textarea)/i, + rspacesAjax = /\s+/, + rts = /([?&])_=[^&]*/, + rurl = /^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/, + + // Keep a copy of the old load method + _load = jQuery.fn.load, + + /* Prefilters + * 1) They are useful to introduce custom dataTypes (see ajax/jsonp.js for an example) + * 2) These are called: + * - BEFORE asking for a transport + * - AFTER param serialization (s.data is a string if s.processData is true) + * 3) key is the dataType + * 4) the catchall symbol "*" can be used + * 5) execution will start with transport dataType and THEN continue down to "*" if needed + */ + prefilters = {}, + + /* Transports bindings + * 1) key is the dataType + * 2) the catchall symbol "*" can be used + * 3) selection will start with transport dataType and THEN go to "*" if needed + */ + transports = {}, + + // Document location + ajaxLocation, + + // Document location segments + ajaxLocParts, + + // Avoid comment-prolog char sequence (#10098); must appease lint and evade compression + allTypes = ["*/"] + ["*"]; + +// #8138, IE may throw an exception when accessing +// a field from window.location if document.domain has been set +try { + ajaxLocation = location.href; +} catch( e ) { + // Use the href attribute of an A element + // since IE will modify it given document.location + ajaxLocation = document.createElement( "a" ); + ajaxLocation.href = ""; + ajaxLocation = ajaxLocation.href; +} + +// Segment location into parts +ajaxLocParts = rurl.exec( ajaxLocation.toLowerCase() ) || []; + +// Base "constructor" for jQuery.ajaxPrefilter and jQuery.ajaxTransport +function addToPrefiltersOrTransports( structure ) { + + // dataTypeExpression is optional and defaults to "*" + return function( dataTypeExpression, func ) { + + if ( typeof dataTypeExpression !== "string" ) { + func = dataTypeExpression; + dataTypeExpression = "*"; + } + + if ( jQuery.isFunction( func ) ) { + var dataTypes = dataTypeExpression.toLowerCase().split( rspacesAjax ), + i = 0, + length = dataTypes.length, + dataType, + list, + placeBefore; + + // For each dataType in the dataTypeExpression + for ( ; i < length; i++ ) { + dataType = dataTypes[ i ]; + // We control if we're asked to add before + // any existing element + placeBefore = /^\+/.test( dataType ); + if ( placeBefore ) { + dataType = dataType.substr( 1 ) || "*"; + } + list = structure[ dataType ] = structure[ dataType ] || []; + // then we add to the structure accordingly + list[ placeBefore ? "unshift" : "push" ]( func ); + } + } + }; +} + +// Base inspection function for prefilters and transports +function inspectPrefiltersOrTransports( structure, options, originalOptions, jqXHR, + dataType /* internal */, inspected /* internal */ ) { + + dataType = dataType || options.dataTypes[ 0 ]; + inspected = inspected || {}; + + inspected[ dataType ] = true; + + var list = structure[ dataType ], + i = 0, + length = list ? list.length : 0, + executeOnly = ( structure === prefilters ), + selection; + + for ( ; i < length && ( executeOnly || !selection ); i++ ) { + selection = list[ i ]( options, originalOptions, jqXHR ); + // If we got redirected to another dataType + // we try there if executing only and not done already + if ( typeof selection === "string" ) { + if ( !executeOnly || inspected[ selection ] ) { + selection = undefined; + } else { + options.dataTypes.unshift( selection ); + selection = inspectPrefiltersOrTransports( + structure, options, originalOptions, jqXHR, selection, inspected ); + } + } + } + // If we're only executing or nothing was selected + // we try the catchall dataType if not done already + if ( ( executeOnly || !selection ) && !inspected[ "*" ] ) { + selection = inspectPrefiltersOrTransports( + structure, options, originalOptions, jqXHR, "*", inspected ); + } + // unnecessary when only executing (prefilters) + // but it'll be ignored by the caller in that case + return selection; +} + +// A special extend for ajax options +// that takes "flat" options (not to be deep extended) +// Fixes #9887 +function ajaxExtend( target, src ) { + var key, deep, + flatOptions = jQuery.ajaxSettings.flatOptions || {}; + for ( key in src ) { + if ( src[ key ] !== undefined ) { + ( flatOptions[ key ] ? target : ( deep || ( deep = {} ) ) )[ key ] = src[ key ]; + } + } + if ( deep ) { + jQuery.extend( true, target, deep ); + } +} + +jQuery.fn.extend({ + load: function( url, params, callback ) { + if ( typeof url !== "string" && _load ) { + return _load.apply( this, arguments ); + + // Don't do a request if no elements are being requested + } else if ( !this.length ) { + return this; + } + + var off = url.indexOf( " " ); + if ( off >= 0 ) { + var selector = url.slice( off, url.length ); + url = url.slice( 0, off ); + } + + // Default to a GET request + var type = "GET"; + + // If the second parameter was provided + if ( params ) { + // If it's a function + if ( jQuery.isFunction( params ) ) { + // We assume that it's the callback + callback = params; + params = undefined; + + // Otherwise, build a param string + } else if ( typeof params === "object" ) { + params = jQuery.param( params, jQuery.ajaxSettings.traditional ); + type = "POST"; + } + } + + var self = this; + + // Request the remote document + jQuery.ajax({ + url: url, + type: type, + dataType: "html", + data: params, + // Complete callback (responseText is used internally) + complete: function( jqXHR, status, responseText ) { + // Store the response as specified by the jqXHR object + responseText = jqXHR.responseText; + // If successful, inject the HTML into all the matched elements + if ( jqXHR.isResolved() ) { + // #4825: Get the actual response in case + // a dataFilter is present in ajaxSettings + jqXHR.done(function( r ) { + responseText = r; + }); + // See if a selector was specified + self.html( selector ? + // Create a dummy div to hold the results + jQuery("<div>") + // inject the contents of the document in, removing the scripts + // to avoid any 'Permission Denied' errors in IE + .append(responseText.replace(rscript, "")) + + // Locate the specified elements + .find(selector) : + + // If not, just inject the full result + responseText ); + } + + if ( callback ) { + self.each( callback, [ responseText, status, jqXHR ] ); + } + } + }); + + return this; + }, + + serialize: function() { + return jQuery.param( this.serializeArray() ); + }, + + serializeArray: function() { + return this.map(function(){ + return this.elements ? jQuery.makeArray( this.elements ) : this; + }) + .filter(function(){ + return this.name && !this.disabled && + ( this.checked || rselectTextarea.test( this.nodeName ) || + rinput.test( this.type ) ); + }) + .map(function( i, elem ){ + var val = jQuery( this ).val(); + + return val == null ? + null : + jQuery.isArray( val ) ? + jQuery.map( val, function( val, i ){ + return { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }) : + { name: elem.name, value: val.replace( rCRLF, "\r\n" ) }; + }).get(); + } +}); + +// Attach a bunch of functions for handling common AJAX events +jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split( " " ), function( i, o ){ + jQuery.fn[ o ] = function( f ){ + return this.on( o, f ); + }; +}); + +jQuery.each( [ "get", "post" ], function( i, method ) { + jQuery[ method ] = function( url, data, callback, type ) { + // shift arguments if data argument was omitted + if ( jQuery.isFunction( data ) ) { + type = type || callback; + callback = data; + data = undefined; + } + + return jQuery.ajax({ + type: method, + url: url, + data: data, + success: callback, + dataType: type + }); + }; +}); + +jQuery.extend({ + + getScript: function( url, callback ) { + return jQuery.get( url, undefined, callback, "script" ); + }, + + getJSON: function( url, data, callback ) { + return jQuery.get( url, data, callback, "json" ); + }, + + // Creates a full fledged settings object into target + // with both ajaxSettings and settings fields. + // If target is omitted, writes into ajaxSettings. + ajaxSetup: function( target, settings ) { + if ( settings ) { + // Building a settings object + ajaxExtend( target, jQuery.ajaxSettings ); + } else { + // Extending ajaxSettings + settings = target; + target = jQuery.ajaxSettings; + } + ajaxExtend( target, settings ); + return target; + }, + + ajaxSettings: { + url: ajaxLocation, + isLocal: rlocalProtocol.test( ajaxLocParts[ 1 ] ), + global: true, + type: "GET", + contentType: "application/x-www-form-urlencoded; charset=UTF-8", + processData: true, + async: true, + /* + timeout: 0, + data: null, + dataType: null, + username: null, + password: null, + cache: null, + traditional: false, + headers: {}, + */ + + accepts: { + xml: "application/xml, text/xml", + html: "text/html", + text: "text/plain", + json: "application/json, text/javascript", + "*": allTypes + }, + + contents: { + xml: /xml/, + html: /html/, + json: /json/ + }, + + responseFields: { + xml: "responseXML", + text: "responseText" + }, + + // List of data converters + // 1) key format is "source_type destination_type" (a single space in-between) + // 2) the catchall symbol "*" can be used for source_type + converters: { + + // Convert anything to text + "* text": window.String, + + // Text to html (true = no transformation) + "text html": true, + + // Evaluate text as a json expression + "text json": jQuery.parseJSON, + + // Parse text as xml + "text xml": jQuery.parseXML + }, + + // For options that shouldn't be deep extended: + // you can add your own custom options here if + // and when you create one that shouldn't be + // deep extended (see ajaxExtend) + flatOptions: { + context: true, + url: true + } + }, + + ajaxPrefilter: addToPrefiltersOrTransports( prefilters ), + ajaxTransport: addToPrefiltersOrTransports( transports ), + + // Main method + ajax: function( url, options ) { + + // If url is an object, simulate pre-1.5 signature + if ( typeof url === "object" ) { + options = url; + url = undefined; + } + + // Force options to be an object + options = options || {}; + + var // Create the final options object + s = jQuery.ajaxSetup( {}, options ), + // Callbacks context + callbackContext = s.context || s, + // Context for global events + // It's the callbackContext if one was provided in the options + // and if it's a DOM node or a jQuery collection + globalEventContext = callbackContext !== s && + ( callbackContext.nodeType || callbackContext instanceof jQuery ) ? + jQuery( callbackContext ) : jQuery.event, + // Deferreds + deferred = jQuery.Deferred(), + completeDeferred = jQuery.Callbacks( "once memory" ), + // Status-dependent callbacks + statusCode = s.statusCode || {}, + // ifModified key + ifModifiedKey, + // Headers (they are sent all at once) + requestHeaders = {}, + requestHeadersNames = {}, + // Response headers + responseHeadersString, + responseHeaders, + // transport + transport, + // timeout handle + timeoutTimer, + // Cross-domain detection vars + parts, + // The jqXHR state + state = 0, + // To know if global events are to be dispatched + fireGlobals, + // Loop variable + i, + // Fake xhr + jqXHR = { + + readyState: 0, + + // Caches the header + setRequestHeader: function( name, value ) { + if ( !state ) { + var lname = name.toLowerCase(); + name = requestHeadersNames[ lname ] = requestHeadersNames[ lname ] || name; + requestHeaders[ name ] = value; + } + return this; + }, + + // Raw string + getAllResponseHeaders: function() { + return state === 2 ? responseHeadersString : null; + }, + + // Builds headers hashtable if needed + getResponseHeader: function( key ) { + var match; + if ( state === 2 ) { + if ( !responseHeaders ) { + responseHeaders = {}; + while( ( match = rheaders.exec( responseHeadersString ) ) ) { + responseHeaders[ match[1].toLowerCase() ] = match[ 2 ]; + } + } + match = responseHeaders[ key.toLowerCase() ]; + } + return match === undefined ? null : match; + }, + + // Overrides response content-type header + overrideMimeType: function( type ) { + if ( !state ) { + s.mimeType = type; + } + return this; + }, + + // Cancel the request + abort: function( statusText ) { + statusText = statusText || "abort"; + if ( transport ) { + transport.abort( statusText ); + } + done( 0, statusText ); + return this; + } + }; + + // Callback for when everything is done + // It is defined here because jslint complains if it is declared + // at the end of the function (which would be more logical and readable) + function done( status, nativeStatusText, responses, headers ) { + + // Called once + if ( state === 2 ) { + return; + } + + // State is "done" now + state = 2; + + // Clear timeout if it exists + if ( timeoutTimer ) { + clearTimeout( timeoutTimer ); + } + + // Dereference transport for early garbage collection + // (no matter how long the jqXHR object will be used) + transport = undefined; + + // Cache response headers + responseHeadersString = headers || ""; + + // Set readyState + jqXHR.readyState = status > 0 ? 4 : 0; + + var isSuccess, + success, + error, + statusText = nativeStatusText, + response = responses ? ajaxHandleResponses( s, jqXHR, responses ) : undefined, + lastModified, + etag; + + // If successful, handle type chaining + if ( status >= 200 && status < 300 || status === 304 ) { + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + + if ( ( lastModified = jqXHR.getResponseHeader( "Last-Modified" ) ) ) { + jQuery.lastModified[ ifModifiedKey ] = lastModified; + } + if ( ( etag = jqXHR.getResponseHeader( "Etag" ) ) ) { + jQuery.etag[ ifModifiedKey ] = etag; + } + } + + // If not modified + if ( status === 304 ) { + + statusText = "notmodified"; + isSuccess = true; + + // If we have data + } else { + + try { + success = ajaxConvert( s, response ); + statusText = "success"; + isSuccess = true; + } catch(e) { + // We have a parsererror + statusText = "parsererror"; + error = e; + } + } + } else { + // We extract error from statusText + // then normalize statusText and status for non-aborts + error = statusText; + if ( !statusText || status ) { + statusText = "error"; + if ( status < 0 ) { + status = 0; + } + } + } + + // Set data for the fake xhr object + jqXHR.status = status; + jqXHR.statusText = "" + ( nativeStatusText || statusText ); + + // Success/Error + if ( isSuccess ) { + deferred.resolveWith( callbackContext, [ success, statusText, jqXHR ] ); + } else { + deferred.rejectWith( callbackContext, [ jqXHR, statusText, error ] ); + } + + // Status-dependent callbacks + jqXHR.statusCode( statusCode ); + statusCode = undefined; + + if ( fireGlobals ) { + globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ), + [ jqXHR, s, isSuccess ? success : error ] ); + } + + // Complete + completeDeferred.fireWith( callbackContext, [ jqXHR, statusText ] ); + + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxComplete", [ jqXHR, s ] ); + // Handle the global AJAX counter + if ( !( --jQuery.active ) ) { + jQuery.event.trigger( "ajaxStop" ); + } + } + } + + // Attach deferreds + deferred.promise( jqXHR ); + jqXHR.success = jqXHR.done; + jqXHR.error = jqXHR.fail; + jqXHR.complete = completeDeferred.add; + + // Status-dependent callbacks + jqXHR.statusCode = function( map ) { + if ( map ) { + var tmp; + if ( state < 2 ) { + for ( tmp in map ) { + statusCode[ tmp ] = [ statusCode[tmp], map[tmp] ]; + } + } else { + tmp = map[ jqXHR.status ]; + jqXHR.then( tmp, tmp ); + } + } + return this; + }; + + // Remove hash character (#7531: and string promotion) + // Add protocol if not provided (#5866: IE7 issue with protocol-less urls) + // We also use the url parameter if available + s.url = ( ( url || s.url ) + "" ).replace( rhash, "" ).replace( rprotocol, ajaxLocParts[ 1 ] + "//" ); + + // Extract dataTypes list + s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( rspacesAjax ); + + // Determine if a cross-domain request is in order + if ( s.crossDomain == null ) { + parts = rurl.exec( s.url.toLowerCase() ); + s.crossDomain = !!( parts && + ( parts[ 1 ] != ajaxLocParts[ 1 ] || parts[ 2 ] != ajaxLocParts[ 2 ] || + ( parts[ 3 ] || ( parts[ 1 ] === "http:" ? 80 : 443 ) ) != + ( ajaxLocParts[ 3 ] || ( ajaxLocParts[ 1 ] === "http:" ? 80 : 443 ) ) ) + ); + } + + // Convert data if not already a string + if ( s.data && s.processData && typeof s.data !== "string" ) { + s.data = jQuery.param( s.data, s.traditional ); + } + + // Apply prefilters + inspectPrefiltersOrTransports( prefilters, s, options, jqXHR ); + + // If request was aborted inside a prefilter, stop there + if ( state === 2 ) { + return false; + } + + // We can fire global events as of now if asked to + fireGlobals = s.global; + + // Uppercase the type + s.type = s.type.toUpperCase(); + + // Determine if request has content + s.hasContent = !rnoContent.test( s.type ); + + // Watch for a new set of requests + if ( fireGlobals && jQuery.active++ === 0 ) { + jQuery.event.trigger( "ajaxStart" ); + } + + // More options handling for requests with no content + if ( !s.hasContent ) { + + // If data is available, append data to url + if ( s.data ) { + s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data; + // #9682: remove data so that it's not used in an eventual retry + delete s.data; + } + + // Get ifModifiedKey before adding the anti-cache parameter + ifModifiedKey = s.url; + + // Add anti-cache in url if needed + if ( s.cache === false ) { + + var ts = jQuery.now(), + // try replacing _= if it is there + ret = s.url.replace( rts, "$1_=" + ts ); + + // if nothing was replaced, add timestamp to the end + s.url = ret + ( ( ret === s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "" ); + } + } + + // Set the correct header, if data is being sent + if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) { + jqXHR.setRequestHeader( "Content-Type", s.contentType ); + } + + // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode. + if ( s.ifModified ) { + ifModifiedKey = ifModifiedKey || s.url; + if ( jQuery.lastModified[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-Modified-Since", jQuery.lastModified[ ifModifiedKey ] ); + } + if ( jQuery.etag[ ifModifiedKey ] ) { + jqXHR.setRequestHeader( "If-None-Match", jQuery.etag[ ifModifiedKey ] ); + } + } + + // Set the Accepts header for the server, depending on the dataType + jqXHR.setRequestHeader( + "Accept", + s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[0] ] ? + s.accepts[ s.dataTypes[0] ] + ( s.dataTypes[ 0 ] !== "*" ? ", " + allTypes + "; q=0.01" : "" ) : + s.accepts[ "*" ] + ); + + // Check for headers option + for ( i in s.headers ) { + jqXHR.setRequestHeader( i, s.headers[ i ] ); + } + + // Allow custom headers/mimetypes and early abort + if ( s.beforeSend && ( s.beforeSend.call( callbackContext, jqXHR, s ) === false || state === 2 ) ) { + // Abort if not done already + jqXHR.abort(); + return false; + + } + + // Install callbacks on deferreds + for ( i in { success: 1, error: 1, complete: 1 } ) { + jqXHR[ i ]( s[ i ] ); + } + + // Get transport + transport = inspectPrefiltersOrTransports( transports, s, options, jqXHR ); + + // If no transport, we auto-abort + if ( !transport ) { + done( -1, "No Transport" ); + } else { + jqXHR.readyState = 1; + // Send global event + if ( fireGlobals ) { + globalEventContext.trigger( "ajaxSend", [ jqXHR, s ] ); + } + // Timeout + if ( s.async && s.timeout > 0 ) { + timeoutTimer = setTimeout( function(){ + jqXHR.abort( "timeout" ); + }, s.timeout ); + } + + try { + state = 1; + transport.send( requestHeaders, done ); + } catch (e) { + // Propagate exception as error if not done + if ( state < 2 ) { + done( -1, e ); + // Simply rethrow otherwise + } else { + throw e; + } + } + } + + return jqXHR; + }, + + // Serialize an array of form elements or a set of + // key/values into a query string + param: function( a, traditional ) { + var s = [], + add = function( key, value ) { + // If value is a function, invoke it and return its value + value = jQuery.isFunction( value ) ? value() : value; + s[ s.length ] = encodeURIComponent( key ) + "=" + encodeURIComponent( value ); + }; + + // Set traditional to true for jQuery <= 1.3.2 behavior. + if ( traditional === undefined ) { + traditional = jQuery.ajaxSettings.traditional; + } + + // If an array was passed in, assume that it is an array of form elements. + if ( jQuery.isArray( a ) || ( a.jquery && !jQuery.isPlainObject( a ) ) ) { + // Serialize the form elements + jQuery.each( a, function() { + add( this.name, this.value ); + }); + + } else { + // If traditional, encode the "old" way (the way 1.3.2 or older + // did it), otherwise encode params recursively. + for ( var prefix in a ) { + buildParams( prefix, a[ prefix ], traditional, add ); + } + } + + // Return the resulting serialization + return s.join( "&" ).replace( r20, "+" ); + } +}); + +function buildParams( prefix, obj, traditional, add ) { + if ( jQuery.isArray( obj ) ) { + // Serialize array item. + jQuery.each( obj, function( i, v ) { + if ( traditional || rbracket.test( prefix ) ) { + // Treat each array item as a scalar. + add( prefix, v ); + + } else { + // If array item is non-scalar (array or object), encode its + // numeric index to resolve deserialization ambiguity issues. + // Note that rack (as of 1.0.0) can't currently deserialize + // nested arrays properly, and attempting to do so may cause + // a server error. Possible fixes are to modify rack's + // deserialization algorithm or to provide an option or flag + // to force array serialization to be shallow. + buildParams( prefix + "[" + ( typeof v === "object" ? i : "" ) + "]", v, traditional, add ); + } + }); + + } else if ( !traditional && jQuery.type( obj ) === "object" ) { + // Serialize object item. + for ( var name in obj ) { + buildParams( prefix + "[" + name + "]", obj[ name ], traditional, add ); + } + + } else { + // Serialize scalar item. + add( prefix, obj ); + } +} + +// This is still on the jQuery object... for now +// Want to move this to jQuery.ajax some day +jQuery.extend({ + + // Counter for holding the number of active queries + active: 0, + + // Last-Modified header cache for next request + lastModified: {}, + etag: {} + +}); + +/* Handles responses to an ajax request: + * - sets all responseXXX fields accordingly + * - finds the right dataType (mediates between content-type and expected dataType) + * - returns the corresponding response + */ +function ajaxHandleResponses( s, jqXHR, responses ) { + + var contents = s.contents, + dataTypes = s.dataTypes, + responseFields = s.responseFields, + ct, + type, + finalDataType, + firstDataType; + + // Fill responseXXX fields + for ( type in responseFields ) { + if ( type in responses ) { + jqXHR[ responseFields[type] ] = responses[ type ]; + } + } + + // Remove auto dataType and get content-type in the process + while( dataTypes[ 0 ] === "*" ) { + dataTypes.shift(); + if ( ct === undefined ) { + ct = s.mimeType || jqXHR.getResponseHeader( "content-type" ); + } + } + + // Check if we're dealing with a known content-type + if ( ct ) { + for ( type in contents ) { + if ( contents[ type ] && contents[ type ].test( ct ) ) { + dataTypes.unshift( type ); + break; + } + } + } + + // Check to see if we have a response for the expected dataType + if ( dataTypes[ 0 ] in responses ) { + finalDataType = dataTypes[ 0 ]; + } else { + // Try convertible dataTypes + for ( type in responses ) { + if ( !dataTypes[ 0 ] || s.converters[ type + " " + dataTypes[0] ] ) { + finalDataType = type; + break; + } + if ( !firstDataType ) { + firstDataType = type; + } + } + // Or just use first one + finalDataType = finalDataType || firstDataType; + } + + // If we found a dataType + // We add the dataType to the list if needed + // and return the corresponding response + if ( finalDataType ) { + if ( finalDataType !== dataTypes[ 0 ] ) { + dataTypes.unshift( finalDataType ); + } + return responses[ finalDataType ]; + } +} + +// Chain conversions given the request and the original response +function ajaxConvert( s, response ) { + + // Apply the dataFilter if provided + if ( s.dataFilter ) { + response = s.dataFilter( response, s.dataType ); + } + + var dataTypes = s.dataTypes, + converters = {}, + i, + key, + length = dataTypes.length, + tmp, + // Current and previous dataTypes + current = dataTypes[ 0 ], + prev, + // Conversion expression + conversion, + // Conversion function + conv, + // Conversion functions (transitive conversion) + conv1, + conv2; + + // For each dataType in the chain + for ( i = 1; i < length; i++ ) { + + // Create converters map + // with lowercased keys + if ( i === 1 ) { + for ( key in s.converters ) { + if ( typeof key === "string" ) { + converters[ key.toLowerCase() ] = s.converters[ key ]; + } + } + } + + // Get the dataTypes + prev = current; + current = dataTypes[ i ]; + + // If current is auto dataType, update it to prev + if ( current === "*" ) { + current = prev; + // If no auto and dataTypes are actually different + } else if ( prev !== "*" && prev !== current ) { + + // Get the converter + conversion = prev + " " + current; + conv = converters[ conversion ] || converters[ "* " + current ]; + + // If there is no direct converter, search transitively + if ( !conv ) { + conv2 = undefined; + for ( conv1 in converters ) { + tmp = conv1.split( " " ); + if ( tmp[ 0 ] === prev || tmp[ 0 ] === "*" ) { + conv2 = converters[ tmp[1] + " " + current ]; + if ( conv2 ) { + conv1 = converters[ conv1 ]; + if ( conv1 === true ) { + conv = conv2; + } else if ( conv2 === true ) { + conv = conv1; + } + break; + } + } + } + } + // If we found no converter, dispatch an error + if ( !( conv || conv2 ) ) { + jQuery.error( "No conversion from " + conversion.replace(" "," to ") ); + } + // If found converter is not an equivalence + if ( conv !== true ) { + // Convert with 1 or 2 converters accordingly + response = conv ? conv( response ) : conv2( conv1(response) ); + } + } + } + return response; +} + + + + +var jsc = jQuery.now(), + jsre = /(\=)\?(&|$)|\?\?/i; + +// Default jsonp settings +jQuery.ajaxSetup({ + jsonp: "callback", + jsonpCallback: function() { + return jQuery.expando + "_" + ( jsc++ ); + } +}); + +// Detect, normalize options and install callbacks for jsonp requests +jQuery.ajaxPrefilter( "json jsonp", function( s, originalSettings, jqXHR ) { + + var inspectData = ( typeof s.data === "string" ) && /^application\/x\-www\-form\-urlencoded/.test( s.contentType ); + + if ( s.dataTypes[ 0 ] === "jsonp" || + s.jsonp !== false && ( jsre.test( s.url ) || + inspectData && jsre.test( s.data ) ) ) { + + var responseContainer, + jsonpCallback = s.jsonpCallback = + jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback, + previous = window[ jsonpCallback ], + url = s.url, + data = s.data, + replace = "$1" + jsonpCallback + "$2"; + + if ( s.jsonp !== false ) { + url = url.replace( jsre, replace ); + if ( s.url === url ) { + if ( inspectData ) { + data = data.replace( jsre, replace ); + } + if ( s.data === data ) { + // Add callback manually + url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback; + } + } + } + + s.url = url; + s.data = data; + + // Install callback + window[ jsonpCallback ] = function( response ) { + responseContainer = [ response ]; + }; + + // Clean-up function + jqXHR.always(function() { + // Set callback back to previous value + window[ jsonpCallback ] = previous; + // Call if it was a function and we have a response + if ( responseContainer && jQuery.isFunction( previous ) ) { + window[ jsonpCallback ]( responseContainer[ 0 ] ); + } + }); + + // Use data converter to retrieve json after script execution + s.converters["script json"] = function() { + if ( !responseContainer ) { + jQuery.error( jsonpCallback + " was not called" ); + } + return responseContainer[ 0 ]; + }; + + // force json dataType + s.dataTypes[ 0 ] = "json"; + + // Delegate to script + return "script"; + } +}); + + + + +// Install script dataType +jQuery.ajaxSetup({ + accepts: { + script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript" + }, + contents: { + script: /javascript|ecmascript/ + }, + converters: { + "text script": function( text ) { + jQuery.globalEval( text ); + return text; + } + } +}); + +// Handle cache's special case and global +jQuery.ajaxPrefilter( "script", function( s ) { + if ( s.cache === undefined ) { + s.cache = false; + } + if ( s.crossDomain ) { + s.type = "GET"; + s.global = false; + } +}); + +// Bind script tag hack transport +jQuery.ajaxTransport( "script", function(s) { + + // This transport only deals with cross domain requests + if ( s.crossDomain ) { + + var script, + head = document.head || document.getElementsByTagName( "head" )[0] || document.documentElement; + + return { + + send: function( _, callback ) { + + script = document.createElement( "script" ); + + script.async = "async"; + + if ( s.scriptCharset ) { + script.charset = s.scriptCharset; + } + + script.src = s.url; + + // Attach handlers for all browsers + script.onload = script.onreadystatechange = function( _, isAbort ) { + + if ( isAbort || !script.readyState || /loaded|complete/.test( script.readyState ) ) { + + // Handle memory leak in IE + script.onload = script.onreadystatechange = null; + + // Remove the script + if ( head && script.parentNode ) { + head.removeChild( script ); + } + + // Dereference the script + script = undefined; + + // Callback if not abort + if ( !isAbort ) { + callback( 200, "success" ); + } + } + }; + // Use insertBefore instead of appendChild to circumvent an IE6 bug. + // This arises when a base node is used (#2709 and #4378). + head.insertBefore( script, head.firstChild ); + }, + + abort: function() { + if ( script ) { + script.onload( 0, 1 ); + } + } + }; + } +}); + + + + +var // #5280: Internet Explorer will keep connections alive if we don't abort on unload + xhrOnUnloadAbort = window.ActiveXObject ? function() { + // Abort all pending requests + for ( var key in xhrCallbacks ) { + xhrCallbacks[ key ]( 0, 1 ); + } + } : false, + xhrId = 0, + xhrCallbacks; + +// Functions to create xhrs +function createStandardXHR() { + try { + return new window.XMLHttpRequest(); + } catch( e ) {} +} + +function createActiveXHR() { + try { + return new window.ActiveXObject( "Microsoft.XMLHTTP" ); + } catch( e ) {} +} + +// Create the request object +// (This is still attached to ajaxSettings for backward compatibility) +jQuery.ajaxSettings.xhr = window.ActiveXObject ? + /* Microsoft failed to properly + * implement the XMLHttpRequest in IE7 (can't request local files), + * so we use the ActiveXObject when it is available + * Additionally XMLHttpRequest can be disabled in IE7/IE8 so + * we need a fallback. + */ + function() { + return !this.isLocal && createStandardXHR() || createActiveXHR(); + } : + // For all other browsers, use the standard XMLHttpRequest object + createStandardXHR; + +// Determine support properties +(function( xhr ) { + jQuery.extend( jQuery.support, { + ajax: !!xhr, + cors: !!xhr && ( "withCredentials" in xhr ) + }); +})( jQuery.ajaxSettings.xhr() ); + +// Create transport if the browser can provide an xhr +if ( jQuery.support.ajax ) { + + jQuery.ajaxTransport(function( s ) { + // Cross domain only allowed if supported through XMLHttpRequest + if ( !s.crossDomain || jQuery.support.cors ) { + + var callback; + + return { + send: function( headers, complete ) { + + // Get a new xhr + var xhr = s.xhr(), + handle, + i; + + // Open the socket + // Passing null username, generates a login popup on Opera (#2865) + if ( s.username ) { + xhr.open( s.type, s.url, s.async, s.username, s.password ); + } else { + xhr.open( s.type, s.url, s.async ); + } + + // Apply custom fields if provided + if ( s.xhrFields ) { + for ( i in s.xhrFields ) { + xhr[ i ] = s.xhrFields[ i ]; + } + } + + // Override mime type if needed + if ( s.mimeType && xhr.overrideMimeType ) { + xhr.overrideMimeType( s.mimeType ); + } + + // X-Requested-With header + // For cross-domain requests, seeing as conditions for a preflight are + // akin to a jigsaw puzzle, we simply never set it to be sure. + // (it can always be set on a per-request basis or even using ajaxSetup) + // For same-domain requests, won't change header if already provided. + if ( !s.crossDomain && !headers["X-Requested-With"] ) { + headers[ "X-Requested-With" ] = "XMLHttpRequest"; + } + + // Need an extra try/catch for cross domain requests in Firefox 3 + try { + for ( i in headers ) { + xhr.setRequestHeader( i, headers[ i ] ); + } + } catch( _ ) {} + + // Do send the request + // This may raise an exception which is actually + // handled in jQuery.ajax (so no try/catch here) + xhr.send( ( s.hasContent && s.data ) || null ); + + // Listener + callback = function( _, isAbort ) { + + var status, + statusText, + responseHeaders, + responses, + xml; + + // Firefox throws exceptions when accessing properties + // of an xhr when a network error occured + // http://helpful.knobs-dials.com/index.php/Component_returned_failure_code:_0x80040111_(NS_ERROR_NOT_AVAILABLE) + try { + + // Was never called and is aborted or complete + if ( callback && ( isAbort || xhr.readyState === 4 ) ) { + + // Only called once + callback = undefined; + + // Do not keep as active anymore + if ( handle ) { + xhr.onreadystatechange = jQuery.noop; + if ( xhrOnUnloadAbort ) { + delete xhrCallbacks[ handle ]; + } + } + + // If it's an abort + if ( isAbort ) { + // Abort it manually if needed + if ( xhr.readyState !== 4 ) { + xhr.abort(); + } + } else { + status = xhr.status; + responseHeaders = xhr.getAllResponseHeaders(); + responses = {}; + xml = xhr.responseXML; + + // Construct response list + if ( xml && xml.documentElement /* #4958 */ ) { + responses.xml = xml; + } + + // When requesting binary data, IE6-9 will throw an exception + // on any attempt to access responseText (#11426) + try { + responses.text = xhr.responseText; + } catch( _ ) { + } + + // Firefox throws an exception when accessing + // statusText for faulty cross-domain requests + try { + statusText = xhr.statusText; + } catch( e ) { + // We normalize with Webkit giving an empty statusText + statusText = ""; + } + + // Filter status for non standard behaviors + + // If the request is local and we have data: assume a success + // (success with no data won't get notified, that's the best we + // can do given current implementations) + if ( !status && s.isLocal && !s.crossDomain ) { + status = responses.text ? 200 : 404; + // IE - #1450: sometimes returns 1223 when it should be 204 + } else if ( status === 1223 ) { + status = 204; + } + } + } + } catch( firefoxAccessException ) { + if ( !isAbort ) { + complete( -1, firefoxAccessException ); + } + } + + // Call complete if needed + if ( responses ) { + complete( status, statusText, responses, responseHeaders ); + } + }; + + // if we're in sync mode or it's in cache + // and has been retrieved directly (IE6 & IE7) + // we need to manually fire the callback + if ( !s.async || xhr.readyState === 4 ) { + callback(); + } else { + handle = ++xhrId; + if ( xhrOnUnloadAbort ) { + // Create the active xhrs callbacks list if needed + // and attach the unload handler + if ( !xhrCallbacks ) { + xhrCallbacks = {}; + jQuery( window ).unload( xhrOnUnloadAbort ); + } + // Add to list of active xhrs callbacks + xhrCallbacks[ handle ] = callback; + } + xhr.onreadystatechange = callback; + } + }, + + abort: function() { + if ( callback ) { + callback(0,1); + } + } + }; + } + }); +} + + + + +var elemdisplay = {}, + iframe, iframeDoc, + rfxtypes = /^(?:toggle|show|hide)$/, + rfxnum = /^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i, + timerId, + fxAttrs = [ + // height animations + [ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ], + // width animations + [ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ], + // opacity animations + [ "opacity" ] + ], + fxNow; + +jQuery.fn.extend({ + show: function( speed, easing, callback ) { + var elem, display; + + if ( speed || speed === 0 ) { + return this.animate( genFx("show", 3), speed, easing, callback ); + + } else { + for ( var i = 0, j = this.length; i < j; i++ ) { + elem = this[ i ]; + + if ( elem.style ) { + display = elem.style.display; + + // Reset the inline display of this element to learn if it is + // being hidden by cascaded rules or not + if ( !jQuery._data(elem, "olddisplay") && display === "none" ) { + display = elem.style.display = ""; + } + + // Set elements which have been overridden with display: none + // in a stylesheet to whatever the default browser style is + // for such an element + if ( (display === "" && jQuery.css(elem, "display") === "none") || + !jQuery.contains( elem.ownerDocument.documentElement, elem ) ) { + jQuery._data( elem, "olddisplay", defaultDisplay(elem.nodeName) ); + } + } + } + + // Set the display of most of the elements in a second loop + // to avoid the constant reflow + for ( i = 0; i < j; i++ ) { + elem = this[ i ]; + + if ( elem.style ) { + display = elem.style.display; + + if ( display === "" || display === "none" ) { + elem.style.display = jQuery._data( elem, "olddisplay" ) || ""; + } + } + } + + return this; + } + }, + + hide: function( speed, easing, callback ) { + if ( speed || speed === 0 ) { + return this.animate( genFx("hide", 3), speed, easing, callback); + + } else { + var elem, display, + i = 0, + j = this.length; + + for ( ; i < j; i++ ) { + elem = this[i]; + if ( elem.style ) { + display = jQuery.css( elem, "display" ); + + if ( display !== "none" && !jQuery._data( elem, "olddisplay" ) ) { + jQuery._data( elem, "olddisplay", display ); + } + } + } + + // Set the display of the elements in a second loop + // to avoid the constant reflow + for ( i = 0; i < j; i++ ) { + if ( this[i].style ) { + this[i].style.display = "none"; + } + } + + return this; + } + }, + + // Save the old toggle function + _toggle: jQuery.fn.toggle, + + toggle: function( fn, fn2, callback ) { + var bool = typeof fn === "boolean"; + + if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) { + this._toggle.apply( this, arguments ); + + } else if ( fn == null || bool ) { + this.each(function() { + var state = bool ? fn : jQuery(this).is(":hidden"); + jQuery(this)[ state ? "show" : "hide" ](); + }); + + } else { + this.animate(genFx("toggle", 3), fn, fn2, callback); + } + + return this; + }, + + fadeTo: function( speed, to, easing, callback ) { + return this.filter(":hidden").css("opacity", 0).show().end() + .animate({opacity: to}, speed, easing, callback); + }, + + animate: function( prop, speed, easing, callback ) { + var optall = jQuery.speed( speed, easing, callback ); + + if ( jQuery.isEmptyObject( prop ) ) { + return this.each( optall.complete, [ false ] ); + } + + // Do not change referenced properties as per-property easing will be lost + prop = jQuery.extend( {}, prop ); + + function doAnimation() { + // XXX 'this' does not always have a nodeName when running the + // test suite + + if ( optall.queue === false ) { + jQuery._mark( this ); + } + + var opt = jQuery.extend( {}, optall ), + isElement = this.nodeType === 1, + hidden = isElement && jQuery(this).is(":hidden"), + name, val, p, e, hooks, replace, + parts, start, end, unit, + method; + + // will store per property easing and be used to determine when an animation is complete + opt.animatedProperties = {}; + + // first pass over propertys to expand / normalize + for ( p in prop ) { + name = jQuery.camelCase( p ); + if ( p !== name ) { + prop[ name ] = prop[ p ]; + delete prop[ p ]; + } + + if ( ( hooks = jQuery.cssHooks[ name ] ) && "expand" in hooks ) { + replace = hooks.expand( prop[ name ] ); + delete prop[ name ]; + + // not quite $.extend, this wont overwrite keys already present. + // also - reusing 'p' from above because we have the correct "name" + for ( p in replace ) { + if ( ! ( p in prop ) ) { + prop[ p ] = replace[ p ]; + } + } + } + } + + for ( name in prop ) { + val = prop[ name ]; + // easing resolution: per property > opt.specialEasing > opt.easing > 'swing' (default) + if ( jQuery.isArray( val ) ) { + opt.animatedProperties[ name ] = val[ 1 ]; + val = prop[ name ] = val[ 0 ]; + } else { + opt.animatedProperties[ name ] = opt.specialEasing && opt.specialEasing[ name ] || opt.easing || 'swing'; + } + + if ( val === "hide" && hidden || val === "show" && !hidden ) { + return opt.complete.call( this ); + } + + if ( isElement && ( name === "height" || name === "width" ) ) { + // Make sure that nothing sneaks out + // Record all 3 overflow attributes because IE does not + // change the overflow attribute when overflowX and + // overflowY are set to the same value + opt.overflow = [ this.style.overflow, this.style.overflowX, this.style.overflowY ]; + + // Set display property to inline-block for height/width + // animations on inline elements that are having width/height animated + if ( jQuery.css( this, "display" ) === "inline" && + jQuery.css( this, "float" ) === "none" ) { + + // inline-level elements accept inline-block; + // block-level elements need to be inline with layout + if ( !jQuery.support.inlineBlockNeedsLayout || defaultDisplay( this.nodeName ) === "inline" ) { + this.style.display = "inline-block"; + + } else { + this.style.zoom = 1; + } + } + } + } + + if ( opt.overflow != null ) { + this.style.overflow = "hidden"; + } + + for ( p in prop ) { + e = new jQuery.fx( this, opt, p ); + val = prop[ p ]; + + if ( rfxtypes.test( val ) ) { + + // Tracks whether to show or hide based on private + // data attached to the element + method = jQuery._data( this, "toggle" + p ) || ( val === "toggle" ? hidden ? "show" : "hide" : 0 ); + if ( method ) { + jQuery._data( this, "toggle" + p, method === "show" ? "hide" : "show" ); + e[ method ](); + } else { + e[ val ](); + } + + } else { + parts = rfxnum.exec( val ); + start = e.cur(); + + if ( parts ) { + end = parseFloat( parts[2] ); + unit = parts[3] || ( jQuery.cssNumber[ p ] ? "" : "px" ); + + // We need to compute starting value + if ( unit !== "px" ) { + jQuery.style( this, p, (end || 1) + unit); + start = ( (end || 1) / e.cur() ) * start; + jQuery.style( this, p, start + unit); + } + + // If a +=/-= token was provided, we're doing a relative animation + if ( parts[1] ) { + end = ( (parts[ 1 ] === "-=" ? -1 : 1) * end ) + start; + } + + e.custom( start, end, unit ); + + } else { + e.custom( start, val, "" ); + } + } + } + + // For JS strict compliance + return true; + } + + return optall.queue === false ? + this.each( doAnimation ) : + this.queue( optall.queue, doAnimation ); + }, + + stop: function( type, clearQueue, gotoEnd ) { + if ( typeof type !== "string" ) { + gotoEnd = clearQueue; + clearQueue = type; + type = undefined; + } + if ( clearQueue && type !== false ) { + this.queue( type || "fx", [] ); + } + + return this.each(function() { + var index, + hadTimers = false, + timers = jQuery.timers, + data = jQuery._data( this ); + + // clear marker counters if we know they won't be + if ( !gotoEnd ) { + jQuery._unmark( true, this ); + } + + function stopQueue( elem, data, index ) { + var hooks = data[ index ]; + jQuery.removeData( elem, index, true ); + hooks.stop( gotoEnd ); + } + + if ( type == null ) { + for ( index in data ) { + if ( data[ index ] && data[ index ].stop && index.indexOf(".run") === index.length - 4 ) { + stopQueue( this, data, index ); + } + } + } else if ( data[ index = type + ".run" ] && data[ index ].stop ){ + stopQueue( this, data, index ); + } + + for ( index = timers.length; index--; ) { + if ( timers[ index ].elem === this && (type == null || timers[ index ].queue === type) ) { + if ( gotoEnd ) { + + // force the next step to be the last + timers[ index ]( true ); + } else { + timers[ index ].saveState(); + } + hadTimers = true; + timers.splice( index, 1 ); + } + } + + // start the next in the queue if the last step wasn't forced + // timers currently will call their complete callbacks, which will dequeue + // but only if they were gotoEnd + if ( !( gotoEnd && hadTimers ) ) { + jQuery.dequeue( this, type ); + } + }); + } + +}); + +// Animations created synchronously will run synchronously +function createFxNow() { + setTimeout( clearFxNow, 0 ); + return ( fxNow = jQuery.now() ); +} + +function clearFxNow() { + fxNow = undefined; +} + +// Generate parameters to create a standard animation +function genFx( type, num ) { + var obj = {}; + + jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice( 0, num )), function() { + obj[ this ] = type; + }); + + return obj; +} + +// Generate shortcuts for custom animations +jQuery.each({ + slideDown: genFx( "show", 1 ), + slideUp: genFx( "hide", 1 ), + slideToggle: genFx( "toggle", 1 ), + fadeIn: { opacity: "show" }, + fadeOut: { opacity: "hide" }, + fadeToggle: { opacity: "toggle" } +}, function( name, props ) { + jQuery.fn[ name ] = function( speed, easing, callback ) { + return this.animate( props, speed, easing, callback ); + }; +}); + +jQuery.extend({ + speed: function( speed, easing, fn ) { + var opt = speed && typeof speed === "object" ? jQuery.extend( {}, speed ) : { + complete: fn || !fn && easing || + jQuery.isFunction( speed ) && speed, + duration: speed, + easing: fn && easing || easing && !jQuery.isFunction( easing ) && easing + }; + + opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration : + opt.duration in jQuery.fx.speeds ? jQuery.fx.speeds[ opt.duration ] : jQuery.fx.speeds._default; + + // normalize opt.queue - true/undefined/null -> "fx" + if ( opt.queue == null || opt.queue === true ) { + opt.queue = "fx"; + } + + // Queueing + opt.old = opt.complete; + + opt.complete = function( noUnmark ) { + if ( jQuery.isFunction( opt.old ) ) { + opt.old.call( this ); + } + + if ( opt.queue ) { + jQuery.dequeue( this, opt.queue ); + } else if ( noUnmark !== false ) { + jQuery._unmark( this ); + } + }; + + return opt; + }, + + easing: { + linear: function( p ) { + return p; + }, + swing: function( p ) { + return ( -Math.cos( p*Math.PI ) / 2 ) + 0.5; + } + }, + + timers: [], + + fx: function( elem, options, prop ) { + this.options = options; + this.elem = elem; + this.prop = prop; + + options.orig = options.orig || {}; + } + +}); + +jQuery.fx.prototype = { + // Simple function for setting a style value + update: function() { + if ( this.options.step ) { + this.options.step.call( this.elem, this.now, this ); + } + + ( jQuery.fx.step[ this.prop ] || jQuery.fx.step._default )( this ); + }, + + // Get the current size + cur: function() { + if ( this.elem[ this.prop ] != null && (!this.elem.style || this.elem.style[ this.prop ] == null) ) { + return this.elem[ this.prop ]; + } + + var parsed, + r = jQuery.css( this.elem, this.prop ); + // Empty strings, null, undefined and "auto" are converted to 0, + // complex values such as "rotate(1rad)" are returned as is, + // simple values such as "10px" are parsed to Float. + return isNaN( parsed = parseFloat( r ) ) ? !r || r === "auto" ? 0 : r : parsed; + }, + + // Start an animation from one number to another + custom: function( from, to, unit ) { + var self = this, + fx = jQuery.fx; + + this.startTime = fxNow || createFxNow(); + this.end = to; + this.now = this.start = from; + this.pos = this.state = 0; + this.unit = unit || this.unit || ( jQuery.cssNumber[ this.prop ] ? "" : "px" ); + + function t( gotoEnd ) { + return self.step( gotoEnd ); + } + + t.queue = this.options.queue; + t.elem = this.elem; + t.saveState = function() { + if ( jQuery._data( self.elem, "fxshow" + self.prop ) === undefined ) { + if ( self.options.hide ) { + jQuery._data( self.elem, "fxshow" + self.prop, self.start ); + } else if ( self.options.show ) { + jQuery._data( self.elem, "fxshow" + self.prop, self.end ); + } + } + }; + + if ( t() && jQuery.timers.push(t) && !timerId ) { + timerId = setInterval( fx.tick, fx.interval ); + } + }, + + // Simple 'show' function + show: function() { + var dataShow = jQuery._data( this.elem, "fxshow" + this.prop ); + + // Remember where we started, so that we can go back to it later + this.options.orig[ this.prop ] = dataShow || jQuery.style( this.elem, this.prop ); + this.options.show = true; + + // Begin the animation + // Make sure that we start at a small width/height to avoid any flash of content + if ( dataShow !== undefined ) { + // This show is picking up where a previous hide or show left off + this.custom( this.cur(), dataShow ); + } else { + this.custom( this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur() ); + } + + // Start by showing the element + jQuery( this.elem ).show(); + }, + + // Simple 'hide' function + hide: function() { + // Remember where we started, so that we can go back to it later + this.options.orig[ this.prop ] = jQuery._data( this.elem, "fxshow" + this.prop ) || jQuery.style( this.elem, this.prop ); + this.options.hide = true; + + // Begin the animation + this.custom( this.cur(), 0 ); + }, + + // Each step of an animation + step: function( gotoEnd ) { + var p, n, complete, + t = fxNow || createFxNow(), + done = true, + elem = this.elem, + options = this.options; + + if ( gotoEnd || t >= options.duration + this.startTime ) { + this.now = this.end; + this.pos = this.state = 1; + this.update(); + + options.animatedProperties[ this.prop ] = true; + + for ( p in options.animatedProperties ) { + if ( options.animatedProperties[ p ] !== true ) { + done = false; + } + } + + if ( done ) { + // Reset the overflow + if ( options.overflow != null && !jQuery.support.shrinkWrapBlocks ) { + + jQuery.each( [ "", "X", "Y" ], function( index, value ) { + elem.style[ "overflow" + value ] = options.overflow[ index ]; + }); + } + + // Hide the element if the "hide" operation was done + if ( options.hide ) { + jQuery( elem ).hide(); + } + + // Reset the properties, if the item has been hidden or shown + if ( options.hide || options.show ) { + for ( p in options.animatedProperties ) { + jQuery.style( elem, p, options.orig[ p ] ); + jQuery.removeData( elem, "fxshow" + p, true ); + // Toggle data is no longer needed + jQuery.removeData( elem, "toggle" + p, true ); + } + } + + // Execute the complete function + // in the event that the complete function throws an exception + // we must ensure it won't be called twice. #5684 + + complete = options.complete; + if ( complete ) { + + options.complete = false; + complete.call( elem ); + } + } + + return false; + + } else { + // classical easing cannot be used with an Infinity duration + if ( options.duration == Infinity ) { + this.now = t; + } else { + n = t - this.startTime; + this.state = n / options.duration; + + // Perform the easing function, defaults to swing + this.pos = jQuery.easing[ options.animatedProperties[this.prop] ]( this.state, n, 0, 1, options.duration ); + this.now = this.start + ( (this.end - this.start) * this.pos ); + } + // Perform the next step of the animation + this.update(); + } + + return true; + } +}; + +jQuery.extend( jQuery.fx, { + tick: function() { + var timer, + timers = jQuery.timers, + i = 0; + + for ( ; i < timers.length; i++ ) { + timer = timers[ i ]; + // Checks the timer has not already been removed + if ( !timer() && timers[ i ] === timer ) { + timers.splice( i--, 1 ); + } + } + + if ( !timers.length ) { + jQuery.fx.stop(); + } + }, + + interval: 13, + + stop: function() { + clearInterval( timerId ); + timerId = null; + }, + + speeds: { + slow: 600, + fast: 200, + // Default speed + _default: 400 + }, + + step: { + opacity: function( fx ) { + jQuery.style( fx.elem, "opacity", fx.now ); + }, + + _default: function( fx ) { + if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) { + fx.elem.style[ fx.prop ] = fx.now + fx.unit; + } else { + fx.elem[ fx.prop ] = fx.now; + } + } + } +}); + +// Ensure props that can't be negative don't go there on undershoot easing +jQuery.each( fxAttrs.concat.apply( [], fxAttrs ), function( i, prop ) { + // exclude marginTop, marginLeft, marginBottom and marginRight from this list + if ( prop.indexOf( "margin" ) ) { + jQuery.fx.step[ prop ] = function( fx ) { + jQuery.style( fx.elem, prop, Math.max(0, fx.now) + fx.unit ); + }; + } +}); + +if ( jQuery.expr && jQuery.expr.filters ) { + jQuery.expr.filters.animated = function( elem ) { + return jQuery.grep(jQuery.timers, function( fn ) { + return elem === fn.elem; + }).length; + }; +} + +// Try to restore the default display value of an element +function defaultDisplay( nodeName ) { + + if ( !elemdisplay[ nodeName ] ) { + + var body = document.body, + elem = jQuery( "<" + nodeName + ">" ).appendTo( body ), + display = elem.css( "display" ); + elem.remove(); + + // If the simple way fails, + // get element's real default display by attaching it to a temp iframe + if ( display === "none" || display === "" ) { + // No iframe to use yet, so create it + if ( !iframe ) { + iframe = document.createElement( "iframe" ); + iframe.frameBorder = iframe.width = iframe.height = 0; + } + + body.appendChild( iframe ); + + // Create a cacheable copy of the iframe document on first call. + // IE and Opera will allow us to reuse the iframeDoc without re-writing the fake HTML + // document to it; WebKit & Firefox won't allow reusing the iframe document. + if ( !iframeDoc || !iframe.createElement ) { + iframeDoc = ( iframe.contentWindow || iframe.contentDocument ).document; + iframeDoc.write( ( jQuery.support.boxModel ? "<!doctype html>" : "" ) + "<html><body>" ); + iframeDoc.close(); + } + + elem = iframeDoc.createElement( nodeName ); + + iframeDoc.body.appendChild( elem ); + + display = jQuery.css( elem, "display" ); + body.removeChild( iframe ); + } + + // Store the correct default display + elemdisplay[ nodeName ] = display; + } + + return elemdisplay[ nodeName ]; +} + + + + +var getOffset, + rtable = /^t(?:able|d|h)$/i, + rroot = /^(?:body|html)$/i; + +if ( "getBoundingClientRect" in document.documentElement ) { + getOffset = function( elem, doc, docElem, box ) { + try { + box = elem.getBoundingClientRect(); + } catch(e) {} + + // Make sure we're not dealing with a disconnected DOM node + if ( !box || !jQuery.contains( docElem, elem ) ) { + return box ? { top: box.top, left: box.left } : { top: 0, left: 0 }; + } + + var body = doc.body, + win = getWindow( doc ), + clientTop = docElem.clientTop || body.clientTop || 0, + clientLeft = docElem.clientLeft || body.clientLeft || 0, + scrollTop = win.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop, + scrollLeft = win.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft, + top = box.top + scrollTop - clientTop, + left = box.left + scrollLeft - clientLeft; + + return { top: top, left: left }; + }; + +} else { + getOffset = function( elem, doc, docElem ) { + var computedStyle, + offsetParent = elem.offsetParent, + prevOffsetParent = elem, + body = doc.body, + defaultView = doc.defaultView, + prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle, + top = elem.offsetTop, + left = elem.offsetLeft; + + while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) { + if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) { + break; + } + + computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle; + top -= elem.scrollTop; + left -= elem.scrollLeft; + + if ( elem === offsetParent ) { + top += elem.offsetTop; + left += elem.offsetLeft; + + if ( jQuery.support.doesNotAddBorder && !(jQuery.support.doesAddBorderForTableAndCells && rtable.test(elem.nodeName)) ) { + top += parseFloat( computedStyle.borderTopWidth ) || 0; + left += parseFloat( computedStyle.borderLeftWidth ) || 0; + } + + prevOffsetParent = offsetParent; + offsetParent = elem.offsetParent; + } + + if ( jQuery.support.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) { + top += parseFloat( computedStyle.borderTopWidth ) || 0; + left += parseFloat( computedStyle.borderLeftWidth ) || 0; + } + + prevComputedStyle = computedStyle; + } + + if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) { + top += body.offsetTop; + left += body.offsetLeft; + } + + if ( jQuery.support.fixedPosition && prevComputedStyle.position === "fixed" ) { + top += Math.max( docElem.scrollTop, body.scrollTop ); + left += Math.max( docElem.scrollLeft, body.scrollLeft ); + } + + return { top: top, left: left }; + }; +} + +jQuery.fn.offset = function( options ) { + if ( arguments.length ) { + return options === undefined ? + this : + this.each(function( i ) { + jQuery.offset.setOffset( this, options, i ); + }); + } + + var elem = this[0], + doc = elem && elem.ownerDocument; + + if ( !doc ) { + return null; + } + + if ( elem === doc.body ) { + return jQuery.offset.bodyOffset( elem ); + } + + return getOffset( elem, doc, doc.documentElement ); +}; + +jQuery.offset = { + + bodyOffset: function( body ) { + var top = body.offsetTop, + left = body.offsetLeft; + + if ( jQuery.support.doesNotIncludeMarginInBodyOffset ) { + top += parseFloat( jQuery.css(body, "marginTop") ) || 0; + left += parseFloat( jQuery.css(body, "marginLeft") ) || 0; + } + + return { top: top, left: left }; + }, + + setOffset: function( elem, options, i ) { + var position = jQuery.css( elem, "position" ); + + // set position first, in-case top/left are set even on static elem + if ( position === "static" ) { + elem.style.position = "relative"; + } + + var curElem = jQuery( elem ), + curOffset = curElem.offset(), + curCSSTop = jQuery.css( elem, "top" ), + curCSSLeft = jQuery.css( elem, "left" ), + calculatePosition = ( position === "absolute" || position === "fixed" ) && jQuery.inArray("auto", [curCSSTop, curCSSLeft]) > -1, + props = {}, curPosition = {}, curTop, curLeft; + + // need to be able to calculate position if either top or left is auto and position is either absolute or fixed + if ( calculatePosition ) { + curPosition = curElem.position(); + curTop = curPosition.top; + curLeft = curPosition.left; + } else { + curTop = parseFloat( curCSSTop ) || 0; + curLeft = parseFloat( curCSSLeft ) || 0; + } + + if ( jQuery.isFunction( options ) ) { + options = options.call( elem, i, curOffset ); + } + + if ( options.top != null ) { + props.top = ( options.top - curOffset.top ) + curTop; + } + if ( options.left != null ) { + props.left = ( options.left - curOffset.left ) + curLeft; + } + + if ( "using" in options ) { + options.using.call( elem, props ); + } else { + curElem.css( props ); + } + } +}; + + +jQuery.fn.extend({ + + position: function() { + if ( !this[0] ) { + return null; + } + + var elem = this[0], + + // Get *real* offsetParent + offsetParent = this.offsetParent(), + + // Get correct offsets + offset = this.offset(), + parentOffset = rroot.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset(); + + // Subtract element margins + // note: when an element has margin: auto the offsetLeft and marginLeft + // are the same in Safari causing offset.left to incorrectly be 0 + offset.top -= parseFloat( jQuery.css(elem, "marginTop") ) || 0; + offset.left -= parseFloat( jQuery.css(elem, "marginLeft") ) || 0; + + // Add offsetParent borders + parentOffset.top += parseFloat( jQuery.css(offsetParent[0], "borderTopWidth") ) || 0; + parentOffset.left += parseFloat( jQuery.css(offsetParent[0], "borderLeftWidth") ) || 0; + + // Subtract the two offsets + return { + top: offset.top - parentOffset.top, + left: offset.left - parentOffset.left + }; + }, + + offsetParent: function() { + return this.map(function() { + var offsetParent = this.offsetParent || document.body; + while ( offsetParent && (!rroot.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) { + offsetParent = offsetParent.offsetParent; + } + return offsetParent; + }); + } +}); + + +// Create scrollLeft and scrollTop methods +jQuery.each( {scrollLeft: "pageXOffset", scrollTop: "pageYOffset"}, function( method, prop ) { + var top = /Y/.test( prop ); + + jQuery.fn[ method ] = function( val ) { + return jQuery.access( this, function( elem, method, val ) { + var win = getWindow( elem ); + + if ( val === undefined ) { + return win ? (prop in win) ? win[ prop ] : + jQuery.support.boxModel && win.document.documentElement[ method ] || + win.document.body[ method ] : + elem[ method ]; + } + + if ( win ) { + win.scrollTo( + !top ? val : jQuery( win ).scrollLeft(), + top ? val : jQuery( win ).scrollTop() + ); + + } else { + elem[ method ] = val; + } + }, method, val, arguments.length, null ); + }; +}); + +function getWindow( elem ) { + return jQuery.isWindow( elem ) ? + elem : + elem.nodeType === 9 ? + elem.defaultView || elem.parentWindow : + false; +} + + + + +// Create width, height, innerHeight, innerWidth, outerHeight and outerWidth methods +jQuery.each( { Height: "height", Width: "width" }, function( name, type ) { + var clientProp = "client" + name, + scrollProp = "scroll" + name, + offsetProp = "offset" + name; + + // innerHeight and innerWidth + jQuery.fn[ "inner" + name ] = function() { + var elem = this[0]; + return elem ? + elem.style ? + parseFloat( jQuery.css( elem, type, "padding" ) ) : + this[ type ]() : + null; + }; + + // outerHeight and outerWidth + jQuery.fn[ "outer" + name ] = function( margin ) { + var elem = this[0]; + return elem ? + elem.style ? + parseFloat( jQuery.css( elem, type, margin ? "margin" : "border" ) ) : + this[ type ]() : + null; + }; + + jQuery.fn[ type ] = function( value ) { + return jQuery.access( this, function( elem, type, value ) { + var doc, docElemProp, orig, ret; + + if ( jQuery.isWindow( elem ) ) { + // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat + doc = elem.document; + docElemProp = doc.documentElement[ clientProp ]; + return jQuery.support.boxModel && docElemProp || + doc.body && doc.body[ clientProp ] || docElemProp; + } + + // Get document width or height + if ( elem.nodeType === 9 ) { + // Either scroll[Width/Height] or offset[Width/Height], whichever is greater + doc = elem.documentElement; + + // when a window > document, IE6 reports a offset[Width/Height] > client[Width/Height] + // so we can't use max, as it'll choose the incorrect offset[Width/Height] + // instead we use the correct client[Width/Height] + // support:IE6 + if ( doc[ clientProp ] >= doc[ scrollProp ] ) { + return doc[ clientProp ]; + } + + return Math.max( + elem.body[ scrollProp ], doc[ scrollProp ], + elem.body[ offsetProp ], doc[ offsetProp ] + ); + } + + // Get width or height on the element + if ( value === undefined ) { + orig = jQuery.css( elem, type ); + ret = parseFloat( orig ); + return jQuery.isNumeric( ret ) ? ret : orig; + } + + // Set the width or height on the element + jQuery( elem ).css( type, value ); + }, type, value, arguments.length, null ); + }; +}); + + + + +// Expose jQuery to the global object +window.jQuery = window.$ = jQuery; + +// Expose jQuery as an AMD module, but only for AMD loaders that +// understand the issues with loading multiple versions of jQuery +// in a page that all might call define(). The loader will indicate +// they have special allowances for multiple jQuery versions by +// specifying define.amd.jQuery = true. Register as a named module, +// since jQuery can be concatenated with other files that may use define, +// but not use a proper concatenation script that understands anonymous +// AMD modules. A named AMD is safest and most robust way to register. +// Lowercase jquery is used because AMD module names are derived from +// file names, and jQuery is normally delivered in a lowercase file name. +// Do this after creating the global so that if an AMD module wants to call +// noConflict to hide this version of jQuery, it will work. +if ( typeof define === "function" && define.amd && define.amd.jQuery ) { + define( "jquery", [], function () { return jQuery; } ); +} + + + +})( window ); diff --git a/WebContent/VAADIN/jquery.atmosphere.js b/WebContent/VAADIN/jquery.atmosphere.js new file mode 100644 index 0000000000..e9def6ae95 --- /dev/null +++ b/WebContent/VAADIN/jquery.atmosphere.js @@ -0,0 +1,2731 @@ +/** + * Copyright 2012 Jeanfrancois Arcand + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/* + * IE streaming/XDR supports is copied/highly inspired by http://code.google.com/p/jquery-stream/ + * + * Copyright 2011, Donghwan Kim + * Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * + * LocalStorage supports is copied/highly inspired by https://github.com/flowersinthesand/jquery-socket + * Copyright 2011, Donghwan Kim + * Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + * */ +/** + * Official documentation of this library: https://github.com/Atmosphere/atmosphere/wiki/jQuery.atmosphere.js-API + */ +jQuery.atmosphere = function() { + jQuery(window).bind("unload.atmosphere", function() { + jQuery.atmosphere.unsubscribe(); + }); + + // Prevent ESC to kill the connection from Firefox. + jQuery(window).keypress(function(e){ + if(e.keyCode == 27){ + e.preventDefault(); + } + }); + + var parseHeaders = function(headerString) { + var match, rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, headers = {}; + while (match = rheaders.exec(headerString)) { + headers[match[1]] = match[2]; + } + return headers; + }; + + return { + version : "1.0.12", + requests : [], + callbacks : [], + + onError : function(response) { + }, + onClose : function(response) { + }, + onOpen : function(response) { + }, + onMessage : function(response) { + }, + onReconnect : function(request, response) { + }, + onMessagePublished : function(response) { + }, + onTransportFailure : function (reason, request) { + }, + onLocalMessage : function (response) { + }, + + AtmosphereRequest : function(options) { + + /** + * {Object} Request parameters. + * @private + */ + var _request = { + timeout: 300000, + method: 'GET', + headers: {}, + contentType : '', + callback: null, + url : '', + data : '', + suspend : true, + maxRequest : -1, + reconnect : true, + maxStreamingLength : 10000000, + lastIndex : 0, + logLevel : 'info', + requestCount : 0, + fallbackMethod: 'GET', + fallbackTransport : 'streaming', + transport : 'long-polling', + webSocketImpl: null, + webSocketUrl: null, + webSocketPathDelimiter: "@@", + enableXDR : false, + rewriteURL : false, + attachHeadersAsQueryString : true, + executeCallbackBeforeReconnect : false, + readyState : 0, + lastTimestamp : 0, + withCredentials : false, + trackMessageLength : false , + messageDelimiter : '|', + connectTimeout : -1, + reconnectInterval : 0, + dropAtmosphereHeaders : true, + uuid : 0, + shared : false, + readResponsesHeaders : true, + maxReconnectOnClose: 5, + enableProtocol: false, + onError : function(response) { + }, + onClose : function(response) { + }, + onOpen : function(response) { + }, + onMessage : function(response) { + }, + onReconnect : function(request, response) { + }, + onMessagePublished : function(response) { + }, + onTransportFailure : function (reason, request) { + }, + onLocalMessage : function (request) { + } + }; + + /** + * {Object} Request's last response. + * @private + */ + var _response = { + status: 200, + responseBody : '', + headers : [], + state : "messageReceived", + transport : "polling", + error: null, + request : null, + partialMessage : "", + id : 0 + }; + + /** + * {websocket} Opened web socket. + * + * @private + */ + var _websocket = null; + + /** + * {SSE} Opened SSE. + * + * @private + */ + var _sse = null; + + /** + * {XMLHttpRequest, ActiveXObject} Opened ajax request (in case of + * http-streaming or long-polling) + * + * @private + */ + var _activeRequest = null; + + /** + * {Object} Object use for streaming with IE. + * + * @private + */ + var _ieStream = null; + + /** + * {Object} Object use for jsonp transport. + * + * @private + */ + var _jqxhr = null; + + /** + * {boolean} If request has been subscribed or not. + * + * @private + */ + var _subscribed = true; + + /** + * {number} Number of test reconnection. + * + * @private + */ + var _requestCount = 0; + + /** + * {boolean} If request is currently aborded. + * + * @private + */ + var _abordingConnection = false; + + /** + * A local "channel' of communication. + * @private + */ + var _localSocketF = null; + + /** + * The storage used. + * @private + */ + var _storageService; + + /** + * Local communication + * @private + */ + var _localStorageService = null; + + /** + * A Unique ID + * @private + */ + var guid = jQuery.now(); + + /** Trace time */ + var _traceTimer; + + // Automatic call to subscribe + _subscribe(options); + + /** + * Initialize atmosphere request object. + * + * @private + */ + function _init() { + _subscribed = true; + _abordingConnection = false; + _requestCount = 0; + + _websocket = null; + _sse = null; + _activeRequest = null; + _ieStream = null; + } + + /** + * Re-initialize atmosphere object. + * @private + */ + function _reinit() { + _clearState(); + _init(); + } + + /** + * Subscribe request using request transport. <br> + * If request is currently opened, this one will be closed. + * + * @param {Object} + * Request parameters. + * @private + */ + function _subscribe(options) { + _reinit(); + + _request = jQuery.extend(_request, options); + // Allow at least 1 request + _request.mrequest = _request.reconnect; + if (!_request.reconnect) { + _request.reconnect = true; + } + } + + /** + * Check if web socket is supported (check for custom implementation + * provided by request object or browser implementation). + * + * @returns {boolean} True if web socket is supported, false + * otherwise. + * @private + */ + function _supportWebsocket() { + return _request.webSocketImpl != null || window.WebSocket || window.MozWebSocket; + } + + /** + * Check if server side events (SSE) is supported (check for custom implementation + * provided by request object or browser implementation). + * + * @returns {boolean} True if web socket is supported, false + * otherwise. + * @private + */ + function _supportSSE() { + return window.EventSource; + } + + /** + * Open request using request transport. <br> + * If request transport is 'websocket' but websocket can't be + * opened, request will automatically reconnect using fallback + * transport. + * + * @private + */ + function _execute() { + // Shared across multiple tabs/windows. + if (_request.shared) { + _localStorageService = _local(_request); + if (_localStorageService != null) { + if (_request.logLevel == 'debug') { + jQuery.atmosphere.debug("Storage service available. All communication will be local"); + } + + if (_localStorageService.open(_request)) { + // Local connection. + return; + } + } + + if (_request.logLevel == 'debug') { + jQuery.atmosphere.debug("No Storage service available."); + } + // Wasn't local or an error occurred + _localStorageService = null; + } + + // Protocol + _request.firstMessage= true; + _request.ctime = jQuery.now(); + + if (_request.transport != 'websocket' && _request.transport != 'sse') { + // Gives a chance to the connection to be established before calling the callback + setTimeout(function() { + _open('opening', _request.transport, _request); + }, 500); + _executeRequest(); + + } else if (_request.transport == 'websocket') { + if (!_supportWebsocket()) { + _reconnectWithFallbackTransport("Websocket is not supported, using request.fallbackTransport (" + _request.fallbackTransport + ")"); + } else { + _executeWebSocket(false); + } + } else if (_request.transport == 'sse') { + if (!_supportSSE()) { + _reconnectWithFallbackTransport("Server Side Events(SSE) is not supported, using request.fallbackTransport (" + _request.fallbackTransport + ")"); + } else { + _executeSSE(false); + } + } + } + + function _local(request) { + var trace, connector, orphan, name = "atmosphere-" + request.url, connectors = { + storage: function() { + if (!jQuery.atmosphere.supportStorage()) { + return; + } + + var storage = window.localStorage, + get = function(key) { + return jQuery.parseJSON(storage.getItem(name + "-" + key)); + }, + set = function(key, value) { + storage.setItem(name + "-" + key, jQuery.stringifyJSON(value)); + }; + + return { + init: function() { + set("children", get("children").concat([guid])); + jQuery(window).on("storage.socket", function(event) { + event = event.originalEvent; + if (event.key === name && event.newValue) { + listener(event.newValue); + } + }); + return get("opened"); + }, + signal: function(type, data) { + storage.setItem(name, jQuery.stringifyJSON({target: "p", type: type, data: data})); + }, + close: function() { + var index, children = get("children"); + + jQuery(window).off("storage.socket"); + if (children) { + index = jQuery.inArray(request.id, children); + if (index > -1) { + children.splice(index, 1); + set("children", children); + } + } + } + }; + }, + windowref: function() { + var win = window.open("", name.replace(/\W/g, "")); + + if (!win || win.closed || !win.callbacks) { + return; + } + + return { + init: function() { + win.callbacks.push(listener); + win.children.push(guid); + return win.opened; + }, + signal: function(type, data) { + if (!win.closed && win.fire) { + win.fire(jQuery.stringifyJSON({target: "p", type: type, data: data})); + } + }, + close : function() { + function remove(array, e) { + var index = jQuery.inArray(e, array); + if (index > -1) { + array.splice(index, 1); + } + } + + // Removes traces only if the parent is alive + if (!orphan) { + remove(win.callbacks, listener); + remove(win.children, guid); + } + } + + }; + } + }; + + // Receives open, close and message command from the parent + function listener(string) { + var command = jQuery.parseJSON(string), data = command.data; + + if (command.target === "c") { + switch (command.type) { + case "open": + _open("opening", 'local', _request) + break; + case "close": + if (!orphan) { + orphan = true; + if (data.reason === "aborted") { + _close(); + } else { + // Gives the heir some time to reconnect + if (data.heir === guid) { + _execute(); + } else { + setTimeout(function() { + _execute(); + }, 100); + } + } + } + break; + case "message": + _prepareCallback(data, "messageReceived", 200, request.transport); + break; + case "localMessage": + _localMessage(data); + break; + } + } + } + + function findTrace() { + var matcher = new RegExp("(?:^|; )(" + encodeURIComponent(name) + ")=([^;]*)").exec(document.cookie); + if (matcher) { + return jQuery.parseJSON(decodeURIComponent(matcher[2])); + } + } + + // Finds and validates the parent socket's trace from the cookie + trace = findTrace(); + if (!trace || jQuery.now() - trace.ts > 1000) { + return; + } + + // Chooses a connector + connector = connectors.storage() || connectors.windowref(); + if (!connector) { + return; + } + + return { + open: function() { + var parentOpened; + + // Checks the shared one is alive + _traceTimer = setInterval(function() { + var oldTrace = trace; + trace = findTrace(); + if (!trace || oldTrace.ts === trace.ts) { + // Simulates a close signal + listener(jQuery.stringifyJSON({target: "c", type: "close", data: {reason: "error", heir: oldTrace.heir}})); + } + }, 1000); + + parentOpened = connector.init(); + if (parentOpened) { + // Firing the open event without delay robs the user of the opportunity to bind connecting event handlers + setTimeout(function() { + _open("opening", 'local', request) + }, 50); + } + return parentOpened; + }, + send: function(event) { + connector.signal("send", event); + }, + localSend: function(event) { + connector.signal("localSend", jQuery.stringifyJSON({id: guid , event: event})); + }, + close: function() { + // Do not signal the parent if this method is executed by the unload event handler + if (!_abordingConnection) { + clearInterval(_traceTimer); + connector.signal("close"); + connector.close(); + } + } + }; + }; + + function share() { + var storageService, name = "atmosphere-" + _request.url, servers = { + // Powered by the storage event and the localStorage + // http://www.w3.org/TR/webstorage/#event-storage + storage: function() { + if (!jQuery.atmosphere.supportStorage()) { + return; + } + + var storage = window.localStorage; + + return { + init: function() { + // Handles the storage event + jQuery(window).on("storage.socket", function(event) { + event = event.originalEvent; + // When a deletion, newValue initialized to null + if (event.key === name && event.newValue) { + listener(event.newValue); + } + }); + }, + signal: function(type, data) { + storage.setItem(name, jQuery.stringifyJSON({target: "c", type: type, data: data})); + }, + get: function(key) { + return jQuery.parseJSON(storage.getItem(name + "-" + key)); + }, + set: function(key, value) { + storage.setItem(name + "-" + key, jQuery.stringifyJSON(value)); + }, + close : function() { + jQuery(window).off("storage.socket"); + storage.removeItem(name); + storage.removeItem(name + "-opened"); + storage.removeItem(name + "-children"); + } + + }; + }, + // Powered by the window.open method + // https://developer.mozilla.org/en/DOM/window.open + windowref: function() { + // Internet Explorer raises an invalid argument error + // when calling the window.open method with the name containing non-word characters + var neim = name.replace(/\W/g, ""), win = (jQuery('iframe[name="' + neim + '"]')[0] + || jQuery('<iframe name="' + neim + '" />').hide().appendTo("body")[0]).contentWindow; + + return { + init: function() { + // Callbacks from different windows + win.callbacks = [listener]; + // In IE 8 and less, only string argument can be safely passed to the function in other window + win.fire = function(string) { + var i; + + for (i = 0; i < win.callbacks.length; i++) { + win.callbacks[i](string); + } + }; + }, + signal: function(type, data) { + if (!win.closed && win.fire) { + win.fire(jQuery.stringifyJSON({target: "c", type: type, data: data})); + } + }, + get: function(key) { + return !win.closed ? win[key] : null; + }, + set: function(key, value) { + if (!win.closed) { + win[key] = value; + } + }, + close : function() {} + }; + } + }; + + + // Receives send and close command from the children + function listener(string) { + var command = jQuery.parseJSON(string), data = command.data; + + if (command.target === "p") { + switch (command.type) { + case "send": + _push(data); + break; + case "localSend": + _localMessage(data); + break; + case "close": + _close(); + break; + } + } + } + + _localSocketF = function propagateMessageEvent(context) { + storageService.signal("message", context); + } + + function leaveTrace() { + document.cookie = encodeURIComponent(name) + "=" + + // Opera's JSON implementation ignores a number whose a last digit of 0 strangely + // but has no problem with a number whose a last digit of 9 + 1 + encodeURIComponent(jQuery.stringifyJSON({ts: jQuery.now() + 1, heir: (storageService.get("children") || [])[0]})); + } + + // Chooses a storageService + storageService = servers.storage() || servers.windowref(); + storageService.init(); + + if (_request.logLevel == 'debug') { + jQuery.atmosphere.debug("Installed StorageService " + storageService); + } + + // List of children sockets + storageService.set("children", []); + + if (storageService.get("opened") != null && !storageService.get("opened")) { + // Flag indicating the parent socket is opened + storageService.set("opened", false); + } + // Leaves traces + leaveTrace(); + _traceTimer = setInterval(leaveTrace, 1000); + + _storageService = storageService; + } + + /** + * @private + */ + function _open(state, transport, request) { + if (_request.shared && transport != 'local') { + share(); + } + + if (_storageService != null) { + _storageService.set("opened", true); + } + + request.close = function() { + _close(); + }; + + _response.request = request; + var prevState = _response.state; + _response.state = state; + _response.status = 200; + var prevTransport = _response.transport; + _response.transport = transport; + + var _body = _response.responseBody; + _invokeCallback(); + _response.responseBody = _body; + + _response.state = prevState; + _response.transport = prevTransport; + } + + /** + * Execute request using jsonp transport. + * + * @param request + * {Object} request Request parameters, if + * undefined _request object will be used. + * @private + */ + function _jsonp(request) { + // When CORS is enabled, make sure we force the proper transport. + request.transport="jsonp"; + + var rq = _request; + if ((request != null) && (typeof(request) != 'undefined')) { + rq = request; + } + + var url = rq.url; + var data = rq.data; + if (rq.attachHeadersAsQueryString) { + url = _attachHeaders(rq); + if (data != '') { + url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(data); + } + data = ''; + } + + _jqxhr = jQuery.ajax({ + url : url, + type : rq.method, + dataType: "jsonp", + error : function(jqXHR, textStatus, errorThrown) { + if (jqXHR.status < 300) { + _reconnect(_jqxhr, rq); + } else { + _prepareCallback(textStatus, "error", jqXHR.status, rq.transport); + } + }, + jsonp : "jsonpTransport", + success: function(json) { + + if (rq.reconnect && (rq.maxRequest == -1 || rq.requestCount++ < rq.maxRequest)) { + _readHeaders(_jqxhr, rq); + + if (!rq.executeCallbackBeforeReconnect) { + _reconnect(_jqxhr, rq); + } + + var msg = json.message; + if (msg != null && typeof msg != 'string') { + try { + msg = jQuery.stringifyJSON(msg); + } catch (err) { + // The message was partial + } + } + + if (_handleProtocol(rq, msg)) { + _prepareCallback(msg, "messageReceived", 200, rq.transport); + } + + if (rq.executeCallbackBeforeReconnect) { + _reconnect(_jqxhr, rq); + } + } else { + jQuery.atmosphere.log(_request.logLevel, ["JSONP reconnect maximum try reached " + _request.requestCount]); + _onError(); + } + }, + data : rq.data, + beforeSend : function(jqXHR) { + _doRequest(jqXHR, rq, false); + } + }); + } + + /** + * Execute request using ajax transport. + * + * @param request + * {Object} request Request parameters, if + * undefined _request object will be used. + * @private + */ + function _ajax(request) { + var rq = _request; + if ((request != null) && (typeof(request) != 'undefined')) { + rq = request; + } + + var url = rq.url; + var data = rq.data; + if (rq.attachHeadersAsQueryString) { + url = _attachHeaders(rq); + if (data != '') { + url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(data); + } + data = ''; + } + + var async = typeof(rq.async) != 'undefined' ? rq.async : true; + _jqxhr = jQuery.ajax({ + url : url, + type : rq.method, + error : function(jqXHR, textStatus, errorThrown) { + if (jqXHR.status < 300) { + _reconnect(_jqxhr, rq); + } else { + _prepareCallback(textStatus, "error", jqXHR.status, rq.transport); + } + }, + success: function(data, textStatus, jqXHR) { + + if (rq.reconnect && (rq.maxRequest == -1 || rq.requestCount++ < rq.maxRequest)) { + if (!rq.executeCallbackBeforeReconnect) { + _reconnect(_jqxhr, rq); + } + + if (_handleProtocol(rq, data)) { + _prepareCallback(data, "messageReceived", 200, rq.transport); + } + + if (rq.executeCallbackBeforeReconnect) { + _reconnect(_jqxhr, rq); + } + } else { + jQuery.atmosphere.log(_request.logLevel, ["AJAX reconnect maximum try reached " + _request.requestCount]); + _onError(); + } + }, + beforeSend : function(jqXHR) { + _doRequest(jqXHR, rq, false); + }, + crossDomain : rq.enableXDR, + async: async + }); + } + + /** + * Build websocket object. + * + * @param location + * {string} Web socket url. + * @returns {websocket} Web socket object. + * @private + */ + function _getWebSocket(location) { + if (_request.webSocketImpl != null) { + return _request.webSocketImpl; + } else { + if (window.WebSocket) { + return new WebSocket(location); + } else { + return new MozWebSocket(location); + } + } + } + + /** + * Build web socket url from request url. + * + * @return {string} Web socket url (start with "ws" or "wss" for + * secure web socket). + * @private + */ + function _buildWebSocketUrl() { + var url = _attachHeaders(_request); + + return decodeURI(jQuery('<a href="' + url + '"/>')[0].href.replace(/^http/, "ws")); + } + + /** + * Build SSE url from request url. + * + * @return a url with Atmosphere's headers + * @private + */ + function _buildSSEUrl() { + var url = _attachHeaders(_request); + return url; + } + + /** + * Open SSE. <br> + * Automatically use fallback transport if SSE can't be + * opened. + * + * @private + */ + function _executeSSE(sseOpened) { + + _response.transport = "sse"; + + var location = _buildSSEUrl(_request.url); + + if (_request.logLevel == 'debug') { + jQuery.atmosphere.debug("Invoking executeSSE"); + jQuery.atmosphere.debug("Using URL: " + location); + } + + if (sseOpened) { + _open('re-opening', "sse", _request); + } + + if (_request.enableProtocol && sseOpened) { + var time = jQuery.now() - _request.ctime; + _request.lastTimestamp = Number(_request.stime) + Number(time); + } + + if (sseOpened && !_request.reconnect) { + if (_sse != null) { + _clearState(); + } + return; + } + _sse = new EventSource(location, {withCredentials: _request.withCredentials}); + + if (_request.connectTimeout > 0) { + _request.id = setTimeout(function() { + if (!sseOpened) { + _clearState(); + } + }, _request.connectTimeout); + } + + _sse.onopen = function(event) { + if (_request.logLevel == 'debug') { + jQuery.atmosphere.debug("SSE successfully opened"); + } + + if (!sseOpened) { + _open('opening', "sse", _request); + } + sseOpened = true; + + if (_request.method == 'POST') { + _response.state = "messageReceived"; + _sse.send(_request.data); + } + }; + + _sse.onmessage = function(message) { + if (message.origin != window.location.protocol + "//" + window.location.host) { + jQuery.atmosphere.log(_request.logLevel, ["Origin was not " + window.location.protocol + "//" + window.location.host]); + return; + } + + if (!_handleProtocol(_request, message.data)) return; + + _response.state = 'messageReceived'; + _response.status = 200; + + var message = message.data; + var skipCallbackInvocation = _trackMessageSize(message, _request, _response); + + if (jQuery.trim(message).length == 0) { + skipCallbackInvocation = true; + } + + if (!skipCallbackInvocation) { + _invokeCallback(); + _response.responseBody = ''; + } + }; + + _sse.onerror = function(message) { + + clearTimeout(_request.id); + _response.state = 'closed'; + _response.responseBody = ""; + _response.status = !sseOpened ? 501 : 200; + _invokeCallback(); + _clearState(); + + if (_abordingConnection) { + jQuery.atmosphere.log(_request.logLevel, ["SSE closed normally"]); + } else if (!sseOpened) { + _reconnectWithFallbackTransport("SSE failed. Downgrading to fallback transport and resending"); + } else if (_request.reconnect && (_response.transport == 'sse')) { + if (_requestCount++ < _request.maxReconnectOnClose) { + _request.id = setTimeout(function() { + _executeSSE(true); + }, _request.reconnectInterval); + _response.responseBody = ""; + } else { + jQuery.atmosphere.log(_request.logLevel, ["SSE reconnect maximum try reached " + _requestCount]); + _onError(); + } + } + }; + } + + /** + * Open web socket. <br> + * Automatically use fallback transport if web socket can't be + * opened. + * + * @private + */ + function _executeWebSocket(webSocketOpened) { + + _response.transport = "websocket"; + + if (_request.enableProtocol && webSocketOpened) { + var time = jQuery.now() - _request.ctime; + _request.lastTimestamp = Number(_request.stime) + Number(time); + } + + var location = _buildWebSocketUrl(_request.url); + var closed = false; + + if (_request.logLevel == 'debug') { + jQuery.atmosphere.debug("Invoking executeWebSocket"); + jQuery.atmosphere.debug("Using URL: " + location); + } + + if (webSocketOpened) { + _open('re-opening', "websocket", _request); + } + + if (webSocketOpened && !_request.reconnect) { + if (_websocket != null) { + _clearState(); + } + return; + } + + _websocket = _getWebSocket(location); + + if (_request.connectTimeout > 0) { + _request.id = setTimeout(function() { + if (!webSocketOpened) { + var _message = { + code : 1002, + reason : "", + wasClean : false + }; + _websocket.onclose(_message); + // Close it anyway + try { + _clearState(); + } catch (e) { + } + return; + } + + }, _request.connectTimeout); + } + + _request.id = setTimeout(function() { + setTimeout(function () { + _clearState(); + }, _request.reconnectInterval) + }, _request.timeout); + + _websocket.onopen = function(message) { + if (_request.logLevel == 'debug') { + jQuery.atmosphere.debug("Websocket successfully opened"); + } + + if (!webSocketOpened) { + _open('opening', "websocket", _request); + } + + webSocketOpened = true; + + if (_request.method == 'POST') { + _response.state = "messageReceived"; + _websocket.send(_request.data); + } + }; + + _websocket.onmessage = function(message) { + + clearTimeout(_request.id); + _request.id = setTimeout(function() { + setTimeout(function () { + _clearState(); + }, _request.reconnectInterval) + }, _request.timeout); + + if (!_handleProtocol(_request, message.data)) return; + + _response.state = 'messageReceived'; + _response.status = 200; + + var message = message.data; + var skipCallbackInvocation = _trackMessageSize(message, _request, _response); + + if (!skipCallbackInvocation) { + _invokeCallback(); + _response.responseBody = ''; + } + }; + + _websocket.onerror = function(message) { + clearTimeout(_request.id) + }; + + _websocket.onclose = function(message) { + if (closed) return + + var reason = message.reason; + if (reason === "") { + switch (message.code) { + case 1000: + reason = "Normal closure; the connection successfully completed whatever purpose for which " + + "it was created."; + break; + case 1001: + reason = "The endpoint is going away, either because of a server failure or because the " + + "browser is navigating away from the page that opened the connection."; + break; + case 1002: + reason = "The endpoint is terminating the connection due to a protocol error."; + break; + case 1003: + reason = "The connection is being terminated because the endpoint received data of a type it " + + "cannot accept (for example, a text-only endpoint received binary data)."; + break; + case 1004: + reason = "The endpoint is terminating the connection because a data frame was received that " + + "is too large."; + break; + case 1005: + reason = "Unknown: no status code was provided even though one was expected."; + break; + case 1006: + reason = "Connection was closed abnormally (that is, with no close frame being sent)."; + break; + } + } + + jQuery.atmosphere.warn("Websocket closed, reason: " + reason); + jQuery.atmosphere.warn("Websocket closed, wasClean: " + message.wasClean); + + _response.state = 'closed'; + _response.responseBody = ""; + _response.status = !webSocketOpened ? 501 : 200; + _invokeCallback(); + clearTimeout(_request.id); + + closed = true; + + if (_abordingConnection) { + jQuery.atmosphere.log(_request.logLevel, ["Websocket closed normally"]); + } else if (!webSocketOpened) { + _reconnectWithFallbackTransport("Websocket failed. Downgrading to Comet and resending"); + + } else if (_request.reconnect && _response.transport == 'websocket') { + _clearState(); + if (_request.reconnect && _requestCount++ < _request.maxReconnectOnClose) { + _request.id = setTimeout(function() { + _response.responseBody = ""; + _executeWebSocket(true); + }, _request.reconnectInterval); + } else { + jQuery.atmosphere.log(_request.logLevel, ["Websocket reconnect maximum try reached " + _requestCount]); + jQuery.atmosphere.warn("Websocket error, reason: " + message.reason); + _onError(); + } + } + }; + } + + function _handleProtocol(request, message) { + // The first messages is always the uuid. + if (request.enableProtocol && request.firstMessage) { + request.firstMessage = false; + var messages = message.split(request.messageDelimiter); + request.uuid = messages[0]; + request.stime = messages[1]; + return false; + } + return true; + } + + function _onError() { + _clearState(); + + _response.state = 'error'; + _response.responseBody = ""; + _response.status = 500; + _invokeCallback(); + } + + /** + * Track received message and make sure callbacks/functions are only invoked when the complete message + * has been received. + * + * @param message + * @param request + * @param response + */ + function _trackMessageSize(message, request, response) { + if (request.trackMessageLength) { + + // If we have found partial message, prepend them. + if (response.partialMessage.length != 0) { + message = response.partialMessage + message; + } + + var messages = []; + var messageLength = 0; + var messageStart = message.indexOf(request.messageDelimiter); + while (messageStart != -1) { + messageLength = message.substring(messageLength, messageStart); + message = message.substring(messageStart + request.messageDelimiter.length, message.length); + + if (message.length == 0 || message.length < messageLength) break; + + messageStart = message.indexOf(request.messageDelimiter); + messages.push(message.substring(0, messageLength)); + } + + if (messages.length == 0 || (messageStart != -1 && message.length != 0 && messageLength != message.length)){ + response.partialMessage = messageLength + request.messageDelimiter + message ; + } else { + response.partialMessage = ""; + } + + if (messages.length != 0) { + response.responseBody = messages.join(request.messageDelimiter); + return false; + } else { + return true; + } + } else { + response.responseBody = message; + } + return false; + } + + /** + * Reconnect request with fallback transport. <br> + * Used in case websocket can't be opened. + * + * @private + */ + function _reconnectWithFallbackTransport(errorMessage) { + jQuery.atmosphere.log(_request.logLevel, [errorMessage]); + + if (typeof(_request.onTransportFailure) != 'undefined') { + _request.onTransportFailure(errorMessage, _request); + } else if (typeof(jQuery.atmosphere.onTransportFailure) != 'undefined') { + jQuery.atmosphere.onTransportFailure(errorMessage, _request); + } + + _request.transport = _request.fallbackTransport; + var reconnect = _request.reconnect && _requestCount++ < _request.maxReconnectOnClose; + if (reconnect && _request.transport != 'none' || _request.transport == null) { + _request.method = _request.fallbackMethod; + _response.transport = _request.fallbackTransport; + _request.id = setTimeout(function() { + _execute(); + }, _request.reconnectInterval); + } else if (!reconnect) { + _onError(); + } + } + + /** + * Get url from request and attach headers to it. + * + * @param request + * {Object} request Request parameters, if + * undefined _request object will be used. + * + * @returns {Object} Request object, if undefined, + * _request object will be used. + * @private + */ + function _attachHeaders(request) { + var rq = _request; + if ((request != null) && (typeof(request) != 'undefined')) { + rq = request; + } + + var url = rq.url; + + // If not enabled + if (!rq.attachHeadersAsQueryString) return url; + + // If already added + if (url.indexOf("X-Atmosphere-Framework") != -1) { + return url; + } + + url += (url.indexOf('?') != -1) ? '&' : '?'; + url += "X-Atmosphere-tracking-id=" + rq.uuid; + url += "&X-Atmosphere-Framework=" + jQuery.atmosphere.version; + url += "&X-Atmosphere-Transport=" + rq.transport; + + if (rq.trackMessageLength) { + url += "&X-Atmosphere-TrackMessageSize=" + "true"; + } + + if (rq.lastTimestamp != undefined) { + url += "&X-Cache-Date=" + rq.lastTimestamp; + } else { + url += "&X-Cache-Date=" + 0; + } + + if (rq.contentType != '') { + url += "&Content-Type=" + rq.contentType; + } + + if (rq.enableProtocol) { + url += "&X-atmo-protocol=true"; + } + + jQuery.each(rq.headers, function(name, value) { + var h = jQuery.isFunction(value) ? value.call(this, rq, request, _response) : value; + if (h != null) { + url += "&" + encodeURIComponent(name) + "=" + encodeURIComponent(h); + } + }); + + return url; + } + + /** + * Build ajax request. <br> + * Ajax Request is an XMLHttpRequest object, except for IE6 where + * ajax request is an ActiveXObject. + * + * @return {XMLHttpRequest, ActiveXObject} Ajax request. + * @private + */ + function _buildAjaxRequest() { + if (jQuery.browser.msie) { + if (typeof XMLHttpRequest == "undefined") + XMLHttpRequest = function () { + try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); } + catch (e) {} + try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); } + catch (e) {} + try { return new ActiveXObject("Microsoft.XMLHTTP"); } + catch (e) {} + //Microsoft.XMLHTTP points to Msxml2.XMLHTTP and is redundant + throw new Error("This browser does not support XMLHttpRequest."); + }; + } + return new XMLHttpRequest(); + } + + /** + * Execute ajax request. <br> + * + * @param request + * {Object} request Request parameters, if + * undefined _request object will be used. + * @private + */ + function _executeRequest(request) { + var rq = _request; + if ((request != null) || (typeof(request) != 'undefined')) { + rq = request; + } + + // CORS fake using JSONP + if ((rq.transport == 'jsonp') || ((rq.enableXDR) && (jQuery.atmosphere.checkCORSSupport()))) { + _jsonp(rq); + return; + } + + if (rq.transport == 'ajax') { + _ajax(request); + return; + } + + if (jQuery.browser.msie && jQuery.browser.version < 10) { + if ((rq.transport == 'streaming')) { + rq.enableXDR && window.XDomainRequest ? _ieXDR(rq) : _ieStreaming(rq); + return; + } + + if ((rq.enableXDR) && (window.XDomainRequest)) { + _ieXDR(rq); + return; + } + } + + if (rq.reconnect && ( rq.maxRequest == -1 || rq.requestCount++ < rq.maxRequest)) { + var ajaxRequest = _buildAjaxRequest(); + _doRequest(ajaxRequest, rq, true); + + if (rq.suspend) { + _activeRequest = ajaxRequest; + } + + if (rq.transport != 'polling') { + _response.transport = rq.transport; + } + + if (!jQuery.browser.msie) { + ajaxRequest.onerror = function() { + try { + _response.status = XMLHttpRequest.status; + } catch(e) { + _response.status = 500; + } + + if (!_response.status) { + _response.status = 500; + } + _clearState(); + + if (rq.reconnect) { + _reconnect(ajaxRequest, rq, true); + } else { + _onError(); + } + }; + } + + ajaxRequest.onreadystatechange = function() { + if (_abordingConnection) { + return; + } + + var skipCallbackInvocation = false; + var update = false; + + // Remote server disconnected us, reconnect. + if (rq.transport == 'streaming' + && rq.readyState > 2 + && ajaxRequest.readyState == 4) { + + rq.readyState = 0; + rq.lastIndex = 0; + + _reconnect(ajaxRequest, rq, true); + return; + } + + rq.readyState = ajaxRequest.readyState; + + if (ajaxRequest.readyState == 4) { + if (jQuery.browser.msie) { + update = true; + } else if (rq.transport == 'streaming') { + update = true; + } else if (rq.transport == 'long-polling') { + update = true; + clearTimeout(rq.id); + } + } else if (rq.transport == 'streaming' && jQuery.browser.msie && ajaxRequest.readyState >= 3) { + update = true; + } else if (!jQuery.browser.msie && ajaxRequest.readyState == 3 && ajaxRequest.status == 200 && rq.transport != 'long-polling') { + update = true; + } else { + clearTimeout(rq.id); + } + + if (update) { + var responseText = ajaxRequest.responseText; + + // MSIE status can be higher than 1000, Chrome can be 0 + if (ajaxRequest.status >= 500 || ajaxRequest.status == 0) { + if (rq.reconnect) { + _reconnect(ajaxRequest, rq, true); + } else { + _onError(); + } + return; + } + + _readHeaders(ajaxRequest, _request); + + if (rq.transport == 'streaming') { + var text = responseText.substring(rq.lastIndex, responseText.length); + _response.isJunkEnded = true; + + //fix junk is comming in parts + if (!_response.junkFull && (text.indexOf("<!-- Welcome to the Atmosphere Framework.") == -1 || text.indexOf("<!-- EOD -->") == -1)) { + return; + } + _response.junkFull = true; + + //if it's the start and we see the junk start + //fix for reconnecting on chrome - junk is comming in parts + if (rq.lastIndex == 0 && text.indexOf("<!-- Welcome to the Atmosphere Framework.") != -1 && text.indexOf("<!-- EOD -->") != -1) { + _response.isJunkEnded = false; + } + + if (!_response.isJunkEnded) { + var endOfJunk = "<!-- EOD -->"; + var endOfJunkLength = endOfJunk.length; + var junkEnd = text.indexOf(endOfJunk) + endOfJunkLength; + + if (junkEnd > endOfJunkLength && junkEnd != text.length) { + _response.responseBody = text.substring(junkEnd); + rq.lastIndex = responseText.length; + if (!_handleProtocol( _request, _response.responseBody)) { + return; + } + skipCallbackInvocation = _trackMessageSize(_response.responseBody, rq, _response); + } else { + skipCallbackInvocation = true; + } + } else { + var message = responseText.substring(rq.lastIndex, responseText.length); + rq.lastIndex = responseText.length; + if (!_handleProtocol( _request, message)) { + return; + } + skipCallbackInvocation = _trackMessageSize(message, rq, _response); + } + rq.lastIndex = responseText.length; + + if (jQuery.browser.opera) { + jQuery.atmosphere.iterate(function() { + if (ajaxRequest.responseText.length > rq.lastIndex) { + try { + _response.status = ajaxRequest.status; + _response.headers = parseHeaders(ajaxRequest.getAllResponseHeaders()); + + _readHeaders(ajaxRequest, _request); + } + catch(e) { + _response.status = 404; + } + _response.state = "messageReceived"; + _response.responseBody = ajaxRequest.responseText.substring(rq.lastIndex); + rq.lastIndex = ajaxRequest.responseText.length; + + if (!_handleProtocol( _request, _response.responseBody)) { + _reconnect(ajaxRequest, rq, false); + return; + } + _invokeCallback(); + if ((rq.transport == 'streaming') && (ajaxRequest.responseText.length > rq.maxStreamingLength)) { + // Close and reopen connection on large data received + _clearState(); + _doRequest(_buildAjaxRequest(), rq, true); + } + } + }, 0); + } + + if (skipCallbackInvocation) { + return; + } + } else { + if (!_handleProtocol( _request, responseText)) { + _reconnect(ajaxRequest, rq, false); + return; + } + + _trackMessageSize(responseText, rq, _response); + rq.lastIndex = responseText.length; + } + + try { + _response.status = ajaxRequest.status; + _response.headers = parseHeaders(ajaxRequest.getAllResponseHeaders()); + + _readHeaders(ajaxRequest, rq); + } catch(e) { + _response.status = 404; + } + + if (rq.suspend) { + _response.state = _response.status == 0 ? "closed" : "messageReceived"; + } else { + _response.state = "messagePublished"; + } + + if (!rq.executeCallbackBeforeReconnect) { + _reconnect(ajaxRequest, rq, false); + } + + // For backward compatibility with Atmosphere < 0.8 + if (_response.responseBody.indexOf("parent.callback") != -1) { + jQuery.atmosphere.log(rq.logLevel, ["parent.callback no longer supported with 0.8 version and up. Please upgrade"]); + } + + _invokeCallback(); + + if (rq.executeCallbackBeforeReconnect) { + _reconnect(ajaxRequest, rq, false); + } + + if ((rq.transport == 'streaming') && (responseText.length > rq.maxStreamingLength)) { + // Close and reopen connection on large data received + _clearState(); + _doRequest(_buildAjaxRequest(), rq, true); + } + } + }; + ajaxRequest.send(rq.data); + + if (rq.suspend) { + rq.id = setTimeout(function() { + if (_subscribed) { + setTimeout(function () { + _clearState(); + _executeRequest(rq); + }, rq.reconnectInterval) + } + }, rq.timeout); + } + _subscribed = true; + + } else { + if (rq.logLevel == 'debug') { + jQuery.atmosphere.log(rq.logLevel, ["Max re-connection reached."]); + } + _onError(); + } + } + + /** + * Do ajax request. + * @param ajaxRequest Ajax request. + * @param request Request parameters. + * @param create If ajax request has to be open. + */ + function _doRequest(ajaxRequest, request, create) { + // Prevent Android to cache request + var url = _attachHeaders(request); + url = jQuery.atmosphere.prepareURL(url); + + if (create) { + ajaxRequest.open(request.method, url, true); + if (request.connectTimeout > -1) { + request.id = setTimeout(function() { + if (request.requestCount == 0) { + _clearState(); + _prepareCallback("Connect timeout", "closed", 200, request.transport); + } + }, request.connectTimeout); + } + } + + if (_request.withCredentials) { + if ("withCredentials" in ajaxRequest) { + ajaxRequest.withCredentials = true; + } + } + + if (!_request.dropAtmosphereHeaders) { + ajaxRequest.setRequestHeader("X-Atmosphere-Framework", jQuery.atmosphere.version); + ajaxRequest.setRequestHeader("X-Atmosphere-Transport", request.transport); + if (request.lastTimestamp != undefined) { + ajaxRequest.setRequestHeader("X-Cache-Date", request.lastTimestamp); + } else { + ajaxRequest.setRequestHeader("X-Cache-Date", 0); + } + + if (request.trackMessageLength) { + ajaxRequest.setRequestHeader("X-Atmosphere-TrackMessageSize", "true") + } + + if (request.contentType != '') { + ajaxRequest.setRequestHeader("Content-Type", request.contentType); + } + ajaxRequest.setRequestHeader("X-Atmosphere-tracking-id", request.uuid); + } + + jQuery.each(request.headers, function(name, value) { + var h = jQuery.isFunction(value) ? value.call(this, ajaxRequest, request, create, _response) : value; + if (h != null) { + ajaxRequest.setRequestHeader(name, h); + } + }); + } + + function _reconnect(ajaxRequest, request, force) { + var reconnect = request.reconnect && _requestCount++ < request.maxReconnectOnClose; + + if (reconnect && force || (request.suspend && ajaxRequest.status == 200 && request.transport != 'streaming' && _subscribed)) { + if (request.reconnect) { + _open('re-opening', request.transport, request); + request.id = setTimeout(function() { + _executeRequest(); + }, request.reconnectInterval); + } + } else if (!reconnect) { + _onError(); + } + } + + // From jquery-stream, which is APL2 licensed as well. + function _ieXDR(request) { + if (request.transport != "polling") { + _ieStream = _configureXDR(request); + _ieStream.open(); + } else { + _configureXDR(request).open(); + } + } + + // From jquery-stream + function _configureXDR(request) { + var rq = _request; + if ((request != null) && (typeof(request) != 'undefined')) { + rq = request; + } + + var transport = rq.transport; + var lastIndex = 0; + var xdrCallback = function (xdr) { + var responseBody = xdr.responseText; + var isJunkEnded = false; + + if (responseBody.indexOf("<!-- Welcome to the Atmosphere Framework.") != -1) { + isJunkEnded = true; + } + + if (isJunkEnded) { + var endOfJunk = "<!-- EOD -->"; + var endOfJunkLenght = endOfJunk.length; + var junkEnd = responseBody.indexOf(endOfJunk); + if (junkEnd !== -1) { + responseBody = responseBody.substring(junkEnd + endOfJunkLenght + lastIndex); + lastIndex += responseBody.length; + } + } + + if (!_handleProtocol(request, responseBody)) return; + + _prepareCallback(responseBody, "messageReceived", 200, transport); + }; + + var xdr = new window.XDomainRequest(); + var rewriteURL = rq.rewriteURL || function(url) { + // Maintaining session by rewriting URL + // http://stackoverflow.com/questions/6453779/maintaining-session-by-rewriting-url + var match = /(?:^|;\s*)(JSESSIONID|PHPSESSID)=([^;]*)/.exec(document.cookie); + + switch (match && match[1]) { + case "JSESSIONID": + return url.replace(/;jsessionid=[^\?]*|(\?)|$/, ";jsessionid=" + match[2] + "$1"); + case "PHPSESSID": + return url.replace(/\?PHPSESSID=[^&]*&?|\?|$/, "?PHPSESSID=" + match[2] + "&").replace(/&$/, ""); + } + return url; + }; + + // Handles open and message event + xdr.onprogress = function() { + handle(xdr); + }; + + // Handles error event + xdr.onerror = function() { + // If the server doesn't send anything back to XDR will fail with polling + if (rq.transport != 'polling') { + _prepareCallback(xdr.responseText, "error", 500, transport); + } + + _reconnect(xdr, rq, false); + }; + + // Handles close event + xdr.onload = function() { + handle(xdr); + }; + + var handle = function (xdr) { + // XDomain loop forever on itself without this. + // TODO: Clearly I need to come with something better than that solution + if (rq.lastMessage == xdr.responseText) return; + + if (rq.executeCallbackBeforeReconnect) { + xdrCallback(xdr); + } + + if (rq.transport == "long-polling" && (rq.reconnect && (rq.maxRequest == -1 || rq.requestCount++ < rq.maxRequest))) { + xdr.status = 200; + _reconnect(xdr, rq, false); + } + + if (!rq.executeCallbackBeforeReconnect) { + xdrCallback(xdr); + } + rq.lastMessage = xdr.responseText; + }; + + return { + open: function() { + if (rq.method == 'POST') { + rq.attachHeadersAsQueryString = true; + } + var url = _attachHeaders(rq); + if (rq.method == 'POST') { + url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(rq.data); + } + xdr.open(rq.method, rewriteURL(url)); + xdr.send(); + if (rq.connectTimeout > -1) { + rq.id = setTimeout(function() { + if (rq.requestCount == 0) { + _clearState(); + _prepareCallback("Connect timeout", "closed", 200, rq.transport); + } + }, rq.connectTimeout); + } + }, + close: function() { + xdr.abort(); + _prepareCallback(xdr.responseText, "closed", 200, transport); + } + }; + } + + // From jquery-stream, which is APL2 licensed as well. + function _ieStreaming(request) { + _ieStream = _configureIE(request); + _ieStream.open(); + } + + function _configureIE(request) { + var rq = _request; + if ((request != null) && (typeof(request) != 'undefined')) { + rq = request; + } + + var stop; + var doc = new window.ActiveXObject("htmlfile"); + + doc.open(); + doc.close(); + + var url = rq.url; + + if (rq.transport != 'polling') { + _response.transport = rq.transport; + } + + return { + open: function() { + var iframe = doc.createElement("iframe"); + + url = _attachHeaders(rq); + if (rq.data != '') { + url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(rq.data); + } + + // Finally attach a timestamp to prevent Android and IE caching. + url = jQuery.atmosphere.prepareURL(url); + + iframe.src = url; + doc.body.appendChild(iframe); + + // For the server to respond in a consistent format regardless of user agent, we polls response text + var cdoc = iframe.contentDocument || iframe.contentWindow.document; + + stop = jQuery.atmosphere.iterate(function() { + try { + if (!cdoc.firstChild) { + return; + } + + // Detects connection failure + if (cdoc.readyState === "complete") { + try { + jQuery.noop(cdoc.fileSize); + } catch(e) { + _prepareCallback("Connection Failure", "error", 500, rq.transport); + return false; + } + } + + var res = cdoc.body ? cdoc.body.lastChild : cdoc; + var readResponse = function() { + // Clones the element not to disturb the original one + var clone = res.cloneNode(true); + + // If the last character is a carriage return or a line feed, IE ignores it in the innerText property + // therefore, we add another non-newline character to preserve it + clone.appendChild(cdoc.createTextNode(".")); + + var text = clone.innerText; + var isJunkEnded = true; + + if (text.indexOf("<!-- Welcome to the Atmosphere Framework.") == -1) { + isJunkEnded = false; + } + + if (isJunkEnded) { + var endOfJunk = "<!-- EOD -->"; + var endOfJunkLength = endOfJunk.length; + var junkEnd = text.indexOf(endOfJunk) + endOfJunkLength; + + text = text.substring(junkEnd); + } + + text = text.substring(0, text.length - 1); + + _handleProtocol(rq, text); + return text; + + }; + + //To support text/html content type + if (!jQuery.nodeName(res, "pre")) { + // Injects a plaintext element which renders text without interpreting the HTML and cannot be stopped + // it is deprecated in HTML5, but still works + var head = cdoc.head || cdoc.getElementsByTagName("head")[0] || cdoc.documentElement || cdoc; + var script = cdoc.createElement("script"); + + script.text = "document.write('<plaintext>')"; + + head.insertBefore(script, head.firstChild); + head.removeChild(script); + + // The plaintext element will be the response container + res = cdoc.body.lastChild; + } + + // Handles open event + _prepareCallback(readResponse(), "opening", 200, rq.transport); + + // Handles message and close event + stop = jQuery.atmosphere.iterate(function() { + var text = readResponse(); + if (text.length > rq.lastIndex) { + _response.status = 200; + + // Empties response every time that it is handled + res.innerText = ""; + _prepareCallback(text, "messageReceived", 200, rq.transport); + + rq.lastIndex = 0; + } + + if (cdoc.readyState === "complete") { + _prepareCallback("", "closed", 200, rq.transport); + _prepareCallback("", "re-opening", 200, rq.transport); + rq.id = setTimeout(function() { + _ieStreaming(rq); + }, rq.reconnectInterval); + return false; + } + }, null); + + return false; + } catch (err) { + if (_requestCount++ < rq.maxReconnectOnClose) { + rq.id = setTimeout(function() { + _ieStreaming(rq); + }, rq.reconnectInterval); + } else { + _onError(); + } + doc.execCommand("Stop"); + doc.close(); + return false; + } + }); + }, + + close: function() { + if (stop) { + stop(); + } + + doc.execCommand("Stop"); + _prepareCallback("", "closed", 200, rq.transport); + } + }; + } + + /* + * Send message. <br> + * Will be automatically dispatch to other connected. + * + * @param {Object,string} Message to send. + * @private + */ + function _push(message) { + + if (_response.status == 408) { + _pushOnClose(message); + } else if (_localStorageService != null) { + _pushLocal(message); + } else if (_activeRequest != null || _sse != null) { + _pushAjaxMessage(message); + } else if (_ieStream != null) { + _pushIE(message); + } else if (_jqxhr != null) { + _pushJsonp(message); + } else if (_websocket != null) { + _pushWebSocket(message); + } + } + + function _pushOnClose(message) { + var rq = _getPushRequest(message); + rq.transport = "ajax"; + rq.method = "GET"; + rq.async = false; + rq.reconnect = false; + _executeRequest(rq); + } + + function _pushLocal(message) { + _localStorageService.send(message); + } + + function _intraPush(message) { + // IE 9 will crash if not. + if (message.length == 0) return; + + try { + if (_localStorageService) { + _localStorageService.localSend(message); + } else if (_storageService) { + _storageService.signal("localMessage", jQuery.stringifyJSON({id: guid , event: message})); + } + } catch (err) { + jQuery.atmosphere.error(err); + } + } + + /** + * Send a message using currently opened ajax request (using + * http-streaming or long-polling). <br> + * + * @param {string, Object} Message to send. This is an object, string + * message is saved in data member. + * @private + */ + function _pushAjaxMessage(message) { + var rq = _getPushRequest(message); + _executeRequest(rq); + } + + /** + * Send a message using currently opened ie streaming (using + * http-streaming or long-polling). <br> + * + * @param {string, Object} Message to send. This is an object, string + * message is saved in data member. + * @private + */ + function _pushIE(message) { + if (_request.enableXDR && jQuery.atmosphere.checkCORSSupport()) { + var rq = _getPushRequest(message); + // Do not reconnect since we are pushing. + rq.reconnect = false; + _jsonp(rq); + } else { + _pushAjaxMessage(message); + } + } + + /** + * Send a message using jsonp transport. <br> + * + * @param {string, Object} Message to send. This is an object, string + * message is saved in data member. + * @private + */ + function _pushJsonp(message) { + _pushAjaxMessage(message); + } + + function _getStringMessage(message) { + var msg = message; + if (typeof(msg) == 'object') { + msg = message.data; + } + return msg; + } + + /** + * Build request use to push message using method 'POST' <br>. + * Transport is defined as 'polling' and 'suspend' is set to false. + * + * @return {Object} Request object use to push message. + * @private + */ + function _getPushRequest(message) { + var msg = _getStringMessage(message); + + var rq = { + connected: false, + timeout: 60000, + method: 'POST', + url: _request.url, + contentType : _request.contentType, + headers: {}, + reconnect : true, + callback: null, + data : msg, + suspend : false, + maxRequest : -1, + logLevel : 'info', + requestCount : 0, + withCredentials : _request.withCredentials, + transport: 'polling', + attachHeadersAsQueryString: true, + enableXDR: _request.enableXDR, + uuid : _request.uuid, + enableProtocol : false, + maxReconnectOnClose : _request.maxReconnectOnClose + }; + + if (typeof(message) == 'object') { + rq = jQuery.extend(rq, message); + } + + return rq; + } + + /** + * Send a message using currently opened websocket. <br> + * + */ + function _pushWebSocket(message) { + var msg = _getStringMessage(message); + var data; + try { + if (_request.webSocketUrl != null) { + data = _request.webSocketPathDelimiter + + _request.webSocketUrl + + _request.webSocketPathDelimiter + + msg; + } else { + data = msg; + } + + _websocket.send(data); + + } catch (e) { + _websocket.onclose = function(message) { + }; + _clearState(); + + _reconnectWithFallbackTransport("Websocket failed. Downgrading to Comet and resending " + data); + _pushAjaxMessage(message); + } + } + + function _localMessage(message) { + var m = jQuery.parseJSON(message); + if (m.id != guid) { + if (typeof(_request.onLocalMessage) != 'undefined') { + _request.onLocalMessage(m.event); + } else if (typeof(jQuery.atmosphere.onLocalMessage) != 'undefined') { + jQuery.atmosphere.onLocalMessage(m.event); + } + } + } + + function _prepareCallback(messageBody, state, errorCode, transport) { + _response.responseBody = messageBody; + if (state == "messageReceived") { + if (_trackMessageSize(messageBody, _request, _response)) { + return; + } + } + + _response.transport = transport; + _response.status = errorCode; + _response.state = state; + + _invokeCallback(); + } + + function _readHeaders(xdr, request) { + if (!request.readResponsesHeaders && !request.enableProtocol) { + request.lastTimestamp = jQuery.now(); + request.uuid = jQuery.atmosphere.guid(); + return; + } + + try { + var tempDate = xdr.getResponseHeader('X-Cache-Date'); + if (tempDate && tempDate != null && tempDate.length > 0 ) { + request.lastTimestamp = tempDate.split(" ").pop(); + } + + var tempUUID = xdr.getResponseHeader('X-Atmosphere-tracking-id'); + if (tempUUID && tempUUID != null) { + request.uuid = tempUUID.split(" ").pop(); + } + + // HOTFIX for firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=608735 + if (request.headers) { + jQuery.each(_request.headers, function (name) { + var v = xdr.getResponseHeader(name); + if (v) { + _response.headers[name] = v; + } + }); + } + } catch (e) { + } + } + + function _invokeFunction(response) { + _f(response, _request); + // Global + _f(response, jQuery.atmosphere); + } + + function _f(response, f) { + switch (response.state) { + case "messageReceived" : + _requestCount = 0; + if (typeof(f.onMessage) != 'undefined') f.onMessage(response); + break; + case "error" : + if (typeof(f.onError) != 'undefined') f.onError(response); + break; + case "opening" : + if (typeof(f.onOpen) != 'undefined') f.onOpen(response); + break; + case "messagePublished" : + if (typeof(f.onMessagePublished) != 'undefined') f.onMessagePublished(response); + break; + case "re-opening" : + if (typeof(f.onReconnect) != 'undefined') f.onReconnect(_request, response); + break; + case "unsubscribe" : + case "closed" : + var closed = typeof(_request.closed) != 'undefined' ? _request.closed : false; + if (typeof(f.onClose) != 'undefined' && !closed) f.onClose(response); + _request.closed = true; + break; + } + } + + /** + * Invoke request callbacks. + * + * @private + */ + function _invokeCallback() { + var call = function (index, func) { + func(_response); + }; + + if (_localStorageService == null && _localSocketF != null) { + _localSocketF(_response.responseBody); + } + + _request.reconnect = _request.mrequest; + + var messages = (typeof(_response.responseBody) == 'string' && _request.trackMessageLength) ? + _response.responseBody.split(_request.messageDelimiter) : new Array(_response.responseBody); + for (var i = 0; i < messages.length; i++) { + + if (messages.length > 1 && messages[i].length == 0) { + continue; + } + _response.responseBody = jQuery.trim(messages[i]); + + // Ugly see issue 400. + if (_response.responseBody.length == 0 && _response.transport == 'streaming' && _response.state == "messageReceived") { + var ua = navigator.userAgent.toLowerCase(); + var isAndroid = ua.indexOf("android") > -1; + if (isAndroid) { + continue; + } + } + + _invokeFunction(_response); + + // Invoke global callbacks + if (jQuery.atmosphere.callbacks.length > 0) { + if (_request.logLevel == 'debug') { + jQuery.atmosphere.debug("Invoking " + jQuery.atmosphere.callbacks.length + " global callbacks: " + _response.state); + } + try { + jQuery.each(jQuery.atmosphere.callbacks, call); + } catch (e) { + jQuery.atmosphere.log(_request.logLevel, ["Callback exception" + e]); + } + } + + // Invoke request callback + if (typeof(_request.callback) == 'function') { + if (_request.logLevel == 'debug') { + jQuery.atmosphere.debug("Invoking request callbacks"); + } + try { + _request.callback(_response); + } catch (e) { + jQuery.atmosphere.log(_request.logLevel, ["Callback exception" + e]); + } + } + } + + } + + /** + * Close request. + * + * @private + */ + function _close() { + _abordingConnection = true; + _request.reconnect = false; + _response.request = _request; + _response.state = 'unsubscribe'; + _response.responseBody = ""; + _response.status = 408; + _invokeCallback(); + + _clearState(); + } + + function _clearState() { + if (_ieStream != null) { + _ieStream.close(); + _ieStream = null; + } + if (_jqxhr != null) { + _jqxhr.abort(); + _jqxhr = null; + } + if (_activeRequest != null) { + _activeRequest.abort(); + _activeRequest = null; + } + if (_websocket != null) { + _websocket.close(); + _websocket = null; + } + if (_sse != null) { + _sse.close(); + _sse = null; + } + _clearStorage(); + } + + function _clearStorage() { + // Stop sharing a connection + if (_storageService != null) { + // Clears trace timer + clearInterval(_traceTimer); + // Removes the trace + document.cookie = encodeURIComponent("atmosphere-" + _request.url) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT"; + // The heir is the parent unless unloading + _storageService.signal("close", {reason: "", heir: !_abordingConnection ? guid : (_storageService.get("children") || [])[0]}); + _storageService.close(); + } + if (_localStorageService != null) { + _localStorageService.close(); + } + } + + this.subscribe = function(options) { + _subscribe(options); + _execute(); + }; + + this.execute = function() { + _execute(); + }; + + this.invokeCallback = function() { + _invokeCallback(); + }; + + this.close = function() { + _close(); + }; + + this.getUrl = function() { + return _request.url; + }; + + this.getUUID = function() { + return _request.uuid; + }; + + this.push = function(message) { + _push(message); + }; + + this.pushLocal = function(message) { + _intraPush(message); + }; + + this.enableProtocol = function(message) { + return _request.enableProtocol; + }; + + this.response = _response; + }, + + subscribe: function(url, callback, request) { + if (typeof(callback) == 'function') { + jQuery.atmosphere.addCallback(callback); + } + + if (typeof(url) != "string") { + request = url; + } else { + request.url = url; + } + + var rq = new jQuery.atmosphere.AtmosphereRequest(request); + rq.execute(); + + jQuery.atmosphere.requests[jQuery.atmosphere.requests.length] = rq; + return rq; + }, + + addCallback: function(func) { + if (jQuery.inArray(func, jQuery.atmosphere.callbacks) == -1) { + jQuery.atmosphere.callbacks.push(func); + } + }, + + removeCallback: function(func) { + var index = jQuery.inArray(func, jQuery.atmosphere.callbacks); + if (index != -1) { + jQuery.atmosphere.callbacks.splice(index, 1); + } + }, + + unsubscribe : function() { + if (jQuery.atmosphere.requests.length > 0) { + var requestsClone = [].concat(jQuery.atmosphere.requests); + for (var i = 0; i < requestsClone.length; i++) { + var rq = requestsClone[i]; + rq.close(); + if (rq.enableProtocol()) { + jQuery.ajax({url: this._closeUrl(rq), async:false}); + } + clearTimeout(rq.response.request.id); + } + } + jQuery.atmosphere.requests = []; + jQuery.atmosphere.callbacks = []; + }, + + _closeUrl : function(rq) { + var query = "X-Atmosphere-Transport=close&X-Atmosphere-tracking-id=" + rq.getUUID(); + var url = rq.getUrl().replace(/([?&])_=[^&]*/, query); + return url + (url === rq.getUrl() ? (/\?/.test(rq.getUrl()) ? "&" : "?") + query : ""); + }, + + unsubscribeUrl: function(url) { + var idx = -1; + if (jQuery.atmosphere.requests.length > 0) { + for (var i = 0; i < jQuery.atmosphere.requests.length; i++) { + var rq = jQuery.atmosphere.requests[i]; + + // Suppose you can subscribe once to an url + if (rq.getUrl() == url) { + rq.close(); + if (rq.enableProtocol()) { + jQuery.ajax({url :this._closeUrl(rq), async:false}); + } + clearTimeout(rq.response.request.id); + idx = i; + break; + } + } + } + if (idx >= 0) { + jQuery.atmosphere.requests.splice(idx, 1); + } + }, + + publish: function(request) { + if (typeof(request.callback) == 'function') { + jQuery.atmosphere.addCallback(callback); + } + request.transport = "polling"; + + var rq = new jQuery.atmosphere.AtmosphereRequest(request); + jQuery.atmosphere.requests[jQuery.atmosphere.requests.length] = rq; + return rq; + }, + + checkCORSSupport : function() { + if (jQuery.browser.msie && !window.XDomainRequest) { + return true; + } else if (jQuery.browser.opera && jQuery.browser.version < 12.0) { + return true; + } + + // Force Android to use CORS as some version like 2.2.3 fail otherwise + var ua = navigator.userAgent.toLowerCase(); + var isAndroid = ua.indexOf("android") > -1; + if (isAndroid) { + return true; + } + return false; + }, + + S4 : function() { + return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); + }, + + guid : function() { + return (jQuery.atmosphere.S4() + jQuery.atmosphere.S4() + "-" + jQuery.atmosphere.S4() + "-" + jQuery.atmosphere.S4() + "-" + jQuery.atmosphere.S4() + "-" + jQuery.atmosphere.S4() + jQuery.atmosphere.S4() + jQuery.atmosphere.S4()); + }, + + // From jQuery-Stream + prepareURL: function(url) { + // Attaches a time stamp to prevent caching + var ts = jQuery.now(); + var ret = url.replace(/([?&])_=[^&]*/, "$1_=" + ts); + + return ret + (ret === url ? (/\?/.test(url) ? "&" : "?") + "_=" + ts : ""); + }, + + // From jQuery-Stream + param : function(data) { + return jQuery.param(data, jQuery.ajaxSettings.traditional); + }, + + supportStorage : function() { + var storage = window.localStorage; + if (storage) { + try { + storage.setItem("t", "t"); + storage.removeItem("t"); + // The storage event of Internet Explorer and Firefox 3 works strangely + return window.StorageEvent && !jQuery.browser.msie && !(jQuery.browser.mozilla && jQuery.browser.version.split(".")[0] === "1"); + } catch (e) { + } + } + + return false; + }, + + iterate : function (fn, interval) { + var timeoutId; + + // Though the interval is 0 for real-time application, there is a delay between setTimeout calls + // For detail, see https://developer.mozilla.org/en/window.setTimeout#Minimum_delay_and_timeout_nesting + interval = interval || 0; + + (function loop() { + timeoutId = setTimeout(function() { + if (fn() === false) { + return; + } + + loop(); + }, interval); + })(); + + return function() { + clearTimeout(timeoutId); + }; + }, + + log: function (level, args) { + if (window.console) { + var logger = window.console[level]; + if (typeof logger == 'function') { + logger.apply(window.console, args); + } + } + }, + + warn: function() { + jQuery.atmosphere.log('warn', arguments); + }, + + info :function() { + jQuery.atmosphere.log('info', arguments); + }, + + debug: function() { + jQuery.atmosphere.log('debug', arguments); + }, + + error: function() { + jQuery.atmosphere.log('error', arguments); + } + }; +}(); + +// http://stackoverflow.com/questions/9645803/whats-the-replacement-for-browser +// Limit scope pollution from any deprecated API +(function () { + + var matched, browser; + +// Use of jQuery.browser is frowned upon. +// More details: http://api.jquery.com/jQuery.browser +// jQuery.uaMatch maintained for back-compat + jQuery.uaMatch = function (ua) { + ua = ua.toLowerCase(); + + var match = /(chrome)[ \/]([\w.]+)/.exec(ua) || + /(webkit)[ \/]([\w.]+)/.exec(ua) || + /(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) || + /(msie) ([\w.]+)/.exec(ua) || + ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || + []; + + return { + browser: match[ 1 ] || "", + version: match[ 2 ] || "0" + }; + }; + + matched = jQuery.uaMatch(navigator.userAgent); + browser = {}; + + if (matched.browser) { + browser[ matched.browser ] = true; + browser.version = matched.version; + } + +// Chrome is Webkit, but Webkit is also Safari. + if (browser.chrome) { + browser.webkit = true; + } else if (browser.webkit) { + browser.safari = true; + } + + jQuery.browser = browser; + + jQuery.sub = function () { + function jQuerySub(selector, context) { + return new jQuerySub.fn.init(selector, context); + } + + jQuery.extend(true, jQuerySub, this); + jQuerySub.superclass = this; + jQuerySub.fn = jQuerySub.prototype = this(); + jQuerySub.fn.constructor = jQuerySub; + jQuerySub.sub = this.sub; + jQuerySub.fn.init = function init(selector, context) { + if (context && context instanceof jQuery && !(context instanceof jQuerySub)) { + context = jQuerySub(context); + } + + return jQuery.fn.init.call(this, selector, context, rootjQuerySub); + }; + jQuerySub.fn.init.prototype = jQuerySub.fn; + var rootjQuerySub = jQuerySub(document); + return jQuerySub; + }; + +})(); + +/* + * jQuery stringifyJSON + * http://github.com/flowersinthesand/jquery-stringifyJSON + * + * Copyright 2011, Donghwan Kim + * Licensed under the Apache License, Version 2.0 + * http://www.apache.org/licenses/LICENSE-2.0 + */ +// This plugin is heavily based on Douglas Crockford's reference implementation +(function(jQuery) { + + var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, meta = { + '\b' : '\\b', + '\t' : '\\t', + '\n' : '\\n', + '\f' : '\\f', + '\r' : '\\r', + '"' : '\\"', + '\\' : '\\\\' + }; + + function quote(string) { + return '"' + string.replace(escapable, function(a) { + var c = meta[a]; + return typeof c === "string" ? c : "\\u" + ("0000" + a.charCodeAt(0).toString(16)).slice(-4); + }) + '"'; + } + + function f(n) { + return n < 10 ? "0" + n : n; + } + + function str(key, holder) { + var i, v, len, partial, value = holder[key], type = typeof value; + + if (value && typeof value === "object" && typeof value.toJSON === "function") { + value = value.toJSON(key); + type = typeof value; + } + + switch (type) { + case "string": + return quote(value); + case "number": + return isFinite(value) ? String(value) : "null"; + case "boolean": + return String(value); + case "object": + if (!value) { + return "null"; + } + + switch (Object.prototype.toString.call(value)) { + case "[object Date]": + return isFinite(value.valueOf()) ? '"' + value.getUTCFullYear() + "-" + f(value.getUTCMonth() + 1) + "-" + f(value.getUTCDate()) + "T" + + f(value.getUTCHours()) + ":" + f(value.getUTCMinutes()) + ":" + f(value.getUTCSeconds()) + "Z" + '"' : "null"; + case "[object Array]": + len = value.length; + partial = []; + for (i = 0; i < len; i++) { + partial.push(str(i, value) || "null"); + } + + return "[" + partial.join(",") + "]"; + default: + partial = []; + for (i in value) { + if (Object.prototype.hasOwnProperty.call(value, i)) { + v = str(i, value); + if (v) { + partial.push(quote(i) + ":" + v); + } + } + } + + return "{" + partial.join(",") + "}"; + } + } + } + + jQuery.stringifyJSON = function(value) { + if (window.JSON && window.JSON.stringify) { + return window.JSON.stringify(value); + } + + return str("", {"": value}); + }; + +}(jQuery));
\ No newline at end of file diff --git a/WebContent/VAADIN/themes/base/base.scss b/WebContent/VAADIN/themes/base/base.scss index 87754b6777..83e463fa00 100644 --- a/WebContent/VAADIN/themes/base/base.scss +++ b/WebContent/VAADIN/themes/base/base.scss @@ -4,6 +4,7 @@ @import "button/nativebutton.scss"; @import "button/checkbox.scss"; @import "layout/layout.scss"; +@import "calendar/calendar.scss"; @import "caption/caption.scss"; @import "colorpicker/colorpicker.scss"; @import "common/common.scss"; @@ -64,6 +65,7 @@ $line-height: normal; @include base-checkbox; @include base-caption; @include base-colorpicker; + @include base-calendar; // here for now to preserve old semantics @include base-common; diff --git a/WebContent/VAADIN/themes/base/calendar/calendar.scss b/WebContent/VAADIN/themes/base/calendar/calendar.scss new file mode 100644 index 0000000000..8ff97df0f9 --- /dev/null +++ b/WebContent/VAADIN/themes/base/calendar/calendar.scss @@ -0,0 +1,378 @@ +@mixin base-calendar($primaryStyleName : v-calendar) { + +/* Global resize style */ +.#{$primaryStyleName}-nresize DIV DIV { + cursor: n-resize !important; +} + +.#{$primaryStyleName}-sresize DIV DIV { + cursor: s-resize !important; +} + +/* Header bar */ +.#{$primaryStyleName} { + background-color: #fff; +} + +.#{$primaryStyleName}-header-month,.#{$primaryStyleName}-header-week { + border-bottom: 1px solid #c1c1c1; +} + +.#{$primaryStyleName}-header-day { + text-align: center; + color: #666; + font-size: 12px; + line-height: normal; +} + +.#{$primaryStyleName}-header-week .#{$primaryStyleName}-header-day:hover { + cursor: pointer; + color: #222 +} + +.#{$primaryStyleName}-header-day-today { + font-weight: bold; + color: #444; +} + +.#{$primaryStyleName}-header-month td:first-child { + padding-left: 19px; + /* Same as VCalendar.MONTHLY_WEEKTOOLBARWIDTH - .#{$primaryStyleName}-week-numbers border */ +} + +.#{$primaryStyleName}-header-week .#{$primaryStyleName}-back,.#{$primaryStyleName}-header-week .#{$primaryStyleName}-next + { + border: none; + padding: 0; + margin: 0; + height: 12px; + width: 12px; + overflow: hidden; + background: transparent url(img/arrows.png) no-repeat 50% 0; + opacity: .3; + filter: alpha(opacity = 30); + cursor: default; +} + +.#{$primaryStyleName}-header-week .#{$primaryStyleName}-back:hover,.#{$primaryStyleName}-header-week .#{$primaryStyleName}-next:hover + { + opacity: .6; + filter: alpha(opacity = 60); +} + +.#{$primaryStyleName}-header-week .#{$primaryStyleName}-back:active,.#{$primaryStyleName}-header-week .#{$primaryStyleName}-next:active + { + opacity: 1; + filter: alpha(opacity = 100); +} + +.#{$primaryStyleName}-header-week .#{$primaryStyleName}-next { + background-position: 50% -12px; +} + +/* Month grid */ +.#{$primaryStyleName}-month { + outline: none; +} + +.#{$primaryStyleName}-week-numbers { + width: 20px; + border-right: 1px solid #ccc; +} + +.#{$primaryStyleName}-week-number { + border: none; + background: transparent; + padding: 0; + margin: 0; + cursor: pointer; + opacity: .5; + width: 20px; + text-align: center; + border-bottom: 1px solid #ddd; +} + +.#{$primaryStyleName}-week-number:hover { + opacity: 1; +} + +.#{$primaryStyleName}-month-day { + border-bottom: 1px solid #ccc; + border-right: 1px solid #ccc; + outline: none; +} + +.#{$primaryStyleName}-month-day-today { + background-color: #e7f0f5; +} + +.#{$primaryStyleName}-month-day-selected { + background-color: #fffee7; +} + +.#{$primaryStyleName}-month-day-dragemphasis { + background-color: #a8a8a8; +} + +.#{$primaryStyleName}-month-day-scrollable { + overflow-y: scroll; +} + +.#{$primaryStyleName}-day-number { + height: 18px; + line-height: 18px; + font-size: 12px; + text-align: right; + padding-right: 3px; + white-space: nowrap; +} + +.#{$primaryStyleName}-day-number:hover { + cursor: pointer; + opacity: .6; + filter: alpha(opacity = 60); +} + +.#{$primaryStyleName}-month .#{$primaryStyleName}-spacer,.#{$primaryStyleName}-month .#{$primaryStyleName}-bottom-spacer,.#{$primaryStyleName}-month .#{$primaryStyleName}-bottom-spacer-empty + { + /* Bottom spacer is used in GWT to measure the event height (offsetHeight) */ + height: 15px; + font-size: 11px; +} + +.#{$primaryStyleName}-month .#{$primaryStyleName}-bottom-spacer:hover { + cursor: pointer; + opacity: .6; + filter: alpha(opacity = 60); +} + +.#{$primaryStyleName}-event { + line-height: 14px; + font-size: 11px; + padding: 0 0 0 4px; + cursor: pointer; + overflow: hidden; + text-overflow: ellipsis; + + outline: none; +} + +.#{$primaryStyleName}-event-month { + margin-bottom: 1px; + white-space: nowrap; +} + +.#{$primaryStyleName}-event-month:hover { + text-decoration: underline; +} + +.#{$primaryStyleName}-event-all-day { + background: #999; + display: block; + margin-left: -2px; +} + +div.#{$primaryStyleName}-event-all-day { + color: #fff; + height: 14px; +} + +.#{$primaryStyleName}-event-continued-from { + margin-left: 0; +} + +.#{$primaryStyleName}-event-start { + -webkit-border-top-left-radius: 6px; + -webkit-border-bottom-left-radius: 6px; + -moz-border-radius-topleft: 6px; + -moz-border-radius-bottomleft: 6px; + border-top-left-radius: 6px; + border-bottom-left-radius: 6px; + margin-left: 0; +} + +.#{$primaryStyleName}-event-end { + -webkit-border-top-right-radius: 6px; + -webkit-border-bottom-right-radius: 6px; + -moz-border-radius-topright: 6px; + -moz-border-radius-bottomright: 6px; + border-top-right-radius: 6px; + border-bottom-right-radius: 6px; +} + +/* Week/day view */ +.#{$primaryStyleName}-week-wrapper { + position: relative; +} + +/*.v-ie7 .#{$primaryStyleName}-week-wrapper TABLE{ + table-layout: fixed; +}*/ +.#{$primaryStyleName}-times { + width: 51px; +} + +.#{$primaryStyleName}-time { + padding: 0 8px 7px 0; + margin-top: -7px; + text-align: right; + font-size: 11px; + color: #666; + border-right: 1px solid #ccc; +} + +.#{$primaryStyleName}-weekly-longevents { + border-left: 1px solid #ccc; + border-bottom: 2px solid #bbb; + margin-left: 50px; +} + +.#{$primaryStyleName}-weekly-longevents .#{$primaryStyleName}-datecell { + border-right: 1px solid #ccc; + padding: 1px 0 0; +} + +.#{$primaryStyleName}-weekly-longevents .#{$primaryStyleName}-event { + height: 14px; + margin-bottom: 1px; +} + +.#{$primaryStyleName}-weekly-longevents .#{$primaryStyleName}-event:hover { + text-decoration: underline; +} + +.#{$primaryStyleName}-day-times { + border-right: 1px solid #ccc; + outline: none; +} + +.#{$primaryStyleName}-day-times .v-datecellslot,.#{$primaryStyleName}-day-times .v-datecellslot-even { + border-bottom: 1px solid #ccc; +} + +.#{$primaryStyleName}-day-times .v-datecellslot-even { + border-bottom-color: #eee; +} + +.#{$primaryStyleName}-day-times .v-daterange { + background-color: #a8a8a8; +} + +.#{$primaryStyleName}-day-times .v-reserved { + background-color: #FF3333; +} + +.#{$primaryStyleName}-day-times .dragemphasis { + background-color: #a8a8a8; +} + +.#{$primaryStyleName}-week-wrapper .#{$primaryStyleName}-event { + padding: 0; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + margin-top: -1px; +} + +.#{$primaryStyleName}-event-caption { + position: absolute; + z-index: 1; + top: 2px; + left: 4px; + width: 100%; + overflow: hidden; + text-overflow: ellipsis; + line-height: normal; +} + +.#{$primaryStyleName}-event-content { + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + border-radius: 4px; + border: 1px solid #777; + background: #eee; + opacity: .8; + filter: alpha(opacity = 80); + height: 14px; /* "min-height" */ +} + +.#{$primaryStyleName}-current-time { + position: absolute; + left: 0; + width: 100%; + height: 1px; + overflow: hidden; + background: #5a6c86; + opacity: .6; + filter: alpha(opacity = 60); + z-index: 2; +} + +.#{$primaryStyleName}-event-resizetop { + position: absolute; + cursor: n-resize; + height: 5%; + min-height: 3px; + top: 0; + width: 100%; + z-index: 1; +} + +.#{$primaryStyleName}-event-resizebottom { + position: absolute; + cursor: s-resize; + height: 5%; + min-height: 3px; + bottom: 0; + width: 100%; + z-index: 1; +} + +.#{$primaryStyleName}-month-sizedheight .#{$primaryStyleName}-month-day { + height: 100px; +} + +.#{$primaryStyleName}-month-sizedwidth .#{$primaryStyleName}-month-day { + width: 100px; +} + +.#{$primaryStyleName}-header-month-Hsized .#{$primaryStyleName}-header-day { + width: 101px; +} + +/* for others */ +.#{$primaryStyleName}-header-month-Hsized td:first-child { + padding-left: 21px; +} + +.#{$primaryStyleName}-header-day-Hsized { + width: 200px; +} + +.#{$primaryStyleName}-week-numbers-Vsized .#{$primaryStyleName}-week-number { + height: 100px; + line-height: 100px; +} + +.#{$primaryStyleName}-week-wrapper-Vsized { + height: 400px; + overflow-x: hidden !important; +} + +.#{$primaryStyleName}-times-Vsized .#{$primaryStyleName}-time { + height: 38px; +} + +.#{$primaryStyleName}-times-Hsized .#{$primaryStyleName}-time { + width: 42px; +} + +.#{$primaryStyleName}-day-times-Vsized .v-datecellslot,.#{$primaryStyleName}-day-times-Vsized .v-datecellslot-even { + height: 18px; +} + +.#{$primaryStyleName}-day-times-Hsized, .#{$primaryStyleName}-day-times-Hsized .v-datecellslot,.#{$primaryStyleName}-day-times-Hsized .v-datecellslot-even { + width: 200px; +} + +}
\ No newline at end of file diff --git a/WebContent/VAADIN/themes/base/calendar/img/arrows.png b/WebContent/VAADIN/themes/base/calendar/img/arrows.png Binary files differnew file mode 100644 index 0000000000..9905c0b065 --- /dev/null +++ b/WebContent/VAADIN/themes/base/calendar/img/arrows.png diff --git a/WebContent/VAADIN/themes/base/common/common.scss b/WebContent/VAADIN/themes/base/common/common.scss index e7fdd3fe84..48890488fb 100644 --- a/WebContent/VAADIN/themes/base/common/common.scss +++ b/WebContent/VAADIN/themes/base/common/common.scss @@ -204,6 +204,11 @@ body &.v-app-loading { padding: 2px; } +/* Removes clear button from input fields introduced by IE10 */ +input::-ms-clear { + display: none; +} + .v-drag-element { z-index: 60000; /* override any other position: properties */ @@ -232,5 +237,13 @@ body &.v-app-loading { width: 0; height: 0; } +} -}
\ No newline at end of file +/* Outside the base mixin because elements might be added directly to the body */ +.v-assistive-device-only { + position: absolute; + top: -2000px; + left: -2000px; + width: 10px; + overflow: hidden; +} diff --git a/WebContent/VAADIN/themes/base/datefield/datefield.scss b/WebContent/VAADIN/themes/base/datefield/datefield.scss index 1d3d408ed2..cbba9b46f3 100644 --- a/WebContent/VAADIN/themes/base/datefield/datefield.scss +++ b/WebContent/VAADIN/themes/base/datefield/datefield.scss @@ -59,10 +59,21 @@ .v-disabled .#{$primaryStyleName}-calendarpanel-day-today { cursor: default; } -.#{$primaryStyleName}-calendarpanel-day-disabled { +.#{$primaryStyleName}-calendarpanel-day-disabled, +.#{$primaryStyleName}-calendarpanel-day-outside-range { cursor: default; opacity: .5; } + +.#{$primaryStyleName}-calendarpanel-prevyear, +.#{$primaryStyleName}-calendarpanel-nextyear, +.#{$primaryStyleName}-calendarpanel-prevmonth, +.#{$primaryStyleName}-calendarpanel-nextmonth { + button.outside-range{ + opacity: .5; + } +} + .#{$primaryStyleName}-calendarpanel-day-selected { cursor: default; background: #333; diff --git a/WebContent/VAADIN/themes/base/debug/debug.scss b/WebContent/VAADIN/themes/base/debug/debug.scss index b6d22e8433..687370270e 100644 --- a/WebContent/VAADIN/themes/base/debug/debug.scss +++ b/WebContent/VAADIN/themes/base/debug/debug.scss @@ -29,4 +29,221 @@ .v-app .invalidlayout * { background: #f99 !important; } + + /* NEW debug window */ + $mainbg: #fff; + $darkborder: #666; + $lightborder: #999; + $maincolor: #666; + $activecolor: #000; + + @font-face { + font-family: 'vdebugfont'; + src:url('fonts/font.eot'); + src:url('fonts/font.eot?#iefix') format('embedded-opentype'), + url('fonts/font.woff') format('woff'), + url('fonts/font.ttf') format('truetype'), + url('fonts/font.svg#fontawesome') format('svg'); + font-weight: normal; + font-style: normal; + } + + .v-debugwindow [data-icon]:before, + .v-debugwindow-menu [data-icon]:before { + font-family: 'vdebugfont'; + content: attr(data-icon); + speak: none; + font-weight: normal; + font-variant: normal; + text-transform: none; + line-height: 1; + -webkit-font-smoothing: antialiased; + font-style: normal; + vertical-align: text-bottom; + } + + .v-debugwindow { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + + -moz-opacity: 0.8; + -webkit-opacity: 0.8; + opacity: 0.8; + color: $maincolor; + + font-size: 13px; + } + .v-debugwindow:hover { + -moz-opacity: 1; + -webkit-opacity: 1; + opacity: 1; + } + .v-debugwindow * { + font-size: inherit !important; + } + + .v-debugwindow-size0, .v-debugwindow-menu .v-debugwindow-button-size0 { + font-size: 10px; + } + .v-debugwindow-size1, .v-debugwindow-menu .v-debugwindow-button-size1 { + font-size: 13px; + } + .v-debugwindow-size2, .v-debugwindow-menu .v-debugwindow-button-size2 { + font-size: 16px; + } + + .v-debugwindow-head { + text-align: right; + cursor: move; + bakcground-color: transparent; + } + + .v-debugwindow-tabs { + display: inline-block; + background-color: $mainbg; + } + + .v-debugwindow-tab, .v-debugwindow-controls > * { + width: 2em; + border: none; + margin: 0; + line-height: 1.5em; + background-color: $mainbg; + color: $maincolor; + } + .v-debugwindow-tab { + position: relative; + top: 1px; + border-width: 1px 0 1px 1px; + border-style: solid; + border-color: $darkborder; + border-radius: 2px 2px 0 0; + } + .v-debugwindow-tab-selected { + color: $maincolor; + background-color: $mainbg; + border-bottom: 1px solid #fff; + } + + .v-debugwindow-controls { + position: relative; + top: 1px; + display: inline-block; + background-color: $mainbg; + border: 1px solid $darkborder; + border-radius: 2px 2px 0 0; + } + + .v-debugwindow-section-head { + text-align: left; + background-color: $mainbg; + border: 1px solid $darkborder; + border-bottom: 1px solid $lightborder; + + box-shadow: 0px 0px 7px 0 rgba(55,55,55,0.6); + min-height: 1.5em; + } + + .v-debugwindow-button { + border: none; + background-color: transparent; + color: $maincolor; + } + .v-debugwindow-button:hover { + color: $activecolor; + text-decoration: underline; + } + .v-debugwindow-button-active { + color: $maincolor; + box-shadow: 1px 1px 3px 0 inset; + } + + .v-debugwindow-content { + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + + box-shadow: 0px 0px 7px 0 rgba(55,55,55,0.6); + + background-color: $mainbg; + border: 1px solid $darkborder; + border-top: none; + + font-family: monospace; + } + + .v-debugwindow-menu { + background-color: $mainbg; + padding: 4px; + border: 1px solid $lightborder; + border-top: none; + + border-radius: 0 0 5px 5px; + + box-shadow: 0px 0px 7px 0 rgba(55,55,55,0.6); + } + .v-debugwindow-menu-content { + min-width: 100px; + } + .v-debugwindow-menu-content .v-debugwindow-button { + line-height: 22px; + } + .v-debugwindow-menu-content > div > .v-debugwindow-button { + width: 33%; + } + + + /* LOG */ + .v-debugwindow-reset { + color: #fff; + background-color: #4C92ED; + padding: 4px; + } + + .v-debugwindow-row { + display: table-row; + } + .v-debugwindow-row:nth-child(odd) { + background-color: rgba(0, 61, 255, 0.11); + } + .v-debugwindow-row.ERROR { + color: #550000; + background-color: #FFC5C5; + } + .v-debugwindow-row.WARNING { + background-color: #FFFF99; + } + + .v-debugwindow-row > span { + display: table-cell; + padding: 4px; + } + + .v-debugwindow-time { + text-align: right; + color: #999; + } + .v-debugwindow-message { + white-space: nowrap; + width: 100% + } + .v-debugwindow-message:hover { + white-space: normal; + word-wrap: break-word; + } + .v-debugwindow-message em { + background-color: #C4E6F8; + } + + /* HIERARCHY */ + .v-debugwindow-row > span.caption { + color: #999; + text-align: right; + white-space: nowrap; + } + .v-debugwindow-row > span.value { + width: 100%; + } + }
\ No newline at end of file diff --git a/WebContent/VAADIN/themes/base/debug/fonts/font.dev.svg b/WebContent/VAADIN/themes/base/debug/fonts/font.dev.svg new file mode 100644 index 0000000000..24fa9ceeed --- /dev/null +++ b/WebContent/VAADIN/themes/base/debug/fonts/font.dev.svg @@ -0,0 +1,39 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > +<svg xmlns="http://www.w3.org/2000/svg"> +<metadata> +This is a custom SVG font generated by IcoMoon. +<iconset grid="14"></iconset> +</metadata> +<defs> +<font id="fontawesome" horiz-adv-x="448" > +<font-face units-per-em="448" ascent="384" descent="-64" /> +<missing-glyph horiz-adv-x="448" /> +<glyph unicode="" d="M 288.00,176.00q0.00,46.25 -32.875,79.125t-79.125,32.875t-79.125-32.875t-32.875-79.125t 32.875-79.125t 79.125-32.875t 79.125,32.875t 32.875,79.125zM 416.00-32.00q0.00-13.00 -9.50-22.50t-22.50-9.50q-13.50,0.00 -22.50,9.50l-85.75,85.50q-44.75-31.00 -99.75-31.00q-35.75,0.00 -68.375,13.875t-56.25,37.50t-37.50,56.25t-13.875,68.375 t 13.875,68.375t 37.50,56.25t 56.25,37.50t 68.375,13.875t 68.375-13.875t 56.25-37.50t 37.50-56.25t 13.875-68.375q0.00-55.00 -31.00-99.75l 85.75-85.75q 9.25-9.25 9.25-22.50z" horiz-adv-x="416" data-tags="search, magnifier, lookup, find" /> +<glyph unicode="" d="M 417.75,242.50q0.00-10.00 -7.00-17.00l-181.00-181.00l-34.00-34.00q-7.00-7.00 -17.00-7.00t-17.00,7.00l-34.00,34.00l-90.50,90.50q-7.00,7.00 -7.00,17.00t 7.00,17.00l 34.00,34.00q 7.00,7.00 17.00,7.00t 17.00-7.00l 73.50-73.75l 164.00,164.25q 7.00,7.00 17.00,7.00t 17.00-7.00l 34.00-34.00q 7.00-7.00 7.00-17.00z" data-tags="ok, checkmark, tick, correct" /> +<glyph unicode="" d="M 324.50,53.50q0.00-10.00 -7.00-17.00l-34.00-34.00q-7.00-7.00 -17.00-7.00t-17.00,7.00l-73.50,73.50l-73.50-73.50q-7.00-7.00 -17.00-7.00t-17.00,7.00l-34.00,34.00q-7.00,7.00 -7.00,17.00t 7.00,17.00l 73.50,73.50l-73.50,73.50q-7.00,7.00 -7.00,17.00t 7.00,17.00l 34.00,34.00q 7.00,7.00 17.00,7.00t 17.00-7.00l 73.50-73.50l 73.50,73.50q 7.00,7.00 17.00,7.00t 17.00-7.00l 34.00-34.00q 7.00-7.00 7.00-17.00 t-7.00-17.00l-73.50-73.50l 73.50-73.50q 7.00-7.00 7.00-17.00z" horiz-adv-x="352" data-tags="remove, cancel, close, delete, mutiply" /> +<glyph unicode="" d="M 384.00,160.00q0.00-39.00 -15.25-74.50t-41.00-61.25t-61.25-41.00t-74.50-15.25t-74.50,15.25t-61.25,41.00t-41.00,61.25t-15.25,74.50q0.00,45.50 20.125,85.75t 56.625,67.50q 10.75,8.00 23.875,6.25t 20.875-12.50q 8.00-10.50 6.125-23.625t-12.375-21.125q-24.50-18.50 -37.875-45.25t-13.375-57.00q0.00-26.00 10.125-49.625t 27.375-40.875t 40.875-27.375 t 49.625-10.125t 49.625,10.125t 40.875,27.375t 27.375,40.875t 10.125,49.625q0.00,30.25 -13.375,57.00t-37.875,45.25q-10.50,8.00 -12.375,21.125t 6.125,23.625q 7.75,10.75 21.00,12.50t 23.75-6.25q 36.50-27.25 56.625-67.50t 20.125-85.75zM 224.00,352.00l0.00-160.00 q0.00-13.00 -9.50-22.50t-22.50-9.50t-22.50,9.50t-9.50,22.50l0.00,160.00 q0.00,13.00 9.50,22.50t 22.50,9.50t 22.50-9.50t 9.50-22.50z" horiz-adv-x="384" data-tags="off, switch, power" /> +<glyph unicode="" d="M 128.00,200.00l0.00-144.00 q0.00-3.50 -2.25-5.75t-5.75-2.25l-16.00,0.00 q-3.50,0.00 -5.75,2.25t-2.25,5.75l0.00,144.00 q0.00,3.50 2.25,5.75t 5.75,2.25l 16.00,0.00 q 3.50,0.00 5.75-2.25t 2.25-5.75zM 192.00,200.00l0.00-144.00 q0.00-3.50 -2.25-5.75t-5.75-2.25l-16.00,0.00 q-3.50,0.00 -5.75,2.25t-2.25,5.75l0.00,144.00 q0.00,3.50 2.25,5.75t 5.75,2.25l 16.00,0.00 q 3.50,0.00 5.75-2.25t 2.25-5.75zM 256.00,200.00l0.00-144.00 q0.00-3.50 -2.25-5.75t-5.75-2.25l-16.00,0.00 q-3.50,0.00 -5.75,2.25t-2.25,5.75l0.00,144.00 q0.00,3.50 2.25,5.75t 5.75,2.25l 16.00,0.00 q 3.50,0.00 5.75-2.25t 2.25-5.75zM 288.00,19.00l0.00,237.00 l-224.00,0.00 l0.00-237.00 q0.00-5.50 1.75-10.125t 3.625-6.75t 2.625-2.125l 208.00,0.00 q 0.75,0.00 2.625,2.125t 3.625,6.75t 1.75,10.125zM 120.00,288.00l 112.00,0.00 l-12.00,29.25q-1.75,2.25 -4.25,2.75l-79.25,0.00 q-2.50-0.50 -4.25-2.75zM 352.00,280.00l0.00-16.00 q0.00-3.50 -2.25-5.75t-5.75-2.25l-24.00,0.00 l0.00-237.00 q0.00-20.75 -11.75-35.875t-28.25-15.125l-208.00,0.00 q-16.50,0.00 -28.25,14.625t-11.75,35.375l0.00,238.00 l-24.00,0.00 q-3.50,0.00 -5.75,2.25t-2.25,5.75l0.00,16.00 q0.00,3.50 2.25,5.75t 5.75,2.25l 77.25,0.00 l 17.50,41.75q 3.75,9.25 13.50,15.75t 19.75,6.50l 80.00,0.00 q 10.00,0.00 19.75-6.50t 13.50-15.75l 17.50-41.75l 77.25,0.00 q 3.50,0.00 5.75-2.25t 2.25-5.75z" horiz-adv-x="352" data-tags="trash, remove, delete, bin" /> +<glyph unicode="" d="M 272.00,152.00l0.00-16.00 q0.00-3.25 -2.375-5.625t-5.625-2.375l-96.00,0.00 q-3.25,0.00 -5.625,2.375t-2.375,5.625l0.00,112.00 q0.00,3.25 2.375,5.625t 5.625,2.375l 16.00,0.00 q 3.25,0.00 5.625-2.375t 2.375-5.625l0.00-88.00 l 72.00,0.00 q 3.25,0.00 5.625-2.375t 2.375-5.625zM 320.00,160.00q0.00,26.00 -10.125,49.625t-27.375,40.875t-40.875,27.375t-49.625,10.125t-49.625-10.125 t-40.875-27.375t-27.375-40.875t-10.125-49.625t 10.125-49.625t 27.375-40.875t 40.875-27.375t 49.625-10.125t 49.625,10.125t 40.875,27.375t 27.375,40.875t 10.125,49.625zM 384.00,160.00q0.00-52.25 -25.75-96.375t-69.875-69.875t-96.375-25.75t-96.375,25.75t-69.875,69.875t-25.75,96.375t 25.75,96.375t 69.875,69.875 t 96.375,25.75t 96.375-25.75t 69.875-69.875t 25.75-96.375z" horiz-adv-x="384" data-tags="time, clock" /> +<glyph unicode="" d="M 192.00,144.00l0.00-112.00 q0.00-6.50 -4.75-11.25t-11.25-4.75t-11.25,4.75l-36.00,36.00l-83.00-83.00q-2.50-2.50 -5.75-2.50t-5.75,2.50l-28.50,28.50q-2.50,2.50 -2.50,5.75t 2.50,5.75l 83.00,83.00l-36.00,36.00q-4.75,4.75 -4.75,11.25t 4.75,11.25t 11.25,4.75l 112.00,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25zM 380.75,312.00q0.00-3.25 -2.50-5.75l-83.00-83.00l 36.00-36.00q 4.75-4.75 4.75-11.25t-4.75-11.25 t-11.25-4.75l-112.00,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,112.00 q0.00,6.50 4.75,11.25t 11.25,4.75t 11.25-4.75l 36.00-36.00l 83.00,83.00q 2.50,2.50 5.75,2.50t 5.75-2.50l 28.50-28.50q 2.50-2.50 2.50-5.75z" horiz-adv-x="384" data-tags="resize-small, contract, collapse" /> +<glyph unicode="" d="M 256.00,40.25l0.00,47.50 q0.00,3.50 -2.375,5.875t-5.625,2.375l-48.00,0.00 q-3.25,0.00 -5.625-2.375t-2.375-5.875l0.00-47.50 q0.00-3.50 2.375-5.875t 5.625-2.375l 48.00,0.00 q 3.25,0.00 5.625,2.375t 2.375,5.875zM 255.50,133.75l 4.50,114.75q0.00,3.00 -2.50,4.75q-3.25,2.75 -6.00,2.75l-55.00,0.00 q-2.75,0.00 -6.00-2.75q-2.50-1.75 -2.50-5.25l 4.25-114.25q0.00-2.50 2.50-4.125t 6.00-1.625l 46.25,0.00 q 3.50,0.00 5.875,1.625t 2.625,4.125zM 252.00,367.25l 192.00-352.00q 8.75-15.75 -0.50-31.50q-4.25-7.25 -11.625-11.50t-15.875-4.25l-384.00,0.00 q-8.50,0.00 -15.875,4.25t-11.625,11.50q-9.25,15.75 -0.50,31.50l 192.00,352.00q 4.25,7.75 11.75,12.25t 16.25,4.50t 16.25-4.50t 11.75-12.25z" data-tags="warning-sign, sign" /> +<glyph unicode="" d="M 256.00,40.00l0.00,16.00 q0.00,3.50 -2.25,5.75t-5.75,2.25l-24.00,0.00 l0.00,120.00 q0.00,3.50 -2.25,5.75t-5.75,2.25l-80.00,0.00 q-3.50,0.00 -5.75-2.25t-2.25-5.75l0.00-16.00 q0.00-3.50 2.25-5.75t 5.75-2.25l 24.00,0.00 l0.00-96.00 l-24.00,0.00 q-3.50,0.00 -5.75-2.25t-2.25-5.75l0.00-16.00 q0.00-3.50 2.25-5.75t 5.75-2.25l 112.00,0.00 q 3.50,0.00 5.75,2.25t 2.25,5.75zM 224.00,232.00l0.00,48.00 q0.00,3.50 -2.25,5.75t-5.75,2.25l-48.00,0.00 q-3.50,0.00 -5.75-2.25t-2.25-5.75l0.00-48.00 q0.00-3.50 2.25-5.75 t 5.75-2.25l 48.00,0.00 q 3.50,0.00 5.75,2.25t 2.25,5.75zM 384.00,160.00q0.00-52.25 -25.75-96.375t-69.875-69.875t-96.375-25.75t-96.375,25.75t-69.875,69.875t-25.75,96.375t 25.75,96.375t 69.875,69.875t 96.375,25.75t 96.375-25.75t 69.875-69.875t 25.75-96.375z" horiz-adv-x="384" data-tags="info-sign, information, sign" /> +<glyph unicode="" d="M 192.00,352.00q 52.25,0.00 96.375-25.75t 69.875-69.875t 25.75-96.375t-25.75-96.375t-69.875-69.875t-96.375-25.75t-96.375,25.75t-69.875,69.875t-25.75,96.375t 25.75,96.375t 69.875,69.875t 96.375,25.75zM 224.00,40.25l0.00,47.50 q0.00,3.50 -2.25,5.875t-5.50,2.375l-48.00,0.00 q-3.25,0.00 -5.75-2.50t-2.50-5.75l0.00-47.50 q0.00-3.25 2.50-5.75t 5.75-2.50l 48.00,0.00 q 3.25,0.00 5.50,2.375t 2.25,5.875zM 223.50,126.25l 4.50,155.25q0.00,3.00 -2.50,4.50q-2.50,2.00 -6.00,2.00l-55.00,0.00 q-3.50,0.00 -6.00-2.00q-2.50-1.50 -2.50-4.50l 4.25-155.25q0.00-2.50 2.50-4.375t 6.00-1.875l 46.25,0.00 q 3.50,0.00 5.875,1.875t 2.625,4.375z" horiz-adv-x="384" data-tags="exclamation-sign, sign, notification, attention, warning" /> +<glyph unicode="" d="M 299.25,128.00l-27.25,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,32.00 q0.00,6.50 4.75,11.25t 11.25,4.75l 27.25,0.00 q-8.00,27.00 -28.125,47.125t-47.125,28.125l0.00-27.25 q0.00-6.50 -4.75-11.25t-11.25-4.75l-32.00,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,27.25 q-27.00-8.00 -47.125-28.125t-28.125-47.125l 27.25,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25l0.00-32.00 q0.00-6.50 -4.75-11.25t-11.25-4.75l-27.25,0.00 q 8.00-27.00 28.125-47.125t 47.125-28.125l0.00,27.25 q0.00,6.50 4.75,11.25t 11.25,4.75l 32.00,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25l0.00-27.25 q 27.00,8.00 47.125,28.125t 28.125,47.125zM 384.00,176.00l0.00-32.00 q0.00-6.50 -4.75-11.25t-11.25-4.75l-35.75,0.00 q-9.25-40.25 -38.625-69.625t-69.625-38.625l0.00-35.75 q0.00-6.50 -4.75-11.25t-11.25-4.75l-32.00,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,35.75 q-40.25,9.25 -69.625,38.625t-38.625,69.625l-35.75,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,32.00 q0.00,6.50 4.75,11.25t 11.25,4.75l 35.75,0.00 q 9.25,40.25 38.625,69.625t 69.625,38.625l0.00,35.75 q0.00,6.50 4.75,11.25t 11.25,4.75l 32.00,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25l0.00-35.75 q 40.25-9.25 69.625-38.625t 38.625-69.625l 35.75,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25z" horiz-adv-x="384" data-tags="screenshot, target, goal, spot" /> +<glyph unicode="" d="M 384.00,48.00l0.00-32.00 q0.00-6.50 -4.75-11.25t-11.25-4.75l-352.00,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,32.00 q0.00,6.50 4.75,11.25t 11.25,4.75l 352.00,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25zM 384.00,176.00l0.00-32.00 q0.00-6.50 -4.75-11.25t-11.25-4.75l-352.00,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,32.00 q0.00,6.50 4.75,11.25t 11.25,4.75l 352.00,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25zM 384.00,304.00l0.00-32.00 q0.00-6.50 -4.75-11.25 t-11.25-4.75l-352.00,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,32.00 q0.00,6.50 4.75,11.25t 11.25,4.75l 352.00,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25z" horiz-adv-x="384" data-tags="reorder, list, menu" /> +<glyph unicode="" d="M 297.50,238.75l 73.25,73.25l-26.75,26.75l-73.25-73.25zM 409.25,312.00q0.00-6.75 -4.50-11.25l-321.50-321.50q-4.50-4.50 -11.25-4.50t-11.25,4.50l-49.50,49.50q-4.50,4.50 -4.50,11.25t 4.50,11.25l 321.50,321.50q 4.50,4.50 11.25,4.50t 11.25-4.50l 49.50-49.50q 4.50-4.50 4.50-11.25zM 71.50,359.50l 24.50-7.50l-24.50-7.50l-7.50-24.50l-7.50,24.50l-24.50,7.50l 24.50,7.50l 7.50,24.50zM 159.00,319.00 l 49.00-15.00l-49.00-15.00l-15.00-49.00l-15.00,49.00l-49.00,15.00l 49.00,15.00l 15.00,49.00zM 391.50,199.50l 24.50-7.50l-24.50-7.50l-7.50-24.50l-7.50,24.50l-24.50,7.50l 24.50,7.50l 7.50,24.50zM 231.50,359.50l 24.50-7.50l-24.50-7.50l-7.50-24.50l-7.50,24.50l-24.50,7.50l 24.50,7.50l 7.50,24.50z" horiz-adv-x="416" data-tags="magic, wand, wizard" /> +<glyph unicode="" d="M 448.00,72.00l0.00-80.00 q0.00-10.00 -7.00-17.00t-17.00-7.00l-80.00,0.00 q-10.00,0.00 -17.00,7.00t-7.00,17.00l0.00,80.00 q0.00,10.00 7.00,17.00t 17.00,7.00l 24.00,0.00 l0.00,48.00 l-128.00,0.00 l0.00-48.00 l 24.00,0.00 q 10.00,0.00 17.00-7.00t 7.00-17.00l0.00-80.00 q0.00-10.00 -7.00-17.00t-17.00-7.00l-80.00,0.00 q-10.00,0.00 -17.00,7.00t-7.00,17.00l0.00,80.00 q0.00,10.00 7.00,17.00t 17.00,7.00l 24.00,0.00 l0.00,48.00 l-128.00,0.00 l0.00-48.00 l 24.00,0.00 q 10.00,0.00 17.00-7.00t 7.00-17.00l0.00-80.00 q0.00-10.00 -7.00-17.00t-17.00-7.00l-80.00,0.00 q-10.00,0.00 -17.00,7.00t-7.00,17.00l0.00,80.00 q0.00,10.00 7.00,17.00t 17.00,7.00l 24.00,0.00 l0.00,48.00 q0.00,13.00 9.50,22.50t 22.50,9.50l 128.00,0.00 l0.00,48.00 l-24.00,0.00 q-10.00,0.00 -17.00,7.00t-7.00,17.00l0.00,80.00 q0.00,10.00 7.00,17.00t 17.00,7.00l 80.00,0.00 q 10.00,0.00 17.00-7.00t 7.00-17.00l0.00-80.00 q0.00-10.00 -7.00-17.00t-17.00-7.00l-24.00,0.00 l0.00-48.00 l 128.00,0.00 q 13.00,0.00 22.50-9.50t 9.50-22.50l0.00-48.00 l 24.00,0.00 q 10.00,0.00 17.00-7.00t 7.00-17.00 z" data-tags="sitemap, tree" /> +<glyph unicode="" d="M 256.00,160.00q0.00,26.50 -18.75,45.25t-45.25,18.75t-45.25-18.75t-18.75-45.25t 18.75-45.25t 45.25-18.75t 45.25,18.75t 18.75,45.25zM 384.00,187.25l0.00-55.50 q0.00-3.00 -2.00-5.75t-5.00-3.25l-46.25-7.00q-4.75-13.50 -9.75-22.75q 8.75-12.50 26.75-34.50q 2.50-3.00 2.50-6.25t-2.25-5.75q-6.75-9.25 -24.75-27.00t-23.50-17.75q-3.00,0.00 -6.50,2.25l-34.50,27.00q-11.00-5.75 -22.75-9.50 q-4.00-34.00 -7.25-46.50q-1.75-7.00 -9.00-7.00l-55.50,0.00 q-3.50,0.00 -6.125,2.125t-2.875,5.375l-7.00,46.00q-12.25,4.00 -22.50,9.25l-35.25-26.75q-2.50-2.25 -6.25-2.25q-3.50,0.00 -6.25,2.75q-31.50,28.50 -41.25,42.00q-1.75,2.50 -1.75,5.75q0.00,3.00 2.00,5.75q 3.75,5.25 12.75,16.625t 13.50,17.625q-6.75,12.50 -10.25,24.75l-45.75,6.75q-3.25,0.50 -5.25,3.125t-2.00,5.875l0.00,55.50 q0.00,3.00 2.00,5.75t 4.75,3.25 l 46.50,7.00q 3.50,11.50 9.75,23.00q-10.00,14.25 -26.75,34.50q-2.50,3.00 -2.50,6.00q0.00,2.50 2.25,5.75q 6.50,9.00 24.625,26.875t 23.625,17.875q 3.25,0.00 6.50-2.50l 34.50-26.75q 11.00,5.75 22.75,9.50q 4.00,34.00 7.25,46.50q 1.75,7.00 9.00,7.00l 55.50,0.00 q 3.50,0.00 6.125-2.125t 2.875-5.375l 7.00-46.00q 12.25-4.00 22.50-9.25l 35.50,26.75q 2.25,2.25 6.00,2.25q 3.25,0.00 6.25-2.50q 32.25-29.75 41.25-42.50q 1.75-2.00 1.75-5.50 q0.00-3.00 -2.00-5.75q-3.75-5.25 -12.75-16.625t-13.50-17.625q 6.50-12.50 10.25-24.50l 45.75-7.00q 3.25-0.50 5.25-3.125t 2.00-5.875z" horiz-adv-x="384" data-tags="cog, settings, options, gear, preferences" /> +<glyph unicode="" d="M 448.00,88.00l0.00-48.00 q0.00-3.25 -2.375-5.625t-5.625-2.375l-344.00,0.00 l0.00-48.00 q0.00-3.25 -2.375-5.625t-5.625-2.375q-3.00,0.00 -6.00,2.50l-79.75,80.00q-2.25,2.25 -2.25,5.50q0.00,3.50 2.25,5.75l 80.00,80.00q 2.25,2.25 5.75,2.25q 3.25,0.00 5.625-2.375t 2.375-5.625l0.00-48.00 l 344.00,0.00 q 3.25,0.00 5.625-2.375t 2.375-5.625zM 448.00,224.00q0.00-3.50 -2.25-5.75l-80.00-80.00q-2.25-2.25 -5.75-2.25 q-3.25,0.00 -5.625,2.375t-2.375,5.625l0.00,48.00 l-344.00,0.00 q-3.25,0.00 -5.625,2.375t-2.375,5.625l0.00,48.00 q0.00,3.25 2.375,5.625t 5.625,2.375l 344.00,0.00 l0.00,48.00 q0.00,3.50 2.25,5.75t 5.75,2.25q 3.00,0.00 6.00-2.50l 79.75-79.75q 2.25-2.25 2.25-5.75z" data-tags="exchange, transfer, tab, traffic" /> +<glyph unicode="" d="M 96.00,48.00q0.00-6.50 -4.75-11.25t-11.25-4.75t-11.25,4.75t-4.75,11.25t 4.75,11.25t 11.25,4.75t 11.25-4.75t 4.75-11.25zM 352.00,32.75q0.00-30.25 -18.25-47.50t-48.50-17.25l-218.50,0.00 q-30.25,0.00 -48.50,17.25t-18.25,47.50q0.00,17.00 1.375,32.75t 6.00,34.50t 11.875,33.125t 20.25,25.75t 30.00,15.125q-5.50-13.00 -5.50-30.00l0.00-50.75 q-14.50-5.00 -23.25-17.50t-8.75-27.75q0.00-20.00 14.00-34.00t 34.00-14.00 t 34.00,14.00t 14.00,34.00q0.00,15.25 -8.875,27.75t-23.125,17.50l0.00,50.75 q0.00,15.50 6.25,23.25q 33.00-26.00 73.75-26.00t 73.75,26.00q 6.25-7.75 6.25-23.25l0.00-16.00 q-26.50,0.00 -45.25-18.75t-18.75-45.25l0.00-22.25 q-8.00-7.25 -8.00-17.75q0.00-10.00 7.00-17.00t 17.00-7.00t 17.00,7.00t 7.00,17.00q0.00,10.50 -8.00,17.75l0.00,22.25 q0.00,13.00 9.50,22.50t 22.50,9.50t 22.50-9.50t 9.50-22.50l0.00-22.25 q-8.00-7.25 -8.00-17.75q0.00-10.00 7.00-17.00 t 17.00-7.00t 17.00,7.00t 7.00,17.00q0.00,10.50 -8.00,17.75l0.00,22.25 q0.00,17.00 -8.625,31.875t-23.375,23.375q0.00,2.50 0.125,10.625t0.00,12.00t-0.625,10.375t-1.75,11.75t-3.25,10.00q 17.00-3.75 30.00-15.125t 20.25-25.75t 11.875-33.125t 6.00-34.50t 1.375-32.75zM 272.00,256.00q0.00-39.75 -28.125-67.875t-67.875-28.125t-67.875,28.125t-28.125,67.875t 28.125,67.875t 67.875,28.125 t 67.875-28.125t 28.125-67.875z" horiz-adv-x="352" data-tags="user-md, medic, doctor" /> +<glyph unicode="" d="M 176.00,128.00q0.00,13.25 -9.375,22.625t-22.625,9.375t-22.625-9.375t-9.375-22.625q0.00-9.25 4.75-16.75t 12.75-11.75l-17.25-57.25q-1.25-3.75 1.25-7.00t 6.50-3.25l 48.00,0.00 q 4.00,0.00 6.50,3.25t 1.25,7.00l-17.25,57.25q 8.00,4.25 12.75,11.75t 4.75,16.75zM 80.00,192.00l 128.00,0.00 l0.00,48.00 q0.00,26.50 -18.75,45.25t-45.25,18.75t-45.25-18.75t-18.75-45.25l0.00-48.00 zM 288.00,168.00l0.00-144.00 q0.00-10.00 -7.00-17.00 t-17.00-7.00l-240.00,0.00 q-10.00,0.00 -17.00,7.00t-7.00,17.00l0.00,144.00 q0.00,10.00 7.00,17.00t 17.00,7.00l 8.00,0.00 l0.00,48.00 q0.00,46.00 33.00,79.00t 79.00,33.00t 79.00-33.00t 33.00-79.00l0.00-48.00 l 8.00,0.00 q 10.00,0.00 17.00-7.00t 7.00-17.00z" horiz-adv-x="288" data-tags="lock, password, secure, private, protected, encrypted" /> +<glyph unicode="" d="M 320.00,160.00q0.00,26.00 -10.125,49.625t-27.375,40.875t-40.875,27.375t-49.625,10.125t-49.625-10.125t-40.875-27.375t-27.375-40.875t-10.125-49.625t 10.125-49.625t 27.375-40.875t 40.875-27.375t 49.625-10.125t 49.625,10.125t 40.875,27.375t 27.375,40.875t 10.125,49.625zM 384.00,160.00q0.00-52.25 -25.75-96.375 t-69.875-69.875t-96.375-25.75t-96.375,25.75t-69.875,69.875t-25.75,96.375t 25.75,96.375t 69.875,69.875t 96.375,25.75t 96.375-25.75t 69.875-69.875t 25.75-96.375z" horiz-adv-x="384" data-tags="circle-blank" /> +<glyph unicode="" d="M 384.00,160.00q0.00-52.25 -25.75-96.375t-69.875-69.875t-96.375-25.75t-96.375,25.75t-69.875,69.875t-25.75,96.375t 25.75,96.375t 69.875,69.875t 96.375,25.75t 96.375-25.75t 69.875-69.875t 25.75-96.375z" horiz-adv-x="384" data-tags="circle" /> +<glyph unicode="" d="M 124.00,48.00q0.00-15.00 -10.625-25.50t-25.375-10.50q-15.00,0.00 -25.50,10.50t-10.50,25.50t 10.50,25.50t 25.50,10.50q 14.75,0.00 25.375-10.50t 10.625-25.50zM 232.00,0.00q0.00-13.25 -9.375-22.625t-22.625-9.375t-22.625,9.375t-9.375,22.625t 9.375,22.625t 22.625,9.375t 22.625-9.375t 9.375-22.625zM 80.00,160.00q0.00-16.50 -11.75-28.25t-28.25-11.75t-28.25,11.75t-11.75,28.25 t 11.75,28.25t 28.25,11.75t 28.25-11.75t 11.75-28.25zM 340.00,48.00q0.00-11.50 -8.25-19.75t-19.75-8.25t-19.75,8.25t-8.25,19.75t 8.25,19.75t 19.75,8.25t 19.75-8.25t 8.25-19.75zM 132.00,272.00q0.00-18.25 -12.875-31.125t-31.125-12.875t-31.125,12.875t-12.875,31.125t 12.875,31.125t 31.125,12.875t 31.125-12.875t 12.875-31.125zM 248.00,320.00q0.00-20.00 -14.00-34.00t-34.00-14.00 t-34.00,14.00t-14.00,34.00t 14.00,34.00t 34.00,14.00t 34.00-14.00t 14.00-34.00zM 384.00,160.00q0.00-10.00 -7.00-17.00t-17.00-7.00t-17.00,7.00t-7.00,17.00t 7.00,17.00t 17.00,7.00t 17.00-7.00t 7.00-17.00zM 332.00,272.00q0.00-8.25 -5.875-14.125t-14.125-5.875t-14.125,5.875t-5.875,14.125t 5.875,14.125t 14.125,5.875t 14.125-5.875t 5.875-14.125z" horiz-adv-x="392" data-tags="spinner, loading, busy, progress" /> +<glyph unicode="" d="M 320.00,160.00q0.00,34.75 -17.75,65.00l-175.25-175.25q 30.25-17.75 65.00-17.75q 26.00,0.00 49.625,10.125t 40.875,27.375t 27.375,40.875t 10.125,49.625zM 81.75,95.00l 175.25,175.25q-30.25,17.75 -65.00,17.75q-26.00,0.00 -49.625-10.125t-40.875-27.375t-27.375-40.875t-10.125-49.625q0.00-34.75 17.75-65.00zM 384.00,160.00q0.00-52.25 -25.75-96.375 t-69.875-69.875t-96.375-25.75t-96.375,25.75t-69.875,69.875t-25.75,96.375t 25.75,96.375t 69.875,69.875t 96.375,25.75t 96.375-25.75t 69.875-69.875t 25.75-96.375z" horiz-adv-x="384" data-tags="ban-circle, block, forbidden" /> +<glyph unicode="" d="M 188.75,120.00q0.00-3.25 -2.50-5.75l-83.00-83.00l 36.00-36.00q 4.75-4.75 4.75-11.25t-4.75-11.25t-11.25-4.75l-112.00,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,112.00 q0.00,6.50 4.75,11.25t 11.25,4.75t 11.25-4.75l 36.00-36.00l 83.00,83.00q 2.50,2.50 5.75,2.50t 5.75-2.50l 28.50-28.50q 2.50-2.50 2.50-5.75zM 384.00,336.00l0.00-112.00 q0.00-6.50 -4.75-11.25t-11.25-4.75t-11.25,4.75l-36.00,36.00l-83.00-83.00 q-2.50-2.50 -5.75-2.50t-5.75,2.50l-28.50,28.50q-2.50,2.50 -2.50,5.75t 2.50,5.75l 83.00,83.00l-36.00,36.00q-4.75,4.75 -4.75,11.25t 4.75,11.25t 11.25,4.75l 112.00,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25z" horiz-adv-x="384" data-tags="resize-full, expand, enlarge" /> +<glyph unicode="" d="M 377.75,120.00q0.00-1.25 -0.25-1.75q-16.00-67.00 -67.00-108.625t-119.50-41.625q-36.50,0.00 -70.625,13.75t-60.875,39.25l-32.25-32.25q-4.75-4.75 -11.25-4.75t-11.25,4.75t-4.75,11.25l0.00,112.00 q0.00,6.50 4.75,11.25t 11.25,4.75l 112.00,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25t-4.75-11.25l-34.25-34.25q 17.75-16.50 40.25-25.50t 46.75-9.00q 33.50,0.00 62.50,16.25t 46.50,44.75q 2.75,4.25 13.25,29.25 q 2.00,5.75 7.50,5.75l 48.00,0.00 q 3.25,0.00 5.625-2.375t 2.375-5.625zM 384.00,320.00l0.00-112.00 q0.00-6.50 -4.75-11.25t-11.25-4.75l-112.00,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25t 4.75,11.25l 34.50,34.50q-37.00,34.25 -87.25,34.25q-33.50,0.00 -62.50-16.25t-46.50-44.75q-2.75-4.25 -13.25-29.25q-2.00-5.75 -7.50-5.75l-49.75,0.00 q-3.25,0.00 -5.625,2.375t-2.375,5.625l0.00,1.75 q 16.25,67.00 67.50,108.625t 120.00,41.625 q 36.50,0.00 71.00-13.875t 61.25-39.125l 32.50,32.25q 4.75,4.75 11.25,4.75t 11.25-4.75t 4.75-11.25z" horiz-adv-x="384" data-tags="refresh, synchronize" /> +<glyph unicode="" d="M 291.00,352.00q 5.75,0.00 11.00-2.25q 8.25-3.25 13.125-10.25t 4.875-15.50l0.00-322.25 q0.00-8.50 -4.875-15.50t-13.125-10.25q-4.75-2.00 -11.00-2.00q-12.00,0.00 -20.75,8.00l-110.25,106.00l-110.25-106.00q-9.00-8.25 -20.75-8.25q-5.75,0.00 -11.00,2.25q-8.25,3.25 -13.125,10.25t-4.875,15.50l0.00,322.25 q0.00,8.50 4.875,15.50t 13.125,10.25q 5.25,2.25 11.00,2.25l 262.00,0.00 z" horiz-adv-x="320" data-tags="bookmark, favorite, ribbon" /> +<glyph unicode=" " horiz-adv-x="224" /> +<glyph class="hidden" unicode="" d="M0,384L 448 -64L0 -64 z" horiz-adv-x="0" /> +</font></defs></svg>
\ No newline at end of file diff --git a/WebContent/VAADIN/themes/base/debug/fonts/font.eot b/WebContent/VAADIN/themes/base/debug/fonts/font.eot Binary files differnew file mode 100644 index 0000000000..310a74dfce --- /dev/null +++ b/WebContent/VAADIN/themes/base/debug/fonts/font.eot diff --git a/WebContent/VAADIN/themes/base/debug/fonts/font.svg b/WebContent/VAADIN/themes/base/debug/fonts/font.svg new file mode 100644 index 0000000000..8149b583fd --- /dev/null +++ b/WebContent/VAADIN/themes/base/debug/fonts/font.svg @@ -0,0 +1,39 @@ +<?xml version="1.0" standalone="no"?> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" > +<svg xmlns="http://www.w3.org/2000/svg"> +<metadata> +This is a custom SVG font generated by IcoMoon. +<iconset grid="14"></iconset> +</metadata> +<defs> +<font id="fontawesome" horiz-adv-x="448" > +<font-face units-per-em="448" ascent="384" descent="-64" /> +<missing-glyph horiz-adv-x="448" /> +<glyph unicode="" d="M 288.00,176.00q0.00,46.25 -32.875,79.125t-79.125,32.875t-79.125-32.875t-32.875-79.125t 32.875-79.125t 79.125-32.875t 79.125,32.875t 32.875,79.125zM 416.00-32.00q0.00-13.00 -9.50-22.50t-22.50-9.50q-13.50,0.00 -22.50,9.50l-85.75,85.50q-44.75-31.00 -99.75-31.00q-35.75,0.00 -68.375,13.875t-56.25,37.50t-37.50,56.25t-13.875,68.375 t 13.875,68.375t 37.50,56.25t 56.25,37.50t 68.375,13.875t 68.375-13.875t 56.25-37.50t 37.50-56.25t 13.875-68.375q0.00-55.00 -31.00-99.75l 85.75-85.75q 9.25-9.25 9.25-22.50z" horiz-adv-x="416" /> +<glyph unicode="" d="M 417.75,242.50q0.00-10.00 -7.00-17.00l-181.00-181.00l-34.00-34.00q-7.00-7.00 -17.00-7.00t-17.00,7.00l-34.00,34.00l-90.50,90.50q-7.00,7.00 -7.00,17.00t 7.00,17.00l 34.00,34.00q 7.00,7.00 17.00,7.00t 17.00-7.00l 73.50-73.75l 164.00,164.25q 7.00,7.00 17.00,7.00t 17.00-7.00l 34.00-34.00q 7.00-7.00 7.00-17.00z" /> +<glyph unicode="" d="M 324.50,53.50q0.00-10.00 -7.00-17.00l-34.00-34.00q-7.00-7.00 -17.00-7.00t-17.00,7.00l-73.50,73.50l-73.50-73.50q-7.00-7.00 -17.00-7.00t-17.00,7.00l-34.00,34.00q-7.00,7.00 -7.00,17.00t 7.00,17.00l 73.50,73.50l-73.50,73.50q-7.00,7.00 -7.00,17.00t 7.00,17.00l 34.00,34.00q 7.00,7.00 17.00,7.00t 17.00-7.00l 73.50-73.50l 73.50,73.50q 7.00,7.00 17.00,7.00t 17.00-7.00l 34.00-34.00q 7.00-7.00 7.00-17.00 t-7.00-17.00l-73.50-73.50l 73.50-73.50q 7.00-7.00 7.00-17.00z" horiz-adv-x="352" /> +<glyph unicode="" d="M 384.00,160.00q0.00-39.00 -15.25-74.50t-41.00-61.25t-61.25-41.00t-74.50-15.25t-74.50,15.25t-61.25,41.00t-41.00,61.25t-15.25,74.50q0.00,45.50 20.125,85.75t 56.625,67.50q 10.75,8.00 23.875,6.25t 20.875-12.50q 8.00-10.50 6.125-23.625t-12.375-21.125q-24.50-18.50 -37.875-45.25t-13.375-57.00q0.00-26.00 10.125-49.625t 27.375-40.875t 40.875-27.375 t 49.625-10.125t 49.625,10.125t 40.875,27.375t 27.375,40.875t 10.125,49.625q0.00,30.25 -13.375,57.00t-37.875,45.25q-10.50,8.00 -12.375,21.125t 6.125,23.625q 7.75,10.75 21.00,12.50t 23.75-6.25q 36.50-27.25 56.625-67.50t 20.125-85.75zM 224.00,352.00l0.00-160.00 q0.00-13.00 -9.50-22.50t-22.50-9.50t-22.50,9.50t-9.50,22.50l0.00,160.00 q0.00,13.00 9.50,22.50t 22.50,9.50t 22.50-9.50t 9.50-22.50z" horiz-adv-x="384" /> +<glyph unicode="" d="M 128.00,200.00l0.00-144.00 q0.00-3.50 -2.25-5.75t-5.75-2.25l-16.00,0.00 q-3.50,0.00 -5.75,2.25t-2.25,5.75l0.00,144.00 q0.00,3.50 2.25,5.75t 5.75,2.25l 16.00,0.00 q 3.50,0.00 5.75-2.25t 2.25-5.75zM 192.00,200.00l0.00-144.00 q0.00-3.50 -2.25-5.75t-5.75-2.25l-16.00,0.00 q-3.50,0.00 -5.75,2.25t-2.25,5.75l0.00,144.00 q0.00,3.50 2.25,5.75t 5.75,2.25l 16.00,0.00 q 3.50,0.00 5.75-2.25t 2.25-5.75zM 256.00,200.00l0.00-144.00 q0.00-3.50 -2.25-5.75t-5.75-2.25l-16.00,0.00 q-3.50,0.00 -5.75,2.25t-2.25,5.75l0.00,144.00 q0.00,3.50 2.25,5.75t 5.75,2.25l 16.00,0.00 q 3.50,0.00 5.75-2.25t 2.25-5.75zM 288.00,19.00l0.00,237.00 l-224.00,0.00 l0.00-237.00 q0.00-5.50 1.75-10.125t 3.625-6.75t 2.625-2.125l 208.00,0.00 q 0.75,0.00 2.625,2.125t 3.625,6.75t 1.75,10.125zM 120.00,288.00l 112.00,0.00 l-12.00,29.25q-1.75,2.25 -4.25,2.75l-79.25,0.00 q-2.50-0.50 -4.25-2.75zM 352.00,280.00l0.00-16.00 q0.00-3.50 -2.25-5.75t-5.75-2.25l-24.00,0.00 l0.00-237.00 q0.00-20.75 -11.75-35.875t-28.25-15.125l-208.00,0.00 q-16.50,0.00 -28.25,14.625t-11.75,35.375l0.00,238.00 l-24.00,0.00 q-3.50,0.00 -5.75,2.25t-2.25,5.75l0.00,16.00 q0.00,3.50 2.25,5.75t 5.75,2.25l 77.25,0.00 l 17.50,41.75q 3.75,9.25 13.50,15.75t 19.75,6.50l 80.00,0.00 q 10.00,0.00 19.75-6.50t 13.50-15.75l 17.50-41.75l 77.25,0.00 q 3.50,0.00 5.75-2.25t 2.25-5.75z" horiz-adv-x="352" /> +<glyph unicode="" d="M 272.00,152.00l0.00-16.00 q0.00-3.25 -2.375-5.625t-5.625-2.375l-96.00,0.00 q-3.25,0.00 -5.625,2.375t-2.375,5.625l0.00,112.00 q0.00,3.25 2.375,5.625t 5.625,2.375l 16.00,0.00 q 3.25,0.00 5.625-2.375t 2.375-5.625l0.00-88.00 l 72.00,0.00 q 3.25,0.00 5.625-2.375t 2.375-5.625zM 320.00,160.00q0.00,26.00 -10.125,49.625t-27.375,40.875t-40.875,27.375t-49.625,10.125t-49.625-10.125 t-40.875-27.375t-27.375-40.875t-10.125-49.625t 10.125-49.625t 27.375-40.875t 40.875-27.375t 49.625-10.125t 49.625,10.125t 40.875,27.375t 27.375,40.875t 10.125,49.625zM 384.00,160.00q0.00-52.25 -25.75-96.375t-69.875-69.875t-96.375-25.75t-96.375,25.75t-69.875,69.875t-25.75,96.375t 25.75,96.375t 69.875,69.875 t 96.375,25.75t 96.375-25.75t 69.875-69.875t 25.75-96.375z" horiz-adv-x="384" /> +<glyph unicode="" d="M 192.00,144.00l0.00-112.00 q0.00-6.50 -4.75-11.25t-11.25-4.75t-11.25,4.75l-36.00,36.00l-83.00-83.00q-2.50-2.50 -5.75-2.50t-5.75,2.50l-28.50,28.50q-2.50,2.50 -2.50,5.75t 2.50,5.75l 83.00,83.00l-36.00,36.00q-4.75,4.75 -4.75,11.25t 4.75,11.25t 11.25,4.75l 112.00,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25zM 380.75,312.00q0.00-3.25 -2.50-5.75l-83.00-83.00l 36.00-36.00q 4.75-4.75 4.75-11.25t-4.75-11.25 t-11.25-4.75l-112.00,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,112.00 q0.00,6.50 4.75,11.25t 11.25,4.75t 11.25-4.75l 36.00-36.00l 83.00,83.00q 2.50,2.50 5.75,2.50t 5.75-2.50l 28.50-28.50q 2.50-2.50 2.50-5.75z" horiz-adv-x="384" /> +<glyph unicode="" d="M 256.00,40.25l0.00,47.50 q0.00,3.50 -2.375,5.875t-5.625,2.375l-48.00,0.00 q-3.25,0.00 -5.625-2.375t-2.375-5.875l0.00-47.50 q0.00-3.50 2.375-5.875t 5.625-2.375l 48.00,0.00 q 3.25,0.00 5.625,2.375t 2.375,5.875zM 255.50,133.75l 4.50,114.75q0.00,3.00 -2.50,4.75q-3.25,2.75 -6.00,2.75l-55.00,0.00 q-2.75,0.00 -6.00-2.75q-2.50-1.75 -2.50-5.25l 4.25-114.25q0.00-2.50 2.50-4.125t 6.00-1.625l 46.25,0.00 q 3.50,0.00 5.875,1.625t 2.625,4.125zM 252.00,367.25l 192.00-352.00q 8.75-15.75 -0.50-31.50q-4.25-7.25 -11.625-11.50t-15.875-4.25l-384.00,0.00 q-8.50,0.00 -15.875,4.25t-11.625,11.50q-9.25,15.75 -0.50,31.50l 192.00,352.00q 4.25,7.75 11.75,12.25t 16.25,4.50t 16.25-4.50t 11.75-12.25z" /> +<glyph unicode="" d="M 256.00,40.00l0.00,16.00 q0.00,3.50 -2.25,5.75t-5.75,2.25l-24.00,0.00 l0.00,120.00 q0.00,3.50 -2.25,5.75t-5.75,2.25l-80.00,0.00 q-3.50,0.00 -5.75-2.25t-2.25-5.75l0.00-16.00 q0.00-3.50 2.25-5.75t 5.75-2.25l 24.00,0.00 l0.00-96.00 l-24.00,0.00 q-3.50,0.00 -5.75-2.25t-2.25-5.75l0.00-16.00 q0.00-3.50 2.25-5.75t 5.75-2.25l 112.00,0.00 q 3.50,0.00 5.75,2.25t 2.25,5.75zM 224.00,232.00l0.00,48.00 q0.00,3.50 -2.25,5.75t-5.75,2.25l-48.00,0.00 q-3.50,0.00 -5.75-2.25t-2.25-5.75l0.00-48.00 q0.00-3.50 2.25-5.75 t 5.75-2.25l 48.00,0.00 q 3.50,0.00 5.75,2.25t 2.25,5.75zM 384.00,160.00q0.00-52.25 -25.75-96.375t-69.875-69.875t-96.375-25.75t-96.375,25.75t-69.875,69.875t-25.75,96.375t 25.75,96.375t 69.875,69.875t 96.375,25.75t 96.375-25.75t 69.875-69.875t 25.75-96.375z" horiz-adv-x="384" /> +<glyph unicode="" d="M 192.00,352.00q 52.25,0.00 96.375-25.75t 69.875-69.875t 25.75-96.375t-25.75-96.375t-69.875-69.875t-96.375-25.75t-96.375,25.75t-69.875,69.875t-25.75,96.375t 25.75,96.375t 69.875,69.875t 96.375,25.75zM 224.00,40.25l0.00,47.50 q0.00,3.50 -2.25,5.875t-5.50,2.375l-48.00,0.00 q-3.25,0.00 -5.75-2.50t-2.50-5.75l0.00-47.50 q0.00-3.25 2.50-5.75t 5.75-2.50l 48.00,0.00 q 3.25,0.00 5.50,2.375t 2.25,5.875zM 223.50,126.25l 4.50,155.25q0.00,3.00 -2.50,4.50q-2.50,2.00 -6.00,2.00l-55.00,0.00 q-3.50,0.00 -6.00-2.00q-2.50-1.50 -2.50-4.50l 4.25-155.25q0.00-2.50 2.50-4.375t 6.00-1.875l 46.25,0.00 q 3.50,0.00 5.875,1.875t 2.625,4.375z" horiz-adv-x="384" /> +<glyph unicode="" d="M 299.25,128.00l-27.25,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,32.00 q0.00,6.50 4.75,11.25t 11.25,4.75l 27.25,0.00 q-8.00,27.00 -28.125,47.125t-47.125,28.125l0.00-27.25 q0.00-6.50 -4.75-11.25t-11.25-4.75l-32.00,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,27.25 q-27.00-8.00 -47.125-28.125t-28.125-47.125l 27.25,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25l0.00-32.00 q0.00-6.50 -4.75-11.25t-11.25-4.75l-27.25,0.00 q 8.00-27.00 28.125-47.125t 47.125-28.125l0.00,27.25 q0.00,6.50 4.75,11.25t 11.25,4.75l 32.00,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25l0.00-27.25 q 27.00,8.00 47.125,28.125t 28.125,47.125zM 384.00,176.00l0.00-32.00 q0.00-6.50 -4.75-11.25t-11.25-4.75l-35.75,0.00 q-9.25-40.25 -38.625-69.625t-69.625-38.625l0.00-35.75 q0.00-6.50 -4.75-11.25t-11.25-4.75l-32.00,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,35.75 q-40.25,9.25 -69.625,38.625t-38.625,69.625l-35.75,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,32.00 q0.00,6.50 4.75,11.25t 11.25,4.75l 35.75,0.00 q 9.25,40.25 38.625,69.625t 69.625,38.625l0.00,35.75 q0.00,6.50 4.75,11.25t 11.25,4.75l 32.00,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25l0.00-35.75 q 40.25-9.25 69.625-38.625t 38.625-69.625l 35.75,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25z" horiz-adv-x="384" /> +<glyph unicode="" d="M 384.00,48.00l0.00-32.00 q0.00-6.50 -4.75-11.25t-11.25-4.75l-352.00,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,32.00 q0.00,6.50 4.75,11.25t 11.25,4.75l 352.00,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25zM 384.00,176.00l0.00-32.00 q0.00-6.50 -4.75-11.25t-11.25-4.75l-352.00,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,32.00 q0.00,6.50 4.75,11.25t 11.25,4.75l 352.00,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25zM 384.00,304.00l0.00-32.00 q0.00-6.50 -4.75-11.25 t-11.25-4.75l-352.00,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,32.00 q0.00,6.50 4.75,11.25t 11.25,4.75l 352.00,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25z" horiz-adv-x="384" /> +<glyph unicode="" d="M 297.50,238.75l 73.25,73.25l-26.75,26.75l-73.25-73.25zM 409.25,312.00q0.00-6.75 -4.50-11.25l-321.50-321.50q-4.50-4.50 -11.25-4.50t-11.25,4.50l-49.50,49.50q-4.50,4.50 -4.50,11.25t 4.50,11.25l 321.50,321.50q 4.50,4.50 11.25,4.50t 11.25-4.50l 49.50-49.50q 4.50-4.50 4.50-11.25zM 71.50,359.50l 24.50-7.50l-24.50-7.50l-7.50-24.50l-7.50,24.50l-24.50,7.50l 24.50,7.50l 7.50,24.50zM 159.00,319.00 l 49.00-15.00l-49.00-15.00l-15.00-49.00l-15.00,49.00l-49.00,15.00l 49.00,15.00l 15.00,49.00zM 391.50,199.50l 24.50-7.50l-24.50-7.50l-7.50-24.50l-7.50,24.50l-24.50,7.50l 24.50,7.50l 7.50,24.50zM 231.50,359.50l 24.50-7.50l-24.50-7.50l-7.50-24.50l-7.50,24.50l-24.50,7.50l 24.50,7.50l 7.50,24.50z" horiz-adv-x="416" /> +<glyph unicode="" d="M 448.00,72.00l0.00-80.00 q0.00-10.00 -7.00-17.00t-17.00-7.00l-80.00,0.00 q-10.00,0.00 -17.00,7.00t-7.00,17.00l0.00,80.00 q0.00,10.00 7.00,17.00t 17.00,7.00l 24.00,0.00 l0.00,48.00 l-128.00,0.00 l0.00-48.00 l 24.00,0.00 q 10.00,0.00 17.00-7.00t 7.00-17.00l0.00-80.00 q0.00-10.00 -7.00-17.00t-17.00-7.00l-80.00,0.00 q-10.00,0.00 -17.00,7.00t-7.00,17.00l0.00,80.00 q0.00,10.00 7.00,17.00t 17.00,7.00l 24.00,0.00 l0.00,48.00 l-128.00,0.00 l0.00-48.00 l 24.00,0.00 q 10.00,0.00 17.00-7.00t 7.00-17.00l0.00-80.00 q0.00-10.00 -7.00-17.00t-17.00-7.00l-80.00,0.00 q-10.00,0.00 -17.00,7.00t-7.00,17.00l0.00,80.00 q0.00,10.00 7.00,17.00t 17.00,7.00l 24.00,0.00 l0.00,48.00 q0.00,13.00 9.50,22.50t 22.50,9.50l 128.00,0.00 l0.00,48.00 l-24.00,0.00 q-10.00,0.00 -17.00,7.00t-7.00,17.00l0.00,80.00 q0.00,10.00 7.00,17.00t 17.00,7.00l 80.00,0.00 q 10.00,0.00 17.00-7.00t 7.00-17.00l0.00-80.00 q0.00-10.00 -7.00-17.00t-17.00-7.00l-24.00,0.00 l0.00-48.00 l 128.00,0.00 q 13.00,0.00 22.50-9.50t 9.50-22.50l0.00-48.00 l 24.00,0.00 q 10.00,0.00 17.00-7.00t 7.00-17.00 z" /> +<glyph unicode="" d="M 256.00,160.00q0.00,26.50 -18.75,45.25t-45.25,18.75t-45.25-18.75t-18.75-45.25t 18.75-45.25t 45.25-18.75t 45.25,18.75t 18.75,45.25zM 384.00,187.25l0.00-55.50 q0.00-3.00 -2.00-5.75t-5.00-3.25l-46.25-7.00q-4.75-13.50 -9.75-22.75q 8.75-12.50 26.75-34.50q 2.50-3.00 2.50-6.25t-2.25-5.75q-6.75-9.25 -24.75-27.00t-23.50-17.75q-3.00,0.00 -6.50,2.25l-34.50,27.00q-11.00-5.75 -22.75-9.50 q-4.00-34.00 -7.25-46.50q-1.75-7.00 -9.00-7.00l-55.50,0.00 q-3.50,0.00 -6.125,2.125t-2.875,5.375l-7.00,46.00q-12.25,4.00 -22.50,9.25l-35.25-26.75q-2.50-2.25 -6.25-2.25q-3.50,0.00 -6.25,2.75q-31.50,28.50 -41.25,42.00q-1.75,2.50 -1.75,5.75q0.00,3.00 2.00,5.75q 3.75,5.25 12.75,16.625t 13.50,17.625q-6.75,12.50 -10.25,24.75l-45.75,6.75q-3.25,0.50 -5.25,3.125t-2.00,5.875l0.00,55.50 q0.00,3.00 2.00,5.75t 4.75,3.25 l 46.50,7.00q 3.50,11.50 9.75,23.00q-10.00,14.25 -26.75,34.50q-2.50,3.00 -2.50,6.00q0.00,2.50 2.25,5.75q 6.50,9.00 24.625,26.875t 23.625,17.875q 3.25,0.00 6.50-2.50l 34.50-26.75q 11.00,5.75 22.75,9.50q 4.00,34.00 7.25,46.50q 1.75,7.00 9.00,7.00l 55.50,0.00 q 3.50,0.00 6.125-2.125t 2.875-5.375l 7.00-46.00q 12.25-4.00 22.50-9.25l 35.50,26.75q 2.25,2.25 6.00,2.25q 3.25,0.00 6.25-2.50q 32.25-29.75 41.25-42.50q 1.75-2.00 1.75-5.50 q0.00-3.00 -2.00-5.75q-3.75-5.25 -12.75-16.625t-13.50-17.625q 6.50-12.50 10.25-24.50l 45.75-7.00q 3.25-0.50 5.25-3.125t 2.00-5.875z" horiz-adv-x="384" /> +<glyph unicode="" d="M 448.00,88.00l0.00-48.00 q0.00-3.25 -2.375-5.625t-5.625-2.375l-344.00,0.00 l0.00-48.00 q0.00-3.25 -2.375-5.625t-5.625-2.375q-3.00,0.00 -6.00,2.50l-79.75,80.00q-2.25,2.25 -2.25,5.50q0.00,3.50 2.25,5.75l 80.00,80.00q 2.25,2.25 5.75,2.25q 3.25,0.00 5.625-2.375t 2.375-5.625l0.00-48.00 l 344.00,0.00 q 3.25,0.00 5.625-2.375t 2.375-5.625zM 448.00,224.00q0.00-3.50 -2.25-5.75l-80.00-80.00q-2.25-2.25 -5.75-2.25 q-3.25,0.00 -5.625,2.375t-2.375,5.625l0.00,48.00 l-344.00,0.00 q-3.25,0.00 -5.625,2.375t-2.375,5.625l0.00,48.00 q0.00,3.25 2.375,5.625t 5.625,2.375l 344.00,0.00 l0.00,48.00 q0.00,3.50 2.25,5.75t 5.75,2.25q 3.00,0.00 6.00-2.50l 79.75-79.75q 2.25-2.25 2.25-5.75z" /> +<glyph unicode="" d="M 96.00,48.00q0.00-6.50 -4.75-11.25t-11.25-4.75t-11.25,4.75t-4.75,11.25t 4.75,11.25t 11.25,4.75t 11.25-4.75t 4.75-11.25zM 352.00,32.75q0.00-30.25 -18.25-47.50t-48.50-17.25l-218.50,0.00 q-30.25,0.00 -48.50,17.25t-18.25,47.50q0.00,17.00 1.375,32.75t 6.00,34.50t 11.875,33.125t 20.25,25.75t 30.00,15.125q-5.50-13.00 -5.50-30.00l0.00-50.75 q-14.50-5.00 -23.25-17.50t-8.75-27.75q0.00-20.00 14.00-34.00t 34.00-14.00 t 34.00,14.00t 14.00,34.00q0.00,15.25 -8.875,27.75t-23.125,17.50l0.00,50.75 q0.00,15.50 6.25,23.25q 33.00-26.00 73.75-26.00t 73.75,26.00q 6.25-7.75 6.25-23.25l0.00-16.00 q-26.50,0.00 -45.25-18.75t-18.75-45.25l0.00-22.25 q-8.00-7.25 -8.00-17.75q0.00-10.00 7.00-17.00t 17.00-7.00t 17.00,7.00t 7.00,17.00q0.00,10.50 -8.00,17.75l0.00,22.25 q0.00,13.00 9.50,22.50t 22.50,9.50t 22.50-9.50t 9.50-22.50l0.00-22.25 q-8.00-7.25 -8.00-17.75q0.00-10.00 7.00-17.00 t 17.00-7.00t 17.00,7.00t 7.00,17.00q0.00,10.50 -8.00,17.75l0.00,22.25 q0.00,17.00 -8.625,31.875t-23.375,23.375q0.00,2.50 0.125,10.625t0.00,12.00t-0.625,10.375t-1.75,11.75t-3.25,10.00q 17.00-3.75 30.00-15.125t 20.25-25.75t 11.875-33.125t 6.00-34.50t 1.375-32.75zM 272.00,256.00q0.00-39.75 -28.125-67.875t-67.875-28.125t-67.875,28.125t-28.125,67.875t 28.125,67.875t 67.875,28.125 t 67.875-28.125t 28.125-67.875z" horiz-adv-x="352" /> +<glyph unicode="" d="M 176.00,128.00q0.00,13.25 -9.375,22.625t-22.625,9.375t-22.625-9.375t-9.375-22.625q0.00-9.25 4.75-16.75t 12.75-11.75l-17.25-57.25q-1.25-3.75 1.25-7.00t 6.50-3.25l 48.00,0.00 q 4.00,0.00 6.50,3.25t 1.25,7.00l-17.25,57.25q 8.00,4.25 12.75,11.75t 4.75,16.75zM 80.00,192.00l 128.00,0.00 l0.00,48.00 q0.00,26.50 -18.75,45.25t-45.25,18.75t-45.25-18.75t-18.75-45.25l0.00-48.00 zM 288.00,168.00l0.00-144.00 q0.00-10.00 -7.00-17.00 t-17.00-7.00l-240.00,0.00 q-10.00,0.00 -17.00,7.00t-7.00,17.00l0.00,144.00 q0.00,10.00 7.00,17.00t 17.00,7.00l 8.00,0.00 l0.00,48.00 q0.00,46.00 33.00,79.00t 79.00,33.00t 79.00-33.00t 33.00-79.00l0.00-48.00 l 8.00,0.00 q 10.00,0.00 17.00-7.00t 7.00-17.00z" horiz-adv-x="288" /> +<glyph unicode="" d="M 320.00,160.00q0.00,26.00 -10.125,49.625t-27.375,40.875t-40.875,27.375t-49.625,10.125t-49.625-10.125t-40.875-27.375t-27.375-40.875t-10.125-49.625t 10.125-49.625t 27.375-40.875t 40.875-27.375t 49.625-10.125t 49.625,10.125t 40.875,27.375t 27.375,40.875t 10.125,49.625zM 384.00,160.00q0.00-52.25 -25.75-96.375 t-69.875-69.875t-96.375-25.75t-96.375,25.75t-69.875,69.875t-25.75,96.375t 25.75,96.375t 69.875,69.875t 96.375,25.75t 96.375-25.75t 69.875-69.875t 25.75-96.375z" horiz-adv-x="384" /> +<glyph unicode="" d="M 384.00,160.00q0.00-52.25 -25.75-96.375t-69.875-69.875t-96.375-25.75t-96.375,25.75t-69.875,69.875t-25.75,96.375t 25.75,96.375t 69.875,69.875t 96.375,25.75t 96.375-25.75t 69.875-69.875t 25.75-96.375z" horiz-adv-x="384" /> +<glyph unicode="" d="M 124.00,48.00q0.00-15.00 -10.625-25.50t-25.375-10.50q-15.00,0.00 -25.50,10.50t-10.50,25.50t 10.50,25.50t 25.50,10.50q 14.75,0.00 25.375-10.50t 10.625-25.50zM 232.00,0.00q0.00-13.25 -9.375-22.625t-22.625-9.375t-22.625,9.375t-9.375,22.625t 9.375,22.625t 22.625,9.375t 22.625-9.375t 9.375-22.625zM 80.00,160.00q0.00-16.50 -11.75-28.25t-28.25-11.75t-28.25,11.75t-11.75,28.25 t 11.75,28.25t 28.25,11.75t 28.25-11.75t 11.75-28.25zM 340.00,48.00q0.00-11.50 -8.25-19.75t-19.75-8.25t-19.75,8.25t-8.25,19.75t 8.25,19.75t 19.75,8.25t 19.75-8.25t 8.25-19.75zM 132.00,272.00q0.00-18.25 -12.875-31.125t-31.125-12.875t-31.125,12.875t-12.875,31.125t 12.875,31.125t 31.125,12.875t 31.125-12.875t 12.875-31.125zM 248.00,320.00q0.00-20.00 -14.00-34.00t-34.00-14.00 t-34.00,14.00t-14.00,34.00t 14.00,34.00t 34.00,14.00t 34.00-14.00t 14.00-34.00zM 384.00,160.00q0.00-10.00 -7.00-17.00t-17.00-7.00t-17.00,7.00t-7.00,17.00t 7.00,17.00t 17.00,7.00t 17.00-7.00t 7.00-17.00zM 332.00,272.00q0.00-8.25 -5.875-14.125t-14.125-5.875t-14.125,5.875t-5.875,14.125t 5.875,14.125t 14.125,5.875t 14.125-5.875t 5.875-14.125z" horiz-adv-x="392" /> +<glyph unicode="" d="M 320.00,160.00q0.00,34.75 -17.75,65.00l-175.25-175.25q 30.25-17.75 65.00-17.75q 26.00,0.00 49.625,10.125t 40.875,27.375t 27.375,40.875t 10.125,49.625zM 81.75,95.00l 175.25,175.25q-30.25,17.75 -65.00,17.75q-26.00,0.00 -49.625-10.125t-40.875-27.375t-27.375-40.875t-10.125-49.625q0.00-34.75 17.75-65.00zM 384.00,160.00q0.00-52.25 -25.75-96.375 t-69.875-69.875t-96.375-25.75t-96.375,25.75t-69.875,69.875t-25.75,96.375t 25.75,96.375t 69.875,69.875t 96.375,25.75t 96.375-25.75t 69.875-69.875t 25.75-96.375z" horiz-adv-x="384" /> +<glyph unicode="" d="M 188.75,120.00q0.00-3.25 -2.50-5.75l-83.00-83.00l 36.00-36.00q 4.75-4.75 4.75-11.25t-4.75-11.25t-11.25-4.75l-112.00,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25l0.00,112.00 q0.00,6.50 4.75,11.25t 11.25,4.75t 11.25-4.75l 36.00-36.00l 83.00,83.00q 2.50,2.50 5.75,2.50t 5.75-2.50l 28.50-28.50q 2.50-2.50 2.50-5.75zM 384.00,336.00l0.00-112.00 q0.00-6.50 -4.75-11.25t-11.25-4.75t-11.25,4.75l-36.00,36.00l-83.00-83.00 q-2.50-2.50 -5.75-2.50t-5.75,2.50l-28.50,28.50q-2.50,2.50 -2.50,5.75t 2.50,5.75l 83.00,83.00l-36.00,36.00q-4.75,4.75 -4.75,11.25t 4.75,11.25t 11.25,4.75l 112.00,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25z" horiz-adv-x="384" /> +<glyph unicode="" d="M 377.75,120.00q0.00-1.25 -0.25-1.75q-16.00-67.00 -67.00-108.625t-119.50-41.625q-36.50,0.00 -70.625,13.75t-60.875,39.25l-32.25-32.25q-4.75-4.75 -11.25-4.75t-11.25,4.75t-4.75,11.25l0.00,112.00 q0.00,6.50 4.75,11.25t 11.25,4.75l 112.00,0.00 q 6.50,0.00 11.25-4.75t 4.75-11.25t-4.75-11.25l-34.25-34.25q 17.75-16.50 40.25-25.50t 46.75-9.00q 33.50,0.00 62.50,16.25t 46.50,44.75q 2.75,4.25 13.25,29.25 q 2.00,5.75 7.50,5.75l 48.00,0.00 q 3.25,0.00 5.625-2.375t 2.375-5.625zM 384.00,320.00l0.00-112.00 q0.00-6.50 -4.75-11.25t-11.25-4.75l-112.00,0.00 q-6.50,0.00 -11.25,4.75t-4.75,11.25t 4.75,11.25l 34.50,34.50q-37.00,34.25 -87.25,34.25q-33.50,0.00 -62.50-16.25t-46.50-44.75q-2.75-4.25 -13.25-29.25q-2.00-5.75 -7.50-5.75l-49.75,0.00 q-3.25,0.00 -5.625,2.375t-2.375,5.625l0.00,1.75 q 16.25,67.00 67.50,108.625t 120.00,41.625 q 36.50,0.00 71.00-13.875t 61.25-39.125l 32.50,32.25q 4.75,4.75 11.25,4.75t 11.25-4.75t 4.75-11.25z" horiz-adv-x="384" /> +<glyph unicode="" d="M 291.00,352.00q 5.75,0.00 11.00-2.25q 8.25-3.25 13.125-10.25t 4.875-15.50l0.00-322.25 q0.00-8.50 -4.875-15.50t-13.125-10.25q-4.75-2.00 -11.00-2.00q-12.00,0.00 -20.75,8.00l-110.25,106.00l-110.25-106.00q-9.00-8.25 -20.75-8.25q-5.75,0.00 -11.00,2.25q-8.25,3.25 -13.125,10.25t-4.875,15.50l0.00,322.25 q0.00,8.50 4.875,15.50t 13.125,10.25q 5.25,2.25 11.00,2.25l 262.00,0.00 z" horiz-adv-x="320" /> +<glyph unicode=" " horiz-adv-x="224" /> +<glyph class="hidden" unicode="" d="M0,384L 448 -64L0 -64 z" horiz-adv-x="0" /> +</font></defs></svg>
\ No newline at end of file diff --git a/WebContent/VAADIN/themes/base/debug/fonts/font.ttf b/WebContent/VAADIN/themes/base/debug/fonts/font.ttf Binary files differnew file mode 100644 index 0000000000..e26c910020 --- /dev/null +++ b/WebContent/VAADIN/themes/base/debug/fonts/font.ttf diff --git a/WebContent/VAADIN/themes/base/debug/fonts/font.woff b/WebContent/VAADIN/themes/base/debug/fonts/font.woff Binary files differnew file mode 100644 index 0000000000..e23e3807d0 --- /dev/null +++ b/WebContent/VAADIN/themes/base/debug/fonts/font.woff diff --git a/WebContent/VAADIN/themes/base/window/window.scss b/WebContent/VAADIN/themes/base/window/window.scss index b9e7b54139..05f3b115ad 100644 --- a/WebContent/VAADIN/themes/base/window/window.scss +++ b/WebContent/VAADIN/themes/base/window/window.scss @@ -38,6 +38,10 @@ } .#{$primaryStyleName}-header { font-weight: bold; + -khtml-user-select: none; + -moz-user-select: none; + -ie-user-select: none; + user-select: none; } /* A more specific selector to make sure padding isn't so easily overridden */ div.#{$primaryStyleName}-header { @@ -77,20 +81,37 @@ div.#{$primaryStyleName}-header { .#{$primaryStyleName} div.#{$primaryStyleName}-footer-noresize { height: 0; } -.#{$primaryStyleName}-resizebox-disabled { +.#{$primaryStyleName}-resizebox-disabled, +.#{$primaryStyleName}-restorebox-disabled, +.#{$primaryStyleName}-maximizebox-disabled { cursor: default; display: none; } -.#{$primaryStyleName}-closebox { +.#{$primaryStyleName}-closebox, +.#{$primaryStyleName}-restorebox, +.#{$primaryStyleName}-maximizebox { position: absolute; top: 0; right: 0; width: 1em; height: 1em; - background: red; cursor: pointer; overflow: hidden; } +.#{$primaryStyleName}-maximizebox, +.#{$primaryStyleName}-restorebox { + right: 1.1em; +} + +.#{$primaryStyleName}-closebox { + background: red; +} +.#{$primaryStyleName}-maximizebox { + background: blue; +} +.#{$primaryStyleName}-restorebox { + background: yellow; +} .#{$primaryStyleName}-modalitycurtain { top: 0; left: 0; diff --git a/WebContent/VAADIN/themes/chameleon/components/window/window.scss b/WebContent/VAADIN/themes/chameleon/components/window/window.scss index e9524745f8..92ada43c90 100644 --- a/WebContent/VAADIN/themes/chameleon/components/window/window.scss +++ b/WebContent/VAADIN/themes/chameleon/components/window/window.scss @@ -1,25 +1,39 @@ @mixin chameleon-window($primaryStyleName : v-window) { -.#{$primaryStyleName}-closebox { +.#{$primaryStyleName}-closebox, +.#{$primaryStyleName}-restorebox, +.#{$primaryStyleName}-maximizebox { width: 14px; height: 15px; overflow: hidden; - text-indent: -50px; - background: transparent url(../../img/close-btn.png) no-repeat; vertical-align: middle; margin: 0; top: .3em; - right: .4em; z-index: 2; + &:hover { + background-position: 0 -25px; } - -.#{$primaryStyleName}-closebox:hover { - background-position: 0 -25px; + &:active { + background-position: 0 -50px; } +} +.#{$primaryStyleName}-closebox { + text-indent: -50px; + background: transparent url(../../img/close-btn.png) no-repeat; + right: .4em; +} +.#{$primaryStyleName}-restorebox, +.#{$primaryStyleName}-maximizebox { + right: 1.8em; +} + +.#{$primaryStyleName}-restorebox { + background: transparent url(../../img/restore.png) no-repeat; +} +.#{$primaryStyleName}-maximizebox { + background: transparent url(../../img/maximize.png) no-repeat; +} -.#{$primaryStyleName}-closebox:active { - background-position: 0 -50px; - } .#{$primaryStyleName} { background-image: none; diff --git a/WebContent/VAADIN/themes/chameleon/img/maximize.png b/WebContent/VAADIN/themes/chameleon/img/maximize.png Binary files differnew file mode 100644 index 0000000000..5c6488d1a5 --- /dev/null +++ b/WebContent/VAADIN/themes/chameleon/img/maximize.png diff --git a/WebContent/VAADIN/themes/chameleon/img/restore.png b/WebContent/VAADIN/themes/chameleon/img/restore.png Binary files differnew file mode 100644 index 0000000000..55d004fe53 --- /dev/null +++ b/WebContent/VAADIN/themes/chameleon/img/restore.png diff --git a/WebContent/VAADIN/themes/liferay/window/maximize_sprites.png b/WebContent/VAADIN/themes/liferay/window/maximize_sprites.png Binary files differnew file mode 100644 index 0000000000..06510063cd --- /dev/null +++ b/WebContent/VAADIN/themes/liferay/window/maximize_sprites.png diff --git a/WebContent/VAADIN/themes/liferay/window/restore_sprites.png b/WebContent/VAADIN/themes/liferay/window/restore_sprites.png Binary files differnew file mode 100644 index 0000000000..41ccfaf79f --- /dev/null +++ b/WebContent/VAADIN/themes/liferay/window/restore_sprites.png diff --git a/WebContent/VAADIN/themes/liferay/window/window.scss b/WebContent/VAADIN/themes/liferay/window/window.scss index 54bfb37938..583a81d9e4 100644 --- a/WebContent/VAADIN/themes/liferay/window/window.scss +++ b/WebContent/VAADIN/themes/liferay/window/window.scss @@ -42,21 +42,35 @@ height: 5px; } +.v-window-restorebox, +.v-window-maximizebox, .v-window-closebox { - background: url(closebutton_sprites.png) no-repeat scroll 0 0 transparent; position: absolute; width: 24px; height: 24px; - right: 9px; top: 8px; + &:hover { + background-position: 0 -24px; + } + &:active { + background-position: 0 -48px; + } + } - -.v-window-closebox:hover { - background-position: 0 -24px; +.v-window-closebox { + right: 9px; + background: url(closebutton_sprites.png) no-repeat scroll 0 0 transparent; } -.v-window-closebox:active { - background-position: 0 -48px; +.v-window-restorebox, +.v-window-maximizebox { + right: 36px; +} +.v-window-restorebox { + background: url(restore_sprites.png) no-repeat scroll 0 0 transparent; +} +.v-window-maximizebox { + background: url(maximize_sprites.png) no-repeat scroll 0 0 transparent; } .v-window-resizebox { diff --git a/WebContent/VAADIN/themes/reindeer/notification/notification.scss b/WebContent/VAADIN/themes/reindeer/notification/notification.scss index 4884f19f8b..ec0d15df2f 100644 --- a/WebContent/VAADIN/themes/reindeer/notification/notification.scss +++ b/WebContent/VAADIN/themes/reindeer/notification/notification.scss @@ -10,9 +10,11 @@ font-weight: bold; } -.v-ie9 & .#{$primaryStyleName} H1 { +.v-ie9 &, .v-ie10 & { + .#{$primaryStyleName} H1 { /* Fix for #6793 */ font-weight: bold; + } } .#{$primaryStyleName} p { diff --git a/WebContent/VAADIN/themes/reindeer/panel/panel.scss b/WebContent/VAADIN/themes/reindeer/panel/panel.scss index b095fb4ffb..74ee10837d 100644 --- a/WebContent/VAADIN/themes/reindeer/panel/panel.scss +++ b/WebContent/VAADIN/themes/reindeer/panel/panel.scss @@ -12,14 +12,24 @@ border-bottom: 1px solid #e5e5e5; line-height: 16px; /* accommodate minimum icon size */ } -.v-webkit & .#{$primaryStyleName}-caption, -.v-webkit & .#{$primaryStyleName}-nocaption, -.v-gecko & .#{$primaryStyleName}-caption, -.v-gecko & .#{$primaryStyleName}-nocaption, -.v-ie9 & .#{$primaryStyleName}-caption, -.v-ie9 & .#{$primaryStyleName}-nocaption { - border-bottom-color: rgba(0,0,0,.08); + +.v-webkit &, +.v-gecko &, +.v-ie9 &, +.v-ie10 & { + .#{$primaryStyleName}-caption, + .#{$primaryStyleName}-nocaption { + border-bottom-color: rgba(0,0,0,.08); + } + .#{$primaryStyleName}-content { + border-top-color: rgba(0,0,0,.07); + } + .#{$primaryStyleName}-deco { + border-top-color: rgba(0,0,0,.1); + background: rgba(0,0,0,.08); + } } + .#{$primaryStyleName}-caption { padding-bottom: 2px; } @@ -30,11 +40,7 @@ border-bottom: none; border-top: none; } -.v-webkit & .#{$primaryStyleName}-content, -.v-gecko & .#{$primaryStyleName}-content, -.v-ie9 & .#{$primaryStyleName}-content { - border-top-color: rgba(0,0,0,.07); -} + .blue .#{$primaryStyleName}-deco { border-color: #92a3ac; background: #adc2cd; @@ -46,12 +52,6 @@ background: #e2e2e2; overflow: hidden; } -.v-webkit & .#{$primaryStyleName}-deco, -.v-gecko & .#{$primaryStyleName}-deco, -.v-ie9 & .#{$primaryStyleName}-deco { - border-top-color: rgba(0,0,0,.1); - background: rgba(0,0,0,.08); -} .#{$primaryStyleName}-caption .v-errorindicator { height: 16px; width: 13px; diff --git a/WebContent/VAADIN/themes/reindeer/tabsheet/tabsheet-minimal-style.scss b/WebContent/VAADIN/themes/reindeer/tabsheet/tabsheet-minimal-style.scss index fe17d90e26..77d4922535 100644 --- a/WebContent/VAADIN/themes/reindeer/tabsheet/tabsheet-minimal-style.scss +++ b/WebContent/VAADIN/themes/reindeer/tabsheet/tabsheet-minimal-style.scss @@ -105,15 +105,17 @@ -moz-border-radius: 7px; } -.v-ie9 & .#{$primaryStyleName}-tabs-minimal .#{$primaryStyleName}-tabitem .#{$primaryStyleName}-caption-close { - &, &:hover, &:active { +.v-ie9 &, .v-ie10 & { + .#{$primaryStyleName}-tabs-minimal .#{$primaryStyleName}-tabitem .#{$primaryStyleName}-caption-close { + &, &:hover, &:active { - /* IE9 suffers from rounding subpixel values errors when measuring the tabs which makes the close button wrap. */ + /* IE9/IE10 suffers from rounding subpixel values errors when measuring the tabs which makes the close button wrap. */ margin-left: 2.5px; - /* The close button is a pixel too high in IE9, adjust for that */ + /* The close button is a pixel too high in IE9/IE10, adjust for that */ margin-top: 1px; - } + } + } } .#{$primaryStyleName}-tabs-minimal .#{$primaryStyleName}-caption-close:hover, diff --git a/WebContent/VAADIN/themes/reindeer/tabsheet/tabsheet-normal-style.scss b/WebContent/VAADIN/themes/reindeer/tabsheet/tabsheet-normal-style.scss index 91bb3915be..b62791e4ce 100644 --- a/WebContent/VAADIN/themes/reindeer/tabsheet/tabsheet-normal-style.scss +++ b/WebContent/VAADIN/themes/reindeer/tabsheet/tabsheet-normal-style.scss @@ -156,10 +156,6 @@ } .v-ie & .#{$primaryStyleName}-tabs .v-errorindicator { zoom: 1; - display: inline; -} -.v-ie8 & .#{$primaryStyleName}-tabs .v-errorindicator, -.v-ie9 & .#{$primaryStyleName}-tabs .v-errorindicator { display: inline-block; } diff --git a/WebContent/VAADIN/themes/reindeer/tabsheet/tabsheet-small-style.scss b/WebContent/VAADIN/themes/reindeer/tabsheet/tabsheet-small-style.scss index 56bad4ed72..4141586d5a 100644 --- a/WebContent/VAADIN/themes/reindeer/tabsheet/tabsheet-small-style.scss +++ b/WebContent/VAADIN/themes/reindeer/tabsheet/tabsheet-small-style.scss @@ -55,11 +55,12 @@ .v-ff & .#{$primaryStyleName}-tabs-bar .#{$primaryStyleName}-caption-close { margin-top: -14px; } -.v-ie9 & .#{$primaryStyleName}-tabs-bar .#{$primaryStyleName}-caption-close:only-child, -.v-ie9 & .#{$primaryStyleName}-tabs-bar .v-captiontext:first-child + .#{$primaryStyleName}-caption-close { +.v-ie9 &, .v-ie10 & { + .#{$primaryStyleName}-tabs-bar .#{$primaryStyleName}-caption-close:only-child, + .#{$primaryStyleName}-tabs-bar .v-captiontext:first-child + .#{$primaryStyleName}-caption-close { margin-top: -14px; + } } - .#{$primaryStyleName}-tabs-bar .#{$primaryStyleName}-caption-close:hover { background: #bfbfbf; -webkit-box-shadow: 0 1px 0 #fff; diff --git a/WebContent/VAADIN/themes/reindeer/window/img/black/close-active.png b/WebContent/VAADIN/themes/reindeer/window/img/black/close-active.png Binary files differnew file mode 100644 index 0000000000..07a837b619 --- /dev/null +++ b/WebContent/VAADIN/themes/reindeer/window/img/black/close-active.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/black/close-hover.png b/WebContent/VAADIN/themes/reindeer/window/img/black/close-hover.png Binary files differnew file mode 100644 index 0000000000..bb0a080373 --- /dev/null +++ b/WebContent/VAADIN/themes/reindeer/window/img/black/close-hover.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/black/close.png b/WebContent/VAADIN/themes/reindeer/window/img/black/close.png Binary files differnew file mode 100644 index 0000000000..b26cfb5d57 --- /dev/null +++ b/WebContent/VAADIN/themes/reindeer/window/img/black/close.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/black/maximize-active.png b/WebContent/VAADIN/themes/reindeer/window/img/black/maximize-active.png Binary files differnew file mode 100644 index 0000000000..526563c94f --- /dev/null +++ b/WebContent/VAADIN/themes/reindeer/window/img/black/maximize-active.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/black/maximize-hover.png b/WebContent/VAADIN/themes/reindeer/window/img/black/maximize-hover.png Binary files differnew file mode 100644 index 0000000000..76d0fdf040 --- /dev/null +++ b/WebContent/VAADIN/themes/reindeer/window/img/black/maximize-hover.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/black/maximize.png b/WebContent/VAADIN/themes/reindeer/window/img/black/maximize.png Binary files differnew file mode 100644 index 0000000000..ecf4cbed35 --- /dev/null +++ b/WebContent/VAADIN/themes/reindeer/window/img/black/maximize.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/black/restore-active.png b/WebContent/VAADIN/themes/reindeer/window/img/black/restore-active.png Binary files differnew file mode 100644 index 0000000000..96f9672605 --- /dev/null +++ b/WebContent/VAADIN/themes/reindeer/window/img/black/restore-active.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/black/restore-hover.png b/WebContent/VAADIN/themes/reindeer/window/img/black/restore-hover.png Binary files differnew file mode 100644 index 0000000000..b319f07684 --- /dev/null +++ b/WebContent/VAADIN/themes/reindeer/window/img/black/restore-hover.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/black/restore.png b/WebContent/VAADIN/themes/reindeer/window/img/black/restore.png Binary files differnew file mode 100644 index 0000000000..b440383b91 --- /dev/null +++ b/WebContent/VAADIN/themes/reindeer/window/img/black/restore.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/close-light-hover.png b/WebContent/VAADIN/themes/reindeer/window/img/light/close-hover.png Binary files differindex 17ac9b8457..17ac9b8457 100644 --- a/WebContent/VAADIN/themes/reindeer/window/img/close-light-hover.png +++ b/WebContent/VAADIN/themes/reindeer/window/img/light/close-hover.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/close-light-pressed.png b/WebContent/VAADIN/themes/reindeer/window/img/light/close-pressed.png Binary files differindex 2c84b5bcfb..2c84b5bcfb 100644 --- a/WebContent/VAADIN/themes/reindeer/window/img/close-light-pressed.png +++ b/WebContent/VAADIN/themes/reindeer/window/img/light/close-pressed.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/close-light.png b/WebContent/VAADIN/themes/reindeer/window/img/light/close.png Binary files differindex 4bbc89cee9..4bbc89cee9 100644 --- a/WebContent/VAADIN/themes/reindeer/window/img/close-light.png +++ b/WebContent/VAADIN/themes/reindeer/window/img/light/close.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/content-bg-light.png b/WebContent/VAADIN/themes/reindeer/window/img/light/content-bg.png Binary files differindex b38c902d83..b38c902d83 100644 --- a/WebContent/VAADIN/themes/reindeer/window/img/content-bg-light.png +++ b/WebContent/VAADIN/themes/reindeer/window/img/light/content-bg.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/light/maximize-active.png b/WebContent/VAADIN/themes/reindeer/window/img/light/maximize-active.png Binary files differnew file mode 100644 index 0000000000..5ca4e2e6eb --- /dev/null +++ b/WebContent/VAADIN/themes/reindeer/window/img/light/maximize-active.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/light/maximize-hover.png b/WebContent/VAADIN/themes/reindeer/window/img/light/maximize-hover.png Binary files differnew file mode 100644 index 0000000000..c7930c2f4a --- /dev/null +++ b/WebContent/VAADIN/themes/reindeer/window/img/light/maximize-hover.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/light/maximize.png b/WebContent/VAADIN/themes/reindeer/window/img/light/maximize.png Binary files differnew file mode 100644 index 0000000000..a4965ef19c --- /dev/null +++ b/WebContent/VAADIN/themes/reindeer/window/img/light/maximize.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/resize-light.png b/WebContent/VAADIN/themes/reindeer/window/img/light/resize.png Binary files differindex 0c0c9123b9..0c0c9123b9 100644 --- a/WebContent/VAADIN/themes/reindeer/window/img/resize-light.png +++ b/WebContent/VAADIN/themes/reindeer/window/img/light/resize.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/light/restore-active.png b/WebContent/VAADIN/themes/reindeer/window/img/light/restore-active.png Binary files differnew file mode 100644 index 0000000000..d135c4d66c --- /dev/null +++ b/WebContent/VAADIN/themes/reindeer/window/img/light/restore-active.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/light/restore-hover.png b/WebContent/VAADIN/themes/reindeer/window/img/light/restore-hover.png Binary files differnew file mode 100644 index 0000000000..b03639c001 --- /dev/null +++ b/WebContent/VAADIN/themes/reindeer/window/img/light/restore-hover.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/light/restore.png b/WebContent/VAADIN/themes/reindeer/window/img/light/restore.png Binary files differnew file mode 100644 index 0000000000..1c3fba4020 --- /dev/null +++ b/WebContent/VAADIN/themes/reindeer/window/img/light/restore.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/maximize.png b/WebContent/VAADIN/themes/reindeer/window/img/maximize.png Binary files differnew file mode 100644 index 0000000000..86ffff9760 --- /dev/null +++ b/WebContent/VAADIN/themes/reindeer/window/img/maximize.png diff --git a/WebContent/VAADIN/themes/reindeer/window/img/restore.png b/WebContent/VAADIN/themes/reindeer/window/img/restore.png Binary files differnew file mode 100644 index 0000000000..119ea04259 --- /dev/null +++ b/WebContent/VAADIN/themes/reindeer/window/img/restore.png diff --git a/WebContent/VAADIN/themes/reindeer/window/window.scss b/WebContent/VAADIN/themes/reindeer/window/window.scss index e6a73ee2c0..e8f0011397 100644 --- a/WebContent/VAADIN/themes/reindeer/window/window.scss +++ b/WebContent/VAADIN/themes/reindeer/window/window.scss @@ -14,7 +14,7 @@ border-color: rgba(0,0,0,.2); } .#{$primaryStyleName}-outerheader { - padding: 12px 32px 0 14px; + padding: 12px 52px 0 14px; height: 37px; background: black repeat-x; background-image: url(img/header-bg.png); /** sprite-ref: verticals; sprite-alignment: repeat */ @@ -61,6 +61,24 @@ .#{$primaryStyleName}-closebox:active { background-image: url(img/close-pressed.png); /** sprite-ref: verticals */ } +.#{$primaryStyleName}-maximizebox, +.#{$primaryStyleName}-restorebox { + top: 12px; + right: 28px; + width: 15px; + height: 16px; + background: transparent; +} +.#{$primaryStyleName}-maximizebox { + &, &:hover,&:active { + background-image: url(img/maximize.png); /** sprite-ref: verticals */ + } +} +.#{$primaryStyleName}-restorebox { + &, &:hover,&:active { + background-image: url(img/restore.png); /** sprite-ref: verticals */ + } +} .#{$primaryStyleName}-contents { background: #fff; } @@ -77,45 +95,71 @@ Light style window ----------------------------- **/ -.#{$primaryStyleName}-light .#{$primaryStyleName}-outerheader { - background: transparent; - padding: 15px 32px 0 18px; - height: 23px; -} -.#{$primaryStyleName}-light .#{$primaryStyleName}-header { - font-size: 16px; - color: #292e34; - text-shadow: none; -} -.#{$primaryStyleName}-light .#{$primaryStyleName}-resizebox { - width: 12px; - height: 12px; - background-image: url(img/resize-light.png); /** sprite-ref: verticals */ -} -.#{$primaryStyleName}-light .#{$primaryStyleName}-footer { - background: transparent; - height: 12px; -} -.#{$primaryStyleName}-light .#{$primaryStyleName}-closebox { - right: 1px; - top: 17px; - width: 19px; - height: 15px; - background-image: url(img/close-light.png); /** sprite-ref: verticals */ -} -.#{$primaryStyleName}-light .#{$primaryStyleName}-closebox:hover { - background-image: url(img/close-light-hover.png); /** sprite-ref: verticals */ -} -.#{$primaryStyleName}-light .#{$primaryStyleName}-closebox:active { - background-image: url(img/close-light-pressed.png); /** sprite-ref: verticals */ -} -.#{$primaryStyleName}-light .#{$primaryStyleName}-contents { - background: transparent; -} -/* This must be the last sprite added to the verticals-sprite image */ -.#{$primaryStyleName}-light .#{$primaryStyleName}-wrap { - background: #f7f7f8 repeat-x; - background-image: url(img/content-bg-light.png); /** sprite-ref: verticals; sprite-alignment: repeat */ +.#{$primaryStyleName}-light { + .#{$primaryStyleName}-outerheader { + background: transparent; + padding: 15px 52px 0 18px; + } + .#{$primaryStyleName}-header { + font-size: 16px; + color: #292e34; + text-shadow: none; + } + .#{$primaryStyleName}-resizebox { + width: 12px; + height: 12px; + background-image: url(img/light/resize.png); /** sprite-ref: verticals */ + } + .#{$primaryStyleName}-footer { + background: transparent; + height: 12px; + } + .#{$primaryStyleName}-closebox { + right: 1px; + top: 17px; + width: 19px; + height: 15px; + + background-image: url(img/light/close.png); /** sprite-ref: verticals */ + &:hover { + background-image: url(img/light/close-hover.png); /** sprite-ref: verticals */ + } + &:active { + background-image: url(img/light/close-pressed.png); /** sprite-ref: verticals */ + } + } + + .#{$primaryStyleName}-maximizebox { + top: 17px; + + background-image: url(img/light/maximize.png); /** sprite-ref: verticals */ + &:hover { + background-image: url(img/light/maximize-hover.png); /** sprite-ref: verticals */ + } + &:active { + background-image: url(img/light/maximize-active.png); /** sprite-ref: verticals */ + } + } + .#{$primaryStyleName}-restorebox { + top: 17px; + + background-image: url(img/light/restore.png); /** sprite-ref: verticals */ + &:hover { + background-image: url(img/light/restore-hover.png); /** sprite-ref: verticals */ + } + &:active { + background-image: url(img/light/restore-active.png); /** sprite-ref: verticals */ + } + } + + .#{$primaryStyleName}-contents { + background: transparent; + } + /* This must be the last sprite added to the verticals-sprite image */ + .#{$primaryStyleName}-wrap { + background: #f7f7f8 repeat-x; + background-image: url(img/light/content-bg.png); /** sprite-ref: verticals; sprite-alignment: repeat */ + } } @@ -123,63 +167,97 @@ Black style window ----------------------------- **/ -.#{$primaryStyleName}-black .#{$primaryStyleName}-wrap { - border-color: #2e3030; - border-radius: 8px; - -webkit-border-radius: 8px; - -moz-border-radius: 8px; - overflow: hidden; -} -.v-sa & .#{$primaryStyleName}-black .#{$primaryStyleName}-wrap, -.v-op & .#{$primaryStyleName}-black .#{$primaryStyleName}-wrap { - border-color: rgba(0,0,0,.8); -} -.#{$primaryStyleName}-black .#{$primaryStyleName}-wrap { - background-color: #1d2021; - -moz-border-radius: 7px; - -webkit-border-radius: 7px; -} -.v-sa & .#{$primaryStyleName}-black .#{$primaryStyleName}-wrap, -.v-op & .#{$primaryStyleName}-black .#{$primaryStyleName}-wrap { - background-color: rgba(29,32,33,.9); -} -.#{$primaryStyleName}-black .#{$primaryStyleName}-outerheader { - height: 29px; - padding: 7px 14px; - background: transparent repeat-x; - background-image: url(img/black/header-bg.png); /** sprite-ref: black-verticals; sprite-alignment: repeat */ - text-align: center; - -moz-border-radius-topright: 7px; - -moz-border-radius-topleft: 7px; - -webkit-border-top-right-radius: 7px; - -webkit-border-top-left-radius: 7px; - overflow: hidden; - border: none; -} -.#{$primaryStyleName}-black .#{$primaryStyleName}-header { - font-size: 12px; - font-weight: normal; - color: #dddfe1; -} -.#{$primaryStyleName}-black .#{$primaryStyleName}-closebox { - top: 8px; -} -.#{$primaryStyleName}-black .#{$primaryStyleName}-footer { - background: transparent; - border: none; - height: 14px; -} -.#{$primaryStyleName}-black .#{$primaryStyleName}-resizebox { - background: transparent no-repeat; - background-image: url(img/black/resize.png); /** sprite-ref: black-verticals; sprite-margin-bottom: 4px */ - width: 14px; - height: 14px; -} -/* Must be last to make this image last in the sprites */ -.#{$primaryStyleName}-black .#{$primaryStyleName}-contents { - border: none; - background: transparent repeat-x; - background-image: url(img/black/content-bg.png); /** sprite-ref: black-verticals; sprite-alignment: repeat */ +.#{$primaryStyleName}-black { + .#{$primaryStyleName}-wrap { + border-color: #2e3030; + border-radius: 8px; + -webkit-border-radius: 8px; + -moz-border-radius: 8px; + overflow: hidden; + } + .v-sa & .#{$primaryStyleName}-wrap, + .v-op & .#{$primaryStyleName}-wrap { + border-color: rgba(0,0,0,.8); + } + + .#{$primaryStyleName}-wrap { + background-color: #1d2021; + -moz-border-radius: 7px; + -webkit-border-radius: 7px; + } + .v-sa & .#{$primaryStyleName}-wrap, + .v-op & .#{$primaryStyleName}-wrap { + background-color: rgba(29,32,33,.9); + } + .#{$primaryStyleName}-outerheader { + height: 29px; + padding: 7px 14px; + background: transparent repeat-x; + background-image: url(img/black/header-bg.png); /** sprite-ref: black-verticals; sprite-alignment: repeat */ + text-align: center; + -moz-border-radius-topright: 7px; + -moz-border-radius-topleft: 7px; + -webkit-border-top-right-radius: 7px; + -webkit-border-top-left-radius: 7px; + overflow: hidden; + border: none; + } + .#{$primaryStyleName}-header { + font-size: 12px; + font-weight: normal; + color: #dddfe1; + } + .#{$primaryStyleName}-closebox { + top: 8px; + + background-image: url(img/black/close.png); /** sprite-ref: verticals */ + &:hover { + background-image: url(img/black/close-hover.png); /** sprite-ref: verticals */ + } + &:active { + background-image: url(img/black/close-pressed.png); /** sprite-ref: verticals */ + } + } + .#{$primaryStyleName}-footer { + background: transparent; + border: none; + height: 14px; + } + .#{$primaryStyleName}-resizebox { + background: transparent no-repeat; + background-image: url(img/black/resize.png); /** sprite-ref: black-verticals; sprite-margin-bottom: 4px */ + width: 14px; + height: 14px; + } + .#{$primaryStyleName}-maximizebox { + top: 8px; + + background-image: url(img/black/maximize.png); /** sprite-ref: verticals */ + &:hover { + background-image: url(img/black/maximize-hover.png); /** sprite-ref: verticals */ + } + &:active { + background-image: url(img/black/maximize-active.png); /** sprite-ref: verticals */ + } + } + .#{$primaryStyleName}-restorebox { + top: 8px; + + background-image: url(img/black/restore.png); /** sprite-ref: verticals */ + &:hover { + background-image: url(img/black/restore-hover.png); /** sprite-ref: verticals */ + } + &:active { + background-image: url(img/black/restore-active.png); /** sprite-ref: verticals */ + } + } + + /* Must be last to make this image last in the sprites */ + .#{$primaryStyleName}-contents { + border: none; + background: transparent repeat-x; + background-image: url(img/black/content-bg.png); /** sprite-ref: black-verticals; sprite-alignment: repeat */ + } } }
\ No newline at end of file diff --git a/WebContent/VAADIN/themes/runo/tabsheet/tabsheet.scss b/WebContent/VAADIN/themes/runo/tabsheet/tabsheet.scss index 3b8773e9e6..ab4afb1c69 100644 --- a/WebContent/VAADIN/themes/runo/tabsheet/tabsheet.scss +++ b/WebContent/VAADIN/themes/runo/tabsheet/tabsheet.scss @@ -142,9 +142,6 @@ background: #babfc0; overflow: hidden; margin: 0; -} -.v-ie8 & .#{$primaryStyleName}-deco, -.v-ie9 & .#{$primaryStyleName}-deco { width: 100%; } /* Light-style */ diff --git a/WebContent/VAADIN/themes/runo/window/img/maximize.png b/WebContent/VAADIN/themes/runo/window/img/maximize.png Binary files differnew file mode 100644 index 0000000000..c7a1a8d418 --- /dev/null +++ b/WebContent/VAADIN/themes/runo/window/img/maximize.png diff --git a/WebContent/VAADIN/themes/runo/window/img/restore.png b/WebContent/VAADIN/themes/runo/window/img/restore.png Binary files differnew file mode 100644 index 0000000000..15ac00ddb2 --- /dev/null +++ b/WebContent/VAADIN/themes/runo/window/img/restore.png diff --git a/WebContent/VAADIN/themes/runo/window/window.scss b/WebContent/VAADIN/themes/runo/window/window.scss index 994238f2ad..d2048e027e 100644 --- a/WebContent/VAADIN/themes/runo/window/window.scss +++ b/WebContent/VAADIN/themes/runo/window/window.scss @@ -13,7 +13,7 @@ .#{$primaryStyleName}-outerheader { height: 49px; margin-left: 9px; - padding: 15px 40px 11px 12px; + padding: 15px 61px 11px 12px; background: transparent url(img/top-right.png) no-repeat right top; } .#{$primaryStyleName}-header { @@ -51,17 +51,48 @@ background: transparent; display: block; } -.#{$primaryStyleName}-closebox { +.#{$primaryStyleName}-closebox, +.#{$primaryStyleName}-maximizebox, +.#{$primaryStyleName}-restorebox { position: absolute; top: 21px; - right: 24px; + height: 12px; + background: transparent url(img/close.png); +} +.#{$primaryStyleName}-closebox { width: 12px; height: 12px; + right: 24px; background: transparent url(img/close.png); + &:hover { + background-position: 0 -12px; + } } -.#{$primaryStyleName}-closebox:hover { - background-position: 0 -12px; + +.#{$primaryStyleName}-maximizebox, +.#{$primaryStyleName}-restorebox { + right: 42px; } + +.#{$primaryStyleName}-restorebox { + width: 15px; + height: 14px; + + background: transparent url(img/restore.png); + &:hover { + background-position: 0 -14px; + } +} +.#{$primaryStyleName}-maximizebox { + width: 13px; + height: 12px; + + background: transparent url(img/maximize.png); + &:hover { + background-position: 0 -12px; + } +} + .#{$primaryStyleName}-modalitycurtain { background: #fff; } diff --git a/WebContent/VAADIN/themes/tests-calendar/styles.css b/WebContent/VAADIN/themes/tests-calendar/styles.css new file mode 100644 index 0000000000..7a37fcfdaf --- /dev/null +++ b/WebContent/VAADIN/themes/tests-calendar/styles.css @@ -0,0 +1,96 @@ +@import url(../reindeer/legacy-styles.css); + +.v-app { + background: #fff; + } + + +/** Customized phase colors*/ + + +/** + * Green + */ + +/* For month view */ +.v-calendar .v-calendar-event-color1 { + color: #4f8324; + } +.v-calendar .v-calendar-event-color1-all-day { + background-color: #61c114; + } + +/* For week/day view */ +.v-calendar .v-calendar-event-color1 .v-calendar-event-caption { + color: #4f8324; + } +.v-calendar .v-calendar-event-color1 .v-calendar-event-content { + border-color: #61c114; + background-color: #daff70; + } + + +/** + * Blue + */ + +/* For month view */ +.v-calendar .v-calendar-event-color2 { + color: #1c4b8b; + } +.v-calendar .v-calendar-event-color2-all-day { + background-color: #0a56bc; + } + +/* For week/day view */ +.v-calendar .v-calendar-event-color2 .v-calendar-event-caption { + color: #1c4b8b; + } +.v-calendar .v-calendar-event-color2 .v-calendar-event-content { + border-color: #0a56bc; + background-color: #529bff; + } + + +/** + * Red + */ + +/* For month view */ +.v-calendar .v-calendar-event-color3 { + color: #831d1d; + } +.v-calendar .v-calendar-event-color3-all-day { + background-color: #bd1a1a; + } + +/* For week/day view */ +.v-calendar .v-calendar-event-color3 .v-calendar-event-caption { + color: #831d1d; + } +.v-calendar .v-calendar-event-color3 .v-calendar-event-content { + border-color: #bd1a1a; + background-color: #ff9d9d; + } + + +/** + * Orange + */ + +/* For month view */ +.v-calendar .v-calendar-event-color4 { + color: #8b5923; + } +.v-calendar .v-calendar-event-color4-all-day { + background-color: #cd6a00; + } + +/* For week/day view */ +.v-calendar .v-calendar-event-color4 .v-calendar-event-caption { + color: #8b5923; + } +.v-calendar .v-calendar-event-color4 .v-calendar-event-content { + border-color: #cd6a00; + background-color: #faa345; + }
\ No newline at end of file diff --git a/WebContent/VAADIN/vaadinBootstrap.js b/WebContent/VAADIN/vaadinBootstrap.js index 81adfcccc6..b2995dd0bd 100644 --- a/WebContent/VAADIN/vaadinBootstrap.js +++ b/WebContent/VAADIN/vaadinBootstrap.js @@ -120,6 +120,11 @@ url += '&theme=' + encodeURIComponent(theme); } + var extraParams = getConfig('extraParams') + if (extraParams !== undefined) { + url += extraParams; + } + url += '&' + vaadin.getBrowserDetailsParameters(appId); // Timestamp to avoid caching @@ -295,8 +300,20 @@ } // Detect touch device support - try { document.createEvent("TouchEvent"); url += "&v-td=1";} catch(e){}; - + var supportsTouch = false; + try { + document.createEvent("TouchEvent"); + supportsTouch = true; + } catch (e) { + // Chrome and IE10 touch detection + supportsTouch = 'ontouchstart' in window + || navigator.msMaxTouchPoints; + } + + if (supportsTouch) { + url += "&v-td=1"; + } + return url; } }; diff --git a/WebContent/VAADIN/vaadinPush.js.tpl b/WebContent/VAADIN/vaadinPush.js.tpl new file mode 100644 index 0000000000..3928fba1b6 --- /dev/null +++ b/WebContent/VAADIN/vaadinPush.js.tpl @@ -0,0 +1,8 @@ +@jquery.js@ +window.jQueryVaadin = window.jQuery.noConflict(true); +(function(jQuery, undefined) { + @jquery.atmosphere.js@ +})(jQueryVaadin); +if (console) { + console.log("Vaadin push loaded"); +}
\ No newline at end of file diff --git a/WebContent/WEB-INF/web.xml b/WebContent/WEB-INF/web.xml index 57207d7f18..848baea318 100644 --- a/WebContent/WEB-INF/web.xml +++ b/WebContent/WEB-INF/web.xml @@ -1,7 +1,8 @@ <?xml version="1.0" encoding="UTF-8"?> -<web-app id="vaadin-uitest" version="2.4" - xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> +<web-app id="vaadin-uitest" version="3.0" + xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> + <!-- THIS IS A DEVELOPMENT AND TESTING web.xml . --> <display-name>Vaadin</display-name> @@ -50,6 +51,10 @@ <servlet-class>com.vaadin.launcher.ApplicationRunnerServlet</servlet-class> <!-- Non-default values for testing purposes --> <init-param> + <param-name>legacyPropertyToString</param-name> + <param-value>false</param-value> + </init-param> + <init-param> <param-name>heartbeatInterval</param-name> <param-value>301</param-value> </init-param> @@ -67,6 +72,16 @@ </init-param> </servlet> + <servlet> + <servlet-name>VaadinApplicationRunnerWithPush</servlet-name> + <servlet-class>com.vaadin.launcher.ApplicationRunnerServlet</servlet-class> + <init-param> + <param-name>pushmode</param-name> + <param-value>automatic</param-value> + </init-param> + <async-supported>true</async-supported> + </servlet> + <!-- For testing GAE - the deployment script changes this to use GAEVaadinServlet --> <servlet> <servlet-name>IntegrationTest</servlet-name> @@ -75,6 +90,7 @@ <param-name>UI</param-name> <param-value>com.vaadin.tests.integration.IntegrationTestUI</param-value> </init-param> + <async-supported>true</async-supported> </servlet> <servlet-mapping> <servlet-name>Embed App 1</servlet-name> @@ -97,6 +113,11 @@ </servlet-mapping> <servlet-mapping> + <servlet-name>VaadinApplicationRunnerWithPush</servlet-name> + <url-pattern>/run-push/*</url-pattern> + </servlet-mapping> + + <servlet-mapping> <servlet-name>IntegrationTest</servlet-name> <url-pattern>/integration/*</url-pattern> </servlet-mapping> diff --git a/WebContent/WEB-INF/web.xml.2.4 b/WebContent/WEB-INF/web.xml.2.4 new file mode 100644 index 0000000000..bcc5f3a651 --- /dev/null +++ b/WebContent/WEB-INF/web.xml.2.4 @@ -0,0 +1,131 @@ +<?xml version="1.0" encoding="UTF-8"?> +<web-app id="vaadin-uitest" version="2.4" + xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> + <!-- THIS IS A DEVELOPMENT AND TESTING web.xml for servlet 2.4 --> + + <display-name>Vaadin</display-name> + <description>Vaadin examples</description> + + <context-param> + <param-name>productionMode</param-name> + <param-value>false</param-value> + </context-param> + + <context-param> + <param-name>resourceCacheTime</param-name> + <param-value>3600</param-value> + </context-param> + <servlet> + <servlet-name>Embed App 1</servlet-name> + <servlet-class>com.vaadin.server.LegacyVaadinServlet</servlet-class> + <init-param> + <param-name>application</param-name> + <param-value>com.vaadin.tests.components.button.Buttons</param-value> + </init-param> + </servlet> + <servlet> + <servlet-name>Embed App 2</servlet-name> + <servlet-class>com.vaadin.server.VaadinServlet</servlet-class> + <init-param> + <param-name>UI</param-name> + <param-value>com.vaadin.tests.components.label.MarginsInLabels</param-value> + </init-param> + </servlet> + <servlet> + <servlet-name>UI provider app</servlet-name> + <servlet-class>com.vaadin.server.VaadinServlet</servlet-class> + <init-param> + <param-name>UIProvider</param-name> + <param-value>com.vaadin.tests.applicationservlet.InitParamUIProvider</param-value> + </init-param> + <init-param> + <param-name>UI</param-name> + <param-value>com.vaadin.tests.VerifyAssertionsEnabled</param-value> + </init-param> + </servlet> + + <servlet> + <servlet-name>VaadinApplicationRunner</servlet-name> + <servlet-class>com.vaadin.launcher.ApplicationRunnerServlet</servlet-class> + <!-- Non-default values for testing purposes --> + <init-param> + <param-name>legacyPropertyToString</param-name> + <param-value>false</param-value> + </init-param> + <init-param> + <param-name>heartbeatInterval</param-name> + <param-value>301</param-value> + </init-param> + <init-param> + <param-name>resourceCacheTime</param-name> + <param-value>3601</param-value> + </init-param> + <init-param> + <param-name>closeIdleSessions</param-name> + <param-value>true</param-value> + </init-param> + <init-param> + <param-name>testParam</param-name> + <param-value>42</param-value> + </init-param> + </servlet> + + <servlet> + <servlet-name>VaadinApplicationRunnerWithPush</servlet-name> + <servlet-class>com.vaadin.launcher.ApplicationRunnerServlet</servlet-class> + <init-param> + <param-name>pushmode</param-name> + <param-value>automatic</param-value> + </init-param> + </servlet> + + <!-- For testing GAE - the deployment script changes this to use GAEVaadinServlet --> + <servlet> + <servlet-name>IntegrationTest</servlet-name> + <servlet-class>com.vaadin.server.VaadinServlet</servlet-class> + <init-param> + <param-name>UI</param-name> + <param-value>com.vaadin.tests.integration.IntegrationTestUI</param-value> + </init-param> + </servlet> + <servlet-mapping> + <servlet-name>Embed App 1</servlet-name> + <url-pattern>/embed1/*</url-pattern> + </servlet-mapping> + + <servlet-mapping> + <servlet-name>Embed App 2</servlet-name> + <url-pattern>/embed2/*</url-pattern> + </servlet-mapping> + + <servlet-mapping> + <servlet-name>UI provider app</servlet-name> + <url-pattern>/uiprovider/*</url-pattern> + </servlet-mapping> + + <servlet-mapping> + <servlet-name>VaadinApplicationRunner</servlet-name> + <url-pattern>/run/*</url-pattern> + </servlet-mapping> + + <servlet-mapping> + <servlet-name>VaadinApplicationRunnerWithPush</servlet-name> + <url-pattern>/run-push/*</url-pattern> + </servlet-mapping> + + <servlet-mapping> + <servlet-name>IntegrationTest</servlet-name> + <url-pattern>/integration/*</url-pattern> + </servlet-mapping> + + <servlet-mapping> + <servlet-name>IntegrationTest</servlet-name> + <url-pattern>/VAADIN/*</url-pattern> + </servlet-mapping> + + <welcome-file-list> + <welcome-file>index.html</welcome-file> + </welcome-file-list> + +</web-app> diff --git a/WebContent/license.html b/WebContent/license.html index 1a855316eb..22678ca981 100644 --- a/WebContent/license.html +++ b/WebContent/license.html @@ -113,10 +113,29 @@ <td><a href="licenses/new-bsd-license.txt">New BSD License</a></td> </tr> + <!-- If vaadin-push used. Atmosphere has some internal dependencies, but they are all Apache 2. --> + <tr> + <td>Atmosphere Framework^</td> + <td><a href="licenses/apache-license-version-2-0.txt">Apache License, Version 2.0</a>,<br/> + <a href="licenses/common-development-and-distribution-license-v1-0.txt">Common Development and Distribution License, Version 1.0</a></td> + </tr> + + <!-- Used by vaadin-push --> + <tr> + <td>SLF4J^</td> + <td><a href="licenses/the-mit-license.txt">The MIT License</a></td> + </tr> + <!-- Used by vaadin-push --> + <tr> + <td>jQuery^</td> + <td><a href="licenses/the-mit-license.txt">The MIT License</a></td> + </tr> + </tbody> </table> - <p>* Not required by Vaadin, only used if provided by the user.</p> + <p>* Not required by Vaadin, only used if provided by the user.<br/> +^ Only if <tt>vaadin-push</tt> is used.</p> <h4>Vaadin Development Dependencies</h4> diff --git a/WebContent/licenses/common-development-and-distribution-license-v1-0.txt b/WebContent/licenses/common-development-and-distribution-license-v1-0.txt new file mode 100644 index 0000000000..5cf7a215ec --- /dev/null +++ b/WebContent/licenses/common-development-and-distribution-license-v1-0.txt @@ -0,0 +1,384 @@ +COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0 + + + 1. Definitions. + + 1.1. ?Contributor? means each individual or entity that + creates or contributes to the creation of Modifications. + + 1.2. ?Contributor Version? means the combination of the + Original Software, prior Modifications used by a + Contributor (if any), and the Modifications made by that + particular Contributor. + + 1.3. ?Covered Software? means (a) the Original Software, or + (b) Modifications, or (c) the combination of files + containing Original Software with files containing + Modifications, in each case including portions thereof. + + 1.4. ?Executable? means the Covered Software in any form + other than Source Code. + + 1.5. ?Initial Developer? means the individual or entity + that first makes Original Software available under this + License. + + 1.6. ?Larger Work? means a work which combines Covered + Software or portions thereof with code not governed by the + terms of this License. + + 1.7. ?License? means this document. + + 1.8. ?Licensable? means having the right to grant, to the + maximum extent possible, whether at the time of the initial + grant or subsequently acquired, any and all of the rights + conveyed herein. + + 1.9. ?Modifications? means the Source Code and Executable + form of any of the following: + + A. Any file that results from an addition to, + deletion from or modification of the contents of a + file containing Original Software or previous + Modifications; + + B. Any new file that contains any part of the + Original Software or previous Modification; or + + C. Any new file that is contributed or otherwise made + available under the terms of this License. + + 1.10. ?Original Software? means the Source Code and + Executable form of computer software code that is + originally released under this License. + + 1.11. ?Patent Claims? means any patent claim(s), now owned + or hereafter acquired, including without limitation, + method, process, and apparatus claims, in any patent + Licensable by grantor. + + 1.12. ?Source Code? means (a) the common form of computer + software code in which modifications are made and (b) + associated documentation included in or with such code. + + 1.13. ?You? (or ?Your?) means an individual or a legal + entity exercising rights under, and complying with all of + the terms of, this License. For legal entities, ?You? + includes any entity which controls, is controlled by, or is + under common control with You. For purposes of this + definition, ?control? means (a) the power, direct or + indirect, to cause the direction or management of such + entity, whether by contract or otherwise, or (b) ownership + of more than fifty percent (50%) of the outstanding shares + or beneficial ownership of such entity. + + 2. License Grants. + + 2.1. The Initial Developer Grant. + + Conditioned upon Your compliance with Section 3.1 below and + subject to third party intellectual property claims, the + Initial Developer hereby grants You a world-wide, + royalty-free, non-exclusive license: + + (a) under intellectual property rights (other than + patent or trademark) Licensable by Initial Developer, + to use, reproduce, modify, display, perform, + sublicense and distribute the Original Software (or + portions thereof), with or without Modifications, + and/or as part of a Larger Work; and + + (b) under Patent Claims infringed by the making, + using or selling of Original Software, to make, have + made, use, practice, sell, and offer for sale, and/or + otherwise dispose of the Original Software (or + portions thereof). + + (c) The licenses granted in Sections 2.1(a) and (b) + are effective on the date Initial Developer first + distributes or otherwise makes the Original Software + available to a third party under the terms of this + License. + + (d) Notwithstanding Section 2.1(b) above, no patent + license is granted: (1) for code that You delete from + the Original Software, or (2) for infringements + caused by: (i) the modification of the Original + Software, or (ii) the combination of the Original + Software with other software or devices. + + 2.2. Contributor Grant. + + Conditioned upon Your compliance with Section 3.1 below and + subject to third party intellectual property claims, each + Contributor hereby grants You a world-wide, royalty-free, + non-exclusive license: + + (a) under intellectual property rights (other than + patent or trademark) Licensable by Contributor to + use, reproduce, modify, display, perform, sublicense + and distribute the Modifications created by such + Contributor (or portions thereof), either on an + unmodified basis, with other Modifications, as + Covered Software and/or as part of a Larger Work; and + + + (b) under Patent Claims infringed by the making, + using, or selling of Modifications made by that + Contributor either alone and/or in combination with + its Contributor Version (or portions of such + combination), to make, use, sell, offer for sale, + have made, and/or otherwise dispose of: (1) + Modifications made by that Contributor (or portions + thereof); and (2) the combination of Modifications + made by that Contributor with its Contributor Version + (or portions of such combination). + + (c) The licenses granted in Sections 2.2(a) and + 2.2(b) are effective on the date Contributor first + distributes or otherwise makes the Modifications + available to a third party. + + (d) Notwithstanding Section 2.2(b) above, no patent + license is granted: (1) for any code that Contributor + has deleted from the Contributor Version; (2) for + infringements caused by: (i) third party + modifications of Contributor Version, or (ii) the + combination of Modifications made by that Contributor + with other software (except as part of the + Contributor Version) or other devices; or (3) under + Patent Claims infringed by Covered Software in the + absence of Modifications made by that Contributor. + + 3. Distribution Obligations. + + 3.1. Availability of Source Code. + + Any Covered Software that You distribute or otherwise make + available in Executable form must also be made available in + Source Code form and that Source Code form must be + distributed only under the terms of this License. You must + include a copy of this License with every copy of the + Source Code form of the Covered Software You distribute or + otherwise make available. You must inform recipients of any + such Covered Software in Executable form as to how they can + obtain such Covered Software in Source Code form in a + reasonable manner on or through a medium customarily used + for software exchange. + + 3.2. Modifications. + + The Modifications that You create or to which You + contribute are governed by the terms of this License. You + represent that You believe Your Modifications are Your + original creation(s) and/or You have sufficient rights to + grant the rights conveyed by this License. + + 3.3. Required Notices. + + You must include a notice in each of Your Modifications + that identifies You as the Contributor of the Modification. + You may not remove or alter any copyright, patent or + trademark notices contained within the Covered Software, or + any notices of licensing or any descriptive text giving + attribution to any Contributor or the Initial Developer. + + 3.4. Application of Additional Terms. + + You may not offer or impose any terms on any Covered + Software in Source Code form that alters or restricts the + applicable version of this License or the recipients? + rights hereunder. You may choose to offer, and to charge a + fee for, warranty, support, indemnity or liability + obligations to one or more recipients of Covered Software. + However, you may do so only on Your own behalf, and not on + behalf of the Initial Developer or any Contributor. You + must make it absolutely clear that any such warranty, + support, indemnity or liability obligation is offered by + You alone, and You hereby agree to indemnify the Initial + Developer and every Contributor for any liability incurred + by the Initial Developer or such Contributor as a result of + warranty, support, indemnity or liability terms You offer. + + + 3.5. Distribution of Executable Versions. + + You may distribute the Executable form of the Covered + Software under the terms of this License or under the terms + of a license of Your choice, which may contain terms + different from this License, provided that You are in + compliance with the terms of this License and that the + license for the Executable form does not attempt to limit + or alter the recipient?s rights in the Source Code form + from the rights set forth in this License. If You + distribute the Covered Software in Executable form under a + different license, You must make it absolutely clear that + any terms which differ from this License are offered by You + alone, not by the Initial Developer or Contributor. You + hereby agree to indemnify the Initial Developer and every + Contributor for any liability incurred by the Initial + Developer or such Contributor as a result of any such terms + You offer. + + 3.6. Larger Works. + + You may create a Larger Work by combining Covered Software + with other code not governed by the terms of this License + and distribute the Larger Work as a single product. In such + a case, You must make sure the requirements of this License + are fulfilled for the Covered Software. + + 4. Versions of the License. + + 4.1. New Versions. + + Sun Microsystems, Inc. is the initial license steward and + may publish revised and/or new versions of this License + from time to time. Each version will be given a + distinguishing version number. Except as provided in + Section 4.3, no one other than the license steward has the + right to modify this License. + + 4.2. Effect of New Versions. + + You may always continue to use, distribute or otherwise + make the Covered Software available under the terms of the + version of the License under which You originally received + the Covered Software. If the Initial Developer includes a + notice in the Original Software prohibiting it from being + distributed or otherwise made available under any + subsequent version of the License, You must distribute and + make the Covered Software available under the terms of the + version of the License under which You originally received + the Covered Software. Otherwise, You may also choose to + use, distribute or otherwise make the Covered Software + available under the terms of any subsequent version of the + License published by the license steward. + + 4.3. Modified Versions. + + When You are an Initial Developer and You want to create a + new license for Your Original Software, You may create and + use a modified version of this License if You: (a) rename + the license and remove any references to the name of the + license steward (except to note that the license differs + from this License); and (b) otherwise make it clear that + the license contains terms which differ from this License. + + + 5. DISCLAIMER OF WARRANTY. + + COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN ?AS IS? + BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, + INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED + SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR + PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND + PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY + COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE + INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF + ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF + WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF + ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS + DISCLAIMER. + + 6. TERMINATION. + + 6.1. This License and the rights granted hereunder will + terminate automatically if You fail to comply with terms + herein and fail to cure such breach within 30 days of + becoming aware of the breach. Provisions which, by their + nature, must remain in effect beyond the termination of + this License shall survive. + + 6.2. If You assert a patent infringement claim (excluding + declaratory judgment actions) against Initial Developer or + a Contributor (the Initial Developer or Contributor against + whom You assert such claim is referred to as ?Participant?) + alleging that the Participant Software (meaning the + Contributor Version where the Participant is a Contributor + or the Original Software where the Participant is the + Initial Developer) directly or indirectly infringes any + patent, then any and all rights granted directly or + indirectly to You by such Participant, the Initial + Developer (if the Initial Developer is not the Participant) + and all Contributors under Sections 2.1 and/or 2.2 of this + License shall, upon 60 days notice from Participant + terminate prospectively and automatically at the expiration + of such 60 day notice period, unless if within such 60 day + period You withdraw Your claim with respect to the + Participant Software against such Participant either + unilaterally or pursuant to a written agreement with + Participant. + + 6.3. In the event of termination under Sections 6.1 or 6.2 + above, all end user licenses that have been validly granted + by You or any distributor hereunder prior to termination + (excluding licenses granted to You by any distributor) + shall survive termination. + + 7. LIMITATION OF LIABILITY. + + UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT + (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE + INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF + COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE + LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR + CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT + LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK + STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER + COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN + INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF + LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL + INJURY RESULTING FROM SUCH PARTY?S NEGLIGENCE TO THE EXTENT + APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO + NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR + CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT + APPLY TO YOU. + + 8. U.S. GOVERNMENT END USERS. + + The Covered Software is a ?commercial item,? as that term is + defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of ?commercial + computer software? (as that term is defined at 48 C.F.R. ? + 252.227-7014(a)(1)) and ?commercial computer software + documentation? as such terms are used in 48 C.F.R. 12.212 (Sept. + 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 + through 227.7202-4 (June 1995), all U.S. Government End Users + acquire Covered Software with only those rights set forth herein. + This U.S. Government Rights clause is in lieu of, and supersedes, + any other FAR, DFAR, or other clause or provision that addresses + Government rights in computer software under this License. + + 9. MISCELLANEOUS. + + This License represents the complete agreement concerning subject + matter hereof. If any provision of this License is held to be + unenforceable, such provision shall be reformed only to the + extent necessary to make it enforceable. This License shall be + governed by the law of the jurisdiction specified in a notice + contained within the Original Software (except to the extent + applicable law, if any, provides otherwise), excluding such + jurisdiction?s conflict-of-law provisions. Any litigation + relating to this License shall be subject to the jurisdiction of + the courts located in the jurisdiction and venue specified in a + notice contained within the Original Software, with the losing + party responsible for costs, including, without limitation, court + costs and reasonable attorneys? fees and expenses. The + application of the United Nations Convention on Contracts for the + International Sale of Goods is expressly excluded. Any law or + regulation which provides that the language of a contract shall + be construed against the drafter shall not apply to this License. + You agree that You alone are responsible for compliance with the + United States export administration regulations (and the export + control laws and regulation of any other countries) when You use, + distribute or otherwise make available any Covered Software. + + 10. RESPONSIBILITY FOR CLAIMS. + + As between Initial Developer and the Contributors, each party is + responsible for claims and damages arising, directly or + indirectly, out of its utilization of rights under this License + and You agree to work with Initial Developer and Contributors to + distribute such responsibility on an equitable basis. Nothing + herein is intended or shall be deemed to constitute any admission + of liability. diff --git a/WebContent/release-notes.html b/WebContent/release-notes.html index 03b3f352b9..eef0b1de0c 100644 --- a/WebContent/release-notes.html +++ b/WebContent/release-notes.html @@ -38,9 +38,8 @@ <h2 id="tableofcontents">Release Notes for Vaadin Framework @version@</h2> <ul> <li><a href="#overview">Overview of Vaadin @version@ Release</a></li> - <li><a href="#security-fixes">Security fixes in Vaadin @version-minor@</a></li> - <li><a href="#changelog">Complete change log for Vaadin @version@</a></li> <li><a href="#enhancements">Enhancements in Vaadin @version-minor@</a></li> + <li><a href="#changelog">Complete change log for Vaadin @version@</a></li> <li><a href="#limitations">Limitations in @version-minor@</a></li> <li><a href="#vaadin">Vaadin Installation</a></li> <li><a href="#package">Package Contents</a></li> @@ -54,49 +53,63 @@ <h2 id="overview">Overview of Vaadin @version@ Release</h2> <p> - Vaadin @version@ is a maintenance release that includes a number of important bug - fixes, as listed in the <a href="#changelog">change log</a> below. You can also - view the <a + Vaadin @version@ is a feature release that includes a number of enhancements as + well as important bug fixes, as listed in the <a href="#changelog">change log</a> + below. You can also view the <a href="http://dev.vaadin.com/query?status=closed&resolution=fixed&milestone=Vaadin+@version@&order=priority">list of the closed issues</a> at the Vaadin developer's site. </p> - <p> - For a list of enhancements in the last feature release, see <a - href="#enhancements">Enhancements in Vaadin @version-minor@</a> and the <a - href="http://vaadin.com/download/release/@version-minor@/@version-minor@.0/release-notes.html">Release - Notes for Vaadin @version-minor@.0</a>. - </p> - - <!-- ================================================================ --> - <h2 id="security-fixes">Security fixes in Vaadin Framework 7.0.4</h2> + <h2 id="enhancements">Enhancements in Vaadin @version-minor@</h2> <p> - Vaadin 7.0.4 fixes a critical security issue discovered during an - internal review. All users of Vaadin portlets are strongly urged to - upgrade to Vaadin 7.0.4 or Vaadin 6.8.10 immediately. - </p> - <p> - Vaadin portlets (Portlet 2.0 - JSR-286) prior to Vaadin versions 6.8.10 - and 7.0.4 are vulnerable to an attack that allows a remote user who has - access to a portlet on the portal to read files in the portlet deployment - directory using specially crafted resource requests provided the attacker - knows the file name. - </p> - <p> - The vulnerability has been classified as critical as it potentially - allows unauthorized access to portlet object code and configuration - information. Files outside the portlet deployment directory are not - accessible using this vulnerability. Portlets that are not visible - to the remote user are not vulnerable to this attack. - Servlet deployments are not vulnerable to this attack. + The @version-minor@ includes many major and minor enhancements. Below is a list of + the most notable changes: </p> + + <ul> + <li>Server push based on Atmosphere</li> + <li>CSS injection through a <b>Styles</b> object from <tt>Page.getCurrent().getStyles()</tt></li> + <li>Enhanced Debug Window</li> + <li><b>Calendar</b> is now included in the core framework</li> + <li>The <b>VaadinServlet</b>/-<b>Portlet</b> and <b>-Service</b> have been refactored</li> + <li><b>DateField</b> can now have a range</li> + <li><b>Window</b> has Maximize/Restore controls</li> + <li>WAI-ARIA support for form fields, <b>Button</b>, and <b>Tree</b></li> + <li>The page can be reloaded programmatically with <tt>Page.reload()</tt></li> + <li>The legacy behavior of Property.toString() can be toggled using the <tt>legacyPropertyToString</tt> init parameter</li> + <li>Sass compiler now supports arithmetics</li> + <li>Sass compiler now supports <tt>@content</tt></li> + <li>Tooltip delays can be configured</li> + <li>Loading indicator delays can be configured</li> + <li>Layout components have a default alignment</li> + <li><b>DefaultFieldGroupFieldFactory</b> supports date fields</li> + + <li>Add-ons containing a theme should specify it with a <tt>Vaadin-Stylesheet</tt> + attribute in the manifest</li> + + <li>Native support for Internet Explorer 10</li> + <li>Many locking fixes</li> + <li>Java <tt>assert</tt> statements added to critical code sections. Start JVM with <tt>-ea</tt> to use.</li> + </ul> + <p> - All users of Vaadin portlets are strongly urged to upgrade Vaadin - in the portlets immediately. Where that is not possible, access to - affected portlets should be restricted to trusted users only. + For enchancements introduced in Vaadin 7, see the <a + href="http://vaadin.com/download/release/7.0/7.0.0/release-notes.html">Release + Notes for Vaadin 7.0.0</a>. </p> + <h3 id="limitations">Limitations</h3> + <ul> + <li>It is currently not possible to specify <tt>font-size</tt> as <tt>em</tt> or + <tt>%</tt>, or layout component sizes with <tt>em</tt> (<a + href="http://dev.vaadin.com/ticket/10634">#10634</a>)</li> + <li>Push using streaming does not work in Opera (<a + href="http://dev.vaadin.com/ticket/11642">#11642</a>)</li> + <li>Some debug console features such as analyze layouts do not work when push is enabled (<a + href="http://dev.vaadin.com/ticket/11536">#11536</a>)</li> + </ul> + <h3 id="changelog">ChangeLog</h3> <p> @@ -113,283 +126,6 @@ list of the closed issues</a> can also be found at <tt>dev.vaadin.com</tt>. </p> - <h2 id="enhancements">Enhancements in Vaadin @version-minor@</h2> - - <p> - The @version-minor@ includes many major and minor enhancements and changes first - introduced in Vaadin @version-minor@.0. Below is a list of the most notable changes: - </p> - - <ul> - <li>UI replaces Application as the main entry point - <ul> - <li>Heartbeat to reliably detect closed UI</li> - <li>Supports multiple browser tabs by default</li> - <li>Browser and request details available in UI init</li> - <li>Direct access to request and session in UI init</li> - <li>Access detected browser details in UI init</li> - <li>Default UI class chosen based on a servlet parameter</li> - <li>Custom UIProvider allows providing different UIs based on request parameters</li> - <li>UI is by default reinitialized when the page is reloaded</li> - </ul> - </li> - <li>Redesigned layouts - <ul> - <li>Minimal or no layout calculations to maximize layout speed</li> - <li>Full control of layouts with CSS including borders and margins</li> - <li>Redesigned lighter DOM for vertical, horizontal and css layout</li> - <li>Client-side ComputedStyle API available</li> - </ul> - </li> - <li>Split to seven jars to allow deploying only what you need</li> - <li>Adding multiple components with varargs in addComponents and appropriate constructors</li> - <li>Support for mixing multiple themes on the same page</li> - - <li>RPC for communication between the server and the browser - <ul> - <li>Static typing allows compile time checking</li> - <li>Supports Java's primitive and boxed types, String, enums, arrays, List, Set, Map and Java beans</li> - <li>Supports references to external or self served resources and references to other components</li> - <li>Call from browser to server can be delayed to piggyback on the next XHR, optionally folding similar calls to only send the last value</li> - <li>Calls to disabled or invisible components are ignored for security reasons</li> - </ul> - </li> - <li>Server-client shared state - <ul> - <li>Java objects can be shared between client and server for easy component development</li> - <li>State is automatically mirrored from server to client</li> - <li>Support both public fields and bean properties</li> - <li>Supports the same types as with RPC</li> - <li>Only parts of the state that are modified are sent over the wire</li> - <li>Allow calculating state on the fly just before state is sent to client</li> - <li>Client-side can listen to shared state changes to simplify connectors</li> - <li>State class can be annotated to automatically delegate state changes to corresponding properties in widgets</li> - </ul> - </li> - <li>Google Web Toolkit included - <ul> - <li>A full copy of GWT is included in Vaadin Framework</li> - <li>Vaadin team maintains a branch of GWT to include bug fixes and new features independent of official GWT release schedules</li> - <li>All functionality of GWT is included to enable writing of client side UI:s, stateless applications, offline functionality and custom widgets</li> - <li>Included Elemental library gives direct access to all cutting edge browser features</li> - <li>Both browser plug-in based dev mode debugging as well as super dev mode are supported</li> - </ul> - </li> - <li>No more need to call requestRepaint() in components</li> - <li>High level view navigation - <ul> - <li>Support for URI fragment based view management</li> - <li>Support for registering both pre-initialized view instances as well as view classes</li> - <li>Programmatic navigation with navigateTo()</li> - <li>Supports saving bookmarks to views</li> - <li>Supports parameterized views</li> - <li>Views can block navigation</li> - </ul> - </li> - <li>Connectors - <ul> - <li>Connectors provide a flexible communication channel between client and server</li> - <li>Separating communication code from widgets promotes reusability</li> - <li>An explicit Connector hierarchy is maintained</li> - <li>Mapping between server-side and client-side defined in client-side code to avoid server-side classpath issues</li> - </ul> - </li> - <li>JavaScript Connectors - <ul> - <li>Implement connector logic using JavaScript instead of Java for easier integration with JavaScript libraries</li> - <li>Wrap around any existing JavaScript based widget to adapt it for use in Vaadin</li> - <li>No widgetset compilation needed</li> - <li>Support for shared state and RPC as well as JSON-based communication based on simple JavaScript functions</li> - </ul> - </li> - <li>ColorPicker component - <ul> - <li>Easy to use interface with clickable color gradients</li> - <li>RGB, HSV and swatches color modes</li> - <li>Color history</li> - <li>Color preview</li> - <li>CSS color code representation and handling</li> - </ul> - </li> - <li>Add listeners without method overloads - <ul> - <li>Write addClickListener() instead of generic addListener()</li> - <li>Supports code completion in IDE:s better</li> - <li>Enables using Java 8 Lambda</li> - </ul> - </li> - <li>Renewed Vaadin Maven Plugin including features from GWT Maven Plugin - <ul> - <li>New Maven architype eases creation of Vaadin 7 applications</li> - </ul> - </li> - <li>Renewed Eclipse Plugin adding Apache Ivy based dependency management</li> - <li>Page bootstrapping renewed - <ul> - <li>Simpler inclusion of Vaadin UIs to custom web pages</li> - <li>Add-ons and applications can dynamically modify bootstrap page HTML</li> - </ul> - </li> - <li>VaadinSession - <ul> - <li>Full control over session lifecycle</li> - <li>Abstract away from servlets and portlets</li> - </ul> - </li> - <li>VaadinService - <ul> - <li>Easily access deployment information and HTTP requests</li> - <li>Abstract away from servlets and portlets</li> - </ul> - </li> - <li>Component extension API - <ul> - <li>Allow adding functionality and customizations to any component</li> - <li>Modify DOM and hook event listeners</li> - </ul> - </li> - <li>JavaScript callbacks - <ul> - <li>Declare client-side JavaScript API from server</li> - <li>Eases integration with parts of the page not controlled with Vaadin</li> - </ul> - </li> - <li>Relative paths used for all requests - <ul> - <li>More flexible deployment</li> - <li>Adds support for Apache ProxyPass and other similar proxies</li> - </ul> - </li> - <li>HTML5 - <ul> - <li>Vaadin 7 uses HTML5 doctype</li> - <li>Use any parts of HTML5 in your application</li> - </ul> - </li> - <li>Page - <ul> - <li>Abstraction for one browser window</li> - <li>Run JavaScript</li> - <li>Listen to page resizes</li> - <li>Control navigation</li> - </ul> - </li> - <li>Loading custom JavaScript - <ul> - <li>Annotate server-side classes with @JavaScript to request loading of JavaScript files</li> - <li>Automated control of loading order and ensuring that files are loaded only once</li> - </ul> - </li> - <li>API cleanup - <ul> - <li>API deprecated in Vaadin 6 or before removed</li> - <li>Use enums instead of integer constants</li> - </ul> - </li> - <li>Embedded split up to different components for different purposes - <ul> - <li>Image for showing images</li> - <li>BrowserFrame for embedding web pages with iframes</li> - <li>Flash for embedding Flash content</li> - <li>Embedded now only intended for embedding using <object> - </ul> - </li> - <li>Support for Firefox 17 extended support release in addition to latest stable Firefox release</li> - <li>Sass Compiler - <ul> - <li>Allows modularization of themes for better reuse and easier maintenance</li> - <li>Support the most important features of SCSS</li> - <li>Pure Java implementation without Ruby dependency</li> - <li>Supports all of CSS</li> - <li>On the fly conversion of SCSS to CSS during development</li> - <li>Built in themes are now based on Sass</li> - <li>Can be used in client-side projects as well</li> - </ul> - </li> - <li>@StyleSheet for automatic injection of css files</li> - <li>ConnectorResource replaces ApplicationResource to reduce memory consumption</li> - <li>Hierarchical error handling</li> - <li>Open popups and start downloads in a way not stopped by popup blockers</li> - <li>ThreadLocal access to VaadinService, VaadinRequest, VaadinResponse, VaadinSession and the current UI instance</li> - <li>Component id replaces debug ids to allow wider use possibilities for identifying corresponding widget elements in DOM</li> - <li>Range retrieval for indexed containers to enable optimize performance</li> - <li>Native support for percent sizes to let the browser do the percent to pixel calculation speeds up rendering</li> - <li>Custom class loader - <ul> - <li>Allow specifying custom class loaders to better support Java EE, CDI and Spring</li> - <li>Supports both servlets and portlets</li> - </ul> - </li> - <li>Updated data model - <ul> - <li>Property getValue() uses generics to return the expected type</li> - <li>Two phase commit support for commit/rollback</li> - <li>BeanItem supports nested properties to allow flattening complex datatypes</li> - </ul> - </li> - <li>Bean Validation - Annotate beans with JSR-303 standard annotations to automatically create validators for the fields</li> - <li>Field group - <ul> - <li>Allow data binding of multiple fields together to item data source</li> - <li>Supports buffering</li> - <li>Supports two phase commit</li> - <li>Annotation based and field name based property mapping</li> - </ul> - </li> - <li>Explicit data model converters - <ul> - <li>All fields support explicit conversion from presentation format to data source format</li> - <li>Conversions are bidirectional</li> - <li>Allow defining a default converter for a specific type and override for specific fields</li> - <li>Converters can be set per Table column to customize column formatting</li> - </ul> - </li> - <li>Built-in default converters - <ul> - <li>automated conversions beween String, Boolean, Long, Date, Double, Float, Integer and Number</li> - <li>Built in converters support internationalization</li> - </ul> - </li> - <li>Custom field component for building new fields as composition of existing components</li> - <li>Simplified validation API - <ul> - <li>No need to implement isValid() in validators any more</li> - </ul> - </li> - <li>Unsupported browser detection with customizable information page</li> - <li>Vaadin 6 compatibility layer to ease migration from Vaadin 6</li> - <li>Explicit layouts for Window and Panel</li> - <ul> - <li>Window and Panel components now require setting layout explicitly</li> - <li>Distinction between Window or Panel and it's layout</li> - </ul> - </li> - <li>Layout manager - <ul> - <li>Allows building custom layout calculations for widgets when browser based layouts are not powerful enough</li> - <li>Optimizes number of reflows by batching layout calculations from multiple widgets together</li> - </ul> - </li> - </ul> - - <p> - There are many other enhancements. Most of them are described in more detail in - the <a href="https://vaadin.com/wiki/-/wiki/Main/Vaadin+7">mini-tutorials</a> in - the Vaadin Wiki. Also see the <a - href="https://vaadin.com/wiki/-/wiki/Main/Migrating+from+Vaadin+6+to+Vaadin+7">Vaadin - 6 to 7 Migration Guide</a>. See also the <a - href="http://vaadin.com/download/release/@version-minor@/@version-minor@.0/release-notes.html">Release - Notes for Vaadin @version-minor@.0</a>. - </p> - - <h3 id="limitations">Limitations</h3> - - <ul> - <li>It is currently not possible to specify <tt>font-size</tt> as <tt>em</tt> or - <tt>%</tt>, or layout component sizes with <tt>em</tt> (<a - href="http://dev.vaadin.com/ticket/10634">#10634</a>)</li> - </ul> - <h2 id="vaadin">Vaadin Installation</h2> <p> @@ -409,7 +145,8 @@ of this release) to create a new project</li> <li>If using Eclipse, use the Vaadin Plugin for Eclipse, which automatically - downloads the Vaadin libraries</li> + downloads the Vaadin libraries. To use this prerelease version, the plugin should be + installed from the experimental update site (<tt>http://vaadin.com/eclipse/experimental</tt>).</li> </ul> <p> @@ -688,12 +425,12 @@ </p> <ul> - <li>Mozilla Firefox 18-19</li> + <li>Mozilla Firefox 18-20</li> <li>Mozilla Firefox 17 ESR</li> <li>Internet Explorer 8-10</li> <li>Safari 6</li> <li>Opera 12</li> - <li>Google Chrome 23-25</li> + <li>Google Chrome 23-26</li> </ul> <p> diff --git a/all/build.xml b/all/build.xml index 40d82b3d47..4e1a557e53 100644 --- a/all/build.xml +++ b/all/build.xml @@ -113,7 +113,7 @@ <target name="checkstyle"> <!-- Checkstyle is handled by all separate modules --> </target> - <target name="tests" depends="checkstyle"> + <target name="test" depends="checkstyle"> <!-- No tests for this zip.. --> </target> diff --git a/all/ivy.xml b/all/ivy.xml index 103d65e812..2b212f6675 100644 --- a/all/ivy.xml +++ b/all/ivy.xml @@ -32,6 +32,8 @@ rev="${vaadin.version}"/> <dependency org="com.vaadin" name="vaadin-client-compiled" rev="${vaadin.version}"/> + <dependency org="com.vaadin" name="vaadin-push" + rev="${vaadin.version}"/> </dependencies> @@ -40,7 +40,10 @@ <target name="checkstyle" depends="buildorder"> <subant buildpathref="build-path" target="checkstyle"/> </target> - <target name="tests" depends="buildorder"> + <target name="test" depends="buildorder"> + <subant buildpathref="build-path" target="test" /> + </target> + <target name="test-all" depends="buildorder"> <property name="war.file" location="result/artifacts/${vaadin.version}/vaadin-uitest/vaadin-uitest-${vaadin.version}.war" /> <parallel> <sequential> @@ -50,7 +53,7 @@ <property name="demo.war" value="${war.file}" /> </ant> </sequential> - <subant buildpathref="build-path" target="tests" /> + <subant buildpathref="build-path" target="test" /> <ant antfile="uitest/test.xml" target="test-package"> <property name="war.file" location="${war.file}" /> </ant> diff --git a/build/ide.xml b/build/ide.xml index a095e9265c..b1845020f3 100755 --- a/build/ide.xml +++ b/build/ide.xml @@ -1,31 +1,55 @@ <?xml version="1.0"?> <project xmlns:antcontrib="antlib:net.sf.antcontrib" xmlns:artifact="antlib:org.apache.maven.artifact.ant" xmlns:ivy="antlib:org.apache.ivy.ant" name="Build script for IDE users" basedir=".." default="theme-and-default-widgetset"> - <!-- FIXME, should use 7.0-SNAPSHOT of client compiler --> + <include file="${basedir}/gwt-files.xml" /> + + <property name="gwt.dev.classes" location="${gwt.eclipse.basedir}/dev/bin" /> + <property name="gwt.user.classes" location="${gwt.eclipse.basedir}/user/bin" /> + <property name="gwt.dev.src" location="${gwt.basedir}/dev/core/src" /> + <property name="gwt.dev.super.src" location="${gwt.basedir}/dev/core/super" /> + <property name="gwt.user.src" location="${gwt.basedir}/user/src" /> + <property name="gwt.user.super.src" location="${gwt.basedir}/user/super" /> + <property name="work.dir" location="work" /> - <property name="gwt.root" location="${basedir}/../trunk" /> - <property name="gwt.lib.dir" location="${gwt.root}/build/lib" /> - <property name="gwt.user.jar" location="${gwt.lib.dir}/gwt-user.jar" /> - <property name="gwt.dev.jar" location="${gwt.lib.dir}/gwt-dev.jar" /> <property name="theme-version" location="9.9.9.INTERNAL-DEBUG-BUILD" /> - <echo>Using gwt-dev.jar from ${gwt.dev.jar}</echo> + <echo>Using gwt files from ${gwt.user.classes} and ${gwt.dev.classes}</echo> - <ivy:resolve file="build/ivy-ide.xml" /> - <ivy:cachepath pathid="ivy.deps" conf="default" /> + <ivy:resolve file="client-compiler/ivy.xml" conf="ide" /> + <ivy:cachepath pathid="client-compiler.deps" conf="ide" /> + <ivy:resolve file="server/ivy.xml" conf="ide" /> + <ivy:cachepath pathid="server.deps" conf="ide" /> + <ivy:resolve file="client/ivy.xml" conf="ide" /> + <ivy:cachepath pathid="client.deps" conf="ide" /> + <ivy:resolve file="shared/ivy.xml" conf="ide" /> + <ivy:cachepath pathid="shared.deps" conf="ide" /> + <ivy:resolve file="uitest/ivy.xml" conf="ide" /> + <ivy:cachepath pathid="uitest.deps" conf="ide" /> + <ivy:resolve file="theme-compiler/ivy.xml" conf="ide" /> + <ivy:cachepath pathid="theme-compiler.deps" conf="ide" /> <path id="classpath"> <path location="bin" /> <path location="build/classes" /> - <pathelement location="${gwt.user.jar}" /> - <pathelement location="${gwt.dev.jar}" /> - <path refid="ivy.deps" /> + <path location="${gwt.user.classes}" /> + <path location="${gwt.user.src}" /> + <path location="${gwt.user.super.src}" /> + <path location="${gwt.dev.classes}" /> + <path location="${gwt.dev.super.src}" /> + <path location="${gwt.dev.src}" /> + <path refid="client-compiler.deps" /> + <path refid="theme-compiler.deps" /> + <path refid="server.deps" /> + <path refid="shared.deps" /> + <path refid="uitest.deps" /> + <path refid="client.deps" /> + <path location="theme-compiler/src" /> <path location="server/src" /> <path location="shared/src" /> <path location="uitest/src" /> <path location="client/src" /> </path> - <target name="theme-and-default-widgetset" depends="default-widgetset, themes"> + <target name="theme-and-default-widgetset" depends="default-widgetset, themes, vaadinPush.js"> </target> <target name="themes"> <antcall target="compile-theme"> @@ -58,8 +82,22 @@ </java> </target> + + <target name="default-widgetset"> - <property name="module" value="com.vaadin.DefaultWidgetSet" /> + <antcall target="compile-widgetset"> + <param name="widgetset" value="com.vaadin.DefaultWidgetSet" /> + </antcall> + </target> + + <target name="testing-widgetset"> + <antcall target="compile-widgetset"> + <param name="widgetset" value="com.vaadin.tests.widgetset.TestingWidgetSet" /> + </antcall> + </target> + + <target name="compile-widgetset"> + <property name="module" value="${widgetset}" /> <property name="module.output.dir" location="WebContent/VAADIN/widgetsets" /> <property name="style" value="PRETTY" /> <property name="localWorkers" value="2" /> @@ -93,7 +131,22 @@ <jvmarg value="-Xss8M" /> <jvmarg value="-XX:MaxPermSize=256M" /> <jvmarg value="-Djava.awt.headless=true" /> + <jvmarg value="-Dgwt.usearchives=false" /> </java> + </target> + <target name="vaadinPush.js"> + <property name="vaadinPush.js.output" location="WebContent/VAADIN/vaadinPush.js" /> + <loadfile srcfile="WebContent/VAADIN/jquery-1.7.2.js" property="jquery.js.contents" /> + <loadfile srcfile="WebContent/VAADIN/jquery.atmosphere.js" property="jquery.atmosphere.js.contents" /> + <loadfile srcfile="WebContent/VAADIN/vaadinPush.js.tpl" property="vaadinPush.js.contents"> + <filterchain> + <replacetokens begintoken="@" endtoken="@"> + <token key="jquery.js" value="${jquery.js.contents}" /> + <token key="jquery.atmosphere.js" value="${jquery.atmosphere.js.contents}" /> + </replacetokens> + </filterchain> + </loadfile> + <echo file="${vaadinPush.js.output}">${vaadinPush.js.contents}</echo> </target> </project>
\ No newline at end of file diff --git a/build/ivy-ide.xml b/build/ivy-ide.xml deleted file mode 100755 index 85d157857a..0000000000 --- a/build/ivy-ide.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<ivy-module version="2.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd"> - - <info organisation="com.vaadin" module="vaadin-ide" - revision="0.0.1.ide" /> - - <configurations> - <conf name="default" /> - </configurations> - <publications> - </publications> - <dependencies defaultconf="default" defaultconfmapping="default->default"> - <dependency org="javax.validation" name="validation-api" - rev="1.0.0.GA" conf="default -> default,sources" /> - <dependency org="commons-cli" name="commons-cli" rev="1.2" /> - <dependency org="org.apache.commons" name="commons-jexl" - rev="2.1.1" /> - - </dependencies> - -</ivy-module> diff --git a/buildhelpers/build.xml b/buildhelpers/build.xml index b56209f6cc..a101bff191 100644 --- a/buildhelpers/build.xml +++ b/buildhelpers/build.xml @@ -43,8 +43,8 @@ </antcall> </target> - <target name="tests" depends="checkstyle"> - <!--<antcall target="common.tests.run" />--> + <target name="test" depends="checkstyle"> + <!--<antcall target="common.test.run" />--> <echo>WHAT? No JUnit tests for ${module.name}!</echo> </target> </project>
\ No newline at end of file diff --git a/buildhelpers/ivy.xml b/buildhelpers/ivy.xml index d8e4457296..7c0a7b82a7 100644 --- a/buildhelpers/ivy.xml +++ b/buildhelpers/ivy.xml @@ -18,7 +18,7 @@ <conf name="build" /> <conf name="build-provided" /> <conf name="ide" visibility="private" /> - <conf name="tests" /> + <conf name="test" /> </configurations> <publications> <artifact type="jar" /> @@ -28,7 +28,7 @@ </publications> <dependencies> - <dependency org="commons-io" name="commons-io" rev="1.4" /> + <dependency org="commons-io" name="commons-io" rev="2.2" /> </dependencies> </ivy-module> diff --git a/client-compiled/build.xml b/client-compiled/build.xml index 1a78b17a7f..c9c3244c0e 100644 --- a/client-compiled/build.xml +++ b/client-compiled/build.xml @@ -85,6 +85,7 @@ <arg value="-localWorkers" /> <arg value="${localWorkers}" /> <arg value="-strict" /> + <arg value="-XenableClosureCompiler" /> <arg line="${extraParams}" /> <arg value="${module}" /> @@ -130,8 +131,8 @@ <target name="checkstyle"> <echo>No java files in module</echo> </target> - <target name="tests" depends="checkstyle"> - <!--<antcall target="common.tests.run" />--> + <target name="test" depends="checkstyle"> + <!--<antcall target="common.test.run" />--> <echo>WHAT? No tests for ${module.name}!</echo> </target> diff --git a/client-compiler/build.xml b/client-compiler/build.xml index 64368b4957..cd8433f1cf 100644 --- a/client-compiler/build.xml +++ b/client-compiler/build.xml @@ -60,8 +60,8 @@ gwt.svnrev=${git.revision}</echo> </antcall> </target> - <target name="tests" depends="checkstyle"> - <!--<antcall target="common.tests.run" />--> + <target name="test" depends="checkstyle"> + <!--<antcall target="common.test.run" />--> <echo>WHAT? No tests for ${module.name}!</echo> </target> diff --git a/client-compiler/ivy.xml b/client-compiler/ivy.xml index ee36e4e261..b26de51aca 100644 --- a/client-compiler/ivy.xml +++ b/client-compiler/ivy.xml @@ -1,57 +1,62 @@ <?xml version="1.0" encoding="UTF-8"?> <ivy-module version="2.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd" - xmlns:m="http://ant.apache.org/ivy/maven"> + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd" + xmlns:m="http://ant.apache.org/ivy/maven"> - <info organisation="com.vaadin" module="vaadin-client-compiler" - revision="${vaadin.version}" /> + <info organisation="com.vaadin" module="vaadin-client-compiler" + revision="${vaadin.version}" /> - <configurations> - <conf name="build" /> - <conf name="build-provided" /> - <conf name="ide" visibility="private" /> - </configurations> - <publications> - <artifact type="jar" ext="jar" /> - <artifact type="source" ext="jar" m:classifier="sources" /> - <artifact type="javadoc" ext="jar" m:classifier="javadoc" /> - <artifact type="pom" ext="pom" /> - </publications> - <dependencies> - <dependency org="com.vaadin" name="vaadin-shared" - rev="${vaadin.version}" conf="build" /> - <dependency org="com.vaadin" name="vaadin-server" - rev="${vaadin.version}" conf="build" /> - <dependency org="com.vaadin" name="vaadin-client" - rev="${vaadin.version}" conf="build" /> + <configurations> + <conf name="build" /> + <conf name="build-provided" /> + <conf name="ide" visibility="private" /> + </configurations> + <publications> + <artifact type="jar" ext="jar" /> + <artifact type="source" ext="jar" m:classifier="sources" /> + <artifact type="javadoc" ext="jar" m:classifier="javadoc" /> + <artifact type="pom" ext="pom" /> + </publications> + <dependencies> + <dependency org="com.vaadin" name="vaadin-shared" rev="${vaadin.version}" + conf="build" /> + <dependency org="com.vaadin" name="vaadin-server" rev="${vaadin.version}" + conf="build" /> + <dependency org="com.vaadin" name="vaadin-client" rev="${vaadin.version}" + conf="build" /> <dependency org="com.vaadin" name="vaadin-theme-compiler" rev="${vaadin.version}" conf="build" /> - - <dependency org="commons-collections" name="commons-collections" - rev="3.1" conf="build,ide -> default" /> - <dependency org="ant" name="ant" rev="1.6.5" - conf="build,ide -> default" /> - <dependency org="net.sourceforge.cssparser" name="cssparser" - rev="0.9.5" conf="build,ide -> default" /> - <dependency org="ant" name="ant" rev="1.6.5" - conf="build,ide -> default" /> - <dependency org="ant" name="ant-launcher" rev="1.6.5" - conf="build,ide -> default" /> - <dependency org="org.mortbay.jetty" name="jetty" rev="6.1.11" - conf="build,ide -> default" /> - <dependency org="org.mortbay.jetty" name="jetty-util" - rev="6.1.11" conf="build,ide -> default" /> - <dependency org="org.jdesktop" name="swing-worker" - rev="1.1" conf="build,ide -> default" /> - <dependency org="commons-codec" name="commons-codec" - rev="1.3" conf="build,ide -> default" /> - <dependency org="commons-io" name="commons-io" rev="1.4" - conf="build,ide -> default" /> - <dependency org="commons-lang" name="commons-lang" - rev="2.6" conf="build,ide -> default" /> - <dependency org="org.apache.james" name="apache-mime4j" - rev="0.6" conf="build,ide -> default" /> - </dependencies> + + <dependency org="commons-collections" name="commons-collections" + rev="3.1" conf="build,ide -> default" /> + <dependency org="commons-logging" name="commons-logging" + rev="1.1.1" conf="build,ide -> default" /> + + <dependency org="ant" name="ant" rev="1.6.5" conf="build,ide -> default" /> + <dependency org="net.sourceforge.cssparser" name="cssparser" + rev="0.9.5" conf="build,ide -> default" /> + <dependency org="ant" name="ant" rev="1.6.5" conf="build,ide -> default" /> + <dependency org="ant" name="ant-launcher" rev="1.6.5" + conf="build,ide -> default" /> + <dependency org="org.mortbay.jetty" name="jetty" rev="6.1.11" + conf="build,ide -> default" /> + <dependency org="org.mortbay.jetty" name="jetty-util" rev="6.1.11" + conf="build,ide -> default" /> + <dependency org="org.jdesktop" name="swing-worker" rev="1.1" + conf="build,ide -> default" /> + <dependency org="commons-codec" name="commons-codec" rev="1.3" + conf="build,ide -> default" /> + <dependency org="commons-io" name="commons-io" rev="2.2" + conf="build,ide -> default" /> + <dependency org="commons-lang" name="commons-lang" rev="2.6" + conf="build,ide -> default" /> + <dependency org="org.apache.james" name="apache-mime4j" + rev="0.6" conf="build,ide -> default" /> + + <dependency org="com.vaadin" name="vaadin-client-compiler-deps" + rev="1.0.1" conf="build,ide -> default" /> + + </dependencies> </ivy-module> diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java b/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java index 2be3bf5a16..f8aa586064 100644 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java +++ b/client-compiler/src/com/vaadin/server/widgetsetutils/ConnectorBundleLoaderFactory.java @@ -334,22 +334,6 @@ public class ConnectorBundleLoaderFactory extends Generator { writeGetters(logger, w, bundle); writeSerializers(logger, w, bundle); writeDelegateToWidget(logger, w, bundle); - writeHasGetTooltip(logger, w, bundle); - } - - /** - * @deprecated As of 7.0.1. This is just a hack to avoid breaking backwards - * compatibility and will be removed in Vaadin 7.1 - */ - @Deprecated - private void writeHasGetTooltip(TreeLogger logger, SplittingSourceWriter w, - ConnectorBundle bundle) { - Set<JClassType> types = bundle.getHasGetTooltip(); - for (JClassType type : types) { - w.println("store.setHasGetTooltipInfo(%s);", - getClassLiteralString(type)); - w.splitIfNeeded(); - } } private void writeDelegateToWidget(TreeLogger logger, diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ConnectorBundle.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ConnectorBundle.java index f6dc982f15..cbdd3e89aa 100644 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ConnectorBundle.java +++ b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/ConnectorBundle.java @@ -63,7 +63,6 @@ public class ConnectorBundle { private final Set<JClassType> needsGwtConstructor = new HashSet<JClassType>(); private final Set<JClassType> visitedTypes = new HashSet<JClassType>(); private final Set<JClassType> needsProxySupport = new HashSet<JClassType>(); - private final Set<JClassType> hasGetTooltip = new HashSet<JClassType>(); private final Map<JClassType, Set<String>> identifiers = new HashMap<JClassType, Set<String>>(); private final Map<JClassType, Set<JMethod>> needsReturnType = new HashMap<JClassType, Set<JMethod>>(); @@ -620,37 +619,4 @@ public class ConnectorBundle { return Collections.unmodifiableSet(needsDelegateToWidget); } - /** - * @deprecated As of 7.0.1. This is just a hack to avoid breaking backwards - * compatibility and will be removed in Vaadin 7.1 - */ - @Deprecated - public Set<JClassType> getHasGetTooltip() { - return Collections.unmodifiableSet(hasGetTooltip); - } - - /** - * @deprecated As of 7.0.1. This is just a hack to avoid breaking backwards - * compatibility and will be removed in Vaadin 7.1 - */ - @Deprecated - public void setHasGetTooltip(JClassType type) { - if (!isHasGetTooltip(type)) { - hasGetTooltip.add(type); - } - } - - /** - * @deprecated As of 7.0.1. This is just a hack to avoid breaking backwards - * compatibility and will be removed in Vaadin 7.1 - */ - @Deprecated - private boolean isHasGetTooltip(JClassType type) { - if (hasGetTooltip.contains(type)) { - return true; - } else { - return previousBundle != null - && previousBundle.isHasGetTooltip(type); - } - } }
\ No newline at end of file diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/WidgetInitVisitor.java b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/WidgetInitVisitor.java index 662ecf872b..4de9d2ae99 100644 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/WidgetInitVisitor.java +++ b/client-compiler/src/com/vaadin/server/widgetsetutils/metadata/WidgetInitVisitor.java @@ -47,25 +47,6 @@ public class WidgetInitVisitor extends TypeVisitor { bundle.setNeedsReturnType(type, getWidget); } - // Hack to detect when getTooltipInfo has a custom implementation - // #11051 - JClassType getTooltipParamType = type.getOracle().findType( - "com.google.gwt.dom.client.Element"); - JMethod getTooltipInfoMethod = findInheritedMethod(type, - "getTooltipInfo", getTooltipParamType); - if (getTooltipInfoMethod == null) { - logger.log(Type.ERROR, "Could not find getTooltipInfo in " - + type.getQualifiedSourceName()); - throw new UnableToCompleteException(); - } - JClassType enclosingType = getTooltipInfoMethod.getEnclosingType(); - if (!enclosingType.getQualifiedSourceName().equals( - AbstractComponentConnector.class.getCanonicalName())) { - logger.log(Type.WARN, type.getQualifiedSourceName() - + " has overridden getTooltipInfo"); - bundle.setHasGetTooltip(type); - } - // Check state properties for @DelegateToWidget JMethod getState = findInheritedMethod(type, "getState"); JClassType stateType = getState.getReturnType().isClass(); diff --git a/client/build.xml b/client/build.xml index d0dae91dfa..a2262eed7d 100644 --- a/client/build.xml +++ b/client/build.xml @@ -20,7 +20,7 @@ --> <fileset file="${gwt.user.jar}" /> </path> - <path id="classpath.tests.custom" /> + <path id="classpath.test.custom" /> <target name="jar"> <property name="jar.file" location="${result.dir}/lib/${module.name}-${vaadin.version}.jar" /> @@ -69,8 +69,8 @@ </antcall> </target> - <target name="tests" depends="checkstyle"> - <antcall target="common.tests.run" /> + <target name="test" depends="checkstyle"> + <antcall target="common.test.run" /> </target> </project>
\ No newline at end of file diff --git a/client/ivy.xml b/client/ivy.xml index 4b56338c24..5d079537b9 100644 --- a/client/ivy.xml +++ b/client/ivy.xml @@ -11,7 +11,7 @@ <conf name="build" /> <conf name="build-provided" /> <conf name="ide" visibility="private" /> - <conf name="tests" /> + <conf name="test" /> </configurations> <publications> <artifact type="jar" ext="jar" /> @@ -25,15 +25,15 @@ <!-- LIBRARY DEPENDENCIES (compile time) --> <!-- Project modules --> <dependency org="com.vaadin" name="vaadin-shared" - rev="${vaadin.version}" conf="build,tests->build"></dependency> + rev="${vaadin.version}" conf="build,test->build"></dependency> <dependency org="com.vaadin" name="vaadin-server" - rev="${vaadin.version}" conf="build->build"></dependency> + rev="${vaadin.version}" conf="build,test->build"></dependency> <!-- gwt-user dependencies --> <dependency org="org.w3c.css" name="sac" rev="1.3" /> <dependency org="junit" name="junit" rev="4.5" - conf="tests->default" /> + conf="test->default" /> <dependency org="javax.validation" name="validation-api" rev="1.0.0.GA" conf="build->default,sources" /> diff --git a/client/src/com/vaadin/Vaadin.gwt.xml b/client/src/com/vaadin/Vaadin.gwt.xml index dcc5b0d294..a4eb88d9b4 100644 --- a/client/src/com/vaadin/Vaadin.gwt.xml +++ b/client/src/com/vaadin/Vaadin.gwt.xml @@ -12,7 +12,13 @@ <inherits name="com.google.gwt.http.HTTP" /> <inherits name="com.google.gwt.json.JSON" /> - + + <inherits name="com.google.gwt.logging.Logging" /> + <!-- Firebug handler is deprecated but for some reason still enabled by default --> + <set-property name="gwt.logging.firebugHandler" value="DISABLED" /> + <!-- Disable popup logging as we have our own popup logger --> + <set-property name="gwt.logging.popupHandler" value="DISABLED" /> + <inherits name="com.vaadin.VaadinBrowserSpecificOverrides" /> <source path="client" /> @@ -24,10 +30,6 @@ <when-type-is class="com.google.gwt.core.client.impl.SchedulerImpl" /> </replace-with> - <replace-with class="com.vaadin.client.VDebugConsole"> - <when-type-is class="com.vaadin.client.Console" /> - </replace-with> - <generate-with class="com.vaadin.server.widgetsetutils.AcceptCriteriaFactoryGenerator"> <when-type-is class="com.vaadin.client.ui.dd.VAcceptCriterionFactory" /> @@ -39,6 +41,10 @@ class="com.vaadin.client.metadata.ConnectorBundleLoader" /> </generate-with> + <replace-with class="com.vaadin.client.communication.AtmospherePushConnection"> + <when-type-is class="com.vaadin.client.communication.PushConnection" /> + </replace-with> + <!-- Set vaadin.profiler to true to include profiling support in the module --> <define-property name="vaadin.profiler" values="true,false" /> <set-property name="vaadin.profiler" value="false" /> diff --git a/client/src/com/vaadin/client/ApplicationConfiguration.java b/client/src/com/vaadin/client/ApplicationConfiguration.java index 2291f21361..adf5e1de9d 100644 --- a/client/src/com/vaadin/client/ApplicationConfiguration.java +++ b/client/src/com/vaadin/client/ApplicationConfiguration.java @@ -20,6 +20,9 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.Logger; import com.google.gwt.core.client.EntryPoint; import com.google.gwt.core.client.GWT; @@ -28,8 +31,15 @@ import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArrayString; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; +import com.google.gwt.logging.client.LogConfiguration; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.Window; +import com.vaadin.client.debug.internal.ErrorNotificationHandler; +import com.vaadin.client.debug.internal.HierarchySection; +import com.vaadin.client.debug.internal.LogSection; +import com.vaadin.client.debug.internal.NetworkSection; +import com.vaadin.client.debug.internal.Section; +import com.vaadin.client.debug.internal.VDebugWindow; import com.vaadin.client.metadata.BundleLoadCallback; import com.vaadin.client.metadata.ConnectorBundleLoader; import com.vaadin.client.metadata.NoDataException; @@ -259,7 +269,16 @@ public class ApplicationConfiguration implements EntryPoint { } public String getThemeUri() { - return vaadinDirUrl + "themes/" + getThemeName(); + return getVaadinDirUrl() + "themes/" + getThemeName(); + } + + /** + * Gets the URL of the VAADIN directory on the server. + * + * @return the URL of the VAADIN directory + */ + public String getVaadinDirUrl() { + return vaadinDirUrl; } public void setAppId(String appId) { @@ -365,7 +384,6 @@ public class ApplicationConfiguration implements EntryPoint { if (jsoConfiguration.getConfigBoolean("initPending") == Boolean.FALSE) { setBrowserDetailsSent(); } - } /** @@ -546,32 +564,50 @@ public class ApplicationConfiguration implements EntryPoint { enableIOS6castFix(); } - // Prepare VConsole for debugging + // Prepare the debugging window if (isDebugMode()) { - Console console = GWT.create(Console.class); - console.setQuietMode(isQuietDebugMode()); - console.init(); - VConsole.setImplementation(console); - } else { - VConsole.setImplementation((Console) GWT.create(NullConsole.class)); - } - /* - * Display some sort of error of exceptions in web mode to debug - * console. After this, exceptions are reported to VConsole and possible - * GWT hosted mode. - */ - GWT.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { + /* + * XXX Lots of implementation details here right now. This should be + * cleared up when an API for extending the debug window is + * implemented. + */ + VDebugWindow window = GWT.create(VDebugWindow.class); - @Override - public void onUncaughtException(Throwable e) { - /* - * Note in case of null console (without ?debug) we eat - * exceptions. "a1 is not an object" style errors helps nobody, - * especially end user. It does not work tells just as much. - */ - VConsole.getImplementation().error(e); + if (LogConfiguration.loggingIsEnabled()) { + window.addSection((Section) GWT.create(LogSection.class)); } - }); + window.addSection((Section) GWT.create(HierarchySection.class)); + window.addSection((Section) GWT.create(NetworkSection.class)); + + if (isQuietDebugMode()) { + window.close(); + } else { + window.init(); + } + + // Connect to the legacy API + VConsole.setImplementation(window); + + Handler errorNotificationHandler = GWT + .create(ErrorNotificationHandler.class); + Logger.getLogger("").addHandler(errorNotificationHandler); + } + + if (LogConfiguration.loggingIsEnabled()) { + GWT.setUncaughtExceptionHandler(new UncaughtExceptionHandler() { + + @Override + public void onUncaughtException(Throwable e) { + /* + * If the debug window is not enabled (?debug), this will + * not show anything to normal users. "a1 is not an object" + * style errors helps nobody, especially end user. It does + * not work tells just as much. + */ + getLogger().log(Level.SEVERE, e.getMessage(), e); + } + }); + } Profiler.leave("ApplicationConfiguration.onModuleLoad"); if (SuperDevMode.enableBasedOnParameter()) { @@ -679,4 +715,8 @@ public class ApplicationConfiguration implements EntryPoint { widgetsetVersionSent = true; } + private static final Logger getLogger() { + return Logger.getLogger(ApplicationConfiguration.class.getName()); + } + } diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java index 4ddbd7c39b..4141f9f2dc 100644 --- a/client/src/com/vaadin/client/ApplicationConnection.java +++ b/client/src/com/vaadin/client/ApplicationConnection.java @@ -27,6 +27,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import com.google.gwt.aria.client.LiveValue; +import com.google.gwt.aria.client.RelevantValue; +import com.google.gwt.aria.client.Roles; import com.google.gwt.core.client.Duration; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.JavaScriptObject; @@ -66,6 +69,7 @@ import com.vaadin.client.communication.HasJavaScriptConnectorHelper; import com.vaadin.client.communication.JavaScriptMethodInvocation; import com.vaadin.client.communication.JsonDecoder; import com.vaadin.client.communication.JsonEncoder; +import com.vaadin.client.communication.PushConnection; import com.vaadin.client.communication.RpcManager; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.extensions.AbstractExtensionConnector; @@ -80,6 +84,7 @@ import com.vaadin.client.ui.AbstractConnector; import com.vaadin.client.ui.VContextMenu; import com.vaadin.client.ui.VNotification; import com.vaadin.client.ui.VNotification.HideEvent; +import com.vaadin.client.ui.VOverlay; import com.vaadin.client.ui.dd.VDragAndDropManager; import com.vaadin.client.ui.ui.UIConnector; import com.vaadin.client.ui.window.WindowConnector; @@ -94,7 +99,7 @@ import com.vaadin.shared.ui.ui.UIConstants; /** * This is the client side communication "engine", managing client-server * communication with its server side counterpart - * com.vaadin.server.AbstractCommunicationManager. + * com.vaadin.server.VaadinService. * * Client-side connectors receive updates from the corresponding server-side * connector (typically component) as state updates or RPC calls. The connector @@ -155,8 +160,8 @@ public class ApplicationConnection { */ public static final String UIDL_REFRESH_TOKEN = "Vaadin-Refresh"; - // will hold the UIDL security key (for XSS protection) once received - private String uidlSecurityKey = "init"; + // will hold the CSRF token once received + private String csrfToken = "init"; private final HashMap<String, String> resourcesMap = new HashMap<String, String>(); @@ -177,11 +182,6 @@ public class ApplicationConnection { private VContextMenu contextMenu = null; - private Timer loadTimer; - private Timer loadTimer2; - private Timer loadTimer3; - private Element loadElement; - private final UIConnector uIConnector; protected boolean applicationRunning = false; @@ -227,6 +227,8 @@ public class ApplicationConnection { private final RpcManager rpcManager; + private PushConnection push; + /** * If responseHandlingLocks contains any objects, response handling is * suspended until the collection is empty or a timeout has occurred. @@ -378,6 +380,8 @@ public class ApplicationConnection { private CommunicationErrorHandler communicationErrorDelegate = null; + private VLoadingIndicator loadingIndicator; + public static class MultiStepDuration extends Duration { private int previousStep = elapsedMillis(); @@ -404,6 +408,8 @@ public class ApplicationConnection { layoutManager = GWT.create(LayoutManager.class); layoutManager.setConnection(this); tooltip = GWT.create(VTooltip.class); + loadingIndicator = GWT.create(VLoadingIndicator.class); + loadingIndicator.setConnection(this); } public void init(WidgetSet widgetSet, ApplicationConfiguration cnf) { @@ -436,7 +442,7 @@ public class ApplicationConnection { tooltip.setOwner(uIConnector.getWidget()); - showLoadingIndicator(); + getLoadingIndicator().show(); scheduleHeartbeat(); @@ -454,6 +460,15 @@ public class ApplicationConnection { webkitMaybeIgnoringRequests = true; } }); + + // Ensure the overlay container is added to the dom and set as a live + // area for assistive devices + Element overlayContainer = VOverlay.getOverlayContainer(this); + Roles.getAlertRole().setAriaLiveProperty(overlayContainer, + LiveValue.ASSERTIVE); + setOverlayContainerLabel(getUIConnector().getState().overlayContainerLabel); + Roles.getAlertRole().setAriaRelevantProperty(overlayContainer, + RelevantValue.ADDITIONS); } /** @@ -656,8 +671,7 @@ public class ApplicationConnection { }-*/; protected void repaintAll() { - String repainAllParameters = getRepaintAllParameters(); - makeUidlRequest("", repainAllParameters, false); + makeUidlRequest("", getRepaintAllParameters()); } /** @@ -667,7 +681,7 @@ public class ApplicationConnection { public void analyzeLayouts() { String params = getRepaintAllParameters() + "&" + ApplicationConstants.PARAM_ANALYZE_LAYOUTS + "=1"; - makeUidlRequest("", params, false); + makeUidlRequest("", params); } /** @@ -681,7 +695,7 @@ public class ApplicationConnection { String params = getRepaintAllParameters() + "&" + ApplicationConstants.PARAM_HIGHLIGHT_CONNECTOR + "=" + serverConnector.getConnectorId(); - makeUidlRequest("", params, false); + makeUidlRequest("", params); } /** @@ -694,14 +708,12 @@ public class ApplicationConnection { * Contains key=value pairs joined by & characters or is empty if * no parameters should be added. Should not start with any * special character. - * @param forceSync - * true if the request should be synchronous, false otherwise */ protected void makeUidlRequest(final String requestData, - final String extraParams, final boolean forceSync) { + final String extraParams) { startRequest(); // Security: double cookie submission pattern - final String payload = uidlSecurityKey + VAR_BURST_SEPARATOR + final String payload = getCsrfToken() + VAR_BURST_SEPARATOR + requestData; VConsole.log("Making UIDL Request with params: " + payload); String uri = translateVaadinUri(ApplicationConstants.APP_PROTOCOL_PREFIX @@ -713,7 +725,7 @@ public class ApplicationConnection { uri = addGetParameters(uri, UIConstants.UI_ID_PARAMETER + "=" + configuration.getUIId()); - doUidlRequest(uri, payload, forceSync); + doUidlRequest(uri, payload); } @@ -725,143 +737,127 @@ public class ApplicationConnection { * The URI to use for the request. May includes GET parameters * @param payload * The contents of the request to send - * @param synchronous - * true if the request should be synchronous, false otherwise */ - protected void doUidlRequest(final String uri, final String payload, - final boolean synchronous) { - if (!synchronous) { - RequestCallback requestCallback = new RequestCallback() { - @Override - public void onError(Request request, Throwable exception) { - handleCommunicationError(exception.getMessage(), -1); - } + protected void doUidlRequest(final String uri, final String payload) { + RequestCallback requestCallback = new RequestCallback() { + @Override + public void onError(Request request, Throwable exception) { + handleCommunicationError(exception.getMessage(), -1); + } - private void handleCommunicationError(String details, - int statusCode) { - if (!handleErrorInDelegate(details, statusCode)) { - showCommunicationError(details, statusCode); - } - endRequest(); + private void handleCommunicationError(String details, int statusCode) { + if (!handleErrorInDelegate(details, statusCode)) { + showCommunicationError(details, statusCode); } + endRequest(); + } - @Override - public void onResponseReceived(Request request, - Response response) { - VConsole.log("Server visit took " - + String.valueOf((new Date()).getTime() - - requestStartTime.getTime()) + "ms"); - - int statusCode = response.getStatusCode(); - - switch (statusCode) { - case 0: - if (retryCanceledActiveRequest) { - /* - * Request was most likely canceled because the - * browser is maybe navigating away from the page. - * Just send the request again without displaying - * any error in case the navigation isn't carried - * through. - */ - retryCanceledActiveRequest = false; - doUidlRequest(uri, payload, synchronous); - } else { - handleCommunicationError( - "Invalid status code 0 (server down?)", - statusCode); - } - return; + @Override + public void onResponseReceived(Request request, Response response) { + VConsole.log("Server visit took " + + String.valueOf((new Date()).getTime() + - requestStartTime.getTime()) + "ms"); - case 401: - /* - * Authorization has failed. Could be that the session - * has timed out and the container is redirecting to a - * login page. - */ - showAuthenticationError(""); - endRequest(); - return; + int statusCode = response.getStatusCode(); - case 503: + switch (statusCode) { + case 0: + if (retryCanceledActiveRequest) { /* - * We'll assume msec instead of the usual seconds. If - * there's no Retry-After header, handle the error like - * a 500, as per RFC 2616 section 10.5.4. + * Request was most likely canceled because the browser + * is maybe navigating away from the page. Just send the + * request again without displaying any error in case + * the navigation isn't carried through. */ - String delay = response.getHeader("Retry-After"); - if (delay != null) { - VConsole.log("503, retrying in " + delay + "msec"); - (new Timer() { - @Override - public void run() { - doUidlRequest(uri, payload, synchronous); - } - }).schedule(Integer.parseInt(delay)); - return; - } + retryCanceledActiveRequest = false; + doUidlRequest(uri, payload); + } else { + handleCommunicationError( + "Invalid status code 0 (server down?)", + statusCode); } + return; - if ((statusCode / 100) == 4) { - // Handle all 4xx errors the same way as (they are - // all permanent errors) - showCommunicationError( - "UIDL could not be read from server. Check servlets mappings. Error code: " - + statusCode, statusCode); - endRequest(); - return; - } else if ((statusCode / 100) == 5) { - // Something's wrong on the server, there's nothing the - // client can do except maybe try again. - handleCommunicationError("Server error. Error code: " - + statusCode, statusCode); + case 401: + /* + * Authorization has failed. Could be that the session has + * timed out and the container is redirecting to a login + * page. + */ + showAuthenticationError(""); + endRequest(); + return; + + case 503: + /* + * We'll assume msec instead of the usual seconds. If + * there's no Retry-After header, handle the error like a + * 500, as per RFC 2616 section 10.5.4. + */ + String delay = response.getHeader("Retry-After"); + if (delay != null) { + VConsole.log("503, retrying in " + delay + "msec"); + (new Timer() { + @Override + public void run() { + doUidlRequest(uri, payload); + } + }).schedule(Integer.parseInt(delay)); return; } + } - String contentType = response.getHeader("Content-Type"); - if (contentType == null - || !contentType.startsWith("application/json")) { - /* - * A servlet filter or equivalent may have intercepted - * the request and served non-UIDL content (for - * instance, a login page if the session has expired.) - * If the response contains a magic substring, do a - * synchronous refresh. See #8241. - */ - MatchResult refreshToken = RegExp.compile( - UIDL_REFRESH_TOKEN + "(:\\s*(.*?))?(\\s|$)") - .exec(response.getText()); - if (refreshToken != null) { - redirect(refreshToken.getGroup(2)); - return; - } - } + if ((statusCode / 100) == 4) { + // Handle all 4xx errors the same way as (they are + // all permanent errors) + showCommunicationError( + "UIDL could not be read from server. Check servlets mappings. Error code: " + + statusCode, statusCode); + endRequest(); + return; + } else if ((statusCode / 100) == 5) { + // Something's wrong on the server, there's nothing the + // client can do except maybe try again. + handleCommunicationError("Server error. Error code: " + + statusCode, statusCode); + return; + } - // for(;;);[realjson] - final String jsonText = response.getText().substring(9, - response.getText().length() - 1); - handleJSONText(jsonText, statusCode); + String contentType = response.getHeader("Content-Type"); + if (contentType == null + || !contentType.startsWith("application/json")) { + /* + * A servlet filter or equivalent may have intercepted the + * request and served non-UIDL content (for instance, a + * login page if the session has expired.) If the response + * contains a magic substring, do a synchronous refresh. See + * #8241. + */ + MatchResult refreshToken = RegExp.compile( + UIDL_REFRESH_TOKEN + "(:\\s*(.*?))?(\\s|$)").exec( + response.getText()); + if (refreshToken != null) { + redirect(refreshToken.getGroup(2)); + return; + } } - }; + // for(;;);[realjson] + final String jsonText = response.getText().substring(9, + response.getText().length() - 1); + handleJSONText(jsonText, statusCode); + } + }; + if (push != null) { + push.push(payload); + } else { try { - doAsyncUIDLRequest(uri, payload, requestCallback); + doAjaxRequest(uri, payload, requestCallback); } catch (RequestException e) { VConsole.error(e); endRequest(); } - } else { - // Synchronized call, discarded response (leaving the page) - SynchronousXHR syncXHR = (SynchronousXHR) SynchronousXHR.create(); - syncXHR.synchronousPost(uri + "&" - + ApplicationConstants.PARAM_UNLOADBURST + "=1", payload); - /* - * Although we are in theory leaving the page, the page may still - * stay open. End request properly here too. See #3289 - */ - endRequest(); } - } /** @@ -905,11 +901,12 @@ public class ApplicationConnection { * @throws RequestException * if the request could not be sent */ - protected void doAsyncUIDLRequest(String uri, String payload, + protected void doAjaxRequest(String uri, String payload, RequestCallback requestCallback) throws RequestException { RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, uri); // TODO enable timeout // rb.setTimeoutMillis(timeoutMillis); + // TODO this should be configurable rb.setHeader("Content-Type", "text/plain;charset=utf-8"); rb.setRequestData(payload); rb.setCallback(requestCallback); @@ -1008,7 +1005,7 @@ public class ApplicationConnection { */ protected boolean isCSSLoaded() { return cssLoaded - || DOM.getElementPropertyInt(loadElement, "offsetHeight") != 0; + || getLoadingIndicator().getElement().getOffsetHeight() != 0; } /** @@ -1042,7 +1039,7 @@ public class ApplicationConnection { * @param details * Optional details for debugging. */ - protected void showSessionExpiredError(String details) { + public void showSessionExpiredError(String details) { VConsole.error("Session expired: " + details); showError(details, configuration.getSessionExpiredError()); } @@ -1106,25 +1103,7 @@ public class ApplicationConnection { } hasActiveRequest = true; requestStartTime = new Date(); - // show initial throbber - if (loadTimer == null) { - loadTimer = new Timer() { - @Override - public void run() { - /* - * IE7 does not properly cancel the event with - * loadTimer.cancel() so we have to check that we really - * should make it visible - */ - if (loadTimer != null) { - showLoadingIndicator(); - } - - } - }; - // First one kicks in at 300ms - } - loadTimer.schedule(300); + loadingIndicator.trigger(); eventBus.fireEvent(new RequestStartingEvent(this)); } @@ -1145,12 +1124,13 @@ public class ApplicationConnection { checkForPendingVariableBursts(); runPostRequestHooks(configuration.getRootPanelId()); } + // deferring to avoid flickering Scheduler.get().scheduleDeferred(new Command() { @Override public void execute() { if (!hasActiveRequest()) { - hideLoadingIndicator(); + getLoadingIndicator().hide(); // If on Liferay and session expiration management is in // use, extend session duration on each request. @@ -1179,7 +1159,7 @@ public class ApplicationConnection { } LinkedHashMap<String, MethodInvocation> nextBurst = pendingBursts .remove(0); - buildAndSendVariableBurst(nextBurst, false); + buildAndSendVariableBurst(nextBurst); } } @@ -1203,54 +1183,6 @@ public class ApplicationConnection { } } - private void showLoadingIndicator() { - // show initial throbber - if (loadElement == null) { - loadElement = DOM.createDiv(); - DOM.setStyleAttribute(loadElement, "position", "absolute"); - DOM.appendChild(uIConnector.getWidget().getElement(), loadElement); - VConsole.log("inserting load indicator"); - } - DOM.setElementProperty(loadElement, "className", "v-loading-indicator"); - DOM.setStyleAttribute(loadElement, "display", "block"); - // Initialize other timers - loadTimer2 = new Timer() { - @Override - public void run() { - DOM.setElementProperty(loadElement, "className", - "v-loading-indicator-delay"); - } - }; - // Second one kicks in at 1500ms from request start - loadTimer2.schedule(1200); - - loadTimer3 = new Timer() { - @Override - public void run() { - DOM.setElementProperty(loadElement, "className", - "v-loading-indicator-wait"); - } - }; - // Third one kicks in at 5000ms from request start - loadTimer3.schedule(4700); - } - - private void hideLoadingIndicator() { - if (loadTimer != null) { - loadTimer.cancel(); - loadTimer = null; - } - if (loadTimer2 != null) { - loadTimer2.cancel(); - loadTimer3.cancel(); - loadTimer2 = null; - loadTimer3 = null; - } - if (loadElement != null) { - DOM.setStyleAttribute(loadElement, "display", "none"); - } - } - /** * Checks if deferred commands are (potentially) still being executed as a * result of an update from the server. Returns true if a deferred command @@ -1273,19 +1205,24 @@ public class ApplicationConnection { } /** + * Returns the loading indicator used by this ApplicationConnection + * + * @return The loading indicator for this ApplicationConnection + */ + public VLoadingIndicator getLoadingIndicator() { + return loadingIndicator; + } + + /** * Determines whether or not the loading indicator is showing. * * @return true if the loading indicator is visible + * @deprecated As of 7.1. Use {@link #getLoadingIndicator()} and + * {@link VLoadingIndicator#isVisible()}.isVisible() instead. */ + @Deprecated public boolean isLoadingIndicatorVisible() { - if (loadElement == null) { - return false; - } - if (loadElement.getStyle().getProperty("display").equals("none")) { - return false; - } - - return true; + return getLoadingIndicator().isVisible(); } private static native ValueMap parseJSONResponse(String jsonText) @@ -1332,6 +1269,14 @@ public class ApplicationConnection { return; } + /* + * Lock response handling to avoid a situation where something pushed + * from the server gets processed while waiting for e.g. lazily loaded + * connectors that are needed for processing the current message. + */ + final Object lock = new Object(); + suspendReponseHandling(lock); + VConsole.log("Handling message from server"); eventBus.fireEvent(new ResponseHandlingStartedEvent(this)); @@ -1349,7 +1294,7 @@ public class ApplicationConnection { // Get security key if (json.containsKey(ApplicationConstants.UIDL_SECURITY_TOKEN_ID)) { - uidlSecurityKey = json + csrfToken = json .getString(ApplicationConstants.UIDL_SECURITY_TOKEN_ID); } VConsole.log(" * Handling resources from server"); @@ -1545,7 +1490,12 @@ public class ApplicationConnection { + jsonText.length() + " characters of JSON"); VConsole.log("Referenced paintables: " + connectorMap.size()); - endRequest(); + if (meta == null || !meta.containsKey("async")) { + // End the request if the received message was a response, + // not sent asynchronously + endRequest(); + } + resumeResponseHandling(lock); if (Profiler.isEnabled()) { Scheduler.get().scheduleDeferred(new ScheduledCommand() { @@ -1556,7 +1506,6 @@ public class ApplicationConnection { } }); } - } /** @@ -2417,6 +2366,23 @@ public class ApplicationConnection { } /** + * Removes any pending invocation of the given method from the queue + * + * @param invocation + * The invocation to remove + */ + public void removePendingInvocations(MethodInvocation invocation) { + Iterator<MethodInvocation> iter = pendingInvocations.values() + .iterator(); + while (iter.hasNext()) { + MethodInvocation mi = iter.next(); + if (mi.equals(invocation)) { + iter.remove(); + } + } + } + + /** * This method sends currently queued variable changes to server. It is * called when immediate variable update must happen. * @@ -2429,7 +2395,7 @@ public class ApplicationConnection { public void sendPendingVariableChanges() { if (!deferedSendPending) { deferedSendPending = true; - Scheduler.get().scheduleDeferred(sendPendingCommand); + Scheduler.get().scheduleFinally(sendPendingCommand); } } @@ -2444,7 +2410,7 @@ public class ApplicationConnection { private void doSendPendingVariableChanges() { if (applicationRunning) { - if (hasActiveRequest()) { + if (hasActiveRequest() || (push != null && !push.isActive())) { // skip empty queues if there are pending bursts to be sent if (pendingInvocations.size() > 0 || pendingBursts.size() == 0) { pendingBursts.add(pendingInvocations); @@ -2453,7 +2419,7 @@ public class ApplicationConnection { lastInvocationTag = 0; } } else { - buildAndSendVariableBurst(pendingInvocations, false); + buildAndSendVariableBurst(pendingInvocations); } } } @@ -2467,12 +2433,9 @@ public class ApplicationConnection { * * @param pendingInvocations * List of RPC method invocations to send - * @param forceSync - * Should we use synchronous request? */ private void buildAndSendVariableBurst( - LinkedHashMap<String, MethodInvocation> pendingInvocations, - boolean forceSync) { + LinkedHashMap<String, MethodInvocation> pendingInvocations) { final StringBuffer req = new StringBuffer(); while (!pendingInvocations.isEmpty()) { @@ -2526,12 +2489,6 @@ public class ApplicationConnection { pendingInvocations.clear(); // Keep tag string short lastInvocationTag = 0; - // Append all the bursts to this synchronous request - if (forceSync && !pendingBursts.isEmpty()) { - pendingInvocations = pendingBursts.get(0); - pendingBursts.remove(0); - req.append(VAR_BURST_SEPARATOR); - } } // Include the browser detail parameters if they aren't already sent @@ -2552,7 +2509,7 @@ public class ApplicationConnection { getConfiguration().setWidgetsetVersionSent(); } - makeUidlRequest(req.toString(), extraParams, forceSync); + makeUidlRequest(req.toString(), extraParams); } private boolean isJavascriptRpc(MethodInvocation invocation) { @@ -3056,7 +3013,17 @@ public class ApplicationConnection { private ConnectorMap connectorMap = GWT.create(ConnectorMap.class); protected String getUidlSecurityKey() { - return uidlSecurityKey; + return getCsrfToken(); + } + + /** + * Gets the token (aka double submit cookie) that the server uses to protect + * against Cross Site Request Forgery attacks. + * + * @return the CSRF token string + */ + public String getCsrfToken() { + return csrfToken; } /** @@ -3309,6 +3276,14 @@ public class ApplicationConnection { Timer forceHandleMessage = new Timer() { @Override public void run() { + if (responseHandlingLocks.isEmpty()) { + /* + * Timer fired but there's nothing to clear. This can happen + * with IE8 as Timer.cancel is not always effective (see GWT + * issue 8101). + */ + return; + } VConsole.log("WARNING: reponse handling was never resumed, forcibly removing locks..."); responseHandlingLocks.clear(); handlePendingMessages(); @@ -3333,9 +3308,13 @@ public class ApplicationConnection { public void resumeResponseHandling(Object lock) { responseHandlingLocks.remove(lock); if (responseHandlingLocks.isEmpty()) { - VConsole.log("No more response handling locks, handling pending requests."); + // Cancel timer that breaks the lock forceHandleMessage.cancel(); - handlePendingMessages(); + + if (!pendingUIDLMessages.isEmpty()) { + VConsole.log("No more response handling locks, handling pending requests."); + handlePendingMessages(); + } } } @@ -3344,11 +3323,19 @@ public class ApplicationConnection { * suspended. */ private void handlePendingMessages() { - for (PendingUIDLMessage pending : pendingUIDLMessages) { - handleUIDLMessage(pending.getStart(), pending.getJsonText(), - pending.getJson()); + if (!pendingUIDLMessages.isEmpty()) { + /* + * Clear the list before processing enqueued messages to support + * reentrancy + */ + List<PendingUIDLMessage> pendingMessages = pendingUIDLMessages; + pendingUIDLMessages = new ArrayList<PendingUIDLMessage>(); + + for (PendingUIDLMessage pending : pendingMessages) { + handleReceivedJSONMessage(pending.getStart(), + pending.getJsonText(), pending.getJson()); + } } - pendingUIDLMessages.clear(); } private boolean handleErrorInDelegate(String details, int statusCode) { @@ -3406,4 +3393,58 @@ public class ApplicationConnection { return Util.getConnectorForElement(this, getUIConnector().getWidget(), focusedElement); } + + /** + * Sets the status for the push connection. + * + * @param enabled + * <code>true</code> to enable the push connection; + * <code>false</code> to disable the push connection. + */ + public void setPushEnabled(boolean enabled) { + if (enabled && push == null) { + push = GWT.create(PushConnection.class); + push.init(this); + } else if (!enabled && push != null && push.isActive()) { + push.disconnect(new Command() { + @Override + public void execute() { + push = null; + /* + * If push has been enabled again while we were waiting for + * the old connection to disconnect, now is the right time + * to open a new connection + */ + if (uIConnector.getState().pushMode.isEnabled()) { + setPushEnabled(true); + } + + /* + * Send anything that was enqueued while we waited for the + * connection to close + */ + if (pendingInvocations.size() > 0) { + sendPendingVariableChanges(); + } + } + }); + } + } + + public void handlePushMessage(String message) { + handleJSONText(message, 200); + } + + /** + * Set the label of the container element, where tooltip, notification and + * dialgs are added to. + * + * @param overlayContainerLabel + * label for the container + */ + public void setOverlayContainerLabel(String overlayContainerLabel) { + Roles.getAlertRole().setAriaLabelProperty( + VOverlay.getOverlayContainer(this), + getUIConnector().getState().overlayContainerLabel); + } } diff --git a/client/src/com/vaadin/client/BrowserInfo.java b/client/src/com/vaadin/client/BrowserInfo.java index f0a4ccde0a..73f3a68193 100644 --- a/client/src/com/vaadin/client/BrowserInfo.java +++ b/client/src/com/vaadin/client/BrowserInfo.java @@ -85,6 +85,8 @@ public class BrowserInfo { if (browserDetails.isChrome()) { touchDevice = detectChromeTouchDevice(); + } else if (browserDetails.isIE()) { + touchDevice = detectIETouchDevice(); } else { touchDevice = detectTouchDevice(); } @@ -100,6 +102,11 @@ public class BrowserInfo { return ("ontouchstart" in window); }-*/; + private native boolean detectIETouchDevice() + /*-{ + return !!navigator.msMaxTouchPoints; + }-*/; + private native int getIEDocumentMode() /*-{ var mode = $wnd.document.documentMode; @@ -331,7 +338,8 @@ public class BrowserInfo { * otherwise <code>false</code> */ public boolean requiresOverflowAutoFix() { - return (getWebkitVersion() > 0 || getOperaVersion() >= 11 || isFirefox()) + return (getWebkitVersion() > 0 || getOperaVersion() >= 11 + || getIEVersion() >= 10 || isFirefox()) && Util.getNativeScrollbarSize() > 0; } diff --git a/client/src/com/vaadin/client/ComponentConnector.java b/client/src/com/vaadin/client/ComponentConnector.java index eecc3fda0c..f923a9dade 100644 --- a/client/src/com/vaadin/client/ComponentConnector.java +++ b/client/src/com/vaadin/client/ComponentConnector.java @@ -119,6 +119,11 @@ public interface ComponentConnector extends ServerConnector { /** * Gets the tooltip info for the given element. + * <p> + * When overriding this method, {@link #hasTooltip()} should also be + * overridden to return <code>true</code> in all situations where this + * method might return a non-empty result. + * </p> * * @param element * The element to lookup a tooltip for @@ -128,6 +133,20 @@ public interface ComponentConnector extends ServerConnector { public TooltipInfo getTooltipInfo(Element element); /** + * Check whether there might be a tooltip for this component. The framework + * will only add event listeners for automatically handling tooltips (using + * {@link #getTooltipInfo(Element)}) if this method returns true. + * <p> + * This is only done to optimize performance, so in cases where the status + * is not known, it's safer to return <code>true</code> so that there will + * be a tooltip handler even though it might not be needed in all cases. + * + * @return <code>true</code> if some part of the component might have a + * tooltip, otherwise <code>false</code> + */ + public boolean hasTooltip(); + + /** * Called for the active (focused) connector when a situation occurs that * the focused connector might have buffered changes which need to be * processed before other activity takes place. diff --git a/client/src/com/vaadin/client/ComponentLocator.java b/client/src/com/vaadin/client/ComponentLocator.java index 05603e8abe..af934470c2 100644 --- a/client/src/com/vaadin/client/ComponentLocator.java +++ b/client/src/com/vaadin/client/ComponentLocator.java @@ -28,6 +28,7 @@ import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ui.SubPartAware; import com.vaadin.client.ui.VCssLayout; import com.vaadin.client.ui.VGridLayout; +import com.vaadin.client.ui.VOverlay; import com.vaadin.client.ui.VTabsheetPanel; import com.vaadin.client.ui.VUI; import com.vaadin.client.ui.VWindow; @@ -446,7 +447,10 @@ public class ComponentLocator { return null; } String elementId = w.getElement().getId(); - if (elementId != null && !elementId.isEmpty()) { + if (elementId != null && !elementId.isEmpty() + && !elementId.startsWith("gwt-uid-")) { + // Use PID_S+id if the user has set an id but do not use it for auto + // generated id:s as these might not be consistent return "PID_S" + elementId; } else if (w instanceof VUI) { return ""; @@ -575,7 +579,12 @@ public class ComponentLocator { // is always 0 which indicates the widget in the active tab widgetPosition = 0; } - + if (w instanceof VOverlay + && "VCalendarPanel".equals(widgetClassName)) { + // Vaadin 7.1 adds a wrapper for datefield popups + parent = (Iterable<?>) ((Iterable) parent).iterator() + .next(); + } /* * The new grid and ordered layotus do not contain * ChildComponentContainer widgets. This is instead simulated by diff --git a/client/src/com/vaadin/client/ConnectorHierarchyChangeEvent.java b/client/src/com/vaadin/client/ConnectorHierarchyChangeEvent.java index 56ae7c44ac..2896386933 100644 --- a/client/src/com/vaadin/client/ConnectorHierarchyChangeEvent.java +++ b/client/src/com/vaadin/client/ConnectorHierarchyChangeEvent.java @@ -67,19 +67,17 @@ public class ConnectorHierarchyChangeEvent extends } /** - * Returns the {@link HasComponentsConnector} for which this event - * occurred. + * Returns the {@link HasComponentsConnector} for which this event occurred. * - * @return The {@link HasComponentsConnector} whose child collection - * has changed. Never returns null. + * @return The {@link HasComponentsConnector} whose child collection has + * changed. Never returns null. */ public HasComponentsConnector getParent() { return parent; } /** - * Sets the {@link HasComponentsConnector} for which this event - * occurred. + * Sets the {@link HasComponentsConnector} for which this event occurred. * * @param The * {@link HasComponentsConnector} whose child collection has diff --git a/client/src/com/vaadin/client/Console.java b/client/src/com/vaadin/client/Console.java deleted file mode 100644 index aa8ef2adc5..0000000000 --- a/client/src/com/vaadin/client/Console.java +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.client; - -import java.util.Set; - -public interface Console { - - public abstract void log(String msg); - - public abstract void log(Throwable e); - - public abstract void error(Throwable e); - - public abstract void error(String msg); - - public abstract void printObject(Object msg); - - public abstract void dirUIDL(ValueMap u, ApplicationConnection client); - - public abstract void printLayoutProblems(ValueMap meta, - ApplicationConnection applicationConnection, - Set<ComponentConnector> zeroHeightComponents, - Set<ComponentConnector> zeroWidthComponents); - - public abstract void setQuietMode(boolean quietDebugMode); - - public abstract void init(); - -}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/HasComponentsConnector.java b/client/src/com/vaadin/client/HasComponentsConnector.java index 0a1a7be97b..ebc6dbcd2a 100644 --- a/client/src/com/vaadin/client/HasComponentsConnector.java +++ b/client/src/com/vaadin/client/HasComponentsConnector.java @@ -21,6 +21,7 @@ import java.util.List; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.ui.HasWidgets; import com.vaadin.client.ConnectorHierarchyChangeEvent.ConnectorHierarchyChangeHandler; +import com.vaadin.ui.HasComponents; /** * An interface used by client-side connectors whose widget is a component diff --git a/client/src/com/vaadin/client/NullConsole.java b/client/src/com/vaadin/client/NullConsole.java deleted file mode 100644 index 2b70454b9d..0000000000 --- a/client/src/com/vaadin/client/NullConsole.java +++ /dev/null @@ -1,74 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.client; - -import java.util.Set; - -import com.google.gwt.core.client.GWT; - -/** - * Client side console implementation for non-debug mode that discards all - * messages. - * - */ -public class NullConsole implements Console { - - @Override - public void dirUIDL(ValueMap u, ApplicationConnection conn) { - } - - @Override - public void error(String msg) { - GWT.log(msg); - } - - @Override - public void log(String msg) { - GWT.log(msg); - } - - @Override - public void printObject(Object msg) { - GWT.log(msg.toString()); - } - - @Override - public void printLayoutProblems(ValueMap meta, - ApplicationConnection applicationConnection, - Set<ComponentConnector> zeroHeightComponents, - Set<ComponentConnector> zeroWidthComponents) { - } - - @Override - public void log(Throwable e) { - GWT.log(e.getMessage(), e); - } - - @Override - public void error(Throwable e) { - GWT.log(e.getMessage(), e); - } - - @Override - public void setQuietMode(boolean quietDebugMode) { - } - - @Override - public void init() { - } - -} diff --git a/client/src/com/vaadin/client/Profiler.java b/client/src/com/vaadin/client/Profiler.java index 6e8f2f5f9a..95b3232723 100644 --- a/client/src/com/vaadin/client/Profiler.java +++ b/client/src/com/vaadin/client/Profiler.java @@ -387,17 +387,12 @@ public class Profiler { StringBuilder stringBuilder = new StringBuilder(); rootNode.buildRecursiveString(stringBuilder, ""); - Console implementation = VConsole.getImplementation(); - if (implementation instanceof VDebugConsole) { - VDebugConsole console = (VDebugConsole) implementation; - SimpleTree tree = (SimpleTree) stack.getFirst().buildTree(); - tree.setText("Profiler data"); - - console.showTree(tree, stringBuilder.toString()); - } else { - VConsole.log(stringBuilder.toString()); - } + /* + * Should really output to a separate section in the debug window, but + * just dump it to the log for now. + */ + VConsole.log(stringBuilder.toString()); Map<String, Node> totals = new HashMap<String, Node>(); rootNode.sumUpTotals(totals); @@ -482,13 +477,11 @@ public class Profiler { return; } - Console implementation = VConsole.getImplementation(); - if (implementation instanceof VDebugConsole) { - VDebugConsole console = (VDebugConsole) implementation; - console.showTree(tree, stringBuilder.toString()); - } else { - VConsole.log(stringBuilder.toString()); - } + /* + * Should really output to a separate section in the debug window, + * but just dump it to the log for now. + */ + VConsole.log(stringBuilder.toString()); } } diff --git a/client/src/com/vaadin/client/SimpleTree.java b/client/src/com/vaadin/client/SimpleTree.java index d4aef4e4f7..23bdc4828f 100644 --- a/client/src/com/vaadin/client/SimpleTree.java +++ b/client/src/com/vaadin/client/SimpleTree.java @@ -25,16 +25,30 @@ import com.google.gwt.dom.client.Style.Display; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.dom.client.DoubleClickEvent; +import com.google.gwt.event.dom.client.DoubleClickHandler; +import com.google.gwt.event.dom.client.HasDoubleClickHandlers; +import com.google.gwt.event.shared.HandlerManager; +import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.ui.ComplexPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.Widget; -public class SimpleTree extends ComplexPanel { +/** + * @author Vaadin Ltd + * + * @deprecated as of 7.1. This class was mainly used by the old debug console + * but is retained for now for backwards compatibility. + */ +@Deprecated +public class SimpleTree extends ComplexPanel implements HasDoubleClickHandlers { private Element children = Document.get().createDivElement().cast(); private SpanElement handle = Document.get().createSpanElement(); private SpanElement text = Document.get().createSpanElement(); + private HandlerManager textDoubleClickHandlerManager; + public SimpleTree() { setElement(Document.get().createDivElement()); Style style = getElement().getStyle(); @@ -126,4 +140,24 @@ public class SimpleTree extends ComplexPanel { getElement().getStyle().setPaddingLeft(3, Unit.PX); } + /** + * {@inheritDoc} Events are not fired when double clicking child widgets. + */ + @Override + public HandlerRegistration addDoubleClickHandler(DoubleClickHandler handler) { + if (textDoubleClickHandlerManager == null) { + textDoubleClickHandlerManager = new HandlerManager(this); + addDomHandler(new DoubleClickHandler() { + @Override + public void onDoubleClick(DoubleClickEvent event) { + if (event.getNativeEvent().getEventTarget().cast() == text) { + textDoubleClickHandlerManager.fireEvent(event); + } + } + }, DoubleClickEvent.getType()); + } + return textDoubleClickHandlerManager.addHandler( + DoubleClickEvent.getType(), handler); + } + } diff --git a/client/src/com/vaadin/client/Util.java b/client/src/com/vaadin/client/Util.java index 2cd01b2dd8..8972670232 100644 --- a/client/src/com/vaadin/client/Util.java +++ b/client/src/com/vaadin/client/Util.java @@ -48,6 +48,7 @@ import com.vaadin.shared.AbstractComponentState; import com.vaadin.shared.ApplicationConstants; import com.vaadin.shared.communication.MethodInvocation; import com.vaadin.shared.ui.ComponentStateUtil; +import com.vaadin.shared.util.SharedUtil; public class Util { @@ -550,12 +551,21 @@ public class Util { } } + /** + * Checks if a and b are equals using {@link #equals(Object)}. Handles null + * values as well. Does not ensure that objects are of the same type. + * Assumes that the first object's equals method handle equals properly. + * + * @param a + * The first value to compare + * @param b + * The second value to compare + * @return + * @deprecated As of 7.1 use {@link SharedUtil#equals(Object)} instead + */ + @Deprecated public static boolean equals(Object a, Object b) { - if (a == null) { - return b == null; - } - - return a.equals(b); + return SharedUtil.equals(a, b); } public static void updateRelativeChildrenAndSendSizeUpdateEvent( @@ -630,6 +640,10 @@ public class Util { /*-{ var cs = element.ownerDocument.defaultView.getComputedStyle(element); var heightPx = cs.height; + if(heightPx == 'auto'){ + // Fallback for when IE reports auto + heightPx = @com.vaadin.client.Util::getRequiredHeightBoundingClientRect(Lcom/google/gwt/dom/client/Element;)(element) + 'px'; + } var borderTopPx = cs.borderTop; var borderBottomPx = cs.borderBottom; var paddingTopPx = cs.paddingTop; @@ -646,6 +660,10 @@ public class Util { /*-{ var cs = element.ownerDocument.defaultView.getComputedStyle(element); var widthPx = cs.width; + if(widthPx == 'auto'){ + // Fallback for when IE reports auto + widthPx = @com.vaadin.client.Util::getRequiredWidthBoundingClientRect(Lcom/google/gwt/dom/client/Element;)(element) + 'px'; + } var borderLeftPx = cs.borderLeft; var borderRightPx = cs.borderRight; var paddingLeftPx = cs.paddingLeft; @@ -1339,5 +1357,4 @@ public class Util { } } - } diff --git a/client/src/com/vaadin/client/VCaption.java b/client/src/com/vaadin/client/VCaption.java index 47287636c4..d033c2b4fe 100644 --- a/client/src/com/vaadin/client/VCaption.java +++ b/client/src/com/vaadin/client/VCaption.java @@ -16,6 +16,7 @@ package com.vaadin.client; +import com.google.gwt.aria.client.Roles; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; @@ -23,6 +24,7 @@ import com.google.gwt.user.client.ui.HTML; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractFieldConnector; import com.vaadin.client.ui.Icon; +import com.vaadin.client.ui.aria.AriaHelper; import com.vaadin.shared.AbstractComponentState; import com.vaadin.shared.AbstractFieldState; import com.vaadin.shared.ComponentConstants; @@ -95,6 +97,24 @@ public class VCaption extends HTML { setStyleName(CLASSNAME); } + @Override + protected void onAttach() { + super.onAttach(); + + if (null != owner) { + AriaHelper.bindCaption(owner.getWidget(), getElement()); + } + } + + @Override + protected void onDetach() { + super.onDetach(); + + if (null != owner) { + AriaHelper.bindCaption(owner.getWidget(), null); + } + } + /** * Updates the caption from UIDL. * @@ -200,6 +220,8 @@ public class VCaption extends HTML { removeStyleDependentName("hasdescription"); } + AriaHelper.handleInputRequired(owner.getWidget(), showRequired); + if (showRequired) { if (requiredFieldIndicator == null) { requiredFieldIndicator = DOM.createDiv(); @@ -209,6 +231,10 @@ public class VCaption extends HTML { DOM.insertChild(getElement(), requiredFieldIndicator, getInsertPosition(InsertPosition.REQUIRED)); + + // Hide the required indicator from assistive device + Roles.getTextboxRole().setAriaHiddenState( + requiredFieldIndicator, true); } } else if (requiredFieldIndicator != null) { // Remove existing @@ -216,6 +242,8 @@ public class VCaption extends HTML { requiredFieldIndicator = null; } + AriaHelper.handleInputInvalid(owner.getWidget(), showError); + if (showError) { if (errorIndicatorElement == null) { errorIndicatorElement = DOM.createDiv(); @@ -225,6 +253,10 @@ public class VCaption extends HTML { DOM.insertChild(getElement(), errorIndicatorElement, getInsertPosition(InsertPosition.ERROR)); + + // Hide error indicator from assistive devices + Roles.getTextboxRole().setAriaHiddenState( + errorIndicatorElement, true); } } else if (errorIndicatorElement != null) { // Remove existing diff --git a/client/src/com/vaadin/client/VConsole.java b/client/src/com/vaadin/client/VConsole.java index db19d1a9fd..f7a7554e34 100644 --- a/client/src/com/vaadin/client/VConsole.java +++ b/client/src/com/vaadin/client/VConsole.java @@ -16,91 +16,67 @@ package com.vaadin.client; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; -import com.google.gwt.core.client.GWT; +import com.google.gwt.logging.client.LogConfiguration; +import com.vaadin.client.debug.internal.VDebugWindow; /** * A helper class to do some client side logging. - * <p> - * This class replaces previously used loggin style: - * ApplicationConnection.getConsole().log("foo"). - * <p> - * The default widgetset provides three modes for debugging: - * <ul> - * <li>NullConsole (Default, displays no errors at all) - * <li>VDebugConsole ( Enabled by appending ?debug to url. Displays a floating - * console in the browser and also prints to browsers internal console (builtin - * or Firebug) and GWT's development mode console if available.) - * <li>VDebugConsole in quiet mode (Enabled by appending ?debug=quiet. Same as - * previous but without the console floating over application). - * </ul> - * <p> - * Implementations can be customized with GWT deferred binding by overriding - * NullConsole.class or VDebugConsole.class. This way developer can for example - * build mechanism to send client side logging data to a server. - * <p> - * Note that logging in client side is not fully optimized away even in - * production mode. Use logging moderately in production code to keep the size - * of client side engine small. An exception is {@link GWT#log(String)} style - * logging, which is available only in GWT development mode, but optimized away - * when compiled to web mode. - * - * - * TODO improve javadocs of individual methods * + * @deprecated as of 7.1, use {@link Logger} from java.util.logging instead. */ +@Deprecated public class VConsole { - private static Console impl; + private static VDebugWindow impl; /** * Used by ApplicationConfiguration to initialize VConsole. * * @param console */ - static void setImplementation(Console console) { + static void setImplementation(VDebugWindow console) { impl = console; } - /** - * Used by ApplicationConnection to support deprecated getConsole() api. - */ - static Console getImplementation() { - return impl; - } - public static void log(String msg) { - if (impl != null) { - impl.log(msg); + if (LogConfiguration.loggingIsEnabled(Level.INFO)) { + getLogger().log(Level.INFO, msg); } } public static void log(Throwable e) { - if (impl != null) { - impl.log(e); + if (LogConfiguration.loggingIsEnabled(Level.INFO)) { + getLogger().log(Level.INFO, e.getMessage(), e); } } public static void error(Throwable e) { - if (impl != null) { - impl.error(e); + if (LogConfiguration.loggingIsEnabled(Level.SEVERE)) { + getLogger().log(Level.SEVERE, e.getMessage(), e); } } public static void error(String msg) { - if (impl != null) { - impl.error(msg); + if (LogConfiguration.loggingIsEnabled(Level.SEVERE)) { + getLogger().log(Level.SEVERE, msg); } } public static void printObject(Object msg) { - if (impl != null) { - impl.printObject(msg); + String str; + if (msg == null) { + str = "null"; + } else { + str = msg.toString(); } + log(str); } public static void dirUIDL(ValueMap u, ApplicationConnection client) { if (impl != null) { - impl.dirUIDL(u, client); + impl.uidl(client, u); } } @@ -109,9 +85,12 @@ public class VConsole { Set<ComponentConnector> zeroHeightComponents, Set<ComponentConnector> zeroWidthComponents) { if (impl != null) { - impl.printLayoutProblems(meta, applicationConnection, - zeroHeightComponents, zeroWidthComponents); + impl.meta(applicationConnection, meta); } } + private static Logger getLogger() { + return Logger.getLogger(VConsole.class.getName()); + } + } diff --git a/client/src/com/vaadin/client/VDebugConsole.java b/client/src/com/vaadin/client/VDebugConsole.java deleted file mode 100644 index ee7505876d..0000000000 --- a/client/src/com/vaadin/client/VDebugConsole.java +++ /dev/null @@ -1,1041 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.client; - -import java.util.Collection; -import java.util.Date; -import java.util.HashSet; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; - -import com.google.gwt.core.client.GWT; -import com.google.gwt.core.client.JsArray; -import com.google.gwt.core.client.Scheduler.ScheduledCommand; -import com.google.gwt.dom.client.Style; -import com.google.gwt.dom.client.Style.FontWeight; -import com.google.gwt.dom.client.Style.Overflow; -import com.google.gwt.dom.client.Style.Position; -import com.google.gwt.dom.client.Style.Unit; -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.event.dom.client.ClickHandler; -import com.google.gwt.event.dom.client.KeyCodes; -import com.google.gwt.event.dom.client.MouseOutEvent; -import com.google.gwt.event.dom.client.MouseOutHandler; -import com.google.gwt.event.logical.shared.ValueChangeEvent; -import com.google.gwt.event.logical.shared.ValueChangeHandler; -import com.google.gwt.event.shared.HandlerRegistration; -import com.google.gwt.event.shared.UmbrellaException; -import com.google.gwt.http.client.Request; -import com.google.gwt.http.client.RequestBuilder; -import com.google.gwt.http.client.RequestCallback; -import com.google.gwt.http.client.RequestException; -import com.google.gwt.http.client.Response; -import com.google.gwt.http.client.UrlBuilder; -import com.google.gwt.i18n.client.DateTimeFormat; -import com.google.gwt.storage.client.Storage; -import com.google.gwt.user.client.Cookies; -import com.google.gwt.user.client.DOM; -import com.google.gwt.user.client.Element; -import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.Event.NativePreviewEvent; -import com.google.gwt.user.client.Event.NativePreviewHandler; -import com.google.gwt.user.client.EventPreview; -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.Window.Location; -import com.google.gwt.user.client.ui.Button; -import com.google.gwt.user.client.ui.CheckBox; -import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.HTML; -import com.google.gwt.user.client.ui.HorizontalPanel; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.Panel; -import com.google.gwt.user.client.ui.RootPanel; -import com.google.gwt.user.client.ui.VerticalPanel; -import com.google.gwt.user.client.ui.Widget; -import com.vaadin.client.ui.VLazyExecutor; -import com.vaadin.client.ui.VNotification; -import com.vaadin.client.ui.VOverlay; -import com.vaadin.client.ui.ui.UIConnector; -import com.vaadin.client.ui.window.WindowConnector; -import com.vaadin.shared.Version; - -/** - * A helper console for client side development. The debug console can also be - * used to resolve layout issues, inspect the communication between browser and - * the server, start GWT dev mode and restart application. - * - * <p> - * This implementation is used vaadin is in debug mode (see manual) and - * developer appends "?debug" query parameter to url. Debug information can also - * be shown on browsers internal console only, by appending "?debug=quiet" query - * parameter. - * <p> - * This implementation can be overridden with GWT deferred binding. - * - */ -public class VDebugConsole extends VOverlay implements Console { - - private final class HighlightModeHandler implements NativePreviewHandler { - private final Label label; - - private HighlightModeHandler(Label label) { - this.label = label; - } - - @Override - public void onPreviewNativeEvent(NativePreviewEvent event) { - if (event.getTypeInt() == Event.ONKEYDOWN - && event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ESCAPE) { - highlightModeRegistration.removeHandler(); - VUIDLBrowser.deHiglight(); - return; - } - if (event.getTypeInt() == Event.ONMOUSEMOVE) { - VUIDLBrowser.deHiglight(); - Element eventTarget = Util.getElementFromPoint(event - .getNativeEvent().getClientX(), event.getNativeEvent() - .getClientY()); - if (getElement().isOrHasChild(eventTarget)) { - return; - } - - for (ApplicationConnection a : ApplicationConfiguration - .getRunningApplications()) { - ComponentConnector connector = Util.getConnectorForElement( - a, a.getUIConnector().getWidget(), eventTarget); - if (connector == null) { - connector = Util.getConnectorForElement(a, - RootPanel.get(), eventTarget); - } - if (connector != null) { - String pid = connector.getConnectorId(); - VUIDLBrowser.highlight(connector); - label.setText("Currently focused :" - + connector.getClass() + " ID:" + pid); - event.cancel(); - event.consume(); - event.getNativeEvent().stopPropagation(); - return; - } - } - } - if (event.getTypeInt() == Event.ONCLICK) { - VUIDLBrowser.deHiglight(); - event.cancel(); - event.consume(); - event.getNativeEvent().stopPropagation(); - highlightModeRegistration.removeHandler(); - Element eventTarget = Util.getElementFromPoint(event - .getNativeEvent().getClientX(), event.getNativeEvent() - .getClientY()); - for (ApplicationConnection a : ApplicationConfiguration - .getRunningApplications()) { - ComponentConnector paintable = Util.getConnectorForElement( - a, a.getUIConnector().getWidget(), eventTarget); - if (paintable == null) { - paintable = Util.getConnectorForElement(a, - RootPanel.get(), eventTarget); - } - - if (paintable != null) { - a.highlightConnector(paintable); - return; - } - } - } - event.cancel(); - } - } - - private static final String POS_COOKIE_NAME = "VDebugConsolePos"; - - private HandlerRegistration highlightModeRegistration; - - Element caption = DOM.createDiv(); - - private Panel panel; - - private Button clear = new Button("C"); - private Button restart = new Button("R"); - private Button forceLayout = new Button("FL"); - private Button analyzeLayout = new Button("AL"); - private Button savePosition = new Button("S"); - private Button highlight = new Button("H"); - private Button connectorStats = new Button("CS"); - private CheckBox devMode = new CheckBox("Dev"); - private CheckBox superDevMode = new CheckBox("SDev"); - private CheckBox autoScroll = new CheckBox("Autoscroll "); - private HorizontalPanel actions; - private boolean collapsed = false; - - private boolean resizing; - private int startX; - private int startY; - private int initialW; - private int initialH; - - private boolean moving = false; - - private int origTop; - - private int origLeft; - - private static final String help = "Drag title=move, shift-drag=resize, doubleclick title=min/max." - + "Use debug=quiet to log only to browser console."; - - private static final int DEFAULT_WIDTH = 650; - private static final int DEFAULT_HEIGHT = 400; - - public VDebugConsole() { - super(false, false); - getElement().getStyle().setOverflow(Overflow.HIDDEN); - clear.setTitle("Clear console"); - restart.setTitle("Restart app"); - forceLayout.setTitle("Force layout"); - analyzeLayout.setTitle("Analyze layouts"); - savePosition.setTitle("Save pos"); - } - - private EventPreview dragpreview = new EventPreview() { - - @Override - public boolean onEventPreview(Event event) { - onBrowserEvent(event); - return false; - } - }; - - private boolean quietMode; - - @Override - public void onBrowserEvent(Event event) { - super.onBrowserEvent(event); - switch (DOM.eventGetType(event)) { - case Event.ONMOUSEDOWN: - if (DOM.eventGetShiftKey(event)) { - resizing = true; - DOM.setCapture(getElement()); - startX = DOM.eventGetScreenX(event); - startY = DOM.eventGetScreenY(event); - initialW = VDebugConsole.this.getOffsetWidth(); - initialH = VDebugConsole.this.getOffsetHeight(); - DOM.eventCancelBubble(event, true); - DOM.eventPreventDefault(event); - DOM.addEventPreview(dragpreview); - } else if (DOM.eventGetTarget(event) == caption) { - moving = true; - startX = DOM.eventGetScreenX(event); - startY = DOM.eventGetScreenY(event); - origTop = getAbsoluteTop(); - origLeft = getAbsoluteLeft(); - DOM.eventCancelBubble(event, true); - DOM.eventPreventDefault(event); - DOM.addEventPreview(dragpreview); - } - - break; - case Event.ONMOUSEMOVE: - if (resizing) { - int deltaX = startX - DOM.eventGetScreenX(event); - int detalY = startY - DOM.eventGetScreenY(event); - int w = initialW - deltaX; - if (w < 30) { - w = 30; - } - int h = initialH - detalY; - if (h < 40) { - h = 40; - } - VDebugConsole.this.setPixelSize(w, h); - DOM.eventCancelBubble(event, true); - DOM.eventPreventDefault(event); - } else if (moving) { - int deltaX = startX - DOM.eventGetScreenX(event); - int detalY = startY - DOM.eventGetScreenY(event); - int left = origLeft - deltaX; - if (left < 0) { - left = 0; - } - int top = origTop - detalY; - if (top < 0) { - top = 0; - } - VDebugConsole.this.setPopupPosition(left, top); - DOM.eventCancelBubble(event, true); - DOM.eventPreventDefault(event); - } - break; - case Event.ONLOSECAPTURE: - case Event.ONMOUSEUP: - if (resizing) { - DOM.releaseCapture(getElement()); - resizing = false; - } else if (moving) { - DOM.releaseCapture(getElement()); - moving = false; - } - DOM.removeEventPreview(dragpreview); - break; - case Event.ONDBLCLICK: - if (DOM.eventGetTarget(event) == caption) { - if (collapsed) { - panel.setVisible(true); - setToDefaultSizeAndPos(); - } else { - panel.setVisible(false); - setPixelSize(120, 20); - setPopupPosition(Window.getClientWidth() - 125, - Window.getClientHeight() - 25); - } - collapsed = !collapsed; - } - break; - default: - break; - } - - } - - private void setToDefaultSizeAndPos() { - String cookie = Cookies.getCookie(POS_COOKIE_NAME); - int width, height, top, left; - boolean autoScrollValue = false; - if (cookie != null) { - String[] split = cookie.split(","); - left = Integer.parseInt(split[0]); - top = Integer.parseInt(split[1]); - width = Integer.parseInt(split[2]); - height = Integer.parseInt(split[3]); - autoScrollValue = Boolean.valueOf(split[4]); - } else { - int windowHeight = Window.getClientHeight(); - int windowWidth = Window.getClientWidth(); - width = DEFAULT_WIDTH; - height = DEFAULT_HEIGHT; - - if (height > windowHeight / 2) { - height = windowHeight / 2; - } - if (width > windowWidth / 2) { - width = windowWidth / 2; - } - - top = windowHeight - (height + 10); - left = windowWidth - (width + 10); - } - setPixelSize(width, height); - setPopupPosition(left, top); - autoScroll.setValue(autoScrollValue); - } - - @Override - public void setPixelSize(int width, int height) { - if (height < 20) { - height = 20; - } - if (width < 2) { - width = 2; - } - panel.setHeight((height - 20) + "px"); - panel.setWidth((width - 2) + "px"); - getElement().getStyle().setWidth(width, Unit.PX); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.client.Console#log(java.lang.String) - */ - @Override - public void log(String msg) { - if (msg == null) { - msg = "null"; - } - msg = addTimestamp(msg); - // remoteLog(msg); - - logToDebugWindow(msg, false); - GWT.log(msg); - consoleLog(msg); - System.out.println(msg); - } - - private List<String> msgQueue = new LinkedList<String>(); - - private ScheduledCommand doSend = new ScheduledCommand() { - @Override - public void execute() { - if (!msgQueue.isEmpty()) { - RequestBuilder requestBuilder = new RequestBuilder( - RequestBuilder.POST, getRemoteLogUrl()); - try { - String requestData = ""; - for (String str : msgQueue) { - requestData += str; - requestData += "\n"; - } - requestBuilder.sendRequest(requestData, - new RequestCallback() { - - @Override - public void onResponseReceived(Request request, - Response response) { - // TODO Auto-generated method stub - - } - - @Override - public void onError(Request request, - Throwable exception) { - // TODO Auto-generated method stub - - } - }); - } catch (RequestException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - msgQueue.clear(); - } - } - - }; - private VLazyExecutor sendToRemoteLog = new VLazyExecutor(350, doSend); - - protected String getRemoteLogUrl() { - return "http://sun-vehje.local:8080/remotelog/"; - } - - protected void remoteLog(String msg) { - msgQueue.add(msg); - sendToRemoteLog.trigger(); - } - - /** - * Logs the given message to the debug window. - * - * @param msg - * The message to log. Must not be null. - */ - private void logToDebugWindow(String msg, boolean error) { - Widget row; - if (error) { - row = createErrorHtml(msg); - } else { - row = new HTML(msg); - } - panel.add(row); - if (autoScroll.getValue()) { - row.getElement().scrollIntoView(); - } - } - - private HTML createErrorHtml(String msg) { - HTML html = new HTML(msg); - html.getElement().getStyle().setColor("#f00"); - html.getElement().getStyle().setFontWeight(FontWeight.BOLD); - return html; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.client.Console#error(java.lang.String) - */ - @Override - public void error(String msg) { - if (msg == null) { - msg = "null"; - } - msg = addTimestamp(msg); - logToDebugWindow(msg, true); - - GWT.log(msg); - consoleErr(msg); - System.out.println(msg); - - } - - DateTimeFormat timestampFormat = DateTimeFormat.getFormat("HH:mm:ss:SSS"); - - @SuppressWarnings("deprecation") - private String addTimestamp(String msg) { - Date date = new Date(); - String timestamp = timestampFormat.format(date); - return timestamp + " " + msg; - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.client.Console#printObject(java.lang. Object) - */ - @Override - public void printObject(Object msg) { - String str; - if (msg == null) { - str = "null"; - } else { - str = msg.toString(); - } - panel.add((new Label(str))); - consoleLog(str); - } - - /* - * (non-Javadoc) - * - * @see com.vaadin.client.Console#dirUIDL(com.vaadin.client.UIDL) - */ - @Override - public void dirUIDL(ValueMap u, ApplicationConnection client) { - if (panel.isAttached()) { - VUIDLBrowser vuidlBrowser = new VUIDLBrowser(u, client); - vuidlBrowser.setText("Response:"); - panel.add(vuidlBrowser); - } - consoleDir(u); - // consoleLog(u.getChildrenAsXML()); - } - - /** - * Adds a {@link SimpleTree} to the console and prints a string - * representation of the tree structure to the text console. - * - * @param tree - * the simple tree to display in the console window - * @param stringRepresentation - * the string representation of the tree to output to the text - * console - */ - public void showTree(SimpleTree tree, String stringRepresentation) { - if (panel.isAttached()) { - panel.add(tree); - } - consoleLog(stringRepresentation); - } - - private static native void consoleDir(ValueMap u) - /*-{ - if($wnd.console && $wnd.console.log) { - if($wnd.console.dir) { - $wnd.console.dir(u); - } else { - $wnd.console.log(u); - } - } - - }-*/; - - private static native void consoleLog(String msg) - /*-{ - if($wnd.console && $wnd.console.log) { - $wnd.console.log(msg); - } - }-*/; - - private static native void consoleErr(String msg) - /*-{ - if($wnd.console) { - if ($wnd.console.error) - $wnd.console.error(msg); - else if ($wnd.console.log) - $wnd.console.log(msg); - } - }-*/; - - @Override - public void printLayoutProblems(ValueMap meta, ApplicationConnection ac, - Set<ComponentConnector> zeroHeightComponents, - Set<ComponentConnector> zeroWidthComponents) { - JsArray<ValueMap> valueMapArray = meta - .getJSValueMapArray("invalidLayouts"); - int size = valueMapArray.length(); - panel.add(new HTML("<div>************************</di>" - + "<h4>Layouts analyzed on server, total top level problems: " - + size + " </h4>")); - if (size > 0) { - SimpleTree root = new SimpleTree("Root problems"); - - for (int i = 0; i < size; i++) { - printLayoutError(valueMapArray.get(i), root, ac); - } - panel.add(root); - - } - if (zeroHeightComponents.size() > 0 || zeroWidthComponents.size() > 0) { - panel.add(new HTML("<h4> Client side notifications</h4>" - + " <em>The following relative sized components were " - + "rendered to a zero size container on the client side." - + " Note that these are not necessarily invalid " - + "states, but reported here as they might be.</em>")); - if (zeroHeightComponents.size() > 0) { - panel.add(new HTML( - "<p><strong>Vertically zero size:</strong><p>")); - printClientSideDetectedIssues(zeroHeightComponents, ac); - } - if (zeroWidthComponents.size() > 0) { - panel.add(new HTML( - "<p><strong>Horizontally zero size:</strong><p>")); - printClientSideDetectedIssues(zeroWidthComponents, ac); - } - } - log("************************"); - } - - private void printClientSideDetectedIssues( - Set<ComponentConnector> zeroHeightComponents, - ApplicationConnection ac) { - for (final ComponentConnector paintable : zeroHeightComponents) { - final ServerConnector parent = paintable.getParent(); - - VerticalPanel errorDetails = new VerticalPanel(); - errorDetails.add(new Label("" + Util.getSimpleName(paintable) - + " inside " + Util.getSimpleName(parent))); - if (parent instanceof ComponentConnector) { - ComponentConnector parentComponent = (ComponentConnector) parent; - final Widget layout = parentComponent.getWidget(); - - final CheckBox emphasisInUi = new CheckBox( - "Emphasize components parent in UI (the actual component is not visible)"); - emphasisInUi.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - Element element2 = layout.getElement(); - Widget.setStyleName(element2, "invalidlayout", - emphasisInUi.getValue().booleanValue()); - } - }); - - errorDetails.add(emphasisInUi); - } - panel.add(errorDetails); - } - } - - private void printLayoutError(ValueMap valueMap, SimpleTree root, - final ApplicationConnection ac) { - final String pid = valueMap.getString("id"); - final ComponentConnector paintable = (ComponentConnector) ConnectorMap - .get(ac).getConnector(pid); - - SimpleTree errorNode = new SimpleTree(); - VerticalPanel errorDetails = new VerticalPanel(); - errorDetails.add(new Label(Util.getSimpleName(paintable) + " id: " - + pid)); - if (valueMap.containsKey("heightMsg")) { - errorDetails.add(new Label("Height problem: " - + valueMap.getString("heightMsg"))); - } - if (valueMap.containsKey("widthMsg")) { - errorDetails.add(new Label("Width problem: " - + valueMap.getString("widthMsg"))); - } - final CheckBox emphasisInUi = new CheckBox("Emphasize component in UI"); - emphasisInUi.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - if (paintable != null) { - Element element2 = paintable.getWidget().getElement(); - Widget.setStyleName(element2, "invalidlayout", - emphasisInUi.getValue()); - } - } - }); - errorDetails.add(emphasisInUi); - errorNode.add(errorDetails); - if (valueMap.containsKey("subErrors")) { - HTML l = new HTML( - "<em>Expand this node to show problems that may be dependent on this problem.</em>"); - errorDetails.add(l); - JsArray<ValueMap> suberrors = valueMap - .getJSValueMapArray("subErrors"); - for (int i = 0; i < suberrors.length(); i++) { - ValueMap value = suberrors.get(i); - printLayoutError(value, errorNode, ac); - } - - } - root.add(errorNode); - } - - @Override - public void log(Throwable e) { - if (e instanceof UmbrellaException) { - UmbrellaException ue = (UmbrellaException) e; - for (Throwable t : ue.getCauses()) { - log(t); - } - return; - } - log(Util.getSimpleName(e) + ": " + e.getMessage()); - GWT.log(e.getMessage(), e); - } - - @Override - public void error(Throwable e) { - handleError(e, this); - } - - static void handleError(Throwable e, Console target) { - if (e instanceof UmbrellaException) { - UmbrellaException ue = (UmbrellaException) e; - for (Throwable t : ue.getCauses()) { - target.error(t); - } - return; - } - String exceptionText = Util.getSimpleName(e); - String message = e.getMessage(); - if (message != null && message.length() != 0) { - exceptionText += ": " + e.getMessage(); - } - target.error(exceptionText); - GWT.log(e.getMessage(), e); - if (!GWT.isProdMode()) { - e.printStackTrace(); - } - try { - Widget owner = null; - - if (!ApplicationConfiguration.getRunningApplications().isEmpty()) { - // Make a wild guess and use the first available - // ApplicationConnection. This is better than than leaving the - // exception completely unstyled... - ApplicationConnection connection = ApplicationConfiguration - .getRunningApplications().get(0); - owner = connection.getUIConnector().getWidget(); - } - VNotification - .createNotification(VNotification.DELAY_FOREVER, owner) - .show("<h1>Uncaught client side exception</h1><br />" - + exceptionText, VNotification.CENTERED, "error"); - } catch (Exception e2) { - // Just swallow this exception - } - } - - @Override - public void init() { - panel = new FlowPanel(); - if (!quietMode) { - DOM.appendChild(getContainerElement(), caption); - setWidget(panel); - caption.setClassName("v-debug-console-caption"); - setStyleName("v-debug-console"); - getElement().getStyle().setZIndex(20000); - getElement().getStyle().setOverflow(Overflow.HIDDEN); - - sinkEvents(Event.ONDBLCLICK); - - sinkEvents(Event.MOUSEEVENTS); - - panel.setStyleName("v-debug-console-content"); - - caption.setInnerHTML("Debug window"); - caption.getStyle().setHeight(25, Unit.PX); - caption.setTitle(help); - - show(); - setToDefaultSizeAndPos(); - - actions = new HorizontalPanel(); - Style style = actions.getElement().getStyle(); - style.setPosition(Position.ABSOLUTE); - style.setBackgroundColor("#666"); - style.setLeft(135, Unit.PX); - style.setHeight(25, Unit.PX); - style.setTop(0, Unit.PX); - - actions.add(clear); - actions.add(restart); - actions.add(forceLayout); - actions.add(analyzeLayout); - actions.add(highlight); - actions.add(connectorStats); - connectorStats.setTitle("Show connector statistics for client"); - highlight - .setTitle("Select a component and print details about it to the server log and client side console."); - actions.add(savePosition); - savePosition - .setTitle("Saves the position and size of debug console to a cookie"); - actions.add(autoScroll); - addDevMode(); - addSuperDevMode(); - - autoScroll - .setTitle("Automatically scroll so that new messages are visible"); - - panel.add(actions); - - panel.add(new HTML("<i>" + help + "</i>")); - - clear.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - int width = panel.getOffsetWidth(); - int height = panel.getOffsetHeight(); - panel = new FlowPanel(); - panel.setPixelSize(width, height); - panel.setStyleName("v-debug-console-content"); - panel.add(actions); - setWidget(panel); - } - }); - - restart.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - - String queryString = Window.Location.getQueryString(); - if (queryString != null - && queryString.contains("restartApplications")) { - Window.Location.reload(); - } else { - String url = Location.getHref(); - String separator = "?"; - if (url.contains("?")) { - separator = "&"; - } - if (!url.contains("restartApplication")) { - url += separator; - url += "restartApplication"; - } - if (!"".equals(Location.getHash())) { - String hash = Location.getHash(); - url = url.replace(hash, "") + hash; - } - Window.Location.replace(url); - } - - } - }); - - forceLayout.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - for (ApplicationConnection applicationConnection : ApplicationConfiguration - .getRunningApplications()) { - applicationConnection.forceLayout(); - } - } - }); - - analyzeLayout.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - List<ApplicationConnection> runningApplications = ApplicationConfiguration - .getRunningApplications(); - for (ApplicationConnection applicationConnection : runningApplications) { - applicationConnection.analyzeLayouts(); - } - } - }); - analyzeLayout - .setTitle("Analyzes currently rendered view and " - + "reports possible common problems in usage of relative sizes." - + "Will cause server visit/rendering of whole screen and loss of" - + " all non committed variables form client side."); - - savePosition.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - String pos = getAbsoluteLeft() + "," + getAbsoluteTop() - + "," + getOffsetWidth() + "," + getOffsetHeight() - + "," + autoScroll.getValue(); - Cookies.setCookie(POS_COOKIE_NAME, pos); - } - }); - - highlight.addClickHandler(new ClickHandler() { - - @Override - public void onClick(ClickEvent event) { - final Label label = new Label("--"); - log("<i>Use mouse to select a component or click ESC to exit highlight mode.</i>"); - panel.add(label); - highlightModeRegistration = Event - .addNativePreviewHandler(new HighlightModeHandler( - label)); - - } - }); - - } - connectorStats.addClickHandler(new ClickHandler() { - - @Override - public void onClick(ClickEvent event) { - for (ApplicationConnection a : ApplicationConfiguration - .getRunningApplications()) { - dumpConnectorInfo(a); - } - } - }); - log("Starting Vaadin client side engine. Widgetset: " - + GWT.getModuleName()); - - log("Widget set is built on version: " + Version.getFullVersion()); - - logToDebugWindow("<div class=\"v-theme-version v-theme-version-" - + Version.getFullVersion().replaceAll("\\.", "_") - + "\">Warning: widgetset version " + Version.getFullVersion() - + " does not seem to match theme version </div>", true); - - } - - private void addSuperDevMode() { - final Storage sessionStorage = Storage.getSessionStorageIfSupported(); - if (sessionStorage == null) { - return; - } - actions.add(superDevMode); - if (Location.getParameter("superdevmode") != null) { - superDevMode.setValue(true); - } - superDevMode.addValueChangeHandler(new ValueChangeHandler<Boolean>() { - - @Override - public void onValueChange(ValueChangeEvent<Boolean> event) { - SuperDevMode.redirect(event.getValue()); - } - - }); - - } - - private void addDevMode() { - actions.add(devMode); - if (Location.getParameter("gwt.codesvr") != null) { - devMode.setValue(true); - } - devMode.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - if (devMode.getValue()) { - addHMParameter(); - } else { - removeHMParameter(); - } - } - - private void addHMParameter() { - UrlBuilder createUrlBuilder = Location.createUrlBuilder(); - createUrlBuilder.setParameter("gwt.codesvr", "localhost:9997"); - Location.assign(createUrlBuilder.buildString()); - } - - private void removeHMParameter() { - UrlBuilder createUrlBuilder = Location.createUrlBuilder(); - createUrlBuilder.removeParameter("gwt.codesvr"); - Location.assign(createUrlBuilder.buildString()); - - } - }); - } - - protected void dumpConnectorInfo(ApplicationConnection a) { - UIConnector root = a.getUIConnector(); - log("================"); - log("Connector hierarchy for Root: " + root.getState().caption + " (" - + root.getConnectorId() + ")"); - Set<ServerConnector> connectorsInHierarchy = new HashSet<ServerConnector>(); - SimpleTree rootHierachy = dumpConnectorHierarchy(root, "", - connectorsInHierarchy); - if (panel.isAttached()) { - rootHierachy.open(true); - panel.add(rootHierachy); - } - - ConnectorMap connectorMap = a.getConnectorMap(); - Collection<? extends ServerConnector> registeredConnectors = connectorMap - .getConnectors(); - log("Sub windows:"); - Set<ServerConnector> subWindowHierarchyConnectors = new HashSet<ServerConnector>(); - for (WindowConnector wc : root.getSubWindows()) { - SimpleTree windowHierachy = dumpConnectorHierarchy(wc, "", - subWindowHierarchyConnectors); - if (panel.isAttached()) { - windowHierachy.open(true); - panel.add(windowHierachy); - } - } - log("Registered connectors not in hierarchy (should be empty):"); - for (ServerConnector registeredConnector : registeredConnectors) { - - if (connectorsInHierarchy.contains(registeredConnector)) { - continue; - } - - if (subWindowHierarchyConnectors.contains(registeredConnector)) { - continue; - } - error(getConnectorString(registeredConnector)); - - } - log("Unregistered connectors in hierarchy (should be empty):"); - for (ServerConnector hierarchyConnector : connectorsInHierarchy) { - if (!connectorMap.hasConnector(hierarchyConnector.getConnectorId())) { - error(getConnectorString(hierarchyConnector)); - } - - } - - log("================"); - - } - - private SimpleTree dumpConnectorHierarchy(final ServerConnector connector, - String indent, Set<ServerConnector> connectors) { - SimpleTree simpleTree = new SimpleTree(getConnectorString(connector)) { - @Override - protected void select(ClickEvent event) { - super.select(event); - if (connector instanceof ComponentConnector) { - VUIDLBrowser.highlight((ComponentConnector) connector); - } - } - }; - simpleTree.addDomHandler(new MouseOutHandler() { - @Override - public void onMouseOut(MouseOutEvent event) { - VUIDLBrowser.deHiglight(); - } - }, MouseOutEvent.getType()); - connectors.add(connector); - - String msg = indent + "* " + getConnectorString(connector); - GWT.log(msg); - consoleLog(msg); - System.out.println(msg); - - for (ServerConnector c : connector.getChildren()) { - simpleTree.add(dumpConnectorHierarchy(c, indent + " ", connectors)); - } - return simpleTree; - } - - private static String getConnectorString(ServerConnector connector) { - return Util.getConnectorString(connector); - } - - @Override - public void setQuietMode(boolean quietDebugMode) { - quietMode = quietDebugMode; - } -} diff --git a/client/src/com/vaadin/client/VErrorMessage.java b/client/src/com/vaadin/client/VErrorMessage.java index a384b451dd..2e42b98a05 100644 --- a/client/src/com/vaadin/client/VErrorMessage.java +++ b/client/src/com/vaadin/client/VErrorMessage.java @@ -31,6 +31,9 @@ public class VErrorMessage extends FlowPanel { public VErrorMessage() { super(); setStyleName(CLASSNAME); + + // Needed for binding with WAI-ARIA attributes + getElement().setId(DOM.createUniqueId()); } /** diff --git a/client/src/com/vaadin/client/VLoadingIndicator.java b/client/src/com/vaadin/client/VLoadingIndicator.java new file mode 100644 index 0000000000..fcce35781d --- /dev/null +++ b/client/src/com/vaadin/client/VLoadingIndicator.java @@ -0,0 +1,292 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.client; + +import com.google.gwt.dom.client.Style.Display; +import com.google.gwt.dom.client.Style.Position; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Timer; + +/** + * Class representing the loading indicator for Vaadin applications. The loading + * indicator has four states: "triggered", "first", "second" and "third". When + * {@link #trigger()} is called the indicator moves to its "triggered" state and + * then transitions from one state to the next when the timeouts specified using + * the set*StateDelay methods occur. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class VLoadingIndicator { + + private static final String PRIMARY_STYLE_NAME = "v-loading-indicator"; + + private ApplicationConnection connection; + + private int firstDelay = 300; + private int secondDelay = 1500; + private int thirdDelay = 5000; + + /** + * Timer with method for checking if it has been cancelled. This class is a + * workaround for a IE8 problem which causes a timer to be fired even if it + * has been cancelled. + * + * @author Vaadin Ltd + * @since 7.1 + */ + private abstract static class LoadingIndicatorTimer extends Timer { + private boolean cancelled = false; + + @Override + public void cancel() { + super.cancel(); + cancelled = true; + } + + @Override + public void schedule(int delayMillis) { + super.schedule(delayMillis); + cancelled = false; + } + + @Override + public void scheduleRepeating(int periodMillis) { + super.scheduleRepeating(periodMillis); + cancelled = false; + } + + /** + * Checks if this timer has been cancelled. + * + * @return true if the timer has been cancelled, false otherwise + */ + public boolean isCancelled() { + return cancelled; + } + } + + private Timer firstTimer = new LoadingIndicatorTimer() { + @Override + public void run() { + if (isCancelled()) { + // IE8 does not properly cancel the timer in all cases. + return; + } + show(); + } + }; + private Timer secondTimer = new LoadingIndicatorTimer() { + @Override + public void run() { + if (isCancelled()) { + // IE8 does not properly cancel the timer in all cases. + return; + } + getElement().setClassName(PRIMARY_STYLE_NAME); + getElement().addClassName("second"); + // For backwards compatibility only + getElement().addClassName(PRIMARY_STYLE_NAME + "-delay"); + } + }; + private Timer thirdTimer = new LoadingIndicatorTimer() { + @Override + public void run() { + if (isCancelled()) { + // IE8 does not properly cancel the timer in all cases. + return; + } + getElement().setClassName(PRIMARY_STYLE_NAME); + getElement().addClassName("third"); + // For backwards compatibility only + getElement().addClassName(PRIMARY_STYLE_NAME + "-wait"); + } + }; + + private Element element; + + /** + * Returns the delay (in ms) which must pass before the loading indicator + * moves into the "first" state and is shown to the user + * + * @return The delay (in ms) until moving into the "first" state. Counted + * from when {@link #trigger()} is called. + */ + public int getFirstDelay() { + return firstDelay; + } + + /** + * Sets the delay (in ms) which must pass before the loading indicator moves + * into the "first" state and is shown to the user + * + * @param firstDelay + * The delay (in ms) until moving into the "first" state. Counted + * from when {@link #trigger()} is called. + */ + public void setFirstDelay(int firstDelay) { + this.firstDelay = firstDelay; + } + + /** + * Returns the delay (in ms) which must pass before the loading indicator + * moves to its "second" state. + * + * @return The delay (in ms) until the loading indicator moves into its + * "second" state. Counted from when {@link #trigger()} is called. + */ + public int getSecondDelay() { + return secondDelay; + } + + /** + * Sets the delay (in ms) which must pass before the loading indicator moves + * to its "second" state. + * + * @param secondDelay + * The delay (in ms) until the loading indicator moves into its + * "second" state. Counted from when {@link #trigger()} is + * called. + */ + public void setSecondDelay(int secondDelay) { + this.secondDelay = secondDelay; + } + + /** + * Returns the delay (in ms) which must pass before the loading indicator + * moves to its "third" state. + * + * @return The delay (in ms) until the loading indicator moves into its + * "third" state. Counted from when {@link #trigger()} is called. + */ + public int getThirdDelay() { + return thirdDelay; + } + + /** + * Sets the delay (in ms) which must pass before the loading indicator moves + * to its "third" state. + * + * @param thirdDelay + * The delay (in ms) from the event until changing the loading + * indicator into its "third" state. Counted from when + * {@link #trigger()} is called. + */ + public void setThirdDelay(int thirdDelay) { + this.thirdDelay = thirdDelay; + } + + /** + * Triggers displaying of this loading indicator. The loading indicator will + * actually be shown by {@link #show()} when the "first" delay (as specified + * by {@link #getFirstDelay()}) has passed. + * <p> + * The loading indicator will be hidden if shown when calling this method. + * </p> + */ + public void trigger() { + hide(); + firstTimer.schedule(getFirstDelay()); + } + + /** + * Shows the loading indicator in its standard state and triggers timers for + * transitioning into the "second" and "third" states. + */ + public void show() { + // Reset possible style name and display mode + getElement().setClassName(PRIMARY_STYLE_NAME); + getElement().addClassName("first"); + getElement().getStyle().setDisplay(Display.BLOCK); + + // Schedule the "second" loading indicator + int secondTimerDelay = getSecondDelay() - getFirstDelay(); + if (secondTimerDelay >= 0) { + secondTimer.schedule(secondTimerDelay); + } + + // Schedule the "third" loading indicator + int thirdTimerDelay = getThirdDelay() - getFirstDelay(); + if (thirdTimerDelay >= 0) { + thirdTimer.schedule(thirdTimerDelay); + } + } + + /** + * Returns the {@link ApplicationConnection} which uses this loading + * indicator + * + * @return The ApplicationConnection for this loading indicator + */ + public ApplicationConnection getConnection() { + return connection; + } + + /** + * Sets the {@link ApplicationConnection} which uses this loading indicator. + * Only used internally. + * + * @param connection + * The ApplicationConnection for this loading indicator + */ + void setConnection(ApplicationConnection connection) { + this.connection = connection; + } + + /** + * Hides the loading indicator (if visible). Cancels any possibly running + * timers. + */ + public void hide() { + firstTimer.cancel(); + secondTimer.cancel(); + thirdTimer.cancel(); + + getElement().getStyle().setDisplay(Display.NONE); + } + + /** + * Returns whether or not the loading indicator is showing. + * + * @return true if the loading indicator is visible, false otherwise + */ + public boolean isVisible() { + if (getElement().getStyle().getDisplay() + .equals(Display.NONE.getCssName())) { + return false; + } + + return true; + } + + /** + * Returns the root element of the loading indicator + * + * @return The loading indicator DOM element + */ + public Element getElement() { + if (element == null) { + element = DOM.createDiv(); + element.getStyle().setPosition(Position.ABSOLUTE); + getConnection().getUIConnector().getWidget().getElement() + .appendChild(element); + } + return element; + } + +} diff --git a/client/src/com/vaadin/client/VTooltip.java b/client/src/com/vaadin/client/VTooltip.java index 759b90a8cd..6191821988 100644 --- a/client/src/com/vaadin/client/VTooltip.java +++ b/client/src/com/vaadin/client/VTooltip.java @@ -15,8 +15,15 @@ */ package com.vaadin.client; +import com.google.gwt.aria.client.Id; +import com.google.gwt.aria.client.Roles; +import com.google.gwt.event.dom.client.BlurEvent; +import com.google.gwt.event.dom.client.BlurHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.dom.client.DomEvent; +import com.google.gwt.event.dom.client.FocusEvent; +import com.google.gwt.event.dom.client.FocusHandler; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyDownHandler; import com.google.gwt.event.dom.client.MouseMoveEvent; @@ -40,11 +47,6 @@ public class VTooltip extends VOverlay { public static final int TOOLTIP_EVENTS = Event.ONKEYDOWN | Event.ONMOUSEOVER | Event.ONMOUSEOUT | Event.ONMOUSEMOVE | Event.ONCLICK; - protected static final int MAX_WIDTH = 500; - private static final int QUICK_OPEN_TIMEOUT = 1000; - private static final int CLOSE_TIMEOUT = 300; - private static final int OPEN_DELAY = 750; - private static final int QUICK_OPEN_DELAY = 100; VErrorMessage em = new VErrorMessage(); Element description = DOM.createDiv(); @@ -54,6 +56,16 @@ public class VTooltip extends VOverlay { // Open next tooltip faster. Disabled after 2 sec of showTooltip-silence. private boolean justClosed = false; + private String uniqueId = DOM.createUniqueId(); + private Element layoutElement; + private int maxWidth; + + // Delays for the tooltip, configurable on the server side + private int openDelay; + private int quickOpenDelay; + private int quickOpenTimeout; + private int closeTimeout; + /** * Used to show tooltips; usually used via the singleton in * {@link ApplicationConnection}. NOTE that #setOwner(Widget)} should be @@ -68,8 +80,17 @@ public class VTooltip extends VOverlay { setWidget(layout); layout.add(em); DOM.setElementProperty(description, "className", CLASSNAME + "-text"); - DOM.appendChild(layout.getElement(), description); + + layoutElement = layout.getElement(); + DOM.appendChild(layoutElement, description); setSinkShadowEvents(true); + + // Used to bind the tooltip to the owner for assistive devices + layoutElement.setId(uniqueId); + + description.setId(DOM.createUniqueId()); + Roles.getTooltipRole().set(layoutElement); + Roles.getTooltipRole().setAriaHiddenState(layoutElement, true); } /** @@ -104,8 +125,8 @@ public class VTooltip extends VOverlay { @Override public void setPosition(int offsetWidth, int offsetHeight) { - if (offsetWidth > MAX_WIDTH) { - setWidth(MAX_WIDTH + "px"); + if (offsetWidth > getMaxWidth()) { + setWidth(getMaxWidth() + "px"); // Check new height and width with reflowed content offsetWidth = getOffsetWidth(); @@ -150,7 +171,7 @@ public class VTooltip extends VOverlay { // Schedule timer for showing the tooltip according to if it was // recently closed or not. - int timeout = justClosed ? QUICK_OPEN_DELAY : OPEN_DELAY; + int timeout = justClosed ? getQuickOpenDelay() : getOpenDelay(); showTimer.schedule(timeout); opening = true; } @@ -200,19 +221,31 @@ public class VTooltip extends VOverlay { // already about to close return; } - closeTimer.schedule(CLOSE_TIMEOUT); + closeTimer.schedule(getCloseTimeout()); closing = true; justClosed = true; - justClosedTimer.schedule(QUICK_OPEN_TIMEOUT); + justClosedTimer.schedule(getQuickOpenTimeout()); + } + @Override + public void hide() { + super.hide(); + Roles.getTooltipRole().setAriaHiddenState(layoutElement, true); + Roles.getTooltipRole().removeAriaDescribedbyProperty( + tooltipEventHandler.currentElement); } private int tooltipEventMouseX; private int tooltipEventMouseY; - public void updatePosition(Event event) { - tooltipEventMouseX = DOM.eventGetClientX(event); - tooltipEventMouseY = DOM.eventGetClientY(event); + public void updatePosition(Event event, boolean isFocused) { + if (isFocused) { + tooltipEventMouseX = -1000; + tooltipEventMouseY = -1000; + } else { + tooltipEventMouseX = DOM.eventGetClientX(event); + tooltipEventMouseY = DOM.eventGetClientY(event); + } } @Override @@ -246,7 +279,7 @@ public class VTooltip extends VOverlay { } private class TooltipEventHandler implements MouseMoveHandler, - ClickHandler, KeyDownHandler { + ClickHandler, KeyDownHandler, FocusHandler, BlurHandler { /** * Current element hovered @@ -254,6 +287,11 @@ public class VTooltip extends VOverlay { private com.google.gwt.dom.client.Element currentElement = null; /** + * Current element focused + */ + private boolean currentIsFocused; + + /** * Current tooltip active */ private TooltipInfo currentTooltipInfo = null; @@ -299,6 +337,9 @@ public class VTooltip extends VOverlay { } if (connector != null && info != null) { + assert connector.hasTooltip() : "getTooltipInfo for " + + Util.getConnectorString(connector) + + " returned a tooltip even though hasTooltip claims there are no tooltips for the connector."; currentTooltipInfo = info; return true; } @@ -319,41 +360,77 @@ public class VTooltip extends VOverlay { @Override public void onMouseMove(MouseMoveEvent mme) { - Event event = Event.as(mme.getNativeEvent()); + handleShowHide(mme, false); + } + + @Override + public void onClick(ClickEvent event) { + handleHideEvent(); + } + + @Override + public void onKeyDown(KeyDownEvent event) { + handleHideEvent(); + } + + /** + * Displays Tooltip when page is navigated with the keyboard. + * + * Tooltip is not visible. This makes it possible for assistive devices + * to recognize the tooltip. + */ + @Override + public void onFocus(FocusEvent fe) { + handleShowHide(fe, true); + } + + /** + * Hides Tooltip when the page is navigated with the keyboard. + * + * Removes the Tooltip from page to make sure assistive devices don't + * recognize it by accident. + */ + @Override + public void onBlur(BlurEvent be) { + handleHideEvent(); + } + + private void handleShowHide(DomEvent domEvent, boolean isFocused) { + Event event = Event.as(domEvent.getNativeEvent()); com.google.gwt.dom.client.Element element = Element.as(event .getEventTarget()); // We can ignore move event if it's handled by move or over already - if (currentElement == element) { + if (currentElement == element && currentIsFocused == isFocused) { return; } - currentElement = element; boolean connectorAndTooltipFound = resolveConnector((com.google.gwt.user.client.Element) element); if (!connectorAndTooltipFound) { if (isShowing()) { handleHideEvent(); + Roles.getButtonRole() + .removeAriaDescribedbyProperty(element); } else { currentTooltipInfo = null; } } else { - updatePosition(event); + updatePosition(event, isFocused); + if (isShowing()) { replaceCurrentTooltip(); + Roles.getTooltipRole().removeAriaDescribedbyProperty( + currentElement); } else { showTooltip(); } - } - } - @Override - public void onClick(ClickEvent event) { - handleHideEvent(); - } + Roles.getTooltipRole().setAriaDescribedbyProperty(element, + Id.of(uniqueId)); + } - @Override - public void onKeyDown(KeyDownEvent event) { - handleHideEvent(); + currentIsFocused = isFocused; + currentElement = element; } } @@ -370,6 +447,141 @@ public class VTooltip extends VOverlay { widget.addDomHandler(tooltipEventHandler, MouseMoveEvent.getType()); widget.addDomHandler(tooltipEventHandler, ClickEvent.getType()); widget.addDomHandler(tooltipEventHandler, KeyDownEvent.getType()); + widget.addDomHandler(tooltipEventHandler, FocusEvent.getType()); + widget.addDomHandler(tooltipEventHandler, BlurEvent.getType()); Profiler.leave("VTooltip.connectHandlersToWidget"); } + + /** + * Returns the unique id of the tooltip element. + * + * @return String containing the unique id of the tooltip, which always has + * a value + */ + public String getUniqueId() { + return uniqueId; + } + + @Override + public void setPopupPositionAndShow(PositionCallback callback) { + super.setPopupPositionAndShow(callback); + + Roles.getTooltipRole().setAriaHiddenState(layoutElement, false); + } + + /** + * Returns the time (in ms) the tooltip should be displayed after an event + * that will cause it to be closed (e.g. mouse click outside the component, + * key down). + * + * @return The close timeout (in ms) + */ + public int getCloseTimeout() { + return closeTimeout; + } + + /** + * Sets the time (in ms) the tooltip should be displayed after an event that + * will cause it to be closed (e.g. mouse click outside the component, key + * down). + * + * @param closeTimeout + * The close timeout (in ms) + */ + public void setCloseTimeout(int closeTimeout) { + this.closeTimeout = closeTimeout; + } + + /** + * Returns the time (in ms) during which {@link #getQuickOpenDelay()} should + * be used instead of {@link #getOpenDelay()}. The quick open delay is used + * when the tooltip has very recently been shown, is currently hidden but + * about to be shown again. + * + * @return The quick open timeout (in ms) + */ + public int getQuickOpenTimeout() { + return quickOpenTimeout; + } + + /** + * Sets the time (in ms) that determines when {@link #getQuickOpenDelay()} + * should be used instead of {@link #getOpenDelay()}. The quick open delay + * is used when the tooltip has very recently been shown, is currently + * hidden but about to be shown again. + * + * @param quickOpenTimeout + * The quick open timeout (in ms) + */ + public void setQuickOpenTimeout(int quickOpenTimeout) { + this.quickOpenTimeout = quickOpenTimeout; + } + + /** + * Returns the time (in ms) that should elapse before a tooltip will be + * shown, in the situation when a tooltip has very recently been shown + * (within {@link #getQuickOpenDelay()} ms). + * + * @return The quick open delay (in ms) + */ + public int getQuickOpenDelay() { + return quickOpenDelay; + } + + /** + * Sets the time (in ms) that should elapse before a tooltip will be shown, + * in the situation when a tooltip has very recently been shown (within + * {@link #getQuickOpenDelay()} ms). + * + * @param quickOpenDelay + * The quick open delay (in ms) + */ + public void setQuickOpenDelay(int quickOpenDelay) { + this.quickOpenDelay = quickOpenDelay; + } + + /** + * Returns the time (in ms) that should elapse after an event triggering + * tooltip showing has occurred (e.g. mouse over) before the tooltip is + * shown. If a tooltip has recently been shown, then + * {@link #getQuickOpenDelay()} is used instead of this. + * + * @return The open delay (in ms) + */ + public int getOpenDelay() { + return openDelay; + } + + /** + * Sets the time (in ms) that should elapse after an event triggering + * tooltip showing has occurred (e.g. mouse over) before the tooltip is + * shown. If a tooltip has recently been shown, then + * {@link #getQuickOpenDelay()} is used instead of this. + * + * @param openDelay + * The open delay (in ms) + */ + public void setOpenDelay(int openDelay) { + this.openDelay = openDelay; + } + + /** + * Sets the maximum width of the tooltip popup. + * + * @param maxWidth + * The maximum width the tooltip popup (in pixels) + */ + public void setMaxWidth(int maxWidth) { + this.maxWidth = maxWidth; + } + + /** + * Returns the maximum width of the tooltip popup. + * + * @return The maximum width the tooltip popup (in pixels) + */ + public int getMaxWidth() { + return maxWidth; + } + } diff --git a/client/src/com/vaadin/client/VUIDLBrowser.java b/client/src/com/vaadin/client/VUIDLBrowser.java index fded37ec5c..e6f38fb167 100644 --- a/client/src/com/vaadin/client/VUIDLBrowser.java +++ b/client/src/com/vaadin/client/VUIDLBrowser.java @@ -42,8 +42,12 @@ import com.vaadin.client.ui.UnknownComponentConnector; import com.vaadin.client.ui.VWindow; /** - * TODO Rename to something more Vaadin7-ish? + * @author Vaadin Ltd + * + * @deprecated as of 7.1. This class was mainly used by the old debug console + * but is retained for now for backwards compatibility. */ +@Deprecated public class VUIDLBrowser extends SimpleTree { private static final String HELP = "Shift click handle to open recursively. " + " Click components to highlight them on client side." diff --git a/client/src/com/vaadin/client/ValueMap.java b/client/src/com/vaadin/client/ValueMap.java index 5157bc91f5..4141eaa9d6 100644 --- a/client/src/com/vaadin/client/ValueMap.java +++ b/client/src/com/vaadin/client/ValueMap.java @@ -70,12 +70,12 @@ public final class ValueMap extends JavaScriptObject { return attrs; } - native JsArrayString getJSStringArray(String name) + public native JsArrayString getJSStringArray(String name) /*-{ return this[name]; }-*/; - native JsArray<ValueMap> getJSValueMapArray(String name) + public native JsArray<ValueMap> getJSValueMapArray(String name) /*-{ return this[name]; }-*/; diff --git a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java new file mode 100644 index 0000000000..bc7e0b3fd2 --- /dev/null +++ b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java @@ -0,0 +1,453 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.client.communication; + +import java.util.ArrayList; + +import com.google.gwt.core.client.JavaScriptObject; +import com.google.gwt.core.client.Scheduler; +import com.google.gwt.user.client.Command; +import com.vaadin.client.ApplicationConnection; +import com.vaadin.client.ResourceLoader; +import com.vaadin.client.ResourceLoader.ResourceLoadEvent; +import com.vaadin.client.ResourceLoader.ResourceLoadListener; +import com.vaadin.client.VConsole; +import com.vaadin.shared.ApplicationConstants; +import com.vaadin.shared.ui.ui.UIConstants; + +/** + * The default {@link PushConnection} implementation that uses Atmosphere for + * handling the communication channel. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class AtmospherePushConnection implements PushConnection { + + protected enum State { + /** + * Opening request has been sent, but still waiting for confirmation + */ + CONNECT_PENDING, + + /** + * Connection is open and ready to use. + */ + CONNECTED, + + /** + * Connection was disconnected while the connection was pending. Wait + * for the connection to get established before closing it. No new + * messages are accepted, but pending messages will still be delivered. + */ + DISCONNECT_PENDING, + + /** + * Connection has been disconnected and should not be used any more. + */ + DISCONNECTED; + } + + /** + * Represents a message that should be sent as multiple fragments. + */ + protected static class FragmentedMessage { + + // Jetty requires length less than buffer size + private int FRAGMENT_LENGTH = ApplicationConstants.WEBSOCKET_BUFFER_SIZE - 1; + + private String message; + private int index = 0; + + public FragmentedMessage(String message) { + this.message = message; + } + + public boolean hasNextFragment() { + return index < message.length(); + } + + public String getNextFragment() { + String result; + if (index == 0) { + String header = "" + message.length() + + ApplicationConstants.WEBSOCKET_MESSAGE_DELIMITER; + int fragmentLen = FRAGMENT_LENGTH - header.length(); + result = header + getFragment(0, fragmentLen); + index += fragmentLen; + } else { + result = getFragment(index, index + FRAGMENT_LENGTH); + index += FRAGMENT_LENGTH; + } + return result; + } + + private String getFragment(int begin, int end) { + return message.substring(begin, Math.min(message.length(), end)); + } + } + + private ApplicationConnection connection; + + private JavaScriptObject socket; + + private ArrayList<String> messageQueue = new ArrayList<String>(); + + private State state = State.CONNECT_PENDING; + + private AtmosphereConfiguration config; + + private String uri; + + private String transport; + + /** + * Keeps track of the disconnect confirmation command for cases where + * pending messages should be pushed before actually disconnecting. + */ + private Command pendingDisconnectCommand; + + public AtmospherePushConnection() { + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.client.communication.PushConenction#init(com.vaadin.client + * .ApplicationConnection) + */ + @Override + public void init(final ApplicationConnection connection) { + this.connection = connection; + + runWhenAtmosphereLoaded(new Command() { + @Override + public void execute() { + Scheduler.get().scheduleDeferred(new Command() { + @Override + public void execute() { + connect(); + } + }); + } + }); + } + + private void connect() { + String baseUrl = connection + .translateVaadinUri(ApplicationConstants.APP_PROTOCOL_PREFIX + + ApplicationConstants.PUSH_PATH + '/'); + String extraParams = UIConstants.UI_ID_PARAMETER + "=" + + connection.getConfiguration().getUIId(); + extraParams += "&" + ApplicationConstants.CSRF_TOKEN_PARAMETER + "=" + + connection.getCsrfToken(); + + // uri is needed to identify the right connection when closing + uri = ApplicationConnection.addGetParameters(baseUrl, extraParams); + + VConsole.log("Establishing push connection"); + socket = doConnect(uri, getConfig()); + } + + @Override + public boolean isActive() { + switch (state) { + case CONNECT_PENDING: + case CONNECTED: + return true; + default: + return false; + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.client.communication.PushConenction#push(java.lang.String) + */ + @Override + public void push(String message) { + switch (state) { + case CONNECT_PENDING: + assert isActive(); + VConsole.log("Queuing push message: " + message); + messageQueue.add(message); + break; + case CONNECTED: + assert isActive(); + VConsole.log("Sending push message: " + message); + + if (transport.equals("websocket")) { + FragmentedMessage fragmented = new FragmentedMessage(message); + while (fragmented.hasNextFragment()) { + doPush(socket, fragmented.getNextFragment()); + } + } else { + doPush(socket, message); + } + break; + case DISCONNECT_PENDING: + case DISCONNECTED: + throw new IllegalStateException("Can not push after disconnecting"); + } + } + + protected AtmosphereConfiguration getConfig() { + if (config == null) { + config = createConfig(); + } + return config; + } + + protected void onOpen(AtmosphereResponse response) { + transport = response.getTransport(); + + VConsole.log("Push connection established using " + transport); + + switch (state) { + case CONNECT_PENDING: + state = State.CONNECTED; + for (String message : messageQueue) { + push(message); + } + messageQueue.clear(); + break; + case DISCONNECT_PENDING: + // Set state to connected to make disconnect close the connection + state = State.CONNECTED; + assert pendingDisconnectCommand != null; + disconnect(pendingDisconnectCommand); + break; + case CONNECTED: + // IE likes to open the same connection multiple times, just ignore + break; + default: + throw new IllegalStateException( + "Got onOpen event when conncetion state is " + state + + ". This should never happen."); + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.client.communication.PushConenction#disconnect() + */ + @Override + public void disconnect(Command command) { + assert command != null; + + switch (state) { + case CONNECT_PENDING: + // Make the connection callback initiate the disconnection again + state = State.DISCONNECT_PENDING; + pendingDisconnectCommand = command; + break; + case CONNECTED: + // Normal disconnect + VConsole.log("Closing push connection"); + doDisconnect(uri); + state = State.DISCONNECTED; + command.execute(); + break; + case DISCONNECT_PENDING: + case DISCONNECTED: + throw new IllegalStateException("Can not disconnect more than once"); + } + } + + protected void onMessage(AtmosphereResponse response) { + String message = response.getResponseBody(); + if (message.startsWith("for(;;);")) { + VConsole.log("Received push message: " + message); + // "for(;;);[{json}]" -> "{json}" + message = message.substring(9, message.length() - 1); + connection.handlePushMessage(message); + } + } + + /** + * Called if the transport mechanism cannot be used and the fallback will be + * tried + */ + protected void onTransportFailure() { + VConsole.log("Push connection using primary method (" + + getConfig().getTransport() + ") failed. Trying with " + + getConfig().getFallbackTransport()); + } + + /** + * Called if the push connection fails. Atmosphere will automatically retry + * the connection until successful. + * + */ + protected void onError() { + VConsole.error("Push connection using " + getConfig().getTransport() + + " failed!"); + } + + public static abstract class AbstractJSO extends JavaScriptObject { + protected AbstractJSO() { + + } + + protected final native String getStringValue(String key) + /*-{ + return this[key]; + }-*/; + + protected final native void setStringValue(String key, String value) + /*-{ + this[key] = value; + }-*/; + + protected final native int getIntValue(String key) + /*-{ + return this[key]; + }-*/; + + protected final native void setIntValue(String key, int value) + /*-{ + this[key] = value; + }-*/; + + } + + public static class AtmosphereConfiguration extends AbstractJSO { + + protected AtmosphereConfiguration() { + super(); + } + + public final String getTransport() { + return getStringValue("transport"); + } + + public final String getFallbackTransport() { + return getStringValue("fallbackTransport"); + } + + public final void setTransport(String transport) { + setStringValue("transport", transport); + } + + public final void setFallbackTransport(String fallbackTransport) { + setStringValue("fallbackTransport", fallbackTransport); + } + } + + public static class AtmosphereResponse extends AbstractJSO { + + protected AtmosphereResponse() { + + } + + public final String getResponseBody() { + return getStringValue("responseBody"); + } + + public final String getState() { + return getStringValue("state"); + } + + public final String getError() { + return getStringValue("error"); + } + + public final String getTransport() { + return getStringValue("transport"); + } + + } + + protected native AtmosphereConfiguration createConfig() + /*-{ + return { + transport: 'websocket', + fallbackTransport: 'streaming', + contentType: 'application/json; charset=UTF-8', + reconnectInterval: '5000', + maxReconnectOnClose: 10000000, + trackMessageLength: true + }; + }-*/; + + private native JavaScriptObject doConnect(String uri, + JavaScriptObject config) + /*-{ + var self = this; + + config.url = uri; + config.onOpen = $entry(function(response) { + self.@com.vaadin.client.communication.AtmospherePushConnection::onOpen(*)(response); + }); + config.onMessage = $entry(function(response) { + self.@com.vaadin.client.communication.AtmospherePushConnection::onMessage(*)(response); + }); + config.onError = $entry(function(response) { + self.@com.vaadin.client.communication.AtmospherePushConnection::onError()(response); + }); + config.onTransportFailure = $entry(function(reason,request) { + self.@com.vaadin.client.communication.AtmospherePushConnection::onTransportFailure(*)(reason); + }); + + return $wnd.jQueryVaadin.atmosphere.subscribe(config); + }-*/; + + private native void doPush(JavaScriptObject socket, String message) + /*-{ + socket.push(message); + }-*/; + + private static native void doDisconnect(String url) + /*-{ + $wnd.jQueryVaadin.atmosphere.unsubscribeUrl(url); + }-*/; + + private static native boolean isAtmosphereLoaded() + /*-{ + return $wnd.jQueryVaadin != undefined; + }-*/; + + private void runWhenAtmosphereLoaded(final Command command) { + + if (isAtmosphereLoaded()) { + command.execute(); + } else { + VConsole.log("Loading " + ApplicationConstants.VAADIN_PUSH_JS); + ResourceLoader.get().loadScript( + connection.getConfiguration().getVaadinDirUrl() + + ApplicationConstants.VAADIN_PUSH_JS, + new ResourceLoadListener() { + @Override + public void onLoad(ResourceLoadEvent event) { + VConsole.log(ApplicationConstants.VAADIN_PUSH_JS + + " loaded"); + command.execute(); + } + + @Override + public void onError(ResourceLoadEvent event) { + VConsole.error(event.getResourceUrl() + + " could not be loaded. Push will not work."); + } + }); + } + } +} diff --git a/client/src/com/vaadin/client/communication/PushConnection.java b/client/src/com/vaadin/client/communication/PushConnection.java new file mode 100644 index 0000000000..61656242bd --- /dev/null +++ b/client/src/com/vaadin/client/communication/PushConnection.java @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.client.communication; + +import com.google.gwt.user.client.Command; +import com.vaadin.client.ApplicationConnection; + +/** + * Represents the client-side endpoint of a bidirectional ("push") communication + * channel. Can be used to send UIDL request messages to the server and to + * receive UIDL messages from the server (either asynchronously or as a response + * to a UIDL request.) Delegates the UIDL handling to the + * {@link ApplicationConnection}. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public interface PushConnection { + + /** + * Two-phase construction to allow using GWT.create(). + * + * @param connection + * The ApplicationConnection + */ + public void init(ApplicationConnection connection); + + /** + * Pushes a message to the server. Will throw an exception if the connection + * is not active (see {@link #isActive()}). + * <p> + * Implementation detail: The implementation is responsible for queuing + * messages that are pushed after {@link #init(ApplicationConnection)} has + * been called but before the connection has internally been set up and then + * replay those messages in the original order when the connection has been + * established. + * + * @param message + * the message to push + * @throws IllegalStateException + * if this connection is not active + * + * @see #isActive() + */ + public void push(String message); + + /** + * Checks whether this push connection is in a state where it can push + * messages to the server. A connection is active until + * {@link #disconnect(Command)} has been called. + * + * @return <code>true</code> if this connection can accept new messages; + * <code>false</code> if this connection is disconnected or + * disconnecting. + */ + public boolean isActive(); + + /** + * Closes the push connection. To ensure correct message delivery order, new + * messages should not be sent using any other channel until it has been + * confirmed that all messages pending for this connection have been + * delivered. The provided command callback is invoked when messages can be + * passed using some other communication channel. + * <p> + * After this method has been called, {@link #isActive()} returns + * <code>false</code>. Calling this method for a connection that is no + * longer active will throw an exception. + * + * @param command + * callback command invoked when the connection has been properly + * disconnected + * @throws IllegalStateException + * if this connection is not active + */ + public void disconnect(Command command); + +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/debug/internal/DebugButton.java b/client/src/com/vaadin/client/debug/internal/DebugButton.java new file mode 100644 index 0000000000..a49a392fbe --- /dev/null +++ b/client/src/com/vaadin/client/debug/internal/DebugButton.java @@ -0,0 +1,109 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.debug.internal; + +import com.google.gwt.user.client.ui.Button; + +/** + * Simple extension of {@link Button} that is preconfigured with for use in + * {@link VDebugWindow}. Uses icon-font for icons, and allows title to be + * specified in the constructor. + * + * @since 7.1 + * @author Vaadin Ltd + */ +public class DebugButton extends Button { + + protected boolean active = false; + + /** + * Creates a {@link Button} with the given icon-font icon. The icon id will + * be used in the <i>data-icon</i> attribute of an <i><i></i> -tag. + * + * @param icon + * Identifier for the desired icon in an icon-font + */ + public DebugButton(Icon icon) { + this(icon, null, null); + } + + /*- + public DebugButton(String caption) { + this(null, null, caption); + } + + public DebugButton(String caption, String title) { + this(null, title, caption); + } + -*/ + + /** + * Creates a {@link Button} with the given icon-font icon and title + * (tooltip). The icon id will be used in the <i>data-icon</i> attribute of + * an <i><i></i> -tag. + * + * @param icon + * Identifier for the desired icon in an icon-font + * @param title + * Button title (tooltip) + * + */ + public DebugButton(Icon icon, String title) { + this(icon, title, null); + } + + /** + * Creates a {@link Button} with the given icon-font icon, title (tooltip), + * and caption. The icon id will be used in the <i>data-icon</i> attribute + * of an <i><i></i> -tag. + * + * @param icon + * Identifier for the desired icon in an icon-font + * @param title + * Title (tooltip) + * @param caption + * Button baption + */ + public DebugButton(Icon icon, String title, String caption) { + super((icon != null ? icon : "") + (caption != null ? caption : "")); + if (title != null) { + setTitle(title); + } + + setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON); + } + + /** + * Adds or removes a stylename, indicating whether or not the button is in + * it's active state. + * + * @param active + */ + public void setActive(boolean active) { + this.active = active; + setStyleDependentName(VDebugWindow.STYLENAME_ACTIVE, active); + } + + /** + * Indicates wheter the Button is currently in its active state or not + * + * @return true if the Button is active, false otherwise + */ + public boolean isActive() { + return active; + } + +} diff --git a/client/src/com/vaadin/client/debug/internal/ErrorNotificationHandler.java b/client/src/com/vaadin/client/debug/internal/ErrorNotificationHandler.java new file mode 100644 index 0000000000..0e4c57494b --- /dev/null +++ b/client/src/com/vaadin/client/debug/internal/ErrorNotificationHandler.java @@ -0,0 +1,86 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.client.debug.internal; + +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; + +import com.google.gwt.logging.client.TextLogFormatter; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.ApplicationConfiguration; +import com.vaadin.client.ApplicationConnection; +import com.vaadin.client.ui.VNotification; + +/** + * Log message handler that shows big red notification for {@link Level#SEVERE} + * messages that have a throwable. + * + * @since 7.1 + * @author Vaadin Ltd + */ +public class ErrorNotificationHandler extends Handler { + public ErrorNotificationHandler() { + setLevel(Level.SEVERE); + setFormatter(new TextLogFormatter(true) { + @Override + protected String getRecordInfo(LogRecord event, String newline) { + return ""; + } + }); + } + + @Override + public void publish(LogRecord record) { + if (!isLoggable(record) || record.getThrown() == null) { + return; + } + + try { + String exceptionText = getFormatter().format(record); + + Widget owner = null; + + if (!ApplicationConfiguration.getRunningApplications().isEmpty()) { + /* + * Make a wild guess and use the first available + * ApplicationConnection. This is better than than leaving the + * exception completely unstyled... + */ + ApplicationConnection connection = ApplicationConfiguration + .getRunningApplications().get(0); + owner = connection.getUIConnector().getWidget(); + } + VNotification + .createNotification(VNotification.DELAY_FOREVER, owner) + .show("<h1>Uncaught client side exception</h1><br />" + + exceptionText, VNotification.CENTERED, "error"); + } catch (Exception e2) { + // Just swallow this exception + } + } + + @Override + public void close() { + // Nothing to do + } + + @Override + public void flush() { + // Nothing todo + } +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/debug/internal/HierarchySection.java b/client/src/com/vaadin/client/debug/internal/HierarchySection.java new file mode 100644 index 0000000000..e4f3e2dcb1 --- /dev/null +++ b/client/src/com/vaadin/client/debug/internal/HierarchySection.java @@ -0,0 +1,676 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.debug.internal; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.google.gwt.core.client.JsArray; +import com.google.gwt.dom.client.Style.TextDecoration; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.dom.client.DoubleClickEvent; +import com.google.gwt.event.dom.client.DoubleClickHandler; +import com.google.gwt.event.dom.client.HasDoubleClickHandlers; +import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.event.dom.client.MouseOutEvent; +import com.google.gwt.event.dom.client.MouseOutHandler; +import com.google.gwt.event.dom.client.MouseOverEvent; +import com.google.gwt.event.dom.client.MouseOverHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.Event.NativePreviewEvent; +import com.google.gwt.user.client.Event.NativePreviewHandler; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.RootPanel; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.ApplicationConfiguration; +import com.vaadin.client.ApplicationConnection; +import com.vaadin.client.ComponentConnector; +import com.vaadin.client.ComputedStyle; +import com.vaadin.client.ConnectorMap; +import com.vaadin.client.JsArrayObject; +import com.vaadin.client.ServerConnector; +import com.vaadin.client.SimpleTree; +import com.vaadin.client.Util; +import com.vaadin.client.VConsole; +import com.vaadin.client.ValueMap; +import com.vaadin.client.metadata.NoDataException; +import com.vaadin.client.metadata.Property; +import com.vaadin.client.ui.AbstractConnector; +import com.vaadin.client.ui.UnknownComponentConnector; +import com.vaadin.shared.AbstractComponentState; +import com.vaadin.shared.communication.SharedState; + +/** + * Provides functionality for examining the UI component hierarchy. + * + * @since 7.1 + * @author Vaadin Ltd + */ +public class HierarchySection implements Section { + private final DebugButton tabButton = new DebugButton(Icon.HIERARCHY, + "Examine component hierarchy"); + + private final FlowPanel content = new FlowPanel(); + private final FlowPanel controls = new FlowPanel(); + + private final Button find = new DebugButton(Icon.HIGHLIGHT, + "Select a component on the page to inspect it"); + private final Button analyze = new DebugButton(Icon.ANALYZE, + "Check layouts for potential problems"); + private final Button generateWS = new DebugButton(Icon.OPTIMIZE, + "Show used connectors and how to optimize widgetset"); + private final Button showHierarchy = new DebugButton(Icon.HIERARCHY, + "Show the connector hierarchy tree"); + + private HandlerRegistration highlightModeRegistration = null; + + public HierarchySection() { + controls.add(showHierarchy); + showHierarchy.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON); + showHierarchy.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + showHierarchy(); + } + }); + + controls.add(find); + find.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON); + find.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + toggleFind(); + } + }); + + controls.add(analyze); + analyze.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON); + analyze.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + stopFind(); + analyzeLayouts(); + } + }); + + controls.add(generateWS); + generateWS.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON); + generateWS.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + generateWidgetset(); + } + }); + + content.setStylePrimaryName(VDebugWindow.STYLENAME + "-hierarchy"); + } + + private void showHierarchy() { + Highlight.hideAll(); + content.clear(); + + // TODO Clearing and rebuilding the contents is not optimal for UX as + // any previous expansions are lost. + SimplePanel trees = new SimplePanel(); + + for (ApplicationConnection application : ApplicationConfiguration + .getRunningApplications()) { + ServerConnector uiConnector = application.getUIConnector(); + Widget connectorTree = buildConnectorTree(uiConnector); + + trees.add(connectorTree); + } + + content.add(trees); + } + + private Widget buildConnectorTree(final ServerConnector connector) { + String connectorString = Util.getConnectorString(connector); + + List<ServerConnector> children = connector.getChildren(); + + Widget widget; + if (children == null || children.isEmpty()) { + // Leaf node, just add a label + Label label = new Label(connectorString); + label.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + Highlight.showOnly(connector); + } + }); + widget = label; + } else { + SimpleTree tree = new SimpleTree(connectorString) { + @Override + protected void select(ClickEvent event) { + super.select(event); + Highlight.showOnly(connector); + } + }; + for (ServerConnector child : children) { + tree.add(buildConnectorTree(child)); + } + widget = tree; + } + + if (widget instanceof HasDoubleClickHandlers) { + HasDoubleClickHandlers has = (HasDoubleClickHandlers) widget; + has.addDoubleClickHandler(new DoubleClickHandler() { + @Override + public void onDoubleClick(DoubleClickEvent event) { + printState(connector); + } + }); + } + + return widget; + } + + @Override + public DebugButton getTabButton() { + return tabButton; + } + + @Override + public Widget getControls() { + return controls; + } + + @Override + public Widget getContent() { + return content; + } + + @Override + public void show() { + + } + + @Override + public void hide() { + stopFind(); + } + + private void generateWidgetset() { + + content.clear(); + HTML h = new HTML("Getting used connectors"); + content.add(h); + + String s = ""; + for (ApplicationConnection ac : ApplicationConfiguration + .getRunningApplications()) { + ApplicationConfiguration conf = ac.getConfiguration(); + s += "<h1>Used connectors for " + conf.getServiceUrl() + "</h1>"; + + for (String connectorName : getUsedConnectorNames(conf)) { + s += connectorName + "<br/>"; + } + + s += "<h2>To make an optimized widgetset based on these connectors, do:</h2>"; + s += "<h3>1. Add to your widgetset.gwt.xml file:</h2>"; + s += "<textarea rows=\"3\" style=\"width:90%\">"; + s += "<generate-with class=\"OptimizedConnectorBundleLoaderFactory\">\n"; + s += " <when-type-assignable class=\"com.vaadin.client.metadata.ConnectorBundleLoader\" />\n"; + s += "</generate-with>"; + s += "</textarea>"; + + s += "<h3>2. Add the following java file to your project:</h2>"; + s += "<textarea rows=\"5\" style=\"width:90%\">"; + s += generateOptimizedWidgetSet(getUsedConnectorNames(conf)); + s += "</textarea>"; + s += "<h3>3. Recompile widgetset</h2>"; + + } + + h.setHTML(s); + } + + private Set<String> getUsedConnectorNames( + ApplicationConfiguration configuration) { + int tag = 0; + Set<String> usedConnectors = new HashSet<String>(); + while (true) { + String serverSideClass = configuration + .getServerSideClassNameForTag(tag); + if (serverSideClass == null) { + break; + } + Class<? extends ServerConnector> connectorClass = configuration + .getConnectorClassByEncodedTag(tag); + if (connectorClass == null) { + break; + } + + if (connectorClass != UnknownComponentConnector.class) { + usedConnectors.add(connectorClass.getName()); + } + tag++; + if (tag > 10000) { + // Sanity check + VConsole.error("Search for used connector classes was forcefully terminated"); + break; + } + } + return usedConnectors; + } + + public String generateOptimizedWidgetSet(Set<String> usedConnectors) { + String s = "import java.util.HashSet;\n"; + s += "import java.util.Set;\n"; + + s += "import com.google.gwt.core.ext.typeinfo.JClassType;\n"; + s += "import com.vaadin.client.ui.ui.UIConnector;\n"; + s += "import com.vaadin.server.widgetsetutils.ConnectorBundleLoaderFactory;\n"; + s += "import com.vaadin.shared.ui.Connect.LoadStyle;\n\n"; + + s += "public class OptimizedConnectorBundleLoaderFactory extends\n"; + s += " ConnectorBundleLoaderFactory {\n"; + s += " private Set<String> eagerConnectors = new HashSet<String>();\n"; + s += " {\n"; + for (String c : usedConnectors) { + s += " eagerConnectors.add(" + c + + ".class.getName());\n"; + } + s += " }\n"; + s += "\n"; + s += " @Override\n"; + s += " protected LoadStyle getLoadStyle(JClassType connectorType) {\n"; + s += " if (eagerConnectors.contains(connectorType.getQualifiedBinaryName())) {\n"; + s += " return LoadStyle.EAGER;\n"; + s += " } else {\n"; + s += " // Loads all other connectors immediately after the initial view has\n"; + s += " // been rendered\n"; + s += " return LoadStyle.DEFERRED;\n"; + s += " }\n"; + s += " }\n"; + s += "}\n"; + + return s; + } + + private void analyzeLayouts() { + content.clear(); + content.add(new Label("Analyzing layouts...")); + List<ApplicationConnection> runningApplications = ApplicationConfiguration + .getRunningApplications(); + for (ApplicationConnection applicationConnection : runningApplications) { + applicationConnection.analyzeLayouts(); + } + } + + @Override + public void meta(ApplicationConnection ac, ValueMap meta) { + content.clear(); + JsArray<ValueMap> valueMapArray = meta + .getJSValueMapArray("invalidLayouts"); + int size = valueMapArray.length(); + + if (size > 0) { + SimpleTree root = new SimpleTree("Layouts analyzed, " + size + + " top level problems"); + for (int i = 0; i < size; i++) { + printLayoutError(ac, valueMapArray.get(i), root); + } + root.open(false); + content.add(root); + } else { + content.add(new Label("Layouts analyzed, no top level problems")); + } + + Set<ComponentConnector> zeroHeightComponents = new HashSet<ComponentConnector>(); + Set<ComponentConnector> zeroWidthComponents = new HashSet<ComponentConnector>(); + findZeroSizeComponents(zeroHeightComponents, zeroWidthComponents, + ac.getUIConnector()); + if (zeroHeightComponents.size() > 0 || zeroWidthComponents.size() > 0) { + content.add(new HTML("<h4> Client side notifications</h4>" + + " <em>The following relative sized components were " + + "rendered to a zero size container on the client side." + + " Note that these are not necessarily invalid " + + "states, but reported here as they might be.</em>")); + if (zeroHeightComponents.size() > 0) { + content.add(new HTML( + "<p><strong>Vertically zero size:</strong></p>")); + printClientSideDetectedIssues(zeroHeightComponents, ac); + } + if (zeroWidthComponents.size() > 0) { + content.add(new HTML( + "<p><strong>Horizontally zero size:</strong></p>")); + printClientSideDetectedIssues(zeroWidthComponents, ac); + } + } + + } + + private void printClientSideDetectedIssues( + Set<ComponentConnector> zeroSized, ApplicationConnection ac) { + + // keep track of already highlighted parents + HashSet<String> parents = new HashSet<String>(); + + for (final ComponentConnector connector : zeroSized) { + final ServerConnector parent = connector.getParent(); + final String parentId = parent.getConnectorId(); + + final Label errorDetails = new Label(Util.getSimpleName(connector) + + "[" + connector.getConnectorId() + "]" + " inside " + + Util.getSimpleName(parent)); + + if (parent instanceof ComponentConnector) { + final ComponentConnector parentConnector = (ComponentConnector) parent; + if (!parents.contains(parentId)) { + parents.add(parentId); + Highlight.show(parentConnector, "yellow"); + } + + errorDetails.addMouseOverHandler(new MouseOverHandler() { + @Override + public void onMouseOver(MouseOverEvent event) { + Highlight.hideAll(); + Highlight.show(parentConnector, "yellow"); + Highlight.show(connector); + errorDetails.getElement().getStyle() + .setTextDecoration(TextDecoration.UNDERLINE); + } + }); + errorDetails.addMouseOutHandler(new MouseOutHandler() { + @Override + public void onMouseOut(MouseOutEvent event) { + Highlight.hideAll(); + errorDetails.getElement().getStyle() + .setTextDecoration(TextDecoration.NONE); + } + }); + errorDetails.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + printState(connector); + Highlight.show(connector); + } + }); + + } + + Highlight.show(connector); + content.add(errorDetails); + + } + } + + private void printLayoutError(ApplicationConnection ac, ValueMap valueMap, + SimpleTree root) { + final String pid = valueMap.getString("id"); + + // find connector + final ComponentConnector connector = (ComponentConnector) ConnectorMap + .get(ac).getConnector(pid); + + if (connector == null) { + root.add(new SimpleTree("[" + pid + "] NOT FOUND")); + return; + } + + Highlight.show(connector); + + final SimpleTree errorNode = new SimpleTree( + Util.getSimpleName(connector) + " id: " + pid); + errorNode.addDomHandler(new MouseOverHandler() { + @Override + public void onMouseOver(MouseOverEvent event) { + Highlight.showOnly(connector); + ((Widget) event.getSource()).getElement().getStyle() + .setTextDecoration(TextDecoration.UNDERLINE); + } + }, MouseOverEvent.getType()); + errorNode.addDomHandler(new MouseOutHandler() { + @Override + public void onMouseOut(MouseOutEvent event) { + Highlight.hideAll(); + ((Widget) event.getSource()).getElement().getStyle() + .setTextDecoration(TextDecoration.NONE); + } + }, MouseOutEvent.getType()); + + errorNode.addDomHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + if (event.getNativeEvent().getEventTarget().cast() == errorNode + .getElement().getChild(1).cast()) { + printState(connector); + } + } + }, ClickEvent.getType()); + + VerticalPanel errorDetails = new VerticalPanel(); + + if (valueMap.containsKey("heightMsg")) { + errorDetails.add(new Label("Height problem: " + + valueMap.getString("heightMsg"))); + } + if (valueMap.containsKey("widthMsg")) { + errorDetails.add(new Label("Width problem: " + + valueMap.getString("widthMsg"))); + } + if (errorDetails.getWidgetCount() > 0) { + errorNode.add(errorDetails); + } + if (valueMap.containsKey("subErrors")) { + HTML l = new HTML( + "<em>Expand this node to show problems that may be dependent on this problem.</em>"); + errorDetails.add(l); + JsArray<ValueMap> suberrors = valueMap + .getJSValueMapArray("subErrors"); + for (int i = 0; i < suberrors.length(); i++) { + ValueMap value = suberrors.get(i); + printLayoutError(ac, value, errorNode); + } + + } + root.add(errorNode); + } + + private void findZeroSizeComponents( + Set<ComponentConnector> zeroHeightComponents, + Set<ComponentConnector> zeroWidthComponents, + ComponentConnector connector) { + Widget widget = connector.getWidget(); + ComputedStyle computedStyle = new ComputedStyle(widget.getElement()); + if (computedStyle.getIntProperty("height") == 0) { + zeroHeightComponents.add(connector); + } + if (computedStyle.getIntProperty("width") == 0) { + zeroWidthComponents.add(connector); + } + List<ServerConnector> children = connector.getChildren(); + for (ServerConnector serverConnector : children) { + if (serverConnector instanceof ComponentConnector) { + findZeroSizeComponents(zeroHeightComponents, + zeroWidthComponents, + (ComponentConnector) serverConnector); + } + } + } + + @Override + public void uidl(ApplicationConnection ac, ValueMap uidl) { + // NOP + } + + private boolean isFindMode() { + return (highlightModeRegistration != null); + } + + private void toggleFind() { + if (isFindMode()) { + stopFind(); + } else { + startFind(); + } + } + + private void startFind() { + Highlight.hideAll(); + if (!isFindMode()) { + highlightModeRegistration = Event + .addNativePreviewHandler(highlightModeHandler); + find.addStyleDependentName(VDebugWindow.STYLENAME_ACTIVE); + } + } + + private void stopFind() { + if (isFindMode()) { + highlightModeRegistration.removeHandler(); + highlightModeRegistration = null; + find.removeStyleDependentName(VDebugWindow.STYLENAME_ACTIVE); + } + } + + private void printState(ServerConnector connector) { + Highlight.showOnly(connector); + + SharedState state = connector.getState(); + + Set<String> ignoreProperties = new HashSet<String>(); + ignoreProperties.add("id"); + + String html = getRowHTML("Id", connector.getConnectorId()); + html += getRowHTML("Connector", Util.getSimpleName(connector)); + + if (connector instanceof ComponentConnector) { + ComponentConnector component = (ComponentConnector) connector; + + ignoreProperties.addAll(Arrays.asList("caption", "description", + "width", "height")); + + AbstractComponentState componentState = component.getState(); + + html += getRowHTML("Widget", + Util.getSimpleName(component.getWidget())); + html += getRowHTML("Caption", componentState.caption); + html += getRowHTML("Description", componentState.description); + html += getRowHTML("Width", componentState.width + " (actual: " + + component.getWidget().getOffsetWidth() + "px)"); + html += getRowHTML("Height", componentState.height + " (actual: " + + component.getWidget().getOffsetHeight() + "px)"); + } + + try { + JsArrayObject<Property> properties = AbstractConnector + .getStateType(connector).getPropertiesAsArray(); + for (int i = 0; i < properties.size(); i++) { + Property property = properties.get(i); + String name = property.getName(); + if (!ignoreProperties.contains(name)) { + html += getRowHTML(property.getDisplayName(), + property.getValue(state)); + } + } + } catch (NoDataException e) { + html += "<div>Could not read state, error has been logged to the console</div>"; + VConsole.error(e); + } + + content.clear(); + content.add(new HTML(html)); + } + + private String getRowHTML(String caption, Object value) { + return "<div class=\"" + VDebugWindow.STYLENAME + + "-row\"><span class=\"caption\">" + caption + + "</span><span class=\"value\">" + + Util.escapeHTML(String.valueOf(value)) + "</span></div>"; + } + + private final NativePreviewHandler highlightModeHandler = new NativePreviewHandler() { + + @Override + public void onPreviewNativeEvent(NativePreviewEvent event) { + + if (event.getTypeInt() == Event.ONKEYDOWN + && event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ESCAPE) { + stopFind(); + Highlight.hideAll(); + return; + } + if (event.getTypeInt() == Event.ONMOUSEMOVE) { + Highlight.hideAll(); + Element eventTarget = Util.getElementFromPoint(event + .getNativeEvent().getClientX(), event.getNativeEvent() + .getClientY()); + if (VDebugWindow.get().getElement().isOrHasChild(eventTarget)) { + content.clear(); + return; + } + + for (ApplicationConnection a : ApplicationConfiguration + .getRunningApplications()) { + ComponentConnector connector = Util.getConnectorForElement( + a, a.getUIConnector().getWidget(), eventTarget); + if (connector == null) { + connector = Util.getConnectorForElement(a, + RootPanel.get(), eventTarget); + } + if (connector != null) { + printState(connector); + event.cancel(); + event.consume(); + event.getNativeEvent().stopPropagation(); + return; + } + } + content.clear(); + } + if (event.getTypeInt() == Event.ONCLICK) { + Highlight.hideAll(); + event.cancel(); + event.consume(); + event.getNativeEvent().stopPropagation(); + stopFind(); + Element eventTarget = Util.getElementFromPoint(event + .getNativeEvent().getClientX(), event.getNativeEvent() + .getClientY()); + for (ApplicationConnection a : ApplicationConfiguration + .getRunningApplications()) { + ComponentConnector connector = Util.getConnectorForElement( + a, a.getUIConnector().getWidget(), eventTarget); + if (connector == null) { + connector = Util.getConnectorForElement(a, + RootPanel.get(), eventTarget); + } + + if (connector != null) { + printState(connector); + return; + } + } + } + event.cancel(); + } + + }; + +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/debug/internal/Highlight.java b/client/src/com/vaadin/client/debug/internal/Highlight.java new file mode 100644 index 0000000000..f2695f58ca --- /dev/null +++ b/client/src/com/vaadin/client/debug/internal/Highlight.java @@ -0,0 +1,210 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.debug.internal; + +import java.util.HashSet; + +import com.google.gwt.dom.client.Style; +import com.google.gwt.dom.client.Style.Position; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.RootPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.BrowserInfo; +import com.vaadin.client.ComponentConnector; +import com.vaadin.client.ServerConnector; +import com.vaadin.client.ui.VWindow; + +/** + * Highlights a widget in the UI by overlaying a semi-transparent colored div. + * <p> + * Multiple highlights can be added, then selectively removed with + * {@link #hide(Element)} or all at once with {@link #hideAll()}. + * </p> + * <p> + * Note that highlights are intended to be short-term; highlights do not move or + * disappear with the highlighted widget, and it is also fairly likely that + * someone else calls {@link #hideAll()} eventually. + * </p> + * + * @since 7.1 + * @author Vaadin Ltd + */ +public class Highlight { + + private static final String DEFAULT_COLOR = "red"; + private static final double DEFAULT_OPACITY = 0.3; + private static final int MIN_WIDTH = 3; + private static final int MIN_HEIGHT = 3; + + static HashSet<Element> highlights; + + /** + * Highlight the {@link Widget} for the given {@link ComponentConnector}. + * <p> + * Pass the returned {@link Element} to {@link #hide(Element)} to remove + * this particular highlight. + * </p> + * + * @param connector + * ComponentConnector + * @return Highlight element + */ + static Element show(ComponentConnector connector) { + return show(connector, DEFAULT_COLOR); + } + + /** + * Highlight the {@link Widget} for the given connector if it is a + * {@link ComponentConnector}. Hide any other highlight. + * <p> + * Pass the returned {@link Element} to {@link #hide(Element)} to remove + * this particular highlight. + * </p> + * + * @since 7.1 + * + * @param connector + * the server connector to highlight + * @return Highlight element, or <code>null</code> if the connector isn't a + * component + */ + static Element showOnly(ServerConnector connector) { + hideAll(); + if (connector instanceof ComponentConnector) { + return show((ComponentConnector) connector); + } else { + return null; + } + } + + /** + * Highlights the {@link Widget} for the given {@link ComponentConnector} + * using the given color. + * <p> + * Pass the returned {@link Element} to {@link #hide(Element)} to remove + * this particular highlight. + * </p> + * + * @param connector + * ComponentConnector + * @param color + * Color of highlight + * @return Highlight element + */ + static Element show(ComponentConnector connector, String color) { + if (connector != null) { + Widget w = connector.getWidget(); + return show(w, color); + } + return null; + } + + /** + * Highlights the given {@link Widget}. + * <p> + * Pass the returned {@link Element} to {@link #hide(Element)} to remove + * this particular highlight. + * </p> + * + * @param widget + * Widget to highlight + * @return Highlight element + */ + static Element show(Widget widget) { + return show(widget, DEFAULT_COLOR); + } + + /** + * Highlight the given {@link Widget} using the given color. + * <p> + * Pass the returned {@link Element} to {@link #hide(Element)} to remove + * this particular highlight. + * </p> + * + * @param widget + * Widget to highlight + * @param color + * Color of highlight + * @return Highlight element + */ + static Element show(Widget widget, String color) { + if (widget != null) { + if (highlights == null) { + highlights = new HashSet<Element>(); + } + + Element highlight = DOM.createDiv(); + Style style = highlight.getStyle(); + style.setTop(widget.getAbsoluteTop(), Unit.PX); + style.setLeft(widget.getAbsoluteLeft(), Unit.PX); + int width = widget.getOffsetWidth(); + if (width < MIN_WIDTH) { + width = MIN_WIDTH; + } + style.setWidth(width, Unit.PX); + int height = widget.getOffsetHeight(); + if (height < MIN_HEIGHT) { + height = MIN_HEIGHT; + } + style.setHeight(height, Unit.PX); + RootPanel.getBodyElement().appendChild(highlight); + + style.setPosition(Position.ABSOLUTE); + style.setZIndex(VWindow.Z_INDEX + 1000); + style.setBackgroundColor(color); + style.setOpacity(DEFAULT_OPACITY); + if (BrowserInfo.get().isIE()) { + style.setProperty("filter", "alpha(opacity=" + + (DEFAULT_OPACITY * 100) + ")"); + } + + highlights.add(highlight); + + return highlight; + } + return null; + } + + /** + * Hides the given highlight. + * + * @param highlight + * Highlight to hide + */ + static void hide(Element highlight) { + if (highlight != null && highlight.getParentElement() != null) { + highlight.getParentElement().removeChild(highlight); + highlights.remove(highlight); + } + } + + /** + * Hides all highlights + */ + static void hideAll() { + if (highlights != null) { + for (Element highlight : highlights) { + if (highlight.getParentElement() != null) { + highlight.getParentElement().removeChild(highlight); + } + } + highlights = null; + } + } + +} diff --git a/client/src/com/vaadin/client/debug/internal/Icon.java b/client/src/com/vaadin/client/debug/internal/Icon.java new file mode 100644 index 0000000000..cc2ef3b348 --- /dev/null +++ b/client/src/com/vaadin/client/debug/internal/Icon.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.client.debug.internal; + +public enum Icon { + + SEARCH(""), // + OK(""), // + REMOVE(""), // + CLOSE(""), // + CLEAR(""), // + RESET_TIMER(""), // + MINIMIZE(""), // + WARNING(""), // + INFO(""), // + ERROR(""), // + HIGHLIGHT(""), // + LOG(""), // + OPTIMIZE(""), // + HIERARCHY(""), // + MENU(""), // + NETWORK(""), // + ANALYZE(""), // + SCROLL_LOCK(""), // + DEVMODE_OFF(""), // + DEVMODE_SUPER(""), // + DEVMODE_ON(""), // + // BAN_CIRCLE(""), // + MAXIMIZE(""), // + RESET(""), // + PERSIST(""); // + + private String id; + + private Icon(String id) { + this.id = id; + } + + @Override + public String toString() { + return "<i data-icon=\"" + id + "\"></i>"; + } + + public String getId() { + return id; + } + +} diff --git a/client/src/com/vaadin/client/debug/internal/LogSection.java b/client/src/com/vaadin/client/debug/internal/LogSection.java new file mode 100644 index 0000000000..74ac3641c2 --- /dev/null +++ b/client/src/com/vaadin/client/debug/internal/LogSection.java @@ -0,0 +1,355 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.debug.internal; + +import java.util.logging.Formatter; +import java.util.logging.Handler; +import java.util.logging.Level; +import java.util.logging.LogRecord; +import java.util.logging.Logger; + +import com.google.gwt.dom.client.Element; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.logging.client.HtmlLogFormatter; +import com.google.gwt.storage.client.Storage; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.ApplicationConnection; +import com.vaadin.client.ValueMap; + +/** + * Displays the log messages. + * <p> + * Scroll lock state is persisted. + * </p> + * + * @since 7.1 + * @author Vaadin Ltd + */ +public class LogSection implements Section { + + private final class LogSectionHandler extends Handler { + private LogSectionHandler() { + setLevel(Level.ALL); + setFormatter(new HtmlLogFormatter(true) { + @Override + protected String getHtmlPrefix(LogRecord event) { + return ""; + } + + @Override + protected String getHtmlSuffix(LogRecord event) { + return ""; + } + + @Override + protected String getRecordInfo(LogRecord event, String newline) { + return ""; + } + }); + } + + @Override + public void publish(LogRecord record) { + if (!isLoggable(record)) { + return; + } + + Formatter formatter = getFormatter(); + String msg = formatter.format(record); + + addRow(record.getLevel(), msg); + } + + @Override + public void close() { + // Nothing to do + } + + @Override + public void flush() { + // Nothing todo + } + } + + // If scroll is not locked, content will be scrolled after delay + private static final int SCROLL_DELAY = 100; + private Timer scrollTimer = null; + + // TODO should be persisted + // log content limit + private int limit = 500; + + private final DebugButton tabButton = new DebugButton(Icon.LOG, + "Debug message log"); + + private final HTML content = new HTML(); + private final Element contentElement; + private final FlowPanel controls = new FlowPanel(); + + private final Button clear = new DebugButton(Icon.CLEAR, "Clear log"); + private final Button reset = new DebugButton(Icon.RESET_TIMER, + "Reset timer"); + private final Button scroll = new DebugButton(Icon.SCROLL_LOCK, + "Scroll lock"); + + public LogSection() { + contentElement = content.getElement(); + content.setStylePrimaryName(VDebugWindow.STYLENAME + "-log"); + + // clear log button + controls.add(clear); + clear.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON); + clear.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + clear(); + } + }); + + // reset timer button + controls.add(reset); + reset.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON); + reset.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + resetTimer(); + } + }); + + // scroll lock toggle + controls.add(scroll); + scroll.setStylePrimaryName(VDebugWindow.STYLENAME_BUTTON); + scroll.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + toggleScrollLock(); + } + }); + + // select message if row is clicked + content.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + Element el = Element + .as(event.getNativeEvent().getEventTarget()); + while (!el.getClassName().contains( + VDebugWindow.STYLENAME + "-message")) { + el = el.getParentElement(); + if (el == contentElement) { + // clicked something else + return; + } + } + selectText(el); + } + }); + + // Add handler to the root logger + Logger.getLogger("").addHandler(new LogSectionHandler()); + } + + /** + * Toggles scroll lock, writes state to persistent storage. + */ + void toggleScrollLock() { + setScrollLock(scrollTimer != null); + + Storage storage = Storage.getLocalStorageIfSupported(); + if (storage == null) { + return; + } + VDebugWindow.writeState(storage, "log-scrollLock", scrollTimer == null); + } + + /** + * Activates or deactivates scroll lock + * + * @param locked + */ + void setScrollLock(boolean locked) { + if (locked && scrollTimer != null) { + scrollTimer.cancel(); + scrollTimer = null; + + } else if (!locked && scrollTimer == null) { + scrollTimer = new Timer() { + @Override + public void run() { + Element el = (Element) contentElement.getLastChild(); + if (el != null) { + el = el.getFirstChildElement(); + if (el != null) { + el.scrollIntoView(); + } + } + } + }; + + } + scroll.setStyleDependentName(VDebugWindow.STYLENAME_ACTIVE, locked); + + } + + private native void selectText(Element el) + /*-{ + if ($doc.selection && $doc.selection.createRange) { + var r = $doc.selection.createRange(); + r.moveToElementText(el); + r.select(); + } else if ($doc.createRange && $wnd.getSelection) { + var r = $doc.createRange(); + r.selectNode(el); + var selection = $wnd.getSelection(); + selection.removeAllRanges(); + selection.addRange(r); + } + }-*/; + + private void clear() { + contentElement.setInnerText(""); + } + + private void applyLimit() { + while (contentElement.getChildCount() > limit) { + contentElement.removeChild(contentElement.getFirstChild()); + } + } + + /** + * Sets the log row limit. + * + * @param limit + */ + public void setLimit(int limit) { + this.limit = limit; + applyLimit(); + + // TODO shoud be persisted + } + + /** + * Gets the current log row limit. + * + * @return + */ + public int getLimit() { + // TODO should be read from persistent storage + return limit; + } + + @Override + public DebugButton getTabButton() { + return tabButton; + } + + @Override + public Widget getControls() { + return controls; + } + + @Override + public Widget getContent() { + return content; + } + + @Override + public void show() { + Storage storage = Storage.getLocalStorageIfSupported(); + if (storage == null) { + return; + } + setScrollLock(VDebugWindow.readState(storage, "log-scrollLock", false)); + } + + @Override + public void hide() { + // remove timer + setScrollLock(true); + } + + /** + * Schedules a scoll if scroll lock is not active. + */ + private void maybeScroll() { + if (scrollTimer != null) { + scrollTimer.cancel(); + scrollTimer.schedule(SCROLL_DELAY); + } + } + + /** + * Resets the timer and inserts a log row indicating this. + */ + private void resetTimer() { + int sinceStart = VDebugWindow.getMillisSinceStart(); + int sinceReset = VDebugWindow.resetTimer(); + Element row = DOM.createDiv(); + row.addClassName(VDebugWindow.STYLENAME + "-reset"); + row.setInnerHTML(Icon.RESET_TIMER + " Timer reset"); + row.setTitle(VDebugWindow.getTimingTooltip(sinceStart, sinceReset)); + contentElement.appendChild(row); + maybeScroll(); + } + + /** + * Adds a row to the log, applies the log row limit by removing old rows if + * needed, and scrolls new row into view if scroll lock is not active. + * + * @param level + * @param msg + * @return + */ + private Element addRow(Level level, String msg) { + int sinceReset = VDebugWindow.getMillisSinceReset(); + int sinceStart = VDebugWindow.getMillisSinceStart(); + + Element row = DOM.createDiv(); + row.addClassName(VDebugWindow.STYLENAME + "-row"); + row.addClassName(level.getName()); + + String inner = "<span class='" + VDebugWindow.STYLENAME + "-" + + "'></span><span class='" + VDebugWindow.STYLENAME + + "-time' title='" + + VDebugWindow.getTimingTooltip(sinceStart, sinceReset) + "'>" + + sinceReset + "ms</span><span class='" + + VDebugWindow.STYLENAME + "-message'>" + msg + "</span>"; + row.setInnerHTML(inner); + + contentElement.appendChild(row); + applyLimit(); + + maybeScroll(); + + return row; + } + + @Override + public void meta(ApplicationConnection ac, ValueMap meta) { + addRow(Level.FINE, "Meta: " + meta.toSource()); + } + + @Override + public void uidl(ApplicationConnection ac, ValueMap uidl) { + addRow(Level.FINE, "UIDL: " + uidl.toSource()); + } + +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/debug/internal/NetworkSection.java b/client/src/com/vaadin/client/debug/internal/NetworkSection.java new file mode 100644 index 0000000000..e94791ce1f --- /dev/null +++ b/client/src/com/vaadin/client/debug/internal/NetworkSection.java @@ -0,0 +1,97 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.debug.internal; + +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.ApplicationConnection; +import com.vaadin.client.VUIDLBrowser; +import com.vaadin.client.ValueMap; + +/** + * Displays network activity; requests and responses. + * + * Currently only displays responses in a simple manner. + * + * @since 7.1 + * @author Vaadin Ltd + */ +public class NetworkSection implements Section { + + private final int maxSize = 10; + + private final DebugButton tabButton = new DebugButton(Icon.NETWORK, + "Communication"); + + private final HorizontalPanel controls = new HorizontalPanel(); + private final FlowPanel content = new FlowPanel(); + + @Override + public DebugButton getTabButton() { + return tabButton; + } + + @Override + public Widget getControls() { + return controls; + } + + @Override + public Widget getContent() { + return content; + } + + @Override + public void show() { + // TODO Auto-generated method stub + + } + + @Override + public void hide() { + // TODO Auto-generated method stub + + } + + @Override + public void meta(ApplicationConnection ac, ValueMap meta) { + // NOP + } + + @Override + public void uidl(ApplicationConnection ac, ValueMap uidl) { + int sinceStart = VDebugWindow.getMillisSinceStart(); + int sinceReset = VDebugWindow.getMillisSinceReset(); + VUIDLBrowser vuidlBrowser = new VUIDLBrowser(uidl, ac); + vuidlBrowser.addStyleName(VDebugWindow.STYLENAME + "-row"); + // TODO style this + /*- + vuidlBrowser.setText("<span class=\"" + VDebugWindow.STYLENAME + + "-time\">" + sinceReset + "ms</span><span class=\"" + + VDebugWindow.STYLENAME + "-message\">response</span>"); + -*/ + vuidlBrowser.setText("Response @ " + sinceReset + "ms"); + vuidlBrowser.setTitle(VDebugWindow.getTimingTooltip(sinceStart, + sinceReset)); + vuidlBrowser.close(); + content.add(vuidlBrowser); + while (content.getWidgetCount() > maxSize) { + content.remove(0); + } + } + +} diff --git a/client/src/com/vaadin/client/debug/internal/Section.java b/client/src/com/vaadin/client/debug/internal/Section.java new file mode 100644 index 0000000000..c6b8af55e8 --- /dev/null +++ b/client/src/com/vaadin/client/debug/internal/Section.java @@ -0,0 +1,76 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.debug.internal; + +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.ApplicationConnection; +import com.vaadin.client.ValueMap; + +/** + * A Section is displayed as a tab in the {@link VDebugWindow}. + * + * @since 7.1 + * @author Vaadin Ltd + */ +public interface Section { + + /** + * Returns a button that will be used to activate this section, displayed as + * a tab in {@link VDebugWindow}. + * <p> + * <em>The same instance <b>must</b> be returned each time this method is called.</em> + * </p> + * <p> + * The button should preferably only have an icon (no caption), and should + * have a longer description as title (tooltip). + * </p> + * + * @return section id + */ + public DebugButton getTabButton(); + + /** + * Returns a widget that is placed on top of the Section content when the + * Section (tab) is active in the {@link VDebugWindow}. + * + * @return section controls + */ + public Widget getControls(); + + /** + * Returns a widget that is the main content of the section, displayed when + * the section is active in the {@link VDebugWindow}. + * + * @return + */ + public Widget getContent(); + + /** + * Called when the section is activated in {@link VDebugWindow}. Provides an + * opportunity to e.g start timers, add listeners etc. + */ + public void show(); + + /** + * Called when the section is deactivated in {@link VDebugWindow}. Provides + * an opportunity to e.g stop timers, remove listeners etc. + */ + public void hide(); + + public void meta(ApplicationConnection ac, ValueMap meta); + + public void uidl(ApplicationConnection ac, ValueMap uidl); +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/debug/internal/VDebugWindow.java b/client/src/com/vaadin/client/debug/internal/VDebugWindow.java new file mode 100644 index 0000000000..5aab95616a --- /dev/null +++ b/client/src/com/vaadin/client/debug/internal/VDebugWindow.java @@ -0,0 +1,1062 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.debug.internal; + +import java.util.ArrayList; +import java.util.Date; + +import com.google.gwt.core.client.Duration; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.NativeEvent; +import com.google.gwt.dom.client.Style; +import com.google.gwt.dom.client.Style.Cursor; +import com.google.gwt.dom.client.Style.Overflow; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.dom.client.MouseDownEvent; +import com.google.gwt.event.dom.client.MouseDownHandler; +import com.google.gwt.event.dom.client.MouseEvent; +import com.google.gwt.event.dom.client.MouseMoveEvent; +import com.google.gwt.event.dom.client.MouseMoveHandler; +import com.google.gwt.event.logical.shared.ResizeEvent; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.http.client.UrlBuilder; +import com.google.gwt.i18n.client.DateTimeFormat; +import com.google.gwt.i18n.client.NumberFormat; +import com.google.gwt.storage.client.Storage; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.Event.NativePreviewEvent; +import com.google.gwt.user.client.Event.NativePreviewHandler; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.Window.Location; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.ApplicationConnection; +import com.vaadin.client.ValueMap; +import com.vaadin.client.ui.VOverlay; + +/** + * Debug window implementation. + * + * @since 7.1 + * @author Vaadin Ltd + */ +public final class VDebugWindow extends VOverlay { + + // CSS classes + static final String STYLENAME = "v-debugwindow"; + static final String STYLENAME_BUTTON = STYLENAME + "-button"; + static final String STYLENAME_ACTIVE = "active"; + + protected static final String STYLENAME_HEAD = STYLENAME + "-head"; + protected static final String STYLENAME_TABS = STYLENAME + "-tabs"; + protected static final String STYLENAME_TAB = STYLENAME + "-tab"; + protected static final String STYLENAME_CONTROLS = STYLENAME + "-controls"; + protected static final String STYLENAME_SECTION_HEAD = STYLENAME + + "-section-head"; + protected static final String STYLENAME_CONTENT = STYLENAME + "-content"; + protected static final String STYLENAME_SELECTED = "selected"; + + // drag this far before actually moving window + protected static final int MOVE_TRESHOLD = 5; + + // window minimum sizes + protected static final int MIN_HEIGHT = 40; + protected static final int HANDLE_SIZE = 5; + + // identifiers for localStorage + private static final String STORAGE_PREFIX = "v-debug-"; + private static final String STORAGE_FULL_X = "x"; + private static final String STORAGE_FULL_Y = "y"; + private static final String STORAGE_FULL_W = "w"; + private static final String STORAGE_FULL_H = "h"; + private static final String STORAGE_MIN_X = "mx"; + private static final String STORAGE_MIN_Y = "my"; + private static final String STORAGE_ACTIVE_SECTION = "t"; + private static final String STORAGE_IS_MINIMIZED = "m"; + private static final String STORAGE_FONT_SIZE = "s"; + + // state, these are persisted + protected Section activeSection; + protected boolean minimized = false; + protected int fullX = -10; + protected int fullY = -10; + protected int fullW = 300; + protected int fullH = 150; + protected int minX = -10; + protected int minY = 10; + protected int fontSize = 1; // 0-2 + + // Timers since application start, and last timer reset + private static final Duration start = new Duration(); + private static Duration lastReset = start; + + // outer panel + protected FlowPanel window = new FlowPanel(); + // top (tabs + controls) + protected FlowPanel head = new FlowPanel(); + protected FlowPanel tabs = new FlowPanel(); + protected FlowPanel controls = new FlowPanel(); + protected Button minimize = new DebugButton(Icon.MINIMIZE, "Minimize"); + protected Button menu = new DebugButton(Icon.MENU, "Menu"); + protected Button close = new DebugButton(Icon.CLOSE, "Close"); + + // menu + protected Menu menuPopup = new Menu(); + + // section specific area + protected FlowPanel sectionHead = new FlowPanel(); + // content wrapper + protected SimplePanel content = new SimplePanel(); + + // sections + protected ArrayList<Section> sections = new ArrayList<Section>(); + + // handles resizing (mouse) + protected ResizeHandler resizeHandler = new ResizeHandler(); + protected HandlerRegistration resizeReg = null; + protected HandlerRegistration resizeReg2 = null; + + // handles window movement (mouse) + protected MoveHandler moveHandler = new MoveHandler(); + protected HandlerRegistration moveReg = null; + + // TODO this class should really be a singleton. + static VDebugWindow instance; + + /** + * This class should only be instantiated by the framework, use + * {@link #get()} instead to get the singleton instance. + * <p> + * {@link VDebugWindow} provides windowing functionality and shows + * {@link Section}s added with {@link #addSection(Section)} as tabs. + * </p> + * <p> + * {@link Section#getTabButton()} is called to obtain a unique id for the + * Sections; the id should actually be an identifier for an icon in the + * icon-font in use. + * </p> + * <p> + * {@link Section#getControls()} and {@link Section#getContent()} is called + * when the Section is activated (displayed). Additionally + * {@link Section#show()} is called to allow the Section to initialize + * itself as needed when shown. Conversely {@link Section#hide()} is called + * when the Section is deactivated. + * </p> + * <p> + * Sections should take care to prefix CSS classnames used with + * {@link VDebugWindow}.{@link #STYLENAME} to avoid that application theme + * interferes with the debug window content. + * </p> + * <p> + * Some of the window state, such as position and size, is persisted to + * localStorage. Sections can use + * {@link #writeState(Storage, String, Object)} and + * {@link #readState(Storage, String, String)} (and relatives) to write and + * read own persisted settings, keys will automatically be prefixed with + * {@value #STORAGE_PREFIX}. + * </p> + */ + public VDebugWindow() { + super(false, false); + instance = this; + getElement().getStyle().setOverflow(Overflow.HIDDEN); + setStylePrimaryName(STYLENAME); + + setWidget(window); + window.add(head); + head.add(tabs); + head.add(controls); + head.add(sectionHead); + window.add(content); + + head.setStylePrimaryName(STYLENAME_HEAD); + tabs.setStylePrimaryName(STYLENAME_TABS); + controls.setStylePrimaryName(STYLENAME_CONTROLS); + sectionHead.setStylePrimaryName(STYLENAME_SECTION_HEAD); + content.setStylePrimaryName(STYLENAME_CONTENT); + + // add controls TODO move these + controls.add(menu); + menu.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + menuPopup.showRelativeTo(menu); + } + }); + + controls.add(minimize); + minimize.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + toggleMinimized(); + writeStoredState(); + } + }); + controls.add(close); + close.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + close(); + } + }); + + Style s = content.getElement().getStyle(); + s.setOverflow(Overflow.AUTO); + + // window can be moved by dragging header + moveReg = head.addDomHandler(moveHandler, MouseDownEvent.getType()); + // resize from all sides and corners + resizeReg = content.addDomHandler(resizeHandler, + MouseDownEvent.getType()); + // changes mouse pointer when hovering sides / corners + resizeReg2 = content.addDomHandler(resizeHandler, + MouseMoveEvent.getType()); + } + + /** + * Gets the {@link #VDebugWindow()} singleton instance. + * + * @return + */ + public static VDebugWindow get() { + if (instance == null) { + instance = new VDebugWindow(); + } + return instance; + } + + /** + * Closes the window and stops visual logging. + */ + public void close() { + // TODO disable even more + if (resizeReg != null) { + resizeReg.removeHandler(); + resizeReg2.removeHandler(); + moveReg.removeHandler(); + } + Highlight.hideAll(); + hide(); + + } + + boolean isClosed() { + return !isShowing(); + } + + /** + * Reads the stored state from localStorage. + */ + private void readStoredState() { + Storage storage = Storage.getLocalStorageIfSupported(); + if (storage == null) { + return; + } + + fullX = readState(storage, STORAGE_FULL_X, -510); + fullY = readState(storage, STORAGE_FULL_Y, -230); + fullW = readState(storage, STORAGE_FULL_W, 500); + fullH = readState(storage, STORAGE_FULL_H, 150); + minX = readState(storage, STORAGE_MIN_X, -40); + minY = readState(storage, STORAGE_MIN_Y, -70); + setFontSize(readState(storage, STORAGE_FONT_SIZE, 1)); + + activateSection(readState(storage, STORAGE_ACTIVE_SECTION, 0)); + + setMinimized(readState(storage, STORAGE_IS_MINIMIZED, false)); + + applyPositionAndSize(); + } + + /** + * Writes the persistent state to localStorage. + */ + private void writeStoredState() { + if (isClosed()) { + return; + } + Storage storage = Storage.getLocalStorageIfSupported(); + if (storage == null) { + return; + } + + writeState(storage, STORAGE_FULL_X, fullX); + writeState(storage, STORAGE_FULL_Y, fullY); + writeState(storage, STORAGE_FULL_W, fullW); + writeState(storage, STORAGE_FULL_H, fullH); + writeState(storage, STORAGE_MIN_X, minX); + writeState(storage, STORAGE_MIN_Y, minY); + writeState(storage, STORAGE_FONT_SIZE, fontSize); + + if (activeSection != null) { + writeState(storage, STORAGE_ACTIVE_SECTION, + activeSection.getTabButton()); + } + + writeState(storage, STORAGE_IS_MINIMIZED, minimized); + } + + /** + * Writes the given value to the given {@link Storage} using the given key + * (automatically prefixed with {@value #STORAGE_PREFIX}). + * + * @param storage + * @param key + * @param value + */ + static void writeState(Storage storage, String key, Object value) { + storage.setItem(STORAGE_PREFIX + key, String.valueOf(value)); + } + + /** + * Returns the item with the given key (automatically prefixed with + * {@value #STORAGE_PREFIX}) as an int from the given {@link Storage}, + * returning the given default value instead if not successful (e.g missing + * item). + * + * @param storage + * @param key + * @param def + * @return stored or default value + */ + static int readState(Storage storage, String key, int def) { + try { + return Integer.parseInt(storage.getItem(STORAGE_PREFIX + key)); + } catch (Exception e) { + return def; + } + } + + /** + * Returns the item with the given key (automatically prefixed with + * {@value #STORAGE_PREFIX}) as a boolean from the given {@link Storage}, + * returning the given default value instead if not successful (e.g missing + * item). + * + * @param storage + * @param key + * @param def + * @return stored or default value + */ + static boolean readState(Storage storage, String key, boolean def) { + try { + return Boolean.parseBoolean(storage.getItem(STORAGE_PREFIX + key)); + } catch (Exception e) { + return def; + } + } + + /** + * Returns the item with the given key (automatically prefixed with + * {@value #STORAGE_PREFIX}) as a String from the given {@link Storage}, + * returning the given default value instead if not successful (e.g missing + * item). + * + * @param storage + * @param key + * @param def + * @return stored or default value + */ + static String readState(Storage storage, String key, String def) { + String val = storage.getItem(STORAGE_PREFIX + key); + return val != null ? val : def; + } + + /** + * Resets (clears) the stored state from localStorage. + */ + private void resetStoredState() { + Storage storage = Storage.getLocalStorageIfSupported(); + if (storage == null) { + return; + } + // note: length is live + for (int i = 0; i < storage.getLength();) { + String key = storage.key(i); + if (key.startsWith(STORAGE_PREFIX)) { + removeState(storage, key.substring(STORAGE_PREFIX.length())); + } else { + i++; + } + } + } + + /** + * Removes the item with the given key (automatically prefixed with + * {@value #STORAGE_PREFIX}) from the given {@link Storage}. + * + * @param storage + * @param key + */ + private void removeState(Storage storage, String key) { + storage.removeItem(STORAGE_PREFIX + key); + } + + /** + * Applies the appropriate instance variables for width, height, x, y + * depending on if the window is minimized or not. + * + * If the value is negative, the window is positioned that amount of pixels + * from the right/bottom instead of left/top. + * + * Finally, the position is bounds-checked so that the window is not moved + * off-screen (the adjusted values are not saved). + */ + private void applyPositionAndSize() { + int x = 0; + int y = 0; + if (minimized) { + x = minX; + if (minX < 0) { + x = Window.getClientWidth() + minX; + } + y = minY; + if (minY < 0) { + y = Window.getClientHeight() + minY; + } + + } else { + x = fullX; + if (fullX < 0) { + x = Window.getClientWidth() + fullX; + } + y = fullY; + if (y < 0) { + y = Window.getClientHeight() + fullY; + } + content.setWidth(fullW + "px"); + content.setHeight(fullH + "px"); + } + + // bounds check + if (x < 0) { + x = 0; + } + if (x > Window.getClientWidth() - getOffsetWidth()) { + // not allowed off-screen to the right + x = Window.getClientWidth() - getOffsetWidth(); + } + if (y > Window.getClientHeight() - getOffsetHeight()) { + y = Window.getClientHeight() - getOffsetHeight(); + } + if (y < 0) { + y = 0; + } + + setPopupPosition(x, y); + } + + /** + * Reads position and size from the DOM to local variables (which in turn + * can be stored to localStorage) + */ + private void readPositionAndSize() { + int x = getPopupLeft(); + int fromRight = Window.getClientWidth() - x - getOffsetWidth(); + if (fromRight < x) { + x -= Window.getClientWidth(); + } + + int y = getPopupTop(); + int fromBottom = Window.getClientHeight() - y - getOffsetHeight(); + if (fromBottom < y) { + y -= Window.getClientHeight(); + } + + if (minimized) { + minY = y; + minX = x; + } else { + fullY = y; + fullX = x; + fullW = content.getOffsetWidth(); + fullH = content.getOffsetHeight(); + } + + } + + /** + * Adds the given {@link Section} as a tab in the {@link VDebugWindow} UI. + * {@link Section#getTabButton()} is called to obtain a button which is used + * tab. + * + * @param section + */ + public void addSection(final Section section) { + Button b = section.getTabButton(); + b.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + activateSection(section); + writeStoredState(); + } + }); + b.setStylePrimaryName(STYLENAME_TAB); + tabs.add(b); + sections.add(section); + + if (activeSection == null) { + activateSection(section); + } + } + + /** + * Activates the given {@link Section} + * + * @param section + */ + void activateSection(Section section) { + if (section != null && section != activeSection) { + Highlight.hideAll(); + // remove old stuff + if (activeSection != null) { + activeSection.hide(); + content.remove(activeSection.getContent()); + sectionHead.remove(activeSection.getControls()); + } + // update tab styles + for (int i = 0; i < tabs.getWidgetCount(); i++) { + Widget tab = tabs.getWidget(i); + tab.setStyleDependentName(STYLENAME_SELECTED, + tab == section.getTabButton()); + } + // add new stuff + content.add(section.getContent()); + sectionHead.add(section.getControls()); + activeSection = section; + activeSection.show(); + } + } + + void activateSection(int n) { + if (n < sections.size()) { + activateSection(sections.get(n)); + } + } + + /** + * Toggles the window between minimized and full states. + */ + private void toggleMinimized() { + setMinimized(!minimized); + writeStoredState(); + } + + /** + * Sets whether or not the window is minimized. + * + * @param minimized + */ + private void setMinimized(boolean minimized) { + this.minimized = minimized; + + tabs.setVisible(!minimized); + content.setVisible(!minimized); + sectionHead.setVisible(!minimized); + menu.setVisible(!minimized); + + applyPositionAndSize(); + } + + /** + * Sets the font size in use. + * + * @param size + */ + private void setFontSize(int size) { + removeStyleDependentName("size" + fontSize); + fontSize = size; + addStyleDependentName("size" + size); + } + + /** + * Gets the font size currently in use. + * + * @return + */ + private int getFontSize() { + return fontSize; + } + + /** + * Gets the milliseconds since application start. + * + * @return + */ + static int getMillisSinceStart() { + return start.elapsedMillis(); + } + + /** + * Gets the milliseconds since last {@link #resetTimer()} call. + * + * @return + */ + static int getMillisSinceReset() { + return lastReset.elapsedMillis(); + } + + /** + * Resets the timer. + * + * @return Milliseconds elapsed since the timer was last reset. + */ + static int resetTimer() { + int sinceLast = lastReset.elapsedMillis(); + lastReset = new Duration(); + return sinceLast; + } + + /** + * Gets a nicely formatted string with timing information suitable for + * display in tooltips. + * + * @param sinceStart + * @param sinceReset + * @return + */ + static String getTimingTooltip(int sinceStart, int sinceReset) { + String title = formatDuration(sinceStart) + " since start"; + title += ", " + formatDuration(sinceReset) + " since timer reset"; + title += " @ " + + DateTimeFormat.getFormat("HH:mm:ss.SSS").format(new Date()); + return title; + } + + /** + * Formats the given milliseconds as hours, minutes, seconds and + * milliseconds. + * + * @param ms + * @return + */ + static String formatDuration(int ms) { + NumberFormat fmt = NumberFormat.getFormat("00"); + String seconds = fmt.format((ms / 1000) % 60); + String minutes = fmt.format((ms / (1000 * 60)) % 60); + String hours = fmt.format((ms / (1000 * 60 * 60)) % 24); + + String millis = NumberFormat.getFormat("000").format(ms % 1000); + + return hours + "h " + minutes + "m " + seconds + "s " + millis + "ms"; + } + + /** + * Called when the window is initialized. + */ + public void init() { + + show(); + readStoredState(); + + Window.addResizeHandler(new com.google.gwt.event.logical.shared.ResizeHandler() { + + Timer t = new Timer() { + @Override + public void run() { + applyPositionAndSize(); + } + }; + + @Override + public void onResize(ResizeEvent event) { + t.cancel(); + // TODO less + t.schedule(1000); + } + }); + } + + /** + * Called when the result from analyzeLayouts is received. + * + * @param ac + * @param meta + */ + public void meta(ApplicationConnection ac, ValueMap meta) { + if (isClosed()) { + return; + } + for (Section s : sections) { + s.meta(ac, meta); + } + } + + /** + * Called when a response is received + * + * @param ac + * @param uidl + */ + public void uidl(ApplicationConnection ac, ValueMap uidl) { + if (isClosed()) { + return; + } + for (Section s : sections) { + s.uidl(ac, uidl); + } + } + + /* + * Inner classes + */ + + /** + * Popup menu for {@link VDebugWindow}. + * + * @since 7.1 + * @author Vaadin Ltd + */ + protected class Menu extends VOverlay { + FlowPanel content = new FlowPanel(); + + DebugButton[] sizes = new DebugButton[] { + new DebugButton(null, "Small", "A"), + new DebugButton(null, "Medium", "A"), + new DebugButton(null, "Large", "A") }; + + DebugButton[] modes = new DebugButton[] { + new DebugButton(Icon.DEVMODE_OFF, + "Debug only (causes page reload)"), + new DebugButton(Icon.DEVMODE_ON, "DevMode (causes page reload)"), + new DebugButton(Icon.DEVMODE_SUPER, + "SuperDevMode (causes page reload)") }; + + Menu() { + super(true, true); + setWidget(content); + + setStylePrimaryName(STYLENAME + "-menu"); + content.setStylePrimaryName(STYLENAME + "-menu-content"); + + FlowPanel size = new FlowPanel(); + content.add(size); + + final ClickHandler sizeHandler = new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + for (int i = 0; i < sizes.length; i++) { + Button b = sizes[i]; + if (b == event.getSource()) { + setSize(i); + } + } + hide(); + } + }; + for (int i = 0; i < sizes.length; i++) { + Button b = sizes[i]; + b.setStyleDependentName("size" + i, true); + b.addClickHandler(sizeHandler); + size.add(b); + } + + FlowPanel mode = new FlowPanel(); + content.add(mode); + final ClickHandler modeHandler = new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + for (int i = 0; i < modes.length; i++) { + Button b = modes[i]; + if (b == event.getSource()) { + setDevMode(i); + } + } + hide(); + } + }; + modes[getDevMode()].setActive(true); + for (int i = 0; i < modes.length; i++) { + Button b = modes[i]; + b.addClickHandler(modeHandler); + mode.add(b); + } + + Button reset = new DebugButton(Icon.RESET, "Restore defaults.", + " Reset"); + reset.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + resetStoredState(); + readStoredState(); + hide(); + } + }); + content.add(reset); + } + + private void setSize(int size) { + for (int i = 0; i < sizes.length; i++) { + Button b = sizes[i]; + b.setStyleDependentName(STYLENAME_ACTIVE, i == size); + } + setFontSize(size); + writeStoredState(); + } + + @Override + public void show() { + super.show(); + setSize(getFontSize()); + } + + private int getDevMode() { + if (Location.getParameter("superdevmode") != null) { + return 2; + } else if (Location.getParameter("gwt.codesvr") != null) { + return 1; + } else { + return 0; + } + } + + private void setDevMode(int mode) { + UrlBuilder u = Location.createUrlBuilder(); + switch (mode) { + case 2: + u.setParameter("superdevmode", ""); + u.removeParameter("gwt.codesvr"); + break; + case 1: + u.setParameter("gwt.codesvr", "localhost:9997"); + u.removeParameter("superdevmode"); + break; + default: + u.removeParameter("gwt.codesvr"); + u.removeParameter("superdevmode"); + } + Location.assign(u.buildString()); + } + + } + + /** + * Handler for moving window. + * + * @since 7.1 + * @author Vaadin Ltd + */ + protected class MoveHandler implements MouseDownHandler, + NativePreviewHandler { + + HandlerRegistration handler; + int startX; + int startY; + int startTop; + int startLeft; + + // moving stopped, remove handler on next event + boolean stop; + + @Override + public void onPreviewNativeEvent(NativePreviewEvent event) { + if (event.getTypeInt() == Event.ONMOUSEMOVE && !stop + && hasMoved(event.getNativeEvent())) { + int dx = event.getNativeEvent().getClientX() - startX; + int dy = event.getNativeEvent().getClientY() - startY; + + setPopupPosition(startLeft + dx, startTop + dy); + event.cancel(); + + } else if (event.getTypeInt() == Event.ONMOUSEUP) { + stop = true; + if (hasMoved(event.getNativeEvent())) { + event.cancel(); + } + + } else if (event.getTypeInt() == Event.ONCLICK) { + stop = true; + if (hasMoved(event.getNativeEvent())) { + event.cancel(); + } + + } else if (stop) { + stop = false; + handler.removeHandler(); + handler = null; + + readPositionAndSize(); + writeStoredState(); + } + } + + private boolean hasMoved(NativeEvent event) { + return Math.abs(startX - event.getClientX()) > MOVE_TRESHOLD + || Math.abs(startY - event.getClientY()) > MOVE_TRESHOLD; + } + + @Override + public void onMouseDown(MouseDownEvent event) { + if (handler == null) { + handler = Event.addNativePreviewHandler(MoveHandler.this); + } + startX = event.getClientX(); + startY = event.getClientY(); + startLeft = getPopupLeft(); + startTop = getPopupTop(); + stop = false; + event.preventDefault(); + } + + } + + /** + * Handler for resizing window. + * + * @since 7.1 + * @author Vaadin Ltd + */ + protected class ResizeHandler implements MouseDownHandler, + MouseMoveHandler, NativePreviewHandler { + + boolean resizeLeft; + boolean resizeRight; + boolean resizeUp; + boolean resizeDown; + + boolean sizing; + + HandlerRegistration dragHandler; + + int startX; + int startY; + int startW; + int startH; + int startTop; + int startLeft; + + @Override + public void onMouseDown(MouseDownEvent event) { + sizing = updateResizeFlags(event); + + if (sizing) { + startX = event.getClientX(); + startY = event.getClientY(); + + startW = content.getOffsetWidth(); + startH = content.getOffsetHeight(); + + startTop = getPopupTop(); + startLeft = getPopupLeft(); + + dragHandler = Event.addNativePreviewHandler(this); + + event.preventDefault(); + } + + } + + @Override + public void onMouseMove(MouseMoveEvent event) { + updateResizeFlags(event); + updateCursor(); + } + + private void updateCursor() { + Element c = content.getElement(); + if (resizeLeft) { + if (resizeUp) { + c.getStyle().setCursor(Cursor.NW_RESIZE); + } else if (resizeDown) { + c.getStyle().setCursor(Cursor.SW_RESIZE); + } else { + c.getStyle().setCursor(Cursor.W_RESIZE); + } + } else if (resizeRight) { + if (resizeUp) { + c.getStyle().setCursor(Cursor.NE_RESIZE); + } else if (resizeDown) { + c.getStyle().setCursor(Cursor.SE_RESIZE); + } else { + c.getStyle().setCursor(Cursor.E_RESIZE); + } + } else if (resizeUp) { + c.getStyle().setCursor(Cursor.N_RESIZE); + } else if (resizeDown) { + c.getStyle().setCursor(Cursor.S_RESIZE); + } else { + c.getStyle().setCursor(Cursor.AUTO); + } + } + + private boolean updateResizeFlags(MouseEvent event) { + Element c = getElement(); + int w = c.getOffsetWidth(); + int h = c.getOffsetHeight() - head.getOffsetHeight(); + int x = event.getRelativeX(c); + int y = event.getRelativeY(c) - head.getOffsetHeight(); + + resizeLeft = x < HANDLE_SIZE; + resizeRight = x > (w - HANDLE_SIZE); + resizeUp = y < HANDLE_SIZE; + resizeDown = y > (h - HANDLE_SIZE); + + return resizeLeft || resizeRight || resizeUp || resizeDown; + + } + + @Override + public void onPreviewNativeEvent(NativePreviewEvent event) { + if (event.getTypeInt() == Event.ONMOUSEMOVE) { + + int dx = event.getNativeEvent().getClientX() - startX; + int dy = event.getNativeEvent().getClientY() - startY; + + int minw = tabs.getOffsetWidth() + controls.getOffsetWidth(); + if (resizeLeft) { + int w = startW - dx; + if (w >= minw) { + content.setWidth(w + "px"); + setPopupPosition(startLeft + dx, getPopupTop()); + } + } else if (resizeRight) { + int w = startW + dx; + if (w >= minw) { + content.setWidth(w + "px"); + } + } + if (resizeUp) { + int h = startH - dy; + if (h >= MIN_HEIGHT) { + content.setHeight(h + "px"); + setPopupPosition(getPopupLeft(), startTop + dy); + } + } else if (resizeDown) { + int h = startH + dy; + if (h >= MIN_HEIGHT) { + content.setHeight(h + "px"); + } + } + + } else if (event.getTypeInt() == Event.ONMOUSEUP) { + dragHandler.removeHandler(); + dragHandler = null; + content.getElement().getStyle().setCursor(Cursor.AUTO); + sizing = false; + readPositionAndSize(); + writeStoredState(); + } + + event.cancel(); + } + + } + +} diff --git a/client/src/com/vaadin/client/extensions/javascriptmanager/JavaScriptManagerConnector.java b/client/src/com/vaadin/client/extensions/javascriptmanager/JavaScriptManagerConnector.java index ce79b4c64c..8e6ad25407 100644 --- a/client/src/com/vaadin/client/extensions/javascriptmanager/JavaScriptManagerConnector.java +++ b/client/src/com/vaadin/client/extensions/javascriptmanager/JavaScriptManagerConnector.java @@ -23,8 +23,8 @@ import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.JsArray; import com.google.gwt.json.client.JSONArray; import com.vaadin.client.ServerConnector; -import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.communication.JavaScriptMethodInvocation; +import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.extensions.AbstractExtensionConnector; import com.vaadin.shared.extension.javascriptmanager.ExecuteJavaScriptRpc; import com.vaadin.shared.extension.javascriptmanager.JavaScriptManagerState; diff --git a/client/src/com/vaadin/client/metadata/Property.java b/client/src/com/vaadin/client/metadata/Property.java index c0c375c14c..2e0ea49c88 100644 --- a/client/src/com/vaadin/client/metadata/Property.java +++ b/client/src/com/vaadin/client/metadata/Property.java @@ -88,4 +88,29 @@ public class Property { return getSignature(); } + /** + * Gets the property name formatted for displaying in a user interface. This + * returns a string where e.g. "camelCase" has been converted to + * "Camel case". + * + * @return the name of this property, formatted for humans to read + */ + public String getDisplayName() { + String camelCase = getName(); + StringBuilder b = new StringBuilder(camelCase.length()); + for (int i = 0; i < camelCase.length(); i++) { + char charAt = camelCase.charAt(i); + if (i == 0) { + // First char always upper case + b.append(Character.toUpperCase(charAt)); + } else if (Character.isUpperCase(charAt)) { + b.append(' '); + b.append(Character.toLowerCase(charAt)); + } else { + b.append(charAt); + } + } + return b.toString(); + } + } diff --git a/client/src/com/vaadin/client/metadata/TypeDataStore.java b/client/src/com/vaadin/client/metadata/TypeDataStore.java index dff02749f8..aa37d75dc8 100644 --- a/client/src/com/vaadin/client/metadata/TypeDataStore.java +++ b/client/src/com/vaadin/client/metadata/TypeDataStore.java @@ -41,7 +41,6 @@ public class TypeDataStore { private final FastStringSet delayedMethods = FastStringSet.create(); private final FastStringSet lastOnlyMethods = FastStringSet.create(); - private final FastStringSet hasGetTooltipInfo = FastStringSet.create(); private final FastStringMap<Type> returnTypes = FastStringMap.create(); private final FastStringMap<Invoker> invokers = FastStringMap.create(); @@ -291,22 +290,4 @@ public class TypeDataStore { public static boolean hasProperties(Type type) { return get().properties.containsKey(type.getSignature()); } - - /** - * @deprecated As of 7.0.1. This is just a hack to avoid breaking backwards - * compatibility and will be removed in Vaadin 7.1 - */ - @Deprecated - public void setHasGetTooltipInfo(Class<?> clazz) { - hasGetTooltipInfo.add(getType(clazz).getSignature()); - } - - /** - * @deprecated As of 7.0.1. This is just a hack to avoid breaking backwards - * compatibility and will be removed in Vaadin 7.1 - */ - @Deprecated - public static boolean getHasGetTooltipInfo(Class clazz) { - return get().hasGetTooltipInfo.contains(getType(clazz).getSignature()); - } } diff --git a/client/src/com/vaadin/client/ui/AbstractClickEventHandler.java b/client/src/com/vaadin/client/ui/AbstractClickEventHandler.java index 2f97d30ece..e91abe9663 100644 --- a/client/src/com/vaadin/client/ui/AbstractClickEventHandler.java +++ b/client/src/com/vaadin/client/ui/AbstractClickEventHandler.java @@ -78,9 +78,8 @@ public abstract class AbstractClickEventHandler implements MouseDownHandler, && elementUnderMouse == lastMouseDownTarget) { mouseUpPreviewMatched = true; } else { - VConsole.log("Ignoring mouseup from " - + elementUnderMouse + " when mousedown was on " - + lastMouseDownTarget); + VConsole.log("Ignoring mouseup from " + elementUnderMouse + + " when mousedown was on " + lastMouseDownTarget); } } } diff --git a/client/src/com/vaadin/client/ui/AbstractComponentConnector.java b/client/src/com/vaadin/client/ui/AbstractComponentConnector.java index ecd6abae08..13d1e6d56c 100644 --- a/client/src/com/vaadin/client/ui/AbstractComponentConnector.java +++ b/client/src/com/vaadin/client/ui/AbstractComponentConnector.java @@ -35,7 +35,6 @@ import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.metadata.NoDataException; import com.vaadin.client.metadata.Type; import com.vaadin.client.metadata.TypeData; -import com.vaadin.client.metadata.TypeDataStore; import com.vaadin.client.ui.datefield.PopupDateFieldConnector; import com.vaadin.client.ui.ui.UIConnector; import com.vaadin.shared.AbstractComponentState; @@ -205,11 +204,13 @@ public abstract class AbstractComponentConnector extends AbstractConnector } } - private void updateComponentSize() { - Profiler.enter("AbstractComponentConnector.updateComponentSize"); + protected void updateComponentSize() { + updateComponentSize(getState().width == null ? "" : getState().width, + getState().height == null ? "" : getState().height); + } - String newWidth = getState().width == null ? "" : getState().width; - String newHeight = getState().height == null ? "" : getState().height; + protected void updateComponentSize(String newWidth, String newHeight) { + Profiler.enter("AbstractComponentConnector.updateComponentSize"); // Parent should be updated if either dimension changed between relative // and non-relative @@ -428,40 +429,13 @@ public abstract class AbstractComponentConnector extends AbstractConnector } } - /** - * {@inheritDoc} - * - * <p> - * When overriding this method, {@link #hasTooltip()} should also be - * overridden to return true in all situations where this method might - * return a non-empty result. - * </p> - * - * @see ComponentConnector#getTooltipInfo(Element) - */ @Override public TooltipInfo getTooltipInfo(Element element) { return new TooltipInfo(getState().description, getState().errorMessage); } - /** - * Check whether there might be a tooltip for this component. The framework - * will only add event listeners for automatically handling tooltips (using - * {@link #getTooltipInfo(Element)}) if this method returns true. - * - * @return <code>true</code> if some part of the component might have a - * tooltip, otherwise <code>false</code> - */ - private boolean hasTooltip() { - /* - * Hack to avoid breaking backwards compatibility - use a generator to - * know whether there's a custom implementation of getTooltipInfo, and - * in that case always assume that there might be tooltip. - */ - if (TypeDataStore.getHasGetTooltipInfo(getClass())) { - return true; - } - + @Override + public boolean hasTooltip() { // Normally, there is a tooltip if description or errorMessage is set AbstractComponentState state = getState(); if (state.description != null && !state.description.equals("")) { diff --git a/client/src/com/vaadin/client/ui/AbstractConnector.java b/client/src/com/vaadin/client/ui/AbstractConnector.java index 2c76aa93fe..6855c5cd2d 100644 --- a/client/src/com/vaadin/client/ui/AbstractConnector.java +++ b/client/src/com/vaadin/client/ui/AbstractConnector.java @@ -439,6 +439,7 @@ public abstract class AbstractConnector implements ServerConnector, * * @see com.vaadin.client.ServerConnector#hasEventListener(java.lang.String) */ + @Override public boolean hasEventListener(String eventIdentifier) { Set<String> reg = getState().registeredEventListeners; return (reg != null && reg.contains(eventIdentifier)); diff --git a/client/src/com/vaadin/client/ui/AbstractHasComponentsConnector.java b/client/src/com/vaadin/client/ui/AbstractHasComponentsConnector.java index 4a6aefd082..d833f076e4 100644 --- a/client/src/com/vaadin/client/ui/AbstractHasComponentsConnector.java +++ b/client/src/com/vaadin/client/ui/AbstractHasComponentsConnector.java @@ -20,9 +20,9 @@ import java.util.List; import com.google.gwt.event.shared.HandlerRegistration; import com.vaadin.client.ComponentConnector; -import com.vaadin.client.HasComponentsConnector; import com.vaadin.client.ConnectorHierarchyChangeEvent; import com.vaadin.client.ConnectorHierarchyChangeEvent.ConnectorHierarchyChangeHandler; +import com.vaadin.client.HasComponentsConnector; public abstract class AbstractHasComponentsConnector extends AbstractComponentConnector implements HasComponentsConnector, diff --git a/client/src/com/vaadin/client/ui/VAbsoluteLayout.java b/client/src/com/vaadin/client/ui/VAbsoluteLayout.java index 88fbae6e88..dc080bcf7d 100644 --- a/client/src/com/vaadin/client/ui/VAbsoluteLayout.java +++ b/client/src/com/vaadin/client/ui/VAbsoluteLayout.java @@ -18,7 +18,6 @@ package com.vaadin.client.ui; import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Style; -import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.ui.ComplexPanel; @@ -304,108 +303,43 @@ public class VAbsoluteLayout extends ComplexPanel { * is added or removed */ public void layoutVertically() { + layout(); + } + + /** + * Performs an horizontal layout. Should be called when a widget is add or + * removed + */ + public void layoutHorizontally() { + layout(); + } + + private void layout() { for (Widget widget : getChildren()) { if (widget instanceof AbsoluteWrapper) { AbsoluteWrapper wrapper = (AbsoluteWrapper) widget; - - /* - * Cleanup old wrappers which have been left empty by other - * inner layouts moving the widget from the wrapper into their - * own hierarchy. This usually happens when a call to - * setWidget(widget) is done in an inner layout which - * automatically detaches the widget from the parent, in this - * case the wrapper, and re-attaches it somewhere else. This has - * to be done in the layout phase since the order of the - * hierarchy events are not defined. - */ - if (wrapper.getWidget() == null) { - wrapper.destroy(); - super.remove(wrapper); - continue; - } - - Style wrapperStyle = wrapper.getElement().getStyle(); - Style widgetStyle = wrapper.getWidget().getElement().getStyle(); - - // Ensure previous heights do not affect the measures - wrapperStyle.clearHeight(); - - if (widgetStyle.getHeight() != null - && widgetStyle.getHeight().endsWith("%")) { - int h; - if (wrapper.top != null && wrapper.bottom != null) { - h = wrapper.getOffsetHeight(); - } else if (wrapper.bottom != null) { - // top not defined, available space 0... bottom of - // wrapper - h = wrapper.getElement().getOffsetTop() - + wrapper.getOffsetHeight(); - } else { - // top defined or both undefined, available space == - // canvas - top - h = canvas.getOffsetHeight() - - wrapper.getElement().getOffsetTop(); - } - wrapperStyle.setHeight(h, Unit.PX); - } - wrapper.updateCaptionPosition(); } } } /** - * Performs an horizontal layout. Should be called when a widget is add or - * removed + * Cleanup old wrappers which have been left empty by other inner layouts + * moving the widget from the wrapper into their own hierarchy. This usually + * happens when a call to setWidget(widget) is done in an inner layout which + * automatically detaches the widget from the parent, in this case the + * wrapper, and re-attaches it somewhere else. This has to be done in the + * layout phase since the order of the hierarchy events are not defined. */ - public void layoutHorizontally() { + public void cleanupWrappers() { for (Widget widget : getChildren()) { if (widget instanceof AbsoluteWrapper) { AbsoluteWrapper wrapper = (AbsoluteWrapper) widget; - - /* - * Cleanup old wrappers which have been left empty by other - * inner layouts moving the widget from the wrapper into their - * own hierarchy. This usually happens when a call to - * setWidget(widget) is done in an inner layout which - * automatically detaches the widget from the parent, in this - * case the wrapper, and re-attaches it somewhere else. This has - * to be done in the layout phase since the order of the - * hierarchy events are not defined. - */ if (wrapper.getWidget() == null) { wrapper.destroy(); super.remove(wrapper); continue; } - - Style wrapperStyle = wrapper.getElement().getStyle(); - Style widgetStyle = wrapper.getWidget().getElement().getStyle(); - - // Ensure previous heights do not affect the measures - wrapperStyle.clearWidth(); - - if (widgetStyle.getWidth() != null - && widgetStyle.getWidth().endsWith("%")) { - int w; - if (wrapper.left != null && wrapper.right != null) { - w = wrapper.getOffsetWidth(); - } else if (wrapper.right != null) { - // left == null - // available width == right edge == offsetleft + width - w = wrapper.getOffsetWidth() - + wrapper.getElement().getOffsetLeft(); - } else { - // left != null && right == null || left == null && - // right == null - // available width == canvas width - offset left - w = canvas.getOffsetWidth() - - wrapper.getElement().getOffsetLeft(); - } - wrapperStyle.setWidth(w, Unit.PX); - } - - wrapper.updateCaptionPosition(); } } } diff --git a/client/src/com/vaadin/client/ui/VButton.java b/client/src/com/vaadin/client/ui/VButton.java index decfb7c0cc..c67a9f8747 100644 --- a/client/src/com/vaadin/client/ui/VButton.java +++ b/client/src/com/vaadin/client/ui/VButton.java @@ -16,6 +16,7 @@ package com.vaadin.client.ui; +import com.google.gwt.aria.client.Roles; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.NativeEvent; @@ -25,7 +26,6 @@ import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; -import com.google.gwt.user.client.ui.Accessibility; import com.google.gwt.user.client.ui.FocusWidget; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.BrowserInfo; @@ -103,7 +103,7 @@ public class VButton extends FocusWidget implements ClickHandler { | Event.KEYEVENTS); // Add a11y role "button" - Accessibility.setRole(getElement(), Accessibility.ROLE_BUTTON); + Roles.getButtonRole().set(getElement()); getElement().appendChild(wrapper); wrapper.appendChild(captionElement); @@ -357,14 +357,14 @@ public class VButton extends FocusWidget implements ClickHandler { this.enabled = enabled; if (!enabled) { cleanupCaptureState(); - Accessibility.removeState(getElement(), - Accessibility.STATE_PRESSED); + Roles.getButtonRole().setAriaDisabledState(getElement(), + !enabled); super.setTabIndex(-1); } else { - Accessibility.setState(getElement(), - Accessibility.STATE_PRESSED, "false"); + Roles.getButtonRole().removeAriaDisabledState(getElement()); super.setTabIndex(tabIndex); } + } } diff --git a/client/src/com/vaadin/client/ui/VCalendar.java b/client/src/com/vaadin/client/ui/VCalendar.java new file mode 100644 index 0000000000..c5c12f2d72 --- /dev/null +++ b/client/src/com/vaadin/client/ui/VCalendar.java @@ -0,0 +1,1446 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.Date; +import java.util.List; + +import com.google.gwt.event.dom.client.ContextMenuEvent; +import com.google.gwt.event.dom.client.ContextMenuHandler; +import com.google.gwt.i18n.client.DateTimeFormat; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.DockPanel; +import com.google.gwt.user.client.ui.ScrollPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.ui.calendar.schedule.CalendarDay; +import com.vaadin.client.ui.calendar.schedule.CalendarEvent; +import com.vaadin.client.ui.calendar.schedule.DayToolbar; +import com.vaadin.client.ui.calendar.schedule.MonthGrid; +import com.vaadin.client.ui.calendar.schedule.SimpleDayCell; +import com.vaadin.client.ui.calendar.schedule.SimpleDayToolbar; +import com.vaadin.client.ui.calendar.schedule.SimpleWeekToolbar; +import com.vaadin.client.ui.calendar.schedule.WeekGrid; +import com.vaadin.client.ui.calendar.schedule.WeeklyLongEvents; +import com.vaadin.shared.ui.calendar.DateConstants; + +/** + * Client side implementation for Calendar + * + * @since 7.1 + * @author Vaadin Ltd. + */ +public class VCalendar extends Composite { + + public static final String ATTR_FIRSTDAYOFWEEK = "firstDay"; + public static final String ATTR_LASTDAYOFWEEK = "lastDay"; + public static final String ATTR_FIRSTHOUROFDAY = "firstHour"; + public static final String ATTR_LASTHOUROFDAY = "lastHour"; + + // private boolean hideWeekends; + private String[] monthNames; + private String[] dayNames; + private boolean format; + private final DockPanel outer = new DockPanel(); + private int rows; + + private boolean rangeSelectAllowed = true; + private boolean rangeMoveAllowed = true; + private boolean eventResizeAllowed = true; + private boolean eventMoveAllowed = true; + + private final SimpleDayToolbar nameToolbar = new SimpleDayToolbar(); + + private final DayToolbar dayToolbar = new DayToolbar(this); + private final SimpleWeekToolbar weekToolbar; + private WeeklyLongEvents weeklyLongEvents; + private MonthGrid monthGrid; + private WeekGrid weekGrid; + private int intWidth = 0; + private int intHeight = 0; + + protected final DateTimeFormat dateformat_datetime = DateTimeFormat + .getFormat("yyyy-MM-dd HH:mm:ss"); + protected final DateTimeFormat dateformat_date = DateTimeFormat + .getFormat("yyyy-MM-dd"); + protected final DateTimeFormat time12format_date = DateTimeFormat + .getFormat("h:mm a"); + protected final DateTimeFormat time24format_date = DateTimeFormat + .getFormat("HH:mm"); + + private boolean readOnly = false; + private boolean disabled = false; + + private boolean isHeightUndefined = false; + + private boolean isWidthUndefined = false; + private int firstDay; + private int lastDay; + private int firstHour; + private int lastHour; + + /** + * Listener interface for listening to event click events + */ + public interface DateClickListener { + /** + * Triggered when a date was clicked + * + * @param date + * The date and time that was clicked + */ + void dateClick(String date); + } + + /** + * Listener interface for listening to week number click events + */ + public interface WeekClickListener { + /** + * Called when a week number was selected. + * + * @param event + * The format of the vent string is "<year>w<week>" + */ + void weekClick(String event); + } + + /** + * Listener interface for listening to forward events + */ + public interface ForwardListener { + + /** + * Called when the calendar should move one view forward + */ + void forward(); + } + + /** + * Listener interface for listening to backward events + */ + public interface BackwardListener { + + /** + * Called when the calendar should move one view backward + */ + void backward(); + } + + /** + * Listener interface for listening to selection events + */ + public interface RangeSelectListener { + + /** + * Called when a user selected a new event by highlighting an area of + * the calendar. + * + * FIXME Fix the value nonsense. + * + * @param value + * The format of the value string is + * "<year>:<start-minutes>:<end-minutes>" if called from the + * {@link SimpleWeekToolbar} and "<yyyy-MM-dd>TO<yyyy-MM-dd>" + * if called from {@link MonthGrid} + */ + void rangeSelected(String value); + } + + /** + * Listener interface for listening to click events + */ + public interface EventClickListener { + /** + * Called when an event was clicked + * + * @param event + * The event that was clicked + */ + void eventClick(CalendarEvent event); + } + + /** + * Listener interface for listening to event moved events. Occurs when a + * user drags an event to a new position + */ + public interface EventMovedListener { + /** + * Triggered when an event was dragged to a new position and the start + * and end dates was changed + * + * @param event + * The event that was moved + */ + void eventMoved(CalendarEvent event); + } + + /** + * Listener interface for when an event gets resized (its start or end date + * changes) + */ + public interface EventResizeListener { + /** + * Triggers when the time limits for the event was changed. + * + * @param event + * The event that was changed. The new time limits have been + * updated in the event before calling this method + */ + void eventResized(CalendarEvent event); + } + + /** + * Listener interface for listening to scroll events. + */ + public interface ScrollListener { + /** + * Triggered when the calendar is scrolled + * + * @param scrollPosition + * The scroll position in pixels as returned by + * {@link ScrollPanel#getScrollPosition()} + */ + void scroll(int scrollPosition); + } + + /** + * Listener interface for listening to mouse events. + */ + public interface MouseEventListener { + /** + * Triggered when a user wants an context menu + * + * @param event + * The context menu event + * + * @param widget + * The widget that the context menu should be added to + */ + void contextMenu(ContextMenuEvent event, Widget widget); + } + + /** + * Default constructor + */ + public VCalendar() { + weekToolbar = new SimpleWeekToolbar(this); + initWidget(outer); + setStylePrimaryName("v-calendar"); + blockSelect(getElement()); + } + + /** + * Hack for IE to not select text when dragging. + * + * @param e + * The element to apply the hack on + */ + private native void blockSelect(Element e) + /*-{ + e.onselectstart = function() { + return false; + } + + e.ondragstart = function() { + return false; + } + }-*/; + + private void updateEventsToWeekGrid(CalendarEvent[] events) { + List<CalendarEvent> allDayLong = new ArrayList<CalendarEvent>(); + List<CalendarEvent> belowDayLong = new ArrayList<CalendarEvent>(); + + for (CalendarEvent e : events) { + if (e.isAllDay()) { + // Event is set on one "allDay" event or more than one. + allDayLong.add(e); + + } else { + // Event is set only on one day. + belowDayLong.add(e); + } + } + + weeklyLongEvents.addEvents(allDayLong); + + for (CalendarEvent e : belowDayLong) { + weekGrid.addEvent(e); + } + } + + /** + * Adds events to the month grid + * + * @param events + * The events to add + * @param drawImmediately + * Should the grid be rendered immediately. (currently not in + * use) + * + */ + public void updateEventsToMonthGrid(Collection<CalendarEvent> events, + boolean drawImmediately) { + for (CalendarEvent e : sortEventsByDuration(events)) { + // FIXME Why is drawImmediately not used ????? + addEventToMonthGrid(e, false); + } + } + + private void addEventToMonthGrid(CalendarEvent e, boolean renderImmediately) { + Date when = e.getStart(); + Date to = e.getEnd(); + boolean eventAdded = false; + boolean inProgress = false; // Event adding has started + boolean eventMoving = false; + List<SimpleDayCell> dayCells = new ArrayList<SimpleDayCell>(); + List<SimpleDayCell> timeCells = new ArrayList<SimpleDayCell>(); + for (int row = 0; row < monthGrid.getRowCount(); row++) { + if (eventAdded) { + break; + } + for (int cell = 0; cell < monthGrid.getCellCount(row); cell++) { + SimpleDayCell sdc = (SimpleDayCell) monthGrid.getWidget(row, + cell); + if (isEventInDay(when, to, sdc.getDate()) + && isEventInDayWithTime(when, to, sdc.getDate(), + e.getEndTime(), e.isAllDay())) { + if (!eventMoving) { + eventMoving = sdc.getMoveEvent() != null; + } + long d = e.getRangeInMilliseconds(); + if ((d > 0 && d <= DateConstants.DAYINMILLIS) + && !e.isAllDay()) { + timeCells.add(sdc); + } else { + dayCells.add(sdc); + } + inProgress = true; + continue; + } else if (inProgress) { + eventAdded = true; + inProgress = false; + break; + } + } + } + + updateEventSlotIndex(e, dayCells); + updateEventSlotIndex(e, timeCells); + + for (SimpleDayCell sdc : dayCells) { + sdc.addCalendarEvent(e); + } + for (SimpleDayCell sdc : timeCells) { + sdc.addCalendarEvent(e); + } + + if (renderImmediately) { + reDrawAllMonthEvents(!eventMoving); + } + } + + /* + * We must also handle the special case when the event lasts exactly for 24 + * hours, thus spanning two days e.g. from 1.1.2001 00:00 to 2.1.2001 00:00. + * That special case still should span one day when rendered. + */ + @SuppressWarnings("deprecation") + // Date methods are not deprecated in GWT + private boolean isEventInDayWithTime(Date from, Date to, Date date, + Date endTime, boolean isAllDay) { + return (isAllDay || !(to.getDay() == date.getDay() + && from.getDay() != to.getDay() && isMidnight(endTime))); + } + + private void updateEventSlotIndex(CalendarEvent e, List<SimpleDayCell> cells) { + if (cells.isEmpty()) { + return; + } + + if (e.getSlotIndex() == -1) { + // Update slot index + int newSlot = -1; + for (SimpleDayCell sdc : cells) { + int slot = sdc.getEventCount(); + if (slot > newSlot) { + newSlot = slot; + } + } + newSlot++; + + for (int i = 0; i < newSlot; i++) { + // check for empty slot + if (isSlotEmpty(e, i, cells)) { + newSlot = i; + break; + } + } + e.setSlotIndex(newSlot); + } + } + + private void reDrawAllMonthEvents(boolean clearCells) { + for (int row = 0; row < monthGrid.getRowCount(); row++) { + for (int cell = 0; cell < monthGrid.getCellCount(row); cell++) { + SimpleDayCell sdc = (SimpleDayCell) monthGrid.getWidget(row, + cell); + sdc.reDraw(clearCells); + } + } + } + + private boolean isSlotEmpty(CalendarEvent addedEvent, int slotIndex, + List<SimpleDayCell> cells) { + for (SimpleDayCell sdc : cells) { + CalendarEvent e = sdc.getCalendarEvent(slotIndex); + if (e != null && !e.equals(addedEvent)) { + return false; + } + } + return true; + } + + /** + * Remove a month event from the view + * + * @param target + * The event to remove + * + * @param repaintImmediately + * Should we repaint after the event was removed? + */ + public void removeMonthEvent(CalendarEvent target, + boolean repaintImmediately) { + if (target != null && target.getSlotIndex() >= 0) { + // Remove event + for (int row = 0; row < monthGrid.getRowCount(); row++) { + for (int cell = 0; cell < monthGrid.getCellCount(row); cell++) { + SimpleDayCell sdc = (SimpleDayCell) monthGrid.getWidget( + row, cell); + if (sdc == null) { + return; + } + sdc.removeEvent(target, repaintImmediately); + } + } + } + } + + /** + * Updates an event in the month grid + * + * @param changedEvent + * The event that has changed + */ + public void updateEventToMonthGrid(CalendarEvent changedEvent) { + removeMonthEvent(changedEvent, true); + changedEvent.setSlotIndex(-1); + addEventToMonthGrid(changedEvent, true); + } + + /** + * Sort the event by how long they are + * + * @param events + * The events to sort + * @return An array where the events has been sorted + */ + public CalendarEvent[] sortEventsByDuration(Collection<CalendarEvent> events) { + CalendarEvent[] sorted = events + .toArray(new CalendarEvent[events.size()]); + Arrays.sort(sorted, getEventComparator()); + return sorted; + } + + /* + * Check if the given event occurs at the given date. + */ + private boolean isEventInDay(Date eventWhen, Date eventTo, Date gridDate) { + if (eventWhen.compareTo(gridDate) <= 0 + && eventTo.compareTo(gridDate) >= 0) { + + return true; + } + + return false; + } + + /** + * Re-render the week grid + * + * @param daysCount + * The amount of days to include in the week + * @param days + * The days + * @param today + * Todays date + * @param realDayNames + * The names of the dates + */ + @SuppressWarnings("deprecation") + public void updateWeekGrid(int daysCount, List<CalendarDay> days, + Date today, String[] realDayNames) { + weekGrid.setFirstHour(getFirstHourOfTheDay()); + weekGrid.setLastHour(getLastHourOfTheDay()); + weekGrid.getTimeBar().updateTimeBar(is24HFormat()); + + dayToolbar.clear(); + dayToolbar.addBackButton(); + dayToolbar.setVerticalSized(isHeightUndefined); + dayToolbar.setHorizontalSized(isWidthUndefined); + weekGrid.clearDates(); + weekGrid.setDisabled(isDisabledOrReadOnly()); + + for (CalendarDay day : days) { + String date = day.getDate(); + String localized_date_format = day.getLocalizedDateFormat(); + Date d = dateformat_date.parse(date); + int dayOfWeek = day.getDayOfWeek(); + if (dayOfWeek < getFirstDayNumber() + || dayOfWeek > getLastDayNumber()) { + continue; + } + boolean isToday = false; + int dayOfMonth = d.getDate(); + if (today.getDate() == dayOfMonth && today.getYear() == d.getYear() + && today.getMonth() == d.getMonth()) { + isToday = true; + } + dayToolbar.add(realDayNames[dayOfWeek - 1], date, + localized_date_format, isToday ? "today" : null); + weeklyLongEvents.addDate(d); + weekGrid.addDate(d); + if (isToday) { + weekGrid.setToday(d, today); + } + } + dayToolbar.addNextButton(); + } + + /** + * Updates the events in the Month view + * + * @param daysCount + * How many days there are + * @param daysUidl + * + * @param today + * Todays date + */ + @SuppressWarnings("deprecation") + public void updateMonthGrid(int daysCount, List<CalendarDay> days, + Date today) { + int columns = getLastDayNumber() - getFirstDayNumber() + 1; + rows = (int) Math.ceil(daysCount / (double) 7); + + monthGrid = new MonthGrid(this, rows, columns); + monthGrid.setEnabled(!isDisabledOrReadOnly()); + weekToolbar.removeAllRows(); + int pos = 0; + boolean monthNameDrawn = true; + boolean firstDayFound = false; + boolean lastDayFound = false; + + for (CalendarDay day : days) { + String date = day.getDate(); + Date d = dateformat_date.parse(date); + int dayOfWeek = day.getDayOfWeek(); + int week = day.getWeek(); + + int dayOfMonth = d.getDate(); + + // reset at start of each month + if (dayOfMonth == 1) { + monthNameDrawn = false; + if (firstDayFound) { + lastDayFound = true; + } + firstDayFound = true; + } + + if (dayOfWeek < getFirstDayNumber() + || dayOfWeek > getLastDayNumber()) { + continue; + } + int y = (pos / columns); + int x = pos - (y * columns); + if (x == 0 && daysCount > 7) { + // Add week to weekToolbar for navigation + weekToolbar.addWeek(week, d.getYear()); + } + final SimpleDayCell cell = new SimpleDayCell(this, y, x); + cell.setMonthGrid(monthGrid); + cell.setDate(d); + cell.addDomHandler(new ContextMenuHandler() { + @Override + public void onContextMenu(ContextMenuEvent event) { + if (mouseEventListener != null) { + event.preventDefault(); + event.stopPropagation(); + mouseEventListener.contextMenu(event, cell); + } + } + }, ContextMenuEvent.getType()); + + if (!firstDayFound) { + cell.addStyleDependentName("prev-month"); + } else if (lastDayFound) { + cell.addStyleDependentName("next-month"); + } + + if (dayOfMonth >= 1 && !monthNameDrawn) { + cell.setMonthNameVisible(true); + monthNameDrawn = true; + } + + if (today.getDate() == dayOfMonth && today.getYear() == d.getYear() + && today.getMonth() == d.getMonth()) { + cell.setToday(true); + + } + monthGrid.setWidget(y, x, cell); + pos++; + } + } + + public void setSizeForChildren(int newWidth, int newHeight) { + intWidth = newWidth; + intHeight = newHeight; + isWidthUndefined = intWidth == -1; + dayToolbar.setVerticalSized(isHeightUndefined); + dayToolbar.setHorizontalSized(isWidthUndefined); + recalculateWidths(); + recalculateHeights(); + } + + /** + * Recalculates the heights of the sub-components in the calendar + */ + protected void recalculateHeights() { + if (monthGrid != null) { + + if (intHeight == -1) { + monthGrid.addStyleDependentName("sizedheight"); + } else { + monthGrid.removeStyleDependentName("sizedheight"); + } + + monthGrid.updateCellSizes(intWidth - weekToolbar.getOffsetWidth(), + intHeight - nameToolbar.getOffsetHeight()); + weekToolbar.setHeightPX((intHeight == -1) ? intHeight : intHeight + - nameToolbar.getOffsetHeight()); + + } else if (weekGrid != null) { + weekGrid.setHeightPX((intHeight == -1) ? intHeight : intHeight + - weeklyLongEvents.getOffsetHeight() + - dayToolbar.getOffsetHeight()); + } + } + + /** + * Recalculates the widths of the sub-components in the calendar + */ + protected void recalculateWidths() { + if (!isWidthUndefined) { + nameToolbar.setWidthPX(intWidth); + dayToolbar.setWidthPX(intWidth); + + if (monthGrid != null) { + monthGrid.updateCellSizes( + intWidth - weekToolbar.getOffsetWidth(), intHeight + - nameToolbar.getOffsetHeight()); + } else if (weekGrid != null) { + weekGrid.setWidthPX(intWidth); + weeklyLongEvents.setWidthPX(weekGrid.getInternalWidth()); + } + } else { + dayToolbar.setWidthPX(intWidth); + nameToolbar.setWidthPX(intWidth); + + if (monthGrid != null) { + if (intWidth == -1) { + monthGrid.addStyleDependentName("sizedwidth"); + + } else { + monthGrid.removeStyleDependentName("sizedwidth"); + } + } else if (weekGrid != null) { + weekGrid.setWidthPX(intWidth); + weeklyLongEvents.setWidthPX(weekGrid.getInternalWidth()); + } + } + } + + /** + * Get the date format used to format dates only (excludes time) + * + * @return + */ + public DateTimeFormat getDateFormat() { + return dateformat_date; + } + + /** + * Get the time format used to format time only (excludes date) + * + * @return + */ + public DateTimeFormat getTimeFormat() { + if (is24HFormat()) { + return time24format_date; + } + return time12format_date; + } + + /** + * Get the date and time format to format the dates (includes both date and + * time) + * + * @return + */ + public DateTimeFormat getDateTimeFormat() { + return dateformat_datetime; + } + + /** + * Is the calendar either disabled or readonly + * + * @return + */ + public boolean isDisabledOrReadOnly() { + return disabled || readOnly; + } + + /** + * Is the component disabled + */ + public boolean isDisabled() { + return disabled; + } + + /** + * Is the component disabled + * + * @param disabled + * True if disabled + */ + public void setDisabled(boolean disabled) { + this.disabled = disabled; + } + + /** + * Is the component read-only + */ + public boolean isReadOnly() { + return readOnly; + } + + /** + * Is the component read-only + * + * @param readOnly + * True if component is readonly + */ + public void setReadOnly(boolean readOnly) { + this.readOnly = readOnly; + } + + /** + * Get the month grid component + * + * @return + */ + public MonthGrid getMonthGrid() { + return monthGrid; + } + + /** + * Get he week grid component + * + * @return + */ + public WeekGrid getWeekGrid() { + return weekGrid; + } + + /** + * Calculates correct size for all cells (size / amount of cells ) and + * distributes any overflow over all the cells. + * + * @param totalSize + * the total amount of size reserved for all cells + * @param numberOfCells + * the number of cells + * @param sizeModifier + * a modifier which is applied to all cells before distributing + * the overflow + * @return an integer array that contains the correct size for each cell + */ + public static int[] distributeSize(int totalSize, int numberOfCells, + int sizeModifier) { + int[] cellSizes = new int[numberOfCells]; + int startingSize = totalSize / numberOfCells; + int cellSizeOverFlow = totalSize % numberOfCells; + + for (int i = 0; i < numberOfCells; i++) { + cellSizes[i] = startingSize + sizeModifier; + } + + // distribute size overflow amongst all slots + int j = 0; + while (cellSizeOverFlow > 0) { + cellSizes[j]++; + cellSizeOverFlow--; + j++; + if (j >= numberOfCells) { + j = 0; + } + } + + // cellSizes[numberOfCells - 1] += cellSizeOverFlow; + + return cellSizes; + } + + /** + * Returns a comparator which can compare calendar events. + * + * @return + */ + public static Comparator<CalendarEvent> getEventComparator() { + return new Comparator<CalendarEvent>() { + + @Override + public int compare(CalendarEvent o1, CalendarEvent o2) { + if (o1.isAllDay() != o2.isAllDay()) { + if (o2.isAllDay()) { + return 1; + } + return -1; + } + + Long d1 = o1.getRangeInMilliseconds(); + Long d2 = o2.getRangeInMilliseconds(); + int r = 0; + if (!d1.equals(0L) && !d2.equals(0L)) { + r = d2.compareTo(d1); + return (r == 0) ? ((Integer) o2.getIndex()).compareTo(o1 + .getIndex()) : r; + } + + if (d2.equals(0L) && d1.equals(0L)) { + return ((Integer) o2.getIndex()).compareTo(o1.getIndex()); + } else if (d2.equals(0L) && d1 >= DateConstants.DAYINMILLIS) { + return -1; + } else if (d2.equals(0L) && d1 < DateConstants.DAYINMILLIS) { + return 1; + } else if (d1.equals(0L) && d2 >= DateConstants.DAYINMILLIS) { + return 1; + } else if (d1.equals(0L) && d2 < DateConstants.DAYINMILLIS) { + return -1; + } + r = d2.compareTo(d1); + return (r == 0) ? ((Integer) o2.getIndex()).compareTo(o1 + .getIndex()) : r; + } + }; + } + + /** + * Is the date at midnight + * + * @param date + * The date to check + * + * @return + */ + @SuppressWarnings("deprecation") + public static boolean isMidnight(Date date) { + return (date.getHours() == 0 && date.getMinutes() == 0 && date + .getSeconds() == 0); + } + + /** + * Are the dates equal (uses second resolution) + * + * @param date1 + * The first the to compare + * @param date2 + * The second date to compare + * @return + */ + @SuppressWarnings("deprecation") + public static boolean areDatesEqualToSecond(Date date1, Date date2) { + return date1.getYear() == date2.getYear() + && date1.getMonth() == date2.getMonth() + && date1.getDay() == date2.getDay() + && date1.getHours() == date2.getHours() + && date1.getSeconds() == date2.getSeconds(); + } + + /** + * Is the calendar event zero seconds long and is occurring at midnight + * + * @param event + * The event to check + * @return + */ + public static boolean isZeroLengthMidnightEvent(CalendarEvent event) { + return areDatesEqualToSecond(event.getStartTime(), event.getEndTime()) + && isMidnight(event.getEndTime()); + } + + /** + * Should the 24h time format be used + * + * @param format + * True if the 24h format should be used else the 12h format is + * used + */ + public void set24HFormat(boolean format) { + this.format = format; + } + + /** + * Is the 24h time format used + */ + public boolean is24HFormat() { + return format; + } + + /** + * Set the names of the week days + * + * @param names + * The names of the days (Monday, Thursday,...) + */ + public void setDayNames(String[] names) { + assert (names.length == 7); + dayNames = names; + } + + /** + * Get the names of the week days + */ + public String[] getDayNames() { + return dayNames; + } + + /** + * Set the names of the months + * + * @param names + * The names of the months (January, February,...) + */ + public void setMonthNames(String[] names) { + assert (names.length == 12); + monthNames = names; + } + + /** + * Get the month names + */ + public String[] getMonthNames() { + return monthNames; + } + + /** + * Set the number when a week starts + * + * @param dayNumber + * The number of the day + */ + public void setFirstDayNumber(int dayNumber) { + assert (dayNumber >= 1 && dayNumber <= 7); + firstDay = dayNumber; + } + + /** + * Get the number when a week starts + */ + public int getFirstDayNumber() { + return firstDay; + } + + /** + * Set the number when a week ends + * + * @param dayNumber + * The number of the day + */ + public void setLastDayNumber(int dayNumber) { + assert (dayNumber >= 1 && dayNumber <= 7); + lastDay = dayNumber; + } + + /** + * Get the number when a week ends + */ + public int getLastDayNumber() { + return lastDay; + } + + /** + * Set the number when a week starts + * + * @param dayNumber + * The number of the day + */ + public void setFirstHourOfTheDay(int hour) { + assert (hour >= 0 && hour <= 23); + firstHour = hour; + } + + /** + * Get the number when a week starts + */ + public int getFirstHourOfTheDay() { + return firstHour; + } + + /** + * Set the number when a week ends + * + * @param dayNumber + * The number of the day + */ + public void setLastHourOfTheDay(int hour) { + assert (hour >= 0 && hour <= 23); + lastHour = hour; + } + + /** + * Get the number when a week ends + */ + public int getLastHourOfTheDay() { + return lastHour; + } + + /** + * Re-renders the whole week view + * + * @param scroll + * The amount of pixels to scroll the week view + * @param today + * Todays date + * @param daysInMonth + * How many days are there in the month + * @param firstDayOfWeek + * The first day of the week + * @param events + * The events to render + */ + public void updateWeekView(int scroll, Date today, int daysInMonth, + int firstDayOfWeek, Collection<CalendarEvent> events, + List<CalendarDay> days) { + + while (outer.getWidgetCount() > 0) { + outer.remove(0); + } + + monthGrid = null; + String[] realDayNames = new String[getDayNames().length]; + int j = 0; + + if (firstDayOfWeek == 2) { + for (int i = 1; i < getDayNames().length; i++) { + realDayNames[j++] = getDayNames()[i]; + } + realDayNames[j] = getDayNames()[0]; + } else { + for (int i = 0; i < getDayNames().length; i++) { + realDayNames[j++] = getDayNames()[i]; + } + + } + + weeklyLongEvents = new WeeklyLongEvents(this); + if (weekGrid == null) { + weekGrid = new WeekGrid(this, is24HFormat()); + } + updateWeekGrid(daysInMonth, days, today, realDayNames); + updateEventsToWeekGrid(sortEventsByDuration(events)); + outer.add(dayToolbar, DockPanel.NORTH); + outer.add(weeklyLongEvents, DockPanel.NORTH); + outer.add(weekGrid, DockPanel.SOUTH); + weekGrid.setVerticalScrollPosition(scroll); + } + + /** + * Re-renders the whole month view + * + * @param firstDayOfWeek + * The first day of the week + * @param today + * Todays date + * @param daysInMonth + * Amount of days in the month + * @param events + * The events to render + * @param days + * The day information + */ + public void updateMonthView(int firstDayOfWeek, Date today, + int daysInMonth, Collection<CalendarEvent> events, + List<CalendarDay> days) { + + // Remove all week numbers from bar + while (outer.getWidgetCount() > 0) { + outer.remove(0); + } + + int firstDay = getFirstDayNumber(); + int lastDay = getLastDayNumber(); + int daysPerWeek = lastDay - firstDay + 1; + int j = 0; + + String[] dayNames = getDayNames(); + String[] realDayNames = new String[daysPerWeek]; + + if (firstDayOfWeek == 2) { + for (int i = firstDay; i < lastDay + 1; i++) { + if (i == 7) { + realDayNames[j++] = dayNames[0]; + } else { + realDayNames[j++] = dayNames[i]; + } + } + } else { + for (int i = firstDay - 1; i < lastDay; i++) { + realDayNames[j++] = dayNames[i]; + } + } + + nameToolbar.setDayNames(realDayNames); + + weeklyLongEvents = null; + weekGrid = null; + + updateMonthGrid(daysInMonth, days, today); + + outer.add(nameToolbar, DockPanel.NORTH); + outer.add(weekToolbar, DockPanel.WEST); + weekToolbar.updateCellHeights(); + outer.add(monthGrid, DockPanel.CENTER); + + updateEventsToMonthGrid(events, false); + } + + private DateClickListener dateClickListener; + + /** + * Sets the listener for listening to event clicks + * + * @param listener + * The listener to use + */ + public void setListener(DateClickListener listener) { + dateClickListener = listener; + } + + /** + * Gets the listener for listening to event clicks + * + * @return + */ + public DateClickListener getDateClickListener() { + return dateClickListener; + } + + private ForwardListener forwardListener; + + /** + * Set the listener which listens to forward events from the calendar + * + * @param listener + * The listener to use + */ + public void setListener(ForwardListener listener) { + forwardListener = listener; + } + + /** + * Get the listener which listens to forward events from the calendar + * + * @return + */ + public ForwardListener getForwardListener() { + return forwardListener; + } + + private BackwardListener backwardListener; + + /** + * Set the listener which listens to backward events from the calendar + * + * @param listener + * The listener to use + */ + public void setListener(BackwardListener listener) { + backwardListener = listener; + } + + /** + * Set the listener which listens to backward events from the calendar + * + * @return + */ + public BackwardListener getBackwardListener() { + return backwardListener; + } + + private WeekClickListener weekClickListener; + + /** + * Set the listener that listens to user clicking on the week numbers + * + * @param listener + * The listener to use + */ + public void setListener(WeekClickListener listener) { + weekClickListener = listener; + } + + /** + * Get the listener that listens to user clicking on the week numbers + * + * @return + */ + public WeekClickListener getWeekClickListener() { + return weekClickListener; + } + + private RangeSelectListener rangeSelectListener; + + /** + * Set the listener that listens to the user highlighting a region in the + * calendar + * + * @param listener + * The listener to use + */ + public void setListener(RangeSelectListener listener) { + rangeSelectListener = listener; + } + + /** + * Get the listener that listens to the user highlighting a region in the + * calendar + * + * @return + */ + public RangeSelectListener getRangeSelectListener() { + return rangeSelectListener; + } + + private EventClickListener eventClickListener; + + /** + * Get the listener that listens to the user clicking on the events + */ + public EventClickListener getEventClickListener() { + return eventClickListener; + } + + /** + * Set the listener that listens to the user clicking on the events + * + * @param listener + * The listener to use + */ + public void setListener(EventClickListener listener) { + eventClickListener = listener; + } + + private EventMovedListener eventMovedListener; + + /** + * Get the listener that listens to when event is dragged to a new location + * + * @return + */ + public EventMovedListener getEventMovedListener() { + return eventMovedListener; + } + + /** + * Set the listener that listens to when event is dragged to a new location + * + * @param eventMovedListener + * The listener to use + */ + public void setListener(EventMovedListener eventMovedListener) { + this.eventMovedListener = eventMovedListener; + } + + private ScrollListener scrollListener; + + /** + * Get the listener that listens to when the calendar widget is scrolled + * + * @return + */ + public ScrollListener getScrollListener() { + return scrollListener; + } + + /** + * Set the listener that listens to when the calendar widget is scrolled + * + * @param scrollListener + * The listener to use + */ + public void setListener(ScrollListener scrollListener) { + this.scrollListener = scrollListener; + } + + private EventResizeListener eventResizeListener; + + /** + * Get the listener that listens to when an events time limits are being + * adjusted + * + * @return + */ + public EventResizeListener getEventResizeListener() { + return eventResizeListener; + } + + /** + * Set the listener that listens to when an events time limits are being + * adjusted + * + * @param eventResizeListener + * The listener to use + */ + public void setListener(EventResizeListener eventResizeListener) { + this.eventResizeListener = eventResizeListener; + } + + private MouseEventListener mouseEventListener; + private boolean forwardNavigationEnabled = true; + private boolean backwardNavigationEnabled = true; + + /** + * Get the listener that listen to mouse events + * + * @return + */ + public MouseEventListener getMouseEventListener() { + return mouseEventListener; + } + + /** + * Set the listener that listen to mouse events + * + * @param mouseEventListener + * The listener to use + */ + public void setListener(MouseEventListener mouseEventListener) { + this.mouseEventListener = mouseEventListener; + } + + /** + * Is selecting a range allowed? + */ + public boolean isRangeSelectAllowed() { + return rangeSelectAllowed; + } + + /** + * Set selecting a range allowed + * + * @param rangeSelectAllowed + * Should selecting a range be allowed + */ + public void setRangeSelectAllowed(boolean rangeSelectAllowed) { + this.rangeSelectAllowed = rangeSelectAllowed; + } + + /** + * Is moving a range allowed + * + * @return + */ + public boolean isRangeMoveAllowed() { + return rangeMoveAllowed; + } + + /** + * Is moving a range allowed + * + * @param rangeMoveAllowed + * Is it allowed + */ + public void setRangeMoveAllowed(boolean rangeMoveAllowed) { + this.rangeMoveAllowed = rangeMoveAllowed; + } + + /** + * Is resizing an event allowed + */ + public boolean isEventResizeAllowed() { + return eventResizeAllowed; + } + + /** + * Is resizing an event allowed + * + * @param eventResizeAllowed + * True if allowed false if not + */ + public void setEventResizeAllowed(boolean eventResizeAllowed) { + this.eventResizeAllowed = eventResizeAllowed; + } + + /** + * Is moving an event allowed + */ + public boolean isEventMoveAllowed() { + return eventMoveAllowed; + } + + /** + * Is moving an event allowed + * + * @param eventMoveAllowed + * True if moving is allowed, false if not + */ + public void setEventMoveAllowed(boolean eventMoveAllowed) { + this.eventMoveAllowed = eventMoveAllowed; + } + + public boolean isBackwardNavigationEnabled() { + return backwardNavigationEnabled; + } + + public void setBackwardNavigationEnabled(boolean enabled) { + backwardNavigationEnabled = enabled; + } + + public boolean isForwardNavigationEnabled() { + return forwardNavigationEnabled; + } + + public void setForwardNavigationEnabled(boolean enabled) { + forwardNavigationEnabled = enabled; + } +} diff --git a/client/src/com/vaadin/client/ui/VCalendarPanel.java b/client/src/com/vaadin/client/ui/VCalendarPanel.java index e234cc911c..311932b819 100644 --- a/client/src/com/vaadin/client/ui/VCalendarPanel.java +++ b/client/src/com/vaadin/client/ui/VCalendarPanel.java @@ -19,6 +19,8 @@ package com.vaadin.client.ui; import java.util.Date; import java.util.Iterator; +import com.google.gwt.aria.client.Roles; +import com.google.gwt.aria.client.SelectedValue; import com.google.gwt.dom.client.Node; import com.google.gwt.event.dom.client.BlurEvent; import com.google.gwt.event.dom.client.BlurHandler; @@ -40,6 +42,7 @@ import com.google.gwt.event.dom.client.MouseOutEvent; import com.google.gwt.event.dom.client.MouseOutHandler; import com.google.gwt.event.dom.client.MouseUpEvent; import com.google.gwt.event.dom.client.MouseUpHandler; +import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.Button; @@ -118,6 +121,8 @@ public class VCalendarPanel extends FocusableFlexTable implements private static final String CN_OFFMONTH = "offmonth"; + private static final String CN_OUTSIDE_RANGE = "outside-range"; + /** * Represents a click handler for when a user selects a value by using the * mouse @@ -133,6 +138,9 @@ public class VCalendarPanel extends FocusableFlexTable implements @Override public void onClick(ClickEvent event) { Date newDate = ((Day) event.getSource()).getDate(); + if (!isDateInsideRange(newDate, Resolution.DAY)) { + return; + } if (newDate.getMonth() != displayedMonth.getMonth() || newDate.getYear() != displayedMonth.getYear()) { // If an off-month date was clicked, we must change the @@ -175,9 +183,9 @@ public class VCalendarPanel extends FocusableFlexTable implements private boolean showISOWeekNumbers; - private Date displayedMonth; + private FocusedDate displayedMonth; - private Date focusedDate; + private FocusedDate focusedDate; private Day selectedDay; @@ -198,8 +206,9 @@ public class VCalendarPanel extends FocusableFlexTable implements private boolean initialRenderDone = false; public VCalendarPanel() { - + getElement().setId(DOM.createUniqueId()); setStyleName(VDateField.CLASSNAME + "-calendarpanel"); + Roles.getGridRole().set(getElement()); /* * Firefox auto-repeat works correctly only if we use a key press @@ -267,6 +276,8 @@ public class VCalendarPanel extends FocusableFlexTable implements private void selectDate(Date date) { if (selectedDay != null) { selectedDay.removeStyleDependentName(CN_SELECTED); + Roles.getGridcellRole().removeAriaSelectedState( + selectedDay.getElement()); } int rowCount = days.getRowCount(); @@ -279,6 +290,8 @@ public class VCalendarPanel extends FocusableFlexTable implements if (curday.getDate().equals(date)) { curday.addStyleDependentName(CN_SELECTED); selectedDay = curday; + Roles.getGridcellRole().setAriaSelectedState( + selectedDay.getElement(), SelectedValue.TRUE); return; } } @@ -290,7 +303,7 @@ public class VCalendarPanel extends FocusableFlexTable implements * Updates year, month, day from focusedDate to value */ private void selectFocused() { - if (focusedDate != null) { + if (focusedDate != null && isDateInsideRange(focusedDate, resolution)) { if (value == null) { // No previously selected value (set to null on server side). // Create a new date using current date and time @@ -397,10 +410,13 @@ public class VCalendarPanel extends FocusableFlexTable implements prevMonth = new VEventButton(); prevMonth.setHTML("‹"); prevMonth.setStyleName("v-button-prevmonth"); + prevMonth.setTabIndex(-1); + nextMonth = new VEventButton(); nextMonth.setHTML("›"); nextMonth.setStyleName("v-button-nextmonth"); + nextMonth.setTabIndex(-1); setWidget(0, 3, nextMonth); @@ -414,18 +430,23 @@ public class VCalendarPanel extends FocusableFlexTable implements } if (prevYear == null) { + prevYear = new VEventButton(); prevYear.setHTML("«"); prevYear.setStyleName("v-button-prevyear"); + prevYear.setTabIndex(-1); nextYear = new VEventButton(); nextYear.setHTML("»"); nextYear.setStyleName("v-button-nextyear"); + nextYear.setTabIndex(-1); setWidget(0, 0, prevYear); setWidget(0, 4, nextYear); } + updateControlButtonRangeStyles(needsMonth); + final String monthName = needsMonth ? getDateTimeService().getMonth( displayedMonth.getMonth()) : ""; final int year = displayedMonth.getYear() + 1900; @@ -446,6 +467,48 @@ public class VCalendarPanel extends FocusableFlexTable implements + "</span>"); } + private void updateControlButtonRangeStyles(boolean needsMonth) { + + if (focusedDate == null) { + return; + } + + if (needsMonth) { + Date prevMonthDate = (Date) focusedDate.clone(); + removeOneMonth(prevMonthDate); + + if (!isDateInsideRange(prevMonthDate, Resolution.MONTH)) { + prevMonth.addStyleName(CN_OUTSIDE_RANGE); + } else { + prevMonth.removeStyleName(CN_OUTSIDE_RANGE); + } + Date nextMonthDate = (Date) focusedDate.clone(); + addOneMonth(nextMonthDate); + if (!isDateInsideRange(nextMonthDate, Resolution.MONTH)) { + nextMonth.addStyleName(CN_OUTSIDE_RANGE); + } else { + nextMonth.removeStyleName(CN_OUTSIDE_RANGE); + } + } + + Date prevYearDate = (Date) focusedDate.clone(); + prevYearDate.setYear(prevYearDate.getYear() - 1); + if (!isDateInsideRange(prevYearDate, Resolution.YEAR)) { + prevYear.addStyleName(CN_OUTSIDE_RANGE); + } else { + prevYear.removeStyleName(CN_OUTSIDE_RANGE); + } + + Date nextYearDate = (Date) focusedDate.clone(); + nextYearDate.setYear(nextYearDate.getYear() + 1); + if (!isDateInsideRange(nextYearDate, Resolution.YEAR)) { + nextYear.addStyleName(CN_OUTSIDE_RANGE); + } else { + nextYear.removeStyleName(CN_OUTSIDE_RANGE); + } + + } + private DateTimeService getDateTimeService() { return dateTimeService; } @@ -470,6 +533,107 @@ public class VCalendarPanel extends FocusableFlexTable implements } /** + * Checks inclusively whether a date is inside a range of dates or not. + * + * @param date + * @return + */ + private boolean isDateInsideRange(Date date, Resolution minResolution) { + assert (date != null); + + return isAcceptedByRangeEnd(date, minResolution) + && isAcceptedByRangeStart(date, minResolution); + } + + /** + * Accepts dates greater than or equal to rangeStart, depending on the + * resolution. If the resolution is set to DAY, the range will compare on a + * day-basis. If the resolution is set to YEAR, only years are compared. So + * even if the range is set to one millisecond in next year, also next year + * will be included. + * + * @param date + * @param minResolution + * @return + */ + private boolean isAcceptedByRangeStart(Date date, Resolution minResolution) { + assert (date != null); + + // rangeStart == null means that we accept all values below rangeEnd + if (rangeStart == null) { + return true; + } + + Date valueDuplicate = (Date) date.clone(); + Date rangeStartDuplicate = (Date) rangeStart.clone(); + + if (minResolution == Resolution.YEAR) { + return valueDuplicate.getYear() >= rangeStartDuplicate.getYear(); + } + if (minResolution == Resolution.MONTH) { + valueDuplicate = clearDateBelowMonth(valueDuplicate); + rangeStartDuplicate = clearDateBelowMonth(rangeStartDuplicate); + } else { + valueDuplicate = clearDateBelowDay(valueDuplicate); + rangeStartDuplicate = clearDateBelowDay(rangeStartDuplicate); + } + + return !rangeStartDuplicate.after(valueDuplicate); + } + + /** + * Accepts dates earlier than or equal to rangeStart, depending on the + * resolution. If the resolution is set to DAY, the range will compare on a + * day-basis. If the resolution is set to YEAR, only years are compared. So + * even if the range is set to one millisecond in next year, also next year + * will be included. + * + * @param date + * @param minResolution + * @return + */ + private boolean isAcceptedByRangeEnd(Date date, Resolution minResolution) { + assert (date != null); + + // rangeEnd == null means that we accept all values above rangeStart + if (rangeEnd == null) { + return true; + } + + Date valueDuplicate = (Date) date.clone(); + Date rangeEndDuplicate = (Date) rangeEnd.clone(); + + if (minResolution == Resolution.YEAR) { + return valueDuplicate.getYear() <= rangeEndDuplicate.getYear(); + } + if (minResolution == Resolution.MONTH) { + valueDuplicate = clearDateBelowMonth(valueDuplicate); + rangeEndDuplicate = clearDateBelowMonth(rangeEndDuplicate); + } else { + valueDuplicate = clearDateBelowDay(valueDuplicate); + rangeEndDuplicate = clearDateBelowDay(rangeEndDuplicate); + } + + return !rangeEndDuplicate.before(valueDuplicate); + + } + + private static Date clearDateBelowMonth(Date date) { + date.setDate(1); + return clearDateBelowDay(date); + } + + private static Date clearDateBelowDay(Date date) { + date.setHours(0); + date.setMinutes(0); + date.setSeconds(0); + // Clearing milliseconds + long time = date.getTime() / 1000; + date = new Date(time * 1000); + return date; + } + + /** * Builds the day and time selectors of the calendar. */ private void buildCalendarBody() { @@ -528,6 +692,10 @@ public class VCalendarPanel extends FocusableFlexTable implements } else { days.setHTML(headerRow, firstWeekdayColumn + i, ""); } + + Roles.getColumnheaderRole().set( + days.getCellFormatter().getElement(headerRow, + firstWeekdayColumn + i)); } // Zero out hours, minutes, seconds, and milliseconds to compare dates @@ -551,12 +719,20 @@ public class VCalendarPanel extends FocusableFlexTable implements for (int dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++) { // Actually write the day of month - Day day = new Day((Date) curr.clone()); + Date dayDate = (Date) curr.clone(); + Day day = new Day(dayDate); + day.setStyleName(parent.getStylePrimaryName() + "-calendarpanel-day"); + if (!isDateInsideRange(dayDate, Resolution.DAY)) { + day.addStyleDependentName(CN_OUTSIDE_RANGE); + } + if (curr.equals(selectedDate)) { day.addStyleDependentName(CN_SELECTED); + Roles.getGridcellRole().setAriaSelectedState( + day.getElement(), SelectedValue.TRUE); selectedDay = day; } if (curr.equals(today)) { @@ -574,10 +750,14 @@ public class VCalendarPanel extends FocusableFlexTable implements } days.setWidget(weekOfMonth, firstWeekdayColumn + dayOfWeek, day); + Roles.getGridcellRole().set( + days.getCellFormatter().getElement(weekOfMonth, + firstWeekdayColumn + dayOfWeek)); // ISO week numbers if requested days.getCellFormatter().setVisible(weekOfMonth, weekColumn, isShowISOWeekNumbers()); + if (isShowISOWeekNumbers()) { final String baseCssClass = parent.getStylePrimaryName() + "-calendarpanel-weeknumber"; @@ -615,8 +795,9 @@ public class VCalendarPanel extends FocusableFlexTable implements if (focusedDate == null) { Date now = new Date(); // focusedDate must have zero hours, mins, secs, millisecs - focusedDate = new Date(now.getYear(), now.getMonth(), now.getDate()); - displayedMonth = new Date(now.getYear(), now.getMonth(), 1); + focusedDate = new FocusedDate(now.getYear(), now.getMonth(), + now.getDate()); + displayedMonth = new FocusedDate(now.getYear(), now.getMonth(), 1); } if (getResolution().getCalendarField() <= Resolution.MONTH @@ -653,6 +834,17 @@ public class VCalendarPanel extends FocusableFlexTable implements * Moves the focus forward the given number of days. */ private void focusNextDay(int days) { + if (focusedDate == null) { + return; + } + + Date focusCopy = ((Date) focusedDate.clone()); + focusCopy.setDate(focusedDate.getDate() + days); + if (!isDateInsideRange(focusCopy, resolution)) { + // If not inside allowed range, then do not move anything + return; + } + int oldMonth = focusedDate.getMonth(); int oldYear = focusedDate.getYear(); focusedDate.setDate(focusedDate.getDate() + days); @@ -662,6 +854,7 @@ public class VCalendarPanel extends FocusableFlexTable implements // Month did not change, only move the selection focusDay(focusedDate); } else { + // If the month changed we need to re-render the calendar displayedMonth.setMonth(focusedDate.getMonth()); displayedMonth.setYear(focusedDate.getYear()); @@ -681,38 +874,83 @@ public class VCalendarPanel extends FocusableFlexTable implements */ private void focusNextMonth() { - int currentMonth = focusedDate.getMonth(); - focusedDate.setMonth(currentMonth + 1); + if (focusedDate == null) { + return; + } + // Trying to request next month + Date requestedNextMonthDate = (Date) focusedDate.clone(); + addOneMonth(requestedNextMonthDate); + + if (!isDateInsideRange(requestedNextMonthDate, Resolution.MONTH)) { + return; + } + + // Now also checking whether the day is inside the range or not. If not + // inside, + // correct it + if (!isDateInsideRange(requestedNextMonthDate, Resolution.DAY)) { + requestedNextMonthDate = adjustDateToFitInsideRange(requestedNextMonthDate); + } + focusedDate.setYear(requestedNextMonthDate.getYear()); + focusedDate.setMonth(requestedNextMonthDate.getMonth()); + focusedDate.setDate(requestedNextMonthDate.getDate()); + displayedMonth.setMonth(displayedMonth.getMonth() + 1); + + renderCalendar(); + } + + private static void addOneMonth(Date date) { + int currentMonth = date.getMonth(); int requestedMonth = (currentMonth + 1) % 12; + date.setMonth(date.getMonth() + 1); + /* * If the selected value was e.g. 31.3 the new value would be 31.4 but * this value is invalid so the new value will be 1.5. This is taken * care of by decreasing the value until we have the correct month. */ - while (focusedDate.getMonth() != requestedMonth) { - focusedDate.setDate(focusedDate.getDate() - 1); + while (date.getMonth() != requestedMonth) { + date.setDate(date.getDate() - 1); } - displayedMonth.setMonth(displayedMonth.getMonth() + 1); - - renderCalendar(); } - /** - * Selects the previous month - */ - private void focusPreviousMonth() { - int currentMonth = focusedDate.getMonth(); - focusedDate.setMonth(currentMonth - 1); + private static void removeOneMonth(Date date) { + int currentMonth = date.getMonth(); + + date.setMonth(date.getMonth() - 1); /* * If the selected value was e.g. 31.12 the new value would be 31.11 but * this value is invalid so the new value will be 1.12. This is taken * care of by decreasing the value until we have the correct month. */ - while (focusedDate.getMonth() == currentMonth) { - focusedDate.setDate(focusedDate.getDate() - 1); + while (date.getMonth() == currentMonth) { + date.setDate(date.getDate() - 1); + } + } + + /** + * Selects the previous month + */ + private void focusPreviousMonth() { + + if (focusedDate == null) { + return; } + Date requestedPreviousMonthDate = (Date) focusedDate.clone(); + removeOneMonth(requestedPreviousMonthDate); + + if (!isDateInsideRange(requestedPreviousMonthDate, Resolution.MONTH)) { + return; + } + + if (!isDateInsideRange(requestedPreviousMonthDate, Resolution.DAY)) { + requestedPreviousMonthDate = adjustDateToFitInsideRange(requestedPreviousMonthDate); + } + focusedDate.setYear(requestedPreviousMonthDate.getYear()); + focusedDate.setMonth(requestedPreviousMonthDate.getMonth()); + focusedDate.setDate(requestedPreviousMonthDate.getDate()); displayedMonth.setMonth(displayedMonth.getMonth() - 1); renderCalendar(); @@ -722,16 +960,41 @@ public class VCalendarPanel extends FocusableFlexTable implements * Selects the previous year */ private void focusPreviousYear(int years) { - int currentMonth = focusedDate.getMonth(); - focusedDate.setYear(focusedDate.getYear() - years); - displayedMonth.setYear(displayedMonth.getYear() - years); - /* - * If the focused date was a leap day (Feb 29), the new date becomes Mar - * 1 if the new year is not also a leap year. Set it to Feb 28 instead. - */ - if (focusedDate.getMonth() != currentMonth) { - focusedDate.setDate(0); + + if (focusedDate == null) { + return; + } + Date previousYearDate = (Date) focusedDate.clone(); + previousYearDate.setYear(previousYearDate.getYear() - years); + // Do not focus if not inside range + if (!isDateInsideRange(previousYearDate, Resolution.YEAR)) { + return; + } + // If we remove one year, but have to roll back a bit, fit it + // into the calendar. Also the months have to be changed + if (!isDateInsideRange(previousYearDate, Resolution.DAY)) { + previousYearDate = adjustDateToFitInsideRange(previousYearDate); + + focusedDate.setYear(previousYearDate.getYear()); + focusedDate.setMonth(previousYearDate.getMonth()); + focusedDate.setDate(previousYearDate.getDate()); + displayedMonth.setYear(previousYearDate.getYear()); + displayedMonth.setMonth(previousYearDate.getMonth()); + } else { + + int currentMonth = focusedDate.getMonth(); + focusedDate.setYear(focusedDate.getYear() - years); + displayedMonth.setYear(displayedMonth.getYear() - years); + /* + * If the focused date was a leap day (Feb 29), the new date becomes + * Mar 1 if the new year is not also a leap year. Set it to Feb 28 + * instead. + */ + if (focusedDate.getMonth() != currentMonth) { + focusedDate.setDate(0); + } } + renderCalendar(); } @@ -739,16 +1002,41 @@ public class VCalendarPanel extends FocusableFlexTable implements * Selects the next year */ private void focusNextYear(int years) { - int currentMonth = focusedDate.getMonth(); - focusedDate.setYear(focusedDate.getYear() + years); - displayedMonth.setYear(displayedMonth.getYear() + years); - /* - * If the focused date was a leap day (Feb 29), the new date becomes Mar - * 1 if the new year is not also a leap year. Set it to Feb 28 instead. - */ - if (focusedDate.getMonth() != currentMonth) { - focusedDate.setDate(0); + + if (focusedDate == null) { + return; } + Date nextYearDate = (Date) focusedDate.clone(); + nextYearDate.setYear(nextYearDate.getYear() + years); + // Do not focus if not inside range + if (!isDateInsideRange(nextYearDate, Resolution.YEAR)) { + return; + } + // If we add one year, but have to roll back a bit, fit it + // into the calendar. Also the months have to be changed + if (!isDateInsideRange(nextYearDate, Resolution.DAY)) { + nextYearDate = adjustDateToFitInsideRange(nextYearDate); + + focusedDate.setYear(nextYearDate.getYear()); + focusedDate.setMonth(nextYearDate.getMonth()); + focusedDate.setDate(nextYearDate.getDate()); + displayedMonth.setYear(nextYearDate.getYear()); + displayedMonth.setMonth(nextYearDate.getMonth()); + } else { + + int currentMonth = focusedDate.getMonth(); + focusedDate.setYear(focusedDate.getYear() + years); + displayedMonth.setYear(displayedMonth.getYear() + years); + /* + * If the focused date was a leap day (Feb 29), the new date becomes + * Mar 1 if the new year is not also a leap year. Set it to Feb 28 + * instead. + */ + if (focusedDate.getMonth() != currentMonth) { + focusedDate.setDate(0); + } + } + renderCalendar(); } @@ -1062,9 +1350,10 @@ public class VCalendarPanel extends FocusableFlexTable implements */ } else if (keycode == getResetKey() && !shift) { // Restore showing value the selected value - focusedDate = new Date(value.getYear(), value.getMonth(), + focusedDate = new FocusedDate(value.getYear(), value.getMonth(), value.getDate()); - displayedMonth = new Date(value.getYear(), value.getMonth(), 1); + displayedMonth = new FocusedDate(value.getYear(), value.getMonth(), + 1); renderCalendar(); return true; } @@ -1246,6 +1535,20 @@ public class VCalendarPanel extends FocusableFlexTable implements } /** + * Adjusts a date to fit inside the range, only if outside + * + * @param date + */ + private Date adjustDateToFitInsideRange(Date date) { + if (rangeStart != null && rangeStart.after(date)) { + date = (Date) rangeStart.clone(); + } else if (rangeEnd != null && rangeEnd.before(date)) { + date = (Date) rangeEnd.clone(); + } + return date; + } + + /** * Sets the data of the Panel. * * @param currentDate @@ -1257,16 +1560,47 @@ public class VCalendarPanel extends FocusableFlexTable implements if (currentDate == value && currentDate != null) { return; } + boolean currentDateWasAdjusted = false; + // Check that selected date is inside the allowed range + if (currentDate != null && !isDateInsideRange(currentDate, resolution)) { + currentDate = adjustDateToFitInsideRange(currentDate); + currentDateWasAdjusted = true; + } Date oldDisplayedMonth = displayedMonth; value = currentDate; - if (value == null) { - focusedDate = displayedMonth = null; + // If current date was adjusted, we will not select any date, + // since that will look like a date is selected. Instead we + // only focus on the adjusted value + if (value == null || currentDateWasAdjusted) { + // If ranges enabled, we may need to focus on a different view to + // potentially not get stuck + if (rangeStart != null || rangeEnd != null) { + Date dateThatFitsInsideRange = adjustDateToFitInsideRange(new Date()); + focusedDate = new FocusedDate( + dateThatFitsInsideRange.getYear(), + dateThatFitsInsideRange.getMonth(), + dateThatFitsInsideRange.getDate()); + displayedMonth = new FocusedDate( + dateThatFitsInsideRange.getYear(), + dateThatFitsInsideRange.getMonth(), 1); + // value was adjusted. Set selected to null to not cause + // confusion, but this is only needed (and allowed) when we have + // a day + // resolution + if (getResolution().getCalendarField() >= Resolution.DAY + .getCalendarField()) { + value = null; + } + } else { + focusedDate = displayedMonth = null; + } } else { - focusedDate = new Date(value.getYear(), value.getMonth(), + focusedDate = new FocusedDate(value.getYear(), value.getMonth(), value.getDate()); - displayedMonth = new Date(value.getYear(), value.getMonth(), 1); + displayedMonth = new FocusedDate(value.getYear(), value.getMonth(), + 1); } // Re-render calendar if the displayed month is changed, @@ -1704,6 +2038,10 @@ public class VCalendarPanel extends FocusableFlexTable implements private static final String SUBPART_DAY = "day"; private static final String SUBPART_MONTH_YEAR_HEADER = "header"; + private Date rangeStart; + + private Date rangeEnd; + @Override public String getSubPartName(Element subElement) { if (contains(nextMonth, subElement)) { @@ -1821,4 +2159,75 @@ public class VCalendarPanel extends FocusableFlexTable implements mouseTimer.cancel(); } } + + /** + * Helper class to inform the screen reader that the user changed the + * selected date. It sets the value of a field that is outside the view, and + * is defined as a live area. That way the screen reader recognizes the + * change and reads it to the user. + */ + public class FocusedDate extends Date { + + public FocusedDate(int year, int month, int date) { + super(year, month, date); + } + + @Override + public void setTime(long time) { + super.setTime(time); + setLabel(); + } + + @Override + @Deprecated + public void setDate(int date) { + super.setDate(date); + setLabel(); + } + + @Override + @Deprecated + public void setMonth(int month) { + super.setMonth(month); + setLabel(); + } + + @Override + @Deprecated + public void setYear(int year) { + super.setYear(year); + setLabel(); + } + + private void setLabel() { + if (parent instanceof VPopupCalendar) { + ((VPopupCalendar) parent).setFocusedDate(this); + } + } + } + + /** + * Sets the start range for this component. The start range is inclusive, + * and it depends on the current resolution, what is considered inside the + * range. + * + * @param startDate + * - the allowed range's start date + */ + public void setRangeStart(Date rangeStart) { + this.rangeStart = rangeStart; + + } + + /** + * Sets the end range for this component. The end range is inclusive, and it + * depends on the current resolution, what is considered inside the range. + * + * @param endDate + * - the allowed range's end date + */ + public void setRangeEnd(Date rangeEnd) { + this.rangeEnd = rangeEnd; + + } } diff --git a/client/src/com/vaadin/client/ui/VCheckBox.java b/client/src/com/vaadin/client/ui/VCheckBox.java index ca1e3ebcdb..bb49dd7f0a 100644 --- a/client/src/com/vaadin/client/ui/VCheckBox.java +++ b/client/src/com/vaadin/client/ui/VCheckBox.java @@ -22,9 +22,12 @@ import com.google.gwt.user.client.Event; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.Util; import com.vaadin.client.VTooltip; +import com.vaadin.client.ui.aria.AriaHelper; +import com.vaadin.client.ui.aria.HandlesAriaInvalid; +import com.vaadin.client.ui.aria.HandlesAriaRequired; public class VCheckBox extends com.google.gwt.user.client.ui.CheckBox implements - Field { + Field, HandlesAriaInvalid, HandlesAriaRequired { public static final String CLASSNAME = "v-checkbox"; @@ -69,4 +72,23 @@ public class VCheckBox extends com.google.gwt.user.client.ui.CheckBox implements } } + /** + * Gives access to the input element. + * + * @return Element of the CheckBox itself + */ + private Element getCheckBoxElement() { + // FIXME: Would love to use a better way to access the checkbox element + return (Element) getElement().getFirstChildElement(); + } + + @Override + public void setAriaRequired(boolean required) { + AriaHelper.handleInputRequired(getCheckBoxElement(), required); + } + + @Override + public void setAriaInvalid(boolean invalid) { + AriaHelper.handleInputInvalid(getCheckBoxElement(), invalid); + } } diff --git a/client/src/com/vaadin/client/ui/VColorPickerArea.java b/client/src/com/vaadin/client/ui/VColorPickerArea.java index bdae65438f..81f2c8fcc7 100644 --- a/client/src/com/vaadin/client/ui/VColorPickerArea.java +++ b/client/src/com/vaadin/client/ui/VColorPickerArea.java @@ -67,6 +67,7 @@ public class VColorPickerArea extends Widget implements ClickHandler, HasHTML, * @param handler * @return HandlerRegistration used to remove the handler */ + @Override public HandlerRegistration addClickHandler(ClickHandler handler) { return addDomHandler(handler, ClickEvent.getType()); } diff --git a/client/src/com/vaadin/client/ui/VContextMenu.java b/client/src/com/vaadin/client/ui/VContextMenu.java index 80751652df..02ee5b07c5 100644 --- a/client/src/com/vaadin/client/ui/VContextMenu.java +++ b/client/src/com/vaadin/client/ui/VContextMenu.java @@ -29,19 +29,26 @@ import com.google.gwt.event.dom.client.HasBlurHandlers; import com.google.gwt.event.dom.client.HasFocusHandlers; import com.google.gwt.event.dom.client.HasKeyDownHandlers; import com.google.gwt.event.dom.client.HasKeyPressHandlers; +import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.dom.client.KeyDownEvent; import com.google.gwt.event.dom.client.KeyDownHandler; import com.google.gwt.event.dom.client.KeyPressEvent; import com.google.gwt.event.dom.client.KeyPressHandler; +import com.google.gwt.event.dom.client.KeyUpEvent; +import com.google.gwt.event.dom.client.KeyUpHandler; import com.google.gwt.event.dom.client.LoadEvent; import com.google.gwt.event.dom.client.LoadHandler; +import com.google.gwt.event.logical.shared.CloseEvent; +import com.google.gwt.event.logical.shared.CloseHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.MenuBar; import com.google.gwt.user.client.ui.MenuItem; import com.google.gwt.user.client.ui.PopupPanel; +import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.impl.FocusImpl; import com.vaadin.client.Focusable; import com.vaadin.client.Util; @@ -56,6 +63,8 @@ public class VContextMenu extends VOverlay implements SubPartAware { private int top; + private Element focusedElement; + private VLazyExecutor delayedImageLoadExecutioner = new VLazyExecutor(100, new ScheduledCommand() { @Override @@ -75,6 +84,21 @@ public class VContextMenu extends VOverlay implements SubPartAware { super(true, false, true); setWidget(menu); setStyleName("v-contextmenu"); + getElement().setId(DOM.createUniqueId()); + + addCloseHandler(new CloseHandler<PopupPanel>() { + @Override + public void onClose(CloseEvent<PopupPanel> event) { + Element currentFocus = Util.getFocusedElement(); + if (focusedElement != null + && (currentFocus == null + || menu.getElement().isOrHasChild(currentFocus) || RootPanel + .getBodyElement().equals(currentFocus))) { + focusedElement.focus(); + focusedElement = null; + } + } + }); } protected void imagesLoaded() { @@ -115,6 +139,10 @@ public class VContextMenu extends VOverlay implements SubPartAware { // Attach onload listeners to all images Util.sinkOnloadForImages(menu.getElement()); + // Store the currently focused element, which will be re-focused when + // context menu is closed + focusedElement = Util.getFocusedElement(); + setPopupPositionAndShow(new PositionCallback() { @Override public void setPosition(int offsetWidth, int offsetHeight) { @@ -168,10 +196,11 @@ public class VContextMenu extends VOverlay implements SubPartAware { */ class CMenuBar extends MenuBar implements HasFocusHandlers, HasBlurHandlers, HasKeyDownHandlers, HasKeyPressHandlers, - Focusable, LoadHandler { + Focusable, LoadHandler, KeyUpHandler { public CMenuBar() { super(true); addDomHandler(this, LoadEvent.getType()); + addDomHandler(this, KeyUpEvent.getType()); } @Override @@ -240,6 +269,13 @@ public class VContextMenu extends VOverlay implements SubPartAware { delayedImageLoadExecutioner.trigger(); } + @Override + public void onKeyUp(KeyUpEvent event) { + // Allow to close context menu with ESC + if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) { + hide(); + } + } } @Override diff --git a/client/src/com/vaadin/client/ui/VFilterSelect.java b/client/src/com/vaadin/client/ui/VFilterSelect.java index a3156221b9..e08fbf8ab6 100644 --- a/client/src/com/vaadin/client/ui/VFilterSelect.java +++ b/client/src/com/vaadin/client/ui/VFilterSelect.java @@ -24,6 +24,7 @@ import java.util.Iterator; import java.util.List; import java.util.Set; +import com.google.gwt.aria.client.Roles; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.Style; @@ -66,6 +67,10 @@ import com.vaadin.client.Focusable; import com.vaadin.client.UIDL; import com.vaadin.client.Util; import com.vaadin.client.VConsole; +import com.vaadin.client.ui.aria.AriaHelper; +import com.vaadin.client.ui.aria.HandlesAriaCaption; +import com.vaadin.client.ui.aria.HandlesAriaInvalid; +import com.vaadin.client.ui.aria.HandlesAriaRequired; import com.vaadin.client.ui.menubar.MenuBar; import com.vaadin.client.ui.menubar.MenuItem; import com.vaadin.shared.AbstractComponentState; @@ -81,7 +86,8 @@ import com.vaadin.shared.ui.combobox.FilteringMode; @SuppressWarnings("deprecation") public class VFilterSelect extends Composite implements Field, KeyDownHandler, KeyUpHandler, ClickHandler, FocusHandler, BlurHandler, Focusable, - SubPartAware { + SubPartAware, HandlesAriaCaption, HandlesAriaInvalid, + HandlesAriaRequired { /** * Represents a suggestion in the suggestion popup box @@ -241,6 +247,8 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, DOM.sinkEvents(root, Event.ONMOUSEDOWN | Event.ONMOUSEWHEEL); addCloseHandler(this); + + Roles.getListRole().set(getElement()); } /** @@ -736,6 +744,7 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, while (it.hasNext()) { final FilterSelectSuggestion s = it.next(); final MenuItem mi = new MenuItem(s.getDisplayString(), true, s); + Roles.getListitemRole().set(mi.getElement()); Util.sinkOnloadForImages(mi.getElement()); @@ -1106,9 +1115,15 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, }); popupOpener.sinkEvents(Event.ONMOUSEDOWN); + Roles.getButtonRole() + .setAriaHiddenState(popupOpener.getElement(), true); + Roles.getButtonRole().set(popupOpener.getElement()); + panel.add(tb); panel.add(popupOpener); initWidget(panel); + Roles.getComboboxRole().set(panel.getElement()); + tb.addKeyDownHandler(this); tb.addKeyUpHandler(this); @@ -1226,8 +1241,11 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, // Always update styles as they might have been overwritten if (textInputEnabled) { removeStyleDependentName(STYLE_NO_INPUT); + Roles.getTextboxRole().removeAriaReadonlyProperty(tb.getElement()); } else { addStyleDependentName(STYLE_NO_INPUT); + Roles.getTextboxRole().setAriaReadonlyProperty(tb.getElement(), + true); } if (this.textInputEnabled == textInputEnabled) { @@ -1937,4 +1955,19 @@ public class VFilterSelect extends Composite implements Field, KeyDownHandler, } return null; } + + @Override + public void setAriaRequired(boolean required) { + AriaHelper.handleInputRequired(tb, required); + } + + @Override + public void setAriaInvalid(boolean invalid) { + AriaHelper.handleInputInvalid(tb, invalid); + } + + @Override + public void bindAriaCaption(Element captionElement) { + AriaHelper.bindCaption(tb, captionElement); + } } diff --git a/client/src/com/vaadin/client/ui/VFormLayout.java b/client/src/com/vaadin/client/ui/VFormLayout.java index 495e842bfd..6c2661d1d8 100644 --- a/client/src/com/vaadin/client/ui/VFormLayout.java +++ b/client/src/com/vaadin/client/ui/VFormLayout.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; +import com.google.gwt.aria.client.Roles; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.DOM; @@ -34,6 +35,7 @@ import com.vaadin.client.ComponentConnector; import com.vaadin.client.Focusable; import com.vaadin.client.StyleConstants; import com.vaadin.client.VTooltip; +import com.vaadin.client.ui.aria.AriaHelper; import com.vaadin.shared.AbstractComponentState; import com.vaadin.shared.ComponentConstants; import com.vaadin.shared.ui.ComponentStateUtil; @@ -93,6 +95,8 @@ public class VFormLayout extends SimplePanel { public VFormLayoutTable() { DOM.setElementProperty(getElement(), "cellPadding", "0"); DOM.setElementProperty(getElement(), "cellSpacing", "0"); + + Roles.getPresentationRole().set(getElement()); } /* @@ -276,6 +280,9 @@ public class VFormLayout extends SimplePanel { if (state.caption != null) { if (captionText == null) { captionText = DOM.createSpan(); + + AriaHelper.bindCaption(owner.getWidget(), captionText); + DOM.insertChild(getElement(), captionText, icon == null ? 0 : 1); } @@ -298,6 +305,9 @@ public class VFormLayout extends SimplePanel { boolean required = owner instanceof AbstractFieldConnector && ((AbstractFieldConnector) owner).isRequired(); + + AriaHelper.handleInputRequired(owner.getWidget(), required); + if (required) { if (requiredFieldIndicator == null) { requiredFieldIndicator = DOM.createSpan(); @@ -305,6 +315,11 @@ public class VFormLayout extends SimplePanel { DOM.setElementProperty(requiredFieldIndicator, "className", "v-required-field-indicator"); DOM.appendChild(getElement(), requiredFieldIndicator); + + // Hide the required indicator from screen reader, as this + // information is set directly at the input field + Roles.getTextboxRole().setAriaHiddenState( + requiredFieldIndicator, true); } } else { if (requiredFieldIndicator != null) { @@ -364,6 +379,8 @@ public class VFormLayout extends SimplePanel { showError = false; } + AriaHelper.handleInputInvalid(owner.getWidget(), showError); + if (showError) { if (errorIndicatorElement == null) { errorIndicatorElement = DOM.createDiv(); @@ -371,6 +388,11 @@ public class VFormLayout extends SimplePanel { DOM.setElementProperty(errorIndicatorElement, "className", "v-errorindicator"); DOM.appendChild(getElement(), errorIndicatorElement); + + // Hide the error indicator from screen reader, as this + // information is set directly at the input field + Roles.getFormRole().setAriaHiddenState( + errorIndicatorElement, true); } } else if (errorIndicatorElement != null) { diff --git a/client/src/com/vaadin/client/ui/VLabel.java b/client/src/com/vaadin/client/ui/VLabel.java index 83fc8e207e..8acd653778 100644 --- a/client/src/com/vaadin/client/ui/VLabel.java +++ b/client/src/com/vaadin/client/ui/VLabel.java @@ -16,7 +16,6 @@ package com.vaadin.client.ui; -import com.google.gwt.dom.client.Style.Display; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.HTML; import com.vaadin.client.ApplicationConnection; @@ -57,10 +56,8 @@ public class VLabel extends HTML { super.setWidth(width); if (width == null || width.equals("")) { setStyleName(getElement(), CLASSNAME_UNDEFINED_WIDTH, true); - getElement().getStyle().setDisplay(Display.INLINE_BLOCK); } else { setStyleName(getElement(), CLASSNAME_UNDEFINED_WIDTH, false); - getElement().getStyle().clearDisplay(); } } diff --git a/client/src/com/vaadin/client/ui/VNativeButton.java b/client/src/com/vaadin/client/ui/VNativeButton.java index 6e1c5bae77..71413a76e6 100644 --- a/client/src/com/vaadin/client/ui/VNativeButton.java +++ b/client/src/com/vaadin/client/ui/VNativeButton.java @@ -16,14 +16,9 @@ package com.vaadin.client.ui; -import com.google.gwt.core.client.Scheduler; -import com.google.gwt.core.client.Scheduler.ScheduledCommand; -import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; -import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; -import com.google.gwt.event.dom.client.MouseEvent; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.ui.Button; @@ -31,7 +26,6 @@ import com.vaadin.client.ApplicationConnection; import com.vaadin.client.BrowserInfo; import com.vaadin.client.MouseEventDetailsBuilder; import com.vaadin.client.Util; -import com.vaadin.client.VConsole; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.ui.button.ButtonServerRpc; diff --git a/client/src/com/vaadin/client/ui/VOptionGroup.java b/client/src/com/vaadin/client/ui/VOptionGroup.java index 2ba8a9e729..eed5549e39 100644 --- a/client/src/com/vaadin/client/ui/VOptionGroup.java +++ b/client/src/com/vaadin/client/ui/VOptionGroup.java @@ -22,6 +22,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import com.google.gwt.aria.client.Roles; import com.google.gwt.core.client.Scheduler; import com.google.gwt.event.dom.client.BlurEvent; import com.google.gwt.event.dom.client.BlurHandler; @@ -99,6 +100,13 @@ public class VOptionGroup extends VOptionGroupBase implements FocusHandler, public void buildOptions(UIDL uidl) { panel.clear(); optionsEnabled.clear(); + + if (isMultiselect()) { + Roles.getGroupRole().set(getElement()); + } else { + Roles.getRadiogroupRole().set(getElement()); + } + for (final Iterator<?> it = uidl.getChildIterator(); it.hasNext();) { final UIDL opUidl = (UIDL) it.next(); CheckBox op; diff --git a/client/src/com/vaadin/client/ui/VOptionGroupBase.java b/client/src/com/vaadin/client/ui/VOptionGroupBase.java index 4d60b2eba8..cc691130ad 100644 --- a/client/src/com/vaadin/client/ui/VOptionGroupBase.java +++ b/client/src/com/vaadin/client/ui/VOptionGroupBase.java @@ -118,6 +118,7 @@ public abstract class VOptionGroupBase extends Composite implements Field, return multiselect; } + @Override public boolean isEnabled() { return enabled; } @@ -190,6 +191,7 @@ public abstract class VOptionGroupBase extends Composite implements Field, } } + @Override public void setEnabled(boolean enabled) { if (this.enabled != enabled) { this.enabled = enabled; diff --git a/client/src/com/vaadin/client/ui/VPopupCalendar.java b/client/src/com/vaadin/client/ui/VPopupCalendar.java index 2a2578aa16..e431da127d 100644 --- a/client/src/com/vaadin/client/ui/VPopupCalendar.java +++ b/client/src/com/vaadin/client/ui/VPopupCalendar.java @@ -18,6 +18,9 @@ package com.vaadin.client.ui; import java.util.Date; +import com.google.gwt.aria.client.Id; +import com.google.gwt.aria.client.LiveValue; +import com.google.gwt.aria.client.Roles; import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; @@ -25,18 +28,25 @@ import com.google.gwt.event.dom.client.DomEvent; import com.google.gwt.event.dom.client.KeyCodes; import com.google.gwt.event.logical.shared.CloseEvent; import com.google.gwt.event.logical.shared.CloseHandler; +import com.google.gwt.i18n.client.DateTimeFormat; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.PopupPanel.PositionCallback; +import com.google.gwt.user.client.ui.RootPanel; +import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.BrowserInfo; import com.vaadin.client.VConsole; import com.vaadin.client.ui.VCalendarPanel.FocusOutListener; import com.vaadin.client.ui.VCalendarPanel.SubmitListener; +import com.vaadin.client.ui.aria.AriaHelper; +import com.vaadin.shared.ui.datefield.PopupDateFieldState; import com.vaadin.shared.ui.datefield.Resolution; /** @@ -68,6 +78,12 @@ public class VPopupCalendar extends VTextualDate implements Field, private boolean textFieldEnabled = true; + private String captionId; + + private Label selectedDate; + + private Element descriptionForAssisitveDevicesElement; + public VPopupCalendar() { super(); @@ -75,8 +91,23 @@ public class VPopupCalendar extends VTextualDate implements Field, calendarToggle.addClickHandler(this); // -2 instead of -1 to avoid FocusWidget.onAttach to reset it calendarToggle.getElement().setTabIndex(-2); + + Roles.getButtonRole().set(calendarToggle.getElement()); + Roles.getButtonRole().setAriaHiddenState(calendarToggle.getElement(), + true); + add(calendarToggle); + // Description of the usage of the widget for assisitve device users + descriptionForAssisitveDevicesElement = DOM.createDiv(); + descriptionForAssisitveDevicesElement + .setInnerText(PopupDateFieldState.DESCRIPTION_FOR_ASSISTIVE_DEVICES); + AriaHelper.ensureHasId(descriptionForAssisitveDevicesElement); + Roles.getTextboxRole().setAriaDescribedbyProperty(text.getElement(), + Id.of(descriptionForAssisitveDevicesElement)); + AriaHelper.setVisibleForAssistiveDevicesOnly( + descriptionForAssisitveDevicesElement, true); + calendar = GWT.create(VCalendarPanel.class); calendar.setParentField(this); calendar.setFocusOutListener(new FocusOutListener() { @@ -88,6 +119,14 @@ public class VPopupCalendar extends VTextualDate implements Field, } }); + // FIXME: Problem is, that the element with the provided id does not + // exist yet in html. This is the same problem as with the context menu. + // Apply here the same fix (#11795) + Roles.getTextboxRole().setAriaControlsProperty(text.getElement(), + Id.of(calendar.getElement())); + Roles.getButtonRole().setAriaControlsProperty( + calendarToggle.getElement(), Id.of(calendar.getElement())); + calendar.setSubmitListener(new SubmitListener() { @Override public void onSubmit() { @@ -109,7 +148,20 @@ public class VPopupCalendar extends VTextualDate implements Field, popup = new VOverlay(true, true, true); popup.setOwner(this); - popup.setWidget(calendar); + FlowPanel wrapper = new FlowPanel(); + selectedDate = new Label(); + selectedDate.setStyleName(getStylePrimaryName() + "-selecteddate"); + AriaHelper.setVisibleForAssistiveDevicesOnly(selectedDate.getElement(), + true); + + Roles.getTextboxRole().setAriaLiveProperty(selectedDate.getElement(), + LiveValue.ASSERTIVE); + Roles.getTextboxRole().setAriaAtomicProperty(selectedDate.getElement(), + true); + wrapper.add(selectedDate); + wrapper.add(calendar); + + popup.setWidget(wrapper); popup.addCloseHandler(this); DOM.setElementProperty(calendar.getElement(), "id", @@ -120,6 +172,19 @@ public class VPopupCalendar extends VTextualDate implements Field, updateStyleNames(); } + @Override + protected void onAttach() { + super.onAttach(); + DOM.appendChild(RootPanel.get().getElement(), + descriptionForAssisitveDevicesElement); + } + + @Override + protected void onDetach() { + super.onDetach(); + descriptionForAssisitveDevicesElement.removeFromParent(); + } + @SuppressWarnings("deprecation") public void updateValue(Date newDate) { Date currentDate = getCurrentDate(); @@ -181,8 +246,54 @@ public class VPopupCalendar extends VTextualDate implements Field, text.setEnabled(textFieldEnabled); if (textFieldEnabled) { calendarToggle.setTabIndex(-1); + Roles.getButtonRole().setAriaHiddenState( + calendarToggle.getElement(), true); } else { calendarToggle.setTabIndex(0); + Roles.getButtonRole().setAriaHiddenState( + calendarToggle.getElement(), false); + } + + handleAriaAttributes(); + } + + @Override + public void bindAriaCaption(Element captionElement) { + if (captionElement == null) { + captionId = null; + } else { + captionId = captionElement.getId(); + } + + if (isTextFieldEnabled()) { + super.bindAriaCaption(captionElement); + } else { + AriaHelper.bindCaption(calendarToggle, captionElement); + } + + handleAriaAttributes(); + } + + private void handleAriaAttributes() { + Widget removeFromWidget; + Widget setForWidget; + + if (isTextFieldEnabled()) { + setForWidget = text; + removeFromWidget = calendarToggle; + } else { + setForWidget = calendarToggle; + removeFromWidget = text; + } + + Roles.getFormRole().removeAriaLabelledbyProperty( + removeFromWidget.getElement()); + if (captionId == null) { + Roles.getFormRole().removeAriaLabelledbyProperty( + setForWidget.getElement()); + } else { + Roles.getFormRole().setAriaLabelledbyProperty( + setForWidget.getElement(), Id.of(captionId)); } } @@ -270,10 +381,6 @@ public class VPopupCalendar extends VTextualDate implements Field, } } - // fix size - popup.setWidth(w + "px"); - popup.setHeight(h + "px"); - popup.setPopupPosition(l, t + calendarToggle.getOffsetHeight() + 2); @@ -350,6 +457,32 @@ public class VPopupCalendar extends VTextualDate implements Field, calendar.setFocus(focus); } + @Override + public void setEnabled(boolean enabled) { + super.setEnabled(enabled); + + if (enabled) { + Roles.getButtonRole().setAriaDisabledState( + calendarToggle.getElement(), true); + } else { + Roles.getButtonRole().setAriaDisabledState( + calendarToggle.getElement(), false); + } + } + + /** + * Sets the content of a special field for assistive devices, so that they + * can recognize the change and inform the user (reading out in case of + * screen reader) + * + * @param selectedDate + * Date that is currently selected + */ + public void setFocusedDate(Date selectedDate) { + this.selectedDate.setText(DateTimeFormat.getFormat("dd, MMMM, yyyy") + .format(selectedDate)); + } + /** * For internal use only. May be removed or replaced in the future. * @@ -439,4 +572,50 @@ public class VPopupCalendar extends VTextualDate implements Field, return super.getSubPartName(subElement); } + /** + * Set a description that explains the usage of the Widget for users of + * assistive devices. + * + * @param descriptionForAssistiveDevices + * String with the description + */ + public void setDescriptionForAssistiveDevices( + String descriptionForAssistiveDevices) { + descriptionForAssisitveDevicesElement + .setInnerText(descriptionForAssistiveDevices); + } + + /** + * Get the description that explains the usage of the Widget for users of + * assistive devices. + * + * @return String with the description + */ + public String getDescriptionForAssistiveDevices() { + return descriptionForAssisitveDevicesElement.getInnerText(); + } + + /** + * Sets the start range for this component. The start range is inclusive, + * and it depends on the current resolution, what is considered inside the + * range. + * + * @param startDate + * - the allowed range's start date + */ + public void setRangeStart(Date rangeStart) { + calendar.setRangeStart(rangeStart); + } + + /** + * Sets the end range for this component. The end range is inclusive, and it + * depends on the current resolution, what is considered inside the range. + * + * @param endDate + * - the allowed range's end date + */ + public void setRangeEnd(Date rangeEnd) { + calendar.setRangeEnd(rangeEnd); + } + } diff --git a/client/src/com/vaadin/client/ui/VPopupView.java b/client/src/com/vaadin/client/ui/VPopupView.java index d983da2b62..05fbd2c073 100644 --- a/client/src/com/vaadin/client/ui/VPopupView.java +++ b/client/src/com/vaadin/client/ui/VPopupView.java @@ -202,7 +202,6 @@ public class VPopupView extends HTML implements Iterable<Widget> { private boolean hasHadMouseOver = false; private boolean hideOnMouseOut = true; private final Set<Element> activeChildren = new HashSet<Element>(); - private boolean hiding = false; private ShortcutActionHandler shortcutActionHandler; @@ -264,7 +263,6 @@ public class VPopupView extends HTML implements Iterable<Widget> { @Override public void hide(boolean autoClosed) { VConsole.log("Hiding popupview"); - hiding = true; syncChildren(); if (popupComponentWidget != null && popupComponentWidget != loading) { remove(popupComponentWidget); @@ -276,8 +274,6 @@ public class VPopupView extends HTML implements Iterable<Widget> { @Override public void show() { - hiding = false; - // Find the shortcut action handler that should handle keyboard // events from the popup. The events do not propagate automatically // because the popup is directly attached to the RootPanel. @@ -353,31 +349,6 @@ public class VPopupView extends HTML implements Iterable<Widget> { this.hideOnMouseOut = hideOnMouseOut; } - /* - * - * We need a hack make popup act as a child of VPopupView in Vaadin's - * component tree, but work in default GWT manner when closing or - * opening. - * - * (non-Javadoc) - * - * @see com.google.gwt.user.client.ui.Widget#getParent() - */ - @Override - public Widget getParent() { - if (!isAttached() || hiding) { - return super.getParent(); - } else { - return VPopupView.this; - } - } - - @Override - protected void onDetach() { - super.onDetach(); - hiding = false; - } - @Override public Element getContainerElement() { return super.getContainerElement(); diff --git a/client/src/com/vaadin/client/ui/VTextualDate.java b/client/src/com/vaadin/client/ui/VTextualDate.java index 2f444a8587..9d7e31faab 100644 --- a/client/src/com/vaadin/client/ui/VTextualDate.java +++ b/client/src/com/vaadin/client/ui/VTextualDate.java @@ -18,6 +18,7 @@ package com.vaadin.client.ui; import java.util.Date; +import com.google.gwt.aria.client.Roles; import com.google.gwt.event.dom.client.BlurEvent; import com.google.gwt.event.dom.client.BlurHandler; import com.google.gwt.event.dom.client.ChangeEvent; @@ -30,11 +31,16 @@ import com.vaadin.client.Focusable; import com.vaadin.client.LocaleNotLoadedException; import com.vaadin.client.LocaleService; import com.vaadin.client.VConsole; +import com.vaadin.client.ui.aria.AriaHelper; +import com.vaadin.client.ui.aria.HandlesAriaCaption; +import com.vaadin.client.ui.aria.HandlesAriaInvalid; +import com.vaadin.client.ui.aria.HandlesAriaRequired; import com.vaadin.shared.EventId; import com.vaadin.shared.ui.datefield.Resolution; public class VTextualDate extends VDateField implements Field, ChangeHandler, - Focusable, SubPartAware { + Focusable, SubPartAware, HandlesAriaCaption, HandlesAriaInvalid, + HandlesAriaRequired { private static final String PARSE_ERROR_CLASSNAME = "-parseerror"; @@ -76,6 +82,9 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler, getClient() .updateVariable(getId(), EventId.FOCUS, "", true); } + + // Needed for tooltip event handling + VTextualDate.this.fireEvent(event); } }); text.addBlurHandler(new BlurHandler() { @@ -94,8 +103,12 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler, EventId.BLUR)) { getClient().updateVariable(getId(), EventId.BLUR, "", true); } + + // Needed for tooltip event handling + VTextualDate.this.fireEvent(event); } }); + add(text); } @@ -150,6 +163,21 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler, return formatStr; } + @Override + public void bindAriaCaption(Element captionElement) { + AriaHelper.bindCaption(text, captionElement); + } + + @Override + public void setAriaRequired(boolean required) { + AriaHelper.handleInputRequired(text, required); + } + + @Override + public void setAriaInvalid(boolean invalid) { + AriaHelper.handleInputInvalid(text, invalid); + } + /** * Updates the text field according to the current date (provided by * {@link #getDate()}). Takes care of updating text, enabling and disabling @@ -178,8 +206,12 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler, if (readonly) { text.addStyleName("v-readonly"); + Roles.getTextboxRole().setAriaReadonlyProperty(text.getElement(), + true); } else { text.removeStyleName("v-readonly"); + Roles.getTextboxRole() + .removeAriaReadonlyProperty(text.getElement()); } } @@ -348,5 +380,4 @@ public class VTextualDate extends VDateField implements Field, ChangeHandler, return null; } - } diff --git a/client/src/com/vaadin/client/ui/VTree.java b/client/src/com/vaadin/client/ui/VTree.java index 624dce4f13..51c00ca310 100644 --- a/client/src/com/vaadin/client/ui/VTree.java +++ b/client/src/com/vaadin/client/ui/VTree.java @@ -24,6 +24,10 @@ import java.util.LinkedList; import java.util.List; import java.util.Set; +import com.google.gwt.aria.client.ExpandedValue; +import com.google.gwt.aria.client.Id; +import com.google.gwt.aria.client.Roles; +import com.google.gwt.aria.client.SelectedValue; import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; @@ -56,6 +60,8 @@ import com.vaadin.client.ConnectorMap; import com.vaadin.client.MouseEventDetailsBuilder; import com.vaadin.client.UIDL; import com.vaadin.client.Util; +import com.vaadin.client.ui.aria.AriaHelper; +import com.vaadin.client.ui.aria.HandlesAriaCaption; import com.vaadin.client.ui.dd.DDUtil; import com.vaadin.client.ui.dd.VAbstractDropHandler; import com.vaadin.client.ui.dd.VAcceptCallback; @@ -75,7 +81,8 @@ import com.vaadin.shared.ui.tree.TreeConstants; */ public class VTree extends FocusElementPanel implements VHasDropHandler, FocusHandler, BlurHandler, KeyPressHandler, KeyDownHandler, - SubPartAware, ActionOwner { + SubPartAware, ActionOwner, HandlesAriaCaption { + private String lastNodeKey = ""; public static final String CLASSNAME = "v-tree"; @@ -168,6 +175,8 @@ public class VTree extends FocusElementPanel implements VHasDropHandler, public VTree() { super(); setStyleName(CLASSNAME); + + Roles.getTreeRole().set(body.getElement()); add(body); addFocusHandler(this); @@ -865,12 +874,24 @@ public class VTree extends FocusElementPanel implements VHasDropHandler, } protected void constructDom() { + String labelId = DOM.createUniqueId(); + addStyleName(CLASSNAME); + String treeItemId = DOM.createUniqueId(); + getElement().setId(treeItemId); + Roles.getTreeitemRole().set(getElement()); + Roles.getTreeitemRole().setAriaSelectedState(getElement(), + SelectedValue.FALSE); + Roles.getTreeitemRole().setAriaLabelledbyProperty(getElement(), + Id.of(labelId)); nodeCaptionDiv = DOM.createDiv(); DOM.setElementProperty(nodeCaptionDiv, "className", CLASSNAME + "-caption"); Element wrapper = DOM.createDiv(); + wrapper.setId(labelId); + wrapper.setAttribute("for", treeItemId); + nodeCaptionSpan = DOM.createSpan(); DOM.appendChild(getElement(), nodeCaptionDiv); DOM.appendChild(nodeCaptionDiv, wrapper); @@ -886,6 +907,7 @@ public class VTree extends FocusElementPanel implements VHasDropHandler, childNodeContainer = new FlowPanel(); childNodeContainer.setStyleName(CLASSNAME + "-children"); + Roles.getGroupRole().set(childNodeContainer.getElement()); setWidget(childNodeContainer); } @@ -914,10 +936,13 @@ public class VTree extends FocusElementPanel implements VHasDropHandler, new String[] { key }, true); } addStyleName(CLASSNAME + "-expanded"); + Roles.getTreeitemRole().setAriaExpandedState(getElement(), + ExpandedValue.TRUE); childNodeContainer.setVisible(true); - } else { removeStyleName(CLASSNAME + "-expanded"); + Roles.getTreeitemRole().setAriaExpandedState(getElement(), + ExpandedValue.FALSE); childNodeContainer.setVisible(false); if (notifyServer) { client.updateVariable(paintableId, "collapse", @@ -1094,15 +1119,17 @@ public class VTree extends FocusElementPanel implements VHasDropHandler, Util.scrollIntoViewVertically(nodeCaptionDiv); } - public void setIcon(String iconUrl) { + public void setIcon(String iconUrl, String altText) { if (iconUrl != null) { // Add icon if not present if (icon == null) { icon = new Icon(client); + Roles.getImgRole().set(icon.getElement()); DOM.insertBefore(DOM.getFirstChild(nodeCaptionDiv), icon.getElement(), nodeCaptionSpan); } icon.setUri(iconUrl); + icon.getElement().setAttribute("alt", altText); } else { // Remove icon if present if (icon != null) { @@ -1517,10 +1544,34 @@ public class VTree extends FocusElementPanel implements VHasDropHandler, // Unfocus previously focused node if (focusedNode != null) { focusedNode.setFocused(false); + + Roles.getTreeRole().removeAriaActivedescendantProperty( + focusedNode.getElement()); } if (node != null) { node.setFocused(true); + Roles.getTreeitemRole().setAriaSelectedState(node.getElement(), + SelectedValue.TRUE); + + /* + * FIXME: This code needs to be changed when the keyboard navigation + * doesn't immediately trigger a selection change anymore. + * + * Right now this function is called before and after the Tree is + * rebuilt when up/down arrow keys are pressed. This leads to the + * problem, that the newly selected item is announced too often with + * a screen reader. + * + * Behaviour is different when using the Tree with and without + * screen reader. + */ + if (node.key.equals(lastNodeKey)) { + Roles.getTreeRole().setAriaActivedescendantProperty( + getFocusElement(), Id.of(node.getElement())); + } else { + lastNodeKey = node.key; + } } focusedNode = node; @@ -2161,4 +2212,8 @@ public class VTree extends FocusElementPanel implements VHasDropHandler, keyToNode.clear(); } + @Override + public void bindAriaCaption(Element captionElement) { + AriaHelper.bindCaption(body, captionElement); + } } diff --git a/client/src/com/vaadin/client/ui/VWindow.java b/client/src/com/vaadin/client/ui/VWindow.java index 51a775cb7e..38dfdba1b8 100644 --- a/client/src/com/vaadin/client/ui/VWindow.java +++ b/client/src/com/vaadin/client/ui/VWindow.java @@ -43,12 +43,13 @@ import com.google.gwt.user.client.ui.Widget; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.BrowserInfo; import com.vaadin.client.ConnectorMap; -import com.vaadin.client.Console; import com.vaadin.client.Focusable; import com.vaadin.client.LayoutManager; import com.vaadin.client.Util; +import com.vaadin.client.debug.internal.VDebugWindow; import com.vaadin.client.ui.ShortcutActionHandler.ShortcutActionHandlerOwner; import com.vaadin.shared.EventId; +import com.vaadin.shared.ui.window.WindowMode; /** * "Sub window" component. @@ -58,18 +59,6 @@ import com.vaadin.shared.EventId; public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, ScrollHandler, KeyDownHandler, FocusHandler, BlurHandler, Focusable { - /** - * Minimum allowed height of a window. This refers to the content area, not - * the outer borders. - */ - private static final int MIN_CONTENT_AREA_HEIGHT = 100; - - /** - * Minimum allowed width of a window. This refers to the content area, not - * the outer borders. - */ - private static final int MIN_CONTENT_AREA_WIDTH = 150; - private static ArrayList<VWindow> windowOrder = new ArrayList<VWindow>(); private static boolean orderingDefered; @@ -114,6 +103,9 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, public Element closeBox; /** For internal use only. May be removed or replaced in the future. */ + public Element maximizeRestoreBox; + + /** For internal use only. May be removed or replaced in the future. */ public ApplicationConnection client; /** For internal use only. May be removed or replaced in the future. */ @@ -262,6 +254,9 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, resizeBox = DOM.createDiv(); DOM.setElementProperty(resizeBox, "className", CLASSNAME + "-resizebox"); closeBox = DOM.createDiv(); + maximizeRestoreBox = DOM.createDiv(); + DOM.setElementProperty(maximizeRestoreBox, "className", CLASSNAME + + "-maximizebox"); DOM.setElementProperty(closeBox, "className", CLASSNAME + "-closebox"); DOM.appendChild(footer, resizeBox); @@ -269,14 +264,15 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, DOM.setElementProperty(wrapper, "className", CLASSNAME + "-wrap"); DOM.appendChild(wrapper, header); + DOM.appendChild(wrapper, maximizeRestoreBox); DOM.appendChild(wrapper, closeBox); DOM.appendChild(header, headerText); DOM.appendChild(wrapper, contents); DOM.appendChild(wrapper, footer); DOM.appendChild(super.getContainerElement(), wrapper); - sinkEvents(Event.MOUSEEVENTS | Event.TOUCHEVENTS | Event.ONCLICK - | Event.ONLOSECAPTURE); + sinkEvents(Event.ONDBLCLICK | Event.MOUSEEVENTS | Event.TOUCHEVENTS + | Event.ONCLICK | Event.ONLOSECAPTURE); setWidget(contentPanel); @@ -575,6 +571,31 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, } } + public void updateMaximizeRestoreClassName(boolean visible, + WindowMode windowMode) { + String className; + if (windowMode == WindowMode.MAXIMIZED) { + className = CLASSNAME + "-restorebox"; + } else { + className = CLASSNAME + "-maximizebox"; + } + if (!visible) { + className = className + " " + className + "-disabled"; + } + maximizeRestoreBox.setClassName(className); + } + + // TODO this will eventually be removed, currently used to avoid updating to + // server side. + public void setPopupPositionNoUpdate(int left, int top) { + if (top < 0) { + // ensure window is not moved out of browser window from top of the + // screen + top = 0; + } + super.setPopupPosition(left, top); + } + @Override public void setPopupPosition(int left, int top) { if (top < 0) { @@ -616,6 +637,8 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, return contents; } + private Event headerDragPending; + @Override public void onBrowserEvent(final Event event) { boolean bubble = true; @@ -632,6 +655,28 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, onCloseClick(); } bubble = false; + } else if (target == maximizeRestoreBox) { + // handled in connector + if (type != Event.ONCLICK) { + bubble = false; + } + } else if (header.isOrHasChild(target) && !dragging) { + // dblclick handled in connector + if (type != Event.ONDBLCLICK && draggable) { + if (type == Event.ONMOUSEDOWN) { + headerDragPending = event; + } else if (type == Event.ONMOUSEMOVE + && headerDragPending != null) { + // ie won't work unless this is set here + dragging = true; + onDragEvent(headerDragPending); + onDragEvent(event); + headerDragPending = null; + } else { + headerDragPending = null; + } + bubble = false; + } } else if (dragging || !contents.isOrHasChild(target)) { onDragEvent(event); bubble = false; @@ -648,7 +693,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, */ if (type == Event.ONMOUSEDOWN && !contentPanel.getElement().isOrHasChild(target) - && target != closeBox) { + && target != closeBox && target != maximizeRestoreBox) { contentPanel.focus(); } @@ -746,16 +791,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, } int w = Util.getTouchOrMouseClientX(event) - startX + origW; - int minWidth = getMinWidth(); - if (w < minWidth) { - w = minWidth; - } - int h = Util.getTouchOrMouseClientY(event) - startY + origH; - int minHeight = getMinHeight(); - if (h < minHeight) { - h = minHeight; - } setWidth(w + "px"); setHeight(h + "px"); @@ -775,7 +811,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, } } - private void updateContentsSize() { + public void updateContentsSize() { LayoutManager layoutManager = getLayoutManager(); layoutManager.setNeedsMeasure(ConnectorMap.get(client).getConnector( this)); @@ -896,7 +932,7 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, // debug window Widget w = Util.findWidget(target, null); while (w != null) { - if (w instanceof Console) { + if (w instanceof VDebugWindow) { return true; // allow debug-window clicks } else if (ConnectorMap.get(client).isConnector(w)) { return false; @@ -959,10 +995,6 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, contentPanel.focus(); } - public int getMinHeight() { - return MIN_CONTENT_AREA_HEIGHT + getDecorationHeight(); - } - private int getDecorationHeight() { LayoutManager lm = getLayoutManager(); int headerHeight = lm.getOuterHeight(header); @@ -974,10 +1006,6 @@ public class VWindow extends VOverlay implements ShortcutActionHandlerOwner, return LayoutManager.get(client); } - public int getMinWidth() { - return MIN_CONTENT_AREA_WIDTH + getDecorationWidth(); - } - private int getDecorationWidth() { LayoutManager layoutManager = getLayoutManager(); return layoutManager.getOuterWidth(getElement()) diff --git a/client/src/com/vaadin/client/ui/absolutelayout/AbsoluteLayoutConnector.java b/client/src/com/vaadin/client/ui/absolutelayout/AbsoluteLayoutConnector.java index 868c14f742..da79639dcd 100644 --- a/client/src/com/vaadin/client/ui/absolutelayout/AbsoluteLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/absolutelayout/AbsoluteLayoutConnector.java @@ -100,8 +100,7 @@ public class AbsoluteLayoutConnector extends /* * (non-Javadoc) * - * @see - * com.vaadin.client.HasComponentsConnector#updateCaption(com.vaadin + * @see com.vaadin.client.HasComponentsConnector#updateCaption(com.vaadin * .client.ComponentConnector) */ @Override @@ -188,6 +187,8 @@ public class AbsoluteLayoutConnector extends oldChild.removeStateChangeHandler(childStateChangeHandler); } } + + getWidget().cleanupWrappers(); } /* diff --git a/client/src/com/vaadin/client/ui/aria/AriaHelper.java b/client/src/com/vaadin/client/ui/aria/AriaHelper.java new file mode 100644 index 0000000000..0ff58cf510 --- /dev/null +++ b/client/src/com/vaadin/client/ui/aria/AriaHelper.java @@ -0,0 +1,188 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.client.ui.aria; + +import com.google.gwt.aria.client.Id; +import com.google.gwt.aria.client.InvalidValue; +import com.google.gwt.aria.client.Roles; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.ui.Widget; + +/** + * Helper class that helps to implement the WAI-ARIA functionality. + */ +public class AriaHelper { + public static final String ASSISTIVE_DEVICE_ONLY_STYLE = "v-assistive-device-only"; + + /** + * Binds a caption (label in HTML speak) to the form element as required by + * WAI-ARIA specification. + * + * @param widget + * Widget, that should be bound to the caption + * @param captionElements + * Element with of caption to bind + */ + public static void bindCaption(Widget widget, Element captionElement) { + assert widget != null : "Valid Widget required"; + + if (widget instanceof HandlesAriaCaption) { + // Let the widget handle special cases itself + if (captionElement == null) { + ((HandlesAriaCaption) widget).bindAriaCaption(null); + } else { + ensureHasId(captionElement); + ((HandlesAriaCaption) widget).bindAriaCaption(captionElement); + } + } else if (captionElement != null) { + // Handle the default case + ensureHasId(captionElement); + String ownerId = ensureHasId(widget.getElement()); + captionElement.setAttribute("for", ownerId); + + Roles.getTextboxRole().setAriaLabelledbyProperty( + widget.getElement(), Id.of(captionElement)); + } else { + clearCaption(widget); + } + } + + /** + * Removes a binding to a caption added with bindCaption() from the provided + * Widget. + * + * @param widget + * Widget, that was bound to a caption before + */ + private static void clearCaption(Widget widget) { + Roles.getTextboxRole() + .removeAriaLabelledbyProperty(widget.getElement()); + } + + /** + * Handles the required actions depending of the input Widget being required + * or not. + * + * @param widget + * Widget, typically an input Widget like TextField + * @param required + * boolean, true when the element is required + */ + public static void handleInputRequired(Widget widget, boolean required) { + assert widget != null : "Valid Widget required"; + + if (widget instanceof HandlesAriaRequired) { + ((HandlesAriaRequired) widget).setAriaRequired(required); + } else { + handleInputRequired(widget.getElement(), required); + } + } + + /** + * Handles the required actions depending of the input element being + * required or not. + * + * @param element + * Element, typically from an input Widget like TextField + * @param required + * boolean, true when the element is required + */ + public static void handleInputRequired(Element element, boolean required) { + if (required) { + Roles.getTextboxRole().setAriaRequiredProperty(element, required); + } else { + Roles.getTextboxRole().removeAriaRequiredProperty(element); + } + } + + /** + * Handles the required actions depending of the input Widget contains + * unaccepted input. + * + * @param widget + * Widget, typically an input Widget like TextField + * @param invalid + * boolean, true when the Widget input has an error + */ + public static void handleInputInvalid(Widget widget, boolean invalid) { + assert widget != null : "Valid Widget required"; + + if (widget instanceof HandlesAriaInvalid) { + ((HandlesAriaInvalid) widget).setAriaInvalid(invalid); + } else { + handleInputInvalid(widget.getElement(), invalid); + } + } + + /** + * Handles the required actions depending of the input element contains + * unaccepted input. + * + * @param element + * Element, typically an input Widget like TextField + * @param invalid + * boolean, true when the element input has an error + */ + public static void handleInputInvalid(Element element, boolean invalid) { + if (invalid) { + Roles.getTextboxRole().setAriaInvalidState(element, + InvalidValue.TRUE); + } else { + Roles.getTextboxRole().removeAriaInvalidState(element); + } + } + + /** + * Makes sure that the provided element has an id attribute. Adds a new + * unique id if not. + * + * @param element + * Element to check + * @return String with the id of the element + */ + public static String ensureHasId(Element element) { + assert element != null : "Valid Element required"; + + String id = element.getId(); + if (null == id || id.isEmpty()) { + id = DOM.createUniqueId(); + element.setId(id); + } + return id; + } + + /** + * Allows to move an element out of the visible area of the browser window. + * + * This makes it possible to have additional information for an assistive + * device, that is not in the way for visual users. + * + * @param element + * Element to move out of sight + * @param boolean assistiveOnly true when element should only be visible for + * assistive devices, false to make the element visible for all + */ + public static void setVisibleForAssistiveDevicesOnly(Element element, + boolean assistiveOnly) { + if (assistiveOnly) { + element.addClassName(ASSISTIVE_DEVICE_ONLY_STYLE); + } else { + element.removeClassName(ASSISTIVE_DEVICE_ONLY_STYLE); + } + } +} diff --git a/client/src/com/vaadin/client/ui/aria/HandlesAriaCaption.java b/client/src/com/vaadin/client/ui/aria/HandlesAriaCaption.java new file mode 100644 index 0000000000..50f83fdede --- /dev/null +++ b/client/src/com/vaadin/client/ui/aria/HandlesAriaCaption.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.client.ui.aria; + +import com.google.gwt.user.client.Element; + +/** + * Some Widgets need to handle the caption handling for WAI-ARIA themselfs, as + * for example the required ids need to be set in a specific way. In such a + * case, the Widget needs to implement this interface. + */ +public interface HandlesAriaCaption { + + /** + * Called to bind the provided caption (label in HTML speak) element to the + * main input element of the Widget. + * + * Binding should be removed from the main input field when captionElement + * is null. + * + * @param captionElement + * Element of the caption + */ + void bindAriaCaption(Element captionElement); +} diff --git a/client/src/com/vaadin/client/ui/aria/HandlesAriaInvalid.java b/client/src/com/vaadin/client/ui/aria/HandlesAriaInvalid.java new file mode 100644 index 0000000000..05cb82b0d6 --- /dev/null +++ b/client/src/com/vaadin/client/ui/aria/HandlesAriaInvalid.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.client.ui.aria; + +/** + * Some Widgets need to handle the required handling for WAI-ARIA themselfs, as + * this attribute needs to be set to the input element itself. In such a case, + * the Widget needs to implement this interface. + */ +public interface HandlesAriaInvalid { + /** + * Called to set the element, typically an input element, as invalid. + * + * @param invalid + * boolean, true when the element should be marked invalid, false + * otherwise + */ + void setAriaInvalid(boolean invalid); +} diff --git a/client/src/com/vaadin/client/ui/aria/HandlesAriaRequired.java b/client/src/com/vaadin/client/ui/aria/HandlesAriaRequired.java new file mode 100644 index 0000000000..9b18bfb4de --- /dev/null +++ b/client/src/com/vaadin/client/ui/aria/HandlesAriaRequired.java @@ -0,0 +1,32 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.client.ui.aria; + +/** + * Some Widgets need to handle the required handling for WAI-ARIA themselfs, as + * this attribute needs to be set to the input element itself. In such a case, + * the Widget needs to implement this interface. + */ +public interface HandlesAriaRequired { + /** + * Called to set the element, typically an input element, as required. + * + * @param required + * boolean true when the element needs to be set as required + */ + void setAriaRequired(boolean required); +} diff --git a/client/src/com/vaadin/client/ui/button/ButtonConnector.java b/client/src/com/vaadin/client/ui/button/ButtonConnector.java index 9733d206c7..fff983c168 100644 --- a/client/src/com/vaadin/client/ui/button/ButtonConnector.java +++ b/client/src/com/vaadin/client/ui/button/ButtonConnector.java @@ -24,6 +24,7 @@ import com.google.gwt.event.dom.client.FocusEvent; import com.google.gwt.event.dom.client.FocusHandler; import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; import com.vaadin.client.EventHelper; import com.vaadin.client.MouseEventDetailsBuilder; import com.vaadin.client.communication.StateChangeEvent; @@ -83,8 +84,10 @@ public class ButtonConnector extends AbstractComponentConnector implements if (getIcon() != null) { if (getWidget().icon == null) { getWidget().icon = new Icon(getConnection()); - getWidget().wrapper.insertBefore( - getWidget().icon.getElement(), + Element iconElement = getWidget().icon.getElement(); + iconElement.setAttribute("alt", getState().iconAltText); + + getWidget().wrapper.insertBefore(iconElement, getWidget().captionElement); } getWidget().icon.setUri(getIcon()); diff --git a/client/src/com/vaadin/client/ui/calendar/CalendarConnector.java b/client/src/com/vaadin/client/ui/calendar/CalendarConnector.java new file mode 100644 index 0000000000..285d15792b --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/CalendarConnector.java @@ -0,0 +1,662 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar; + +import java.text.ParseException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +import com.google.gwt.core.shared.GWT; +import com.google.gwt.dom.client.NativeEvent; +import com.google.gwt.event.dom.client.ContextMenuEvent; +import com.google.gwt.i18n.client.DateTimeFormat; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.ApplicationConnection; +import com.vaadin.client.TooltipInfo; +import com.vaadin.client.UIDL; +import com.vaadin.client.Util; +import com.vaadin.client.VConsole; +import com.vaadin.client.communication.RpcProxy; +import com.vaadin.client.communication.StateChangeEvent; +import com.vaadin.client.ui.AbstractComponentConnector; +import com.vaadin.client.ui.Action; +import com.vaadin.client.ui.ActionOwner; +import com.vaadin.client.ui.SimpleManagedLayout; +import com.vaadin.client.ui.VCalendar; +import com.vaadin.client.ui.VCalendar.BackwardListener; +import com.vaadin.client.ui.VCalendar.DateClickListener; +import com.vaadin.client.ui.VCalendar.EventClickListener; +import com.vaadin.client.ui.VCalendar.EventMovedListener; +import com.vaadin.client.ui.VCalendar.EventResizeListener; +import com.vaadin.client.ui.VCalendar.ForwardListener; +import com.vaadin.client.ui.VCalendar.MouseEventListener; +import com.vaadin.client.ui.VCalendar.RangeSelectListener; +import com.vaadin.client.ui.VCalendar.WeekClickListener; +import com.vaadin.client.ui.calendar.schedule.CalendarDay; +import com.vaadin.client.ui.calendar.schedule.CalendarEvent; +import com.vaadin.client.ui.calendar.schedule.DateCell; +import com.vaadin.client.ui.calendar.schedule.DateCell.DateCellSlot; +import com.vaadin.client.ui.calendar.schedule.DateCellDayEvent; +import com.vaadin.client.ui.calendar.schedule.DateUtil; +import com.vaadin.client.ui.calendar.schedule.HasTooltipKey; +import com.vaadin.client.ui.calendar.schedule.SimpleDayCell; +import com.vaadin.client.ui.calendar.schedule.dd.CalendarDropHandler; +import com.vaadin.client.ui.dd.VHasDropHandler; +import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.Connect.LoadStyle; +import com.vaadin.shared.ui.calendar.CalendarClientRpc; +import com.vaadin.shared.ui.calendar.CalendarEventId; +import com.vaadin.shared.ui.calendar.CalendarServerRpc; +import com.vaadin.shared.ui.calendar.CalendarState; +import com.vaadin.shared.ui.calendar.DateConstants; +import com.vaadin.ui.Calendar; + +/** + * Handles communication between Calendar on the server side and + * {@link VCalendar} on the client side. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +@Connect(value = Calendar.class, loadStyle = LoadStyle.LAZY) +public class CalendarConnector extends AbstractComponentConnector implements + VHasDropHandler, ActionOwner, SimpleManagedLayout { + + private CalendarServerRpc rpc = RpcProxy.create(CalendarServerRpc.class, + this); + + private CalendarDropHandler dropHandler; + + private final HashMap<String, String> actionMap = new HashMap<String, String>(); + private HashMap<Object, String> tooltips = new HashMap<Object, String>(); + + /** + * + */ + public CalendarConnector() { + + // Listen to events + registerListeners(); + } + + @Override + protected void init() { + super.init(); + registerRpc(CalendarClientRpc.class, new CalendarClientRpc() { + @Override + public void scroll(int scrollPosition) { + // TODO widget scroll + } + }); + getLayoutManager().registerDependency(this, getWidget().getElement()); + } + + @Override + public void onUnregister() { + super.onUnregister(); + getLayoutManager().unregisterDependency(this, getWidget().getElement()); + } + + @Override + public VCalendar getWidget() { + return (VCalendar) super.getWidget(); + } + + @Override + public CalendarState getState() { + return (CalendarState) super.getState(); + } + + /** + * Registers listeners on the calendar so server can be notified of the + * events + */ + protected void registerListeners() { + getWidget().setListener(new DateClickListener() { + @Override + public void dateClick(String date) { + if (!getWidget().isDisabledOrReadOnly() + && hasEventListener(CalendarEventId.DATECLICK)) { + rpc.dateClick(date); + } + } + }); + getWidget().setListener(new ForwardListener() { + @Override + public void forward() { + if (hasEventListener(CalendarEventId.FORWARD)) { + rpc.forward(); + } + } + }); + getWidget().setListener(new BackwardListener() { + @Override + public void backward() { + if (hasEventListener(CalendarEventId.BACKWARD)) { + rpc.backward(); + } + } + }); + getWidget().setListener(new RangeSelectListener() { + @Override + public void rangeSelected(String value) { + if (hasEventListener(CalendarEventId.RANGESELECT)) { + rpc.rangeSelect(value); + } + } + }); + getWidget().setListener(new WeekClickListener() { + @Override + public void weekClick(String event) { + if (!getWidget().isDisabledOrReadOnly() + && hasEventListener(CalendarEventId.WEEKCLICK)) { + rpc.weekClick(event); + } + } + }); + getWidget().setListener(new EventMovedListener() { + @Override + public void eventMoved(CalendarEvent event) { + if (hasEventListener(CalendarEventId.EVENTMOVE)) { + StringBuilder sb = new StringBuilder(); + sb.append(DateUtil.formatClientSideDate(event.getStart())); + sb.append("-"); + sb.append(DateUtil.formatClientSideTime(event + .getStartTime())); + rpc.eventMove(event.getIndex(), sb.toString()); + } + } + }); + getWidget().setListener(new EventResizeListener() { + @Override + public void eventResized(CalendarEvent event) { + if (hasEventListener(CalendarEventId.EVENTRESIZE)) { + StringBuilder buffer = new StringBuilder(); + + buffer.append(DateUtil.formatClientSideDate(event + .getStart())); + buffer.append("-"); + buffer.append(DateUtil.formatClientSideTime(event + .getStartTime())); + + String newStartDate = buffer.toString(); + + buffer = new StringBuilder(); + buffer.append(DateUtil.formatClientSideDate(event.getEnd())); + buffer.append("-"); + buffer.append(DateUtil.formatClientSideTime(event + .getEndTime())); + + String newEndDate = buffer.toString(); + + rpc.eventResize(event.getIndex(), newStartDate, newEndDate); + } + } + }); + getWidget().setListener(new VCalendar.ScrollListener() { + @Override + public void scroll(int scrollPosition) { + // This call is @Delayed (== non-immediate) + rpc.scroll(scrollPosition); + } + }); + getWidget().setListener(new EventClickListener() { + @Override + public void eventClick(CalendarEvent event) { + if (hasEventListener(CalendarEventId.EVENTCLICK)) { + rpc.eventClick(event.getIndex()); + } + } + }); + getWidget().setListener(new MouseEventListener() { + @Override + public void contextMenu(ContextMenuEvent event, final Widget widget) { + final NativeEvent ne = event.getNativeEvent(); + int left = ne.getClientX(); + int top = ne.getClientY(); + top += Window.getScrollTop(); + left += Window.getScrollLeft(); + getClient().getContextMenu().showAt(new ActionOwner() { + @Override + public String getPaintableId() { + return CalendarConnector.this.getPaintableId(); + } + + @Override + public ApplicationConnection getClient() { + return CalendarConnector.this.getClient(); + } + + @Override + @SuppressWarnings("deprecation") + public Action[] getActions() { + if (widget instanceof SimpleDayCell) { + /* + * Month view + */ + SimpleDayCell cell = (SimpleDayCell) widget; + Date start = new Date(cell.getDate().getYear(), + cell.getDate().getMonth(), cell.getDate() + .getDate(), 0, 0, 0); + + Date end = new Date(cell.getDate().getYear(), cell + .getDate().getMonth(), cell.getDate() + .getDate(), 23, 59, 59); + + return CalendarConnector.this.getActionsBetween( + start, end); + } else if (widget instanceof DateCell) { + /* + * Week and Day view + */ + DateCell cell = (DateCell) widget; + int slotIndex = DOM.getChildIndex( + cell.getElement(), (Element) ne + .getEventTarget().cast()); + DateCellSlot slot = cell.getSlot(slotIndex); + return CalendarConnector.this.getActionsBetween( + slot.getFrom(), slot.getTo()); + } else if (widget instanceof DateCellDayEvent) { + /* + * Context menu on event + */ + DateCellDayEvent dayEvent = (DateCellDayEvent) widget; + CalendarEvent event = dayEvent.getCalendarEvent(); + Action[] actions = CalendarConnector.this + .getActionsBetween(event.getStartTime(), + event.getEndTime()); + for (Action action : actions) { + ((VCalendarAction) action).setEvent(event); + } + return actions; + + } + return null; + } + }, left, top); + } + }); + } + + @Override + public void onStateChanged(StateChangeEvent stateChangeEvent) { + super.onStateChanged(stateChangeEvent); + + CalendarState state = getState(); + VCalendar widget = getWidget(); + boolean monthView = state.days.size() > 7; + + // Enable or disable the forward and backward navigation buttons + widget.setForwardNavigationEnabled(hasEventListener(CalendarEventId.FORWARD)); + widget.setBackwardNavigationEnabled(hasEventListener(CalendarEventId.BACKWARD)); + + widget.set24HFormat(state.format24H); + widget.setDayNames(state.dayNames); + widget.setMonthNames(state.monthNames); + widget.setFirstDayNumber(state.firstVisibleDayOfWeek); + widget.setLastDayNumber(state.lastVisibleDayOfWeek); + widget.setFirstHourOfTheDay(state.firstHourOfDay); + widget.setLastHourOfTheDay(state.lastHourOfDay); + widget.setReadOnly(state.readOnly); + widget.setDisabled(!state.enabled); + + widget.setRangeSelectAllowed(hasEventListener(CalendarEventId.RANGESELECT)); + widget.setRangeMoveAllowed(hasEventListener(CalendarEventId.EVENTMOVE)); + widget.setEventMoveAllowed(hasEventListener(CalendarEventId.EVENTMOVE)); + widget.setEventResizeAllowed(hasEventListener(CalendarEventId.EVENTRESIZE)); + + List<CalendarState.Day> days = state.days; + List<CalendarState.Event> events = state.events; + + if (monthView) { + updateMonthView(days, events); + } else { + updateWeekView(days, events); + } + + updateSizes(); + + registerEventToolTips(state.events); + updateActionMap(state.actions); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.Paintable#updateFromUIDL(com.vaadin.terminal + * .gwt.client.UIDL, com.vaadin.terminal.gwt.client.ApplicationConnection) + */ + public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { + + // check for DD -related access criteria + // Iterator<Object> childIterator = uidl.getChildIterator(); + // while (childIterator.hasNext()) { + // UIDL child = (UIDL) childIterator.next(); + // + // // Drag&drop + // if (ACCESSCRITERIA.equals(child.getTag())) { + // if (monthView + // && !(getDropHandler() instanceof CalendarMonthDropHandler)) { + // setDropHandler(new CalendarMonthDropHandler()); + // + // } else if (!monthView + // && !(getDropHandler() instanceof CalendarWeekDropHandler)) { + // setDropHandler(new CalendarWeekDropHandler()); + // } + // + // getDropHandler().setCalendarPaintable(this); + // getDropHandler().updateAcceptRules(child); + // + // } else { + // setDropHandler(null); + // } + // + // } + } + + /** + * Returns the ApplicationConnection used to connect to the server side + */ + @Override + public ApplicationConnection getClient() { + return getConnection(); + } + + /** + * Register the description of the events as tooltips. This way, any event + * displaying widget can use the event index as a key to display the + * tooltip. + */ + private void registerEventToolTips(List<CalendarState.Event> events) { + for (CalendarState.Event e : events) { + if (e.description != null && !"".equals(e.description)) { + tooltips.put(e.index, e.description); + } else { + tooltips.remove(e.index); + } + } + } + + @Override + public TooltipInfo getTooltipInfo(com.google.gwt.dom.client.Element element) { + TooltipInfo tooltipInfo = null; + Widget w = Util.findWidget((Element) element, null); + if (w instanceof HasTooltipKey) { + tooltipInfo = GWT.create(TooltipInfo.class); + String title = tooltips.get(((HasTooltipKey) w).getTooltipKey()); + tooltipInfo.setTitle(title != null ? title : ""); + } + if (tooltipInfo == null) { + tooltipInfo = super.getTooltipInfo(element); + } + return tooltipInfo; + } + + @Override + public boolean hasTooltip() { + /* + * Tooltips are not processed until updateFromUIDL, so we can't be sure + * that there are no tooltips during onStateChange when this is used. + */ + return true; + } + + private void updateMonthView(List<CalendarState.Day> days, + List<CalendarState.Event> events) { + CalendarState state = getState(); + getWidget().updateMonthView(state.firstDayOfWeek, + getWidget().getDateTimeFormat().parse(state.now), days.size(), + calendarEventListOf(events, state.format24H), + calendarDayListOf(days)); + } + + private void updateWeekView(List<CalendarState.Day> days, + List<CalendarState.Event> events) { + CalendarState state = getState(); + getWidget().updateWeekView(state.scroll, + getWidget().getDateTimeFormat().parse(state.now), days.size(), + state.firstDayOfWeek, + calendarEventListOf(events, state.format24H), + calendarDayListOf(days)); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.ui.dd.VHasDropHandler#getDropHandler() + */ + @Override + public CalendarDropHandler getDropHandler() { + return dropHandler; + } + + /** + * Set the drop handler + * + * @param dropHandler + * The drophandler to use + */ + public void setDropHandler(CalendarDropHandler dropHandler) { + this.dropHandler = dropHandler; + } + + private Action[] getActionsBetween(Date start, Date end) { + List<Action> actions = new ArrayList<Action>(); + for (int i = 0; i < actionKeys.size(); i++) { + final String actionKey = actionKeys.get(i); + Date actionStartDate; + Date actionEndDate; + try { + actionStartDate = getActionStartDate(actionKey); + actionEndDate = getActionEndDate(actionKey); + } catch (ParseException pe) { + VConsole.error("Failed to parse action date"); + continue; + } + + boolean startIsValid = start.compareTo(actionStartDate) >= 0; + boolean endIsValid = end.compareTo(actionEndDate) <= 0; + if (startIsValid && endIsValid) { + VCalendarAction a = new VCalendarAction(this, rpc, actionKey); + a.setCaption(getActionCaption(actionKey)); + a.setIconUrl(getActionIcon(actionKey)); + a.setActionStartDate(start); + a.setActionEndDate(end); + actions.add(a); + } + } + + return actions.toArray(new Action[actions.size()]); + } + + private List<String> actionKeys = new ArrayList<String>(); + + private void updateActionMap(List<CalendarState.Action> actions) { + actionMap.clear(); + actionKeys.clear(); + + if (actions == null) { + return; + } + + for (CalendarState.Action action : actions) { + String id = action.actionKey + "-" + action.startDate + "-" + + action.endDate; + actionMap.put(id + "_c", action.caption); + actionMap.put(id + "_s", action.startDate); + actionMap.put(id + "_e", action.endDate); + actionKeys.add(id); + if (action.iconKey != null) { + actionMap.put(id + "_i", getResourceUrl(action.iconKey)); + + } else { + actionMap.remove(id + "_i"); + } + } + } + + /** + * Get the text that is displayed for a context menu item + * + * @param actionKey + * The unique action key + * @return + */ + public String getActionCaption(String actionKey) { + return actionMap.get(actionKey + "_c"); + } + + /** + * Get the icon url for a context menu item + * + * @param actionKey + * The unique action key + * @return + */ + public String getActionIcon(String actionKey) { + return actionMap.get(actionKey + "_i"); + } + + /** + * Get the start date for an action item + * + * @param actionKey + * The unique action key + * @return + * @throws ParseException + */ + public Date getActionStartDate(String actionKey) throws ParseException { + String dateStr = actionMap.get(actionKey + "_s"); + DateTimeFormat formatter = DateTimeFormat + .getFormat(DateConstants.ACTION_DATE_FORMAT_PATTERN); + return formatter.parse(dateStr); + } + + /** + * Get the end date for an action item + * + * @param actionKey + * The unique action key + * @return + * @throws ParseException + */ + public Date getActionEndDate(String actionKey) throws ParseException { + String dateStr = actionMap.get(actionKey + "_e"); + DateTimeFormat formatter = DateTimeFormat + .getFormat(DateConstants.ACTION_DATE_FORMAT_PATTERN); + return formatter.parse(dateStr); + } + + /** + * Returns ALL currently registered events. Use {@link #getActions(Date)} to + * get the actions for a specific date + */ + @Override + public Action[] getActions() { + List<Action> actions = new ArrayList<Action>(); + for (int i = 0; i < actionKeys.size(); i++) { + final String actionKey = actionKeys.get(i); + final VCalendarAction a = new VCalendarAction(this, rpc, actionKey); + a.setCaption(getActionCaption(actionKey)); + a.setIconUrl(getActionIcon(actionKey)); + + try { + a.setActionStartDate(getActionStartDate(actionKey)); + a.setActionEndDate(getActionEndDate(actionKey)); + } catch (ParseException pe) { + VConsole.error(pe); + } + + actions.add(a); + } + return actions.toArray(new Action[actions.size()]); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.terminal.gwt.client.ui.ActionOwner#getPaintableId() + */ + @Override + public String getPaintableId() { + return getConnectorId(); + } + + private List<CalendarEvent> calendarEventListOf( + List<CalendarState.Event> events, boolean format24h) { + List<CalendarEvent> list = new ArrayList<CalendarEvent>(events.size()); + for (CalendarState.Event event : events) { + final String dateFrom = event.dateFrom; + final String dateTo = event.dateTo; + final String timeFrom = event.timeFrom; + final String timeTo = event.timeTo; + CalendarEvent calendarEvent = new CalendarEvent(); + calendarEvent.setAllDay(event.allDay); + calendarEvent.setCaption(event.caption); + calendarEvent.setDescription(event.description); + calendarEvent.setStart(getWidget().getDateFormat().parse(dateFrom)); + calendarEvent.setEnd(getWidget().getDateFormat().parse(dateTo)); + calendarEvent.setFormat24h(format24h); + calendarEvent.setStartTime(getWidget().getDateTimeFormat().parse( + dateFrom + " " + timeFrom)); + calendarEvent.setEndTime(getWidget().getDateTimeFormat().parse( + dateTo + " " + timeTo)); + calendarEvent.setStyleName(event.styleName); + calendarEvent.setIndex(event.index); + list.add(calendarEvent); + } + return list; + } + + private List<CalendarDay> calendarDayListOf(List<CalendarState.Day> days) { + List<CalendarDay> list = new ArrayList<CalendarDay>(days.size()); + for (CalendarState.Day day : days) { + CalendarDay d = new CalendarDay(day.date, day.localizedDateFormat, + day.dayOfWeek, day.week); + + list.add(d); + } + return list; + } + + @Override + public void layout() { + updateSizes(); + } + + private void updateSizes() { + int height = getLayoutManager() + .getOuterHeight(getWidget().getElement()); + int width = getLayoutManager().getOuterWidth(getWidget().getElement()); + + if (isUndefinedWidth()) { + width = -1; + } + if (isUndefinedHeight()) { + height = -1; + } + + getWidget().setSizeForChildren(width, height); + + } +} diff --git a/client/src/com/vaadin/client/ui/calendar/VCalendarAction.java b/client/src/com/vaadin/client/ui/calendar/VCalendarAction.java new file mode 100644 index 0000000000..2a529354e5 --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/VCalendarAction.java @@ -0,0 +1,138 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar; + +import java.util.Date; + +import com.google.gwt.i18n.client.DateTimeFormat; +import com.vaadin.client.ui.Action; +import com.vaadin.client.ui.calendar.schedule.CalendarEvent; +import com.vaadin.shared.ui.calendar.CalendarServerRpc; +import com.vaadin.shared.ui.calendar.DateConstants; + +/** + * Action performed by the calendar + * + * @since 7.1 + * @author Vaadin Ltd. + */ +public class VCalendarAction extends Action { + + private CalendarServerRpc rpc; + + private String actionKey = ""; + + private Date actionStartDate; + + private Date actionEndDate; + + private CalendarEvent event; + + private final DateTimeFormat dateformat_datetime = DateTimeFormat + .getFormat(DateConstants.ACTION_DATE_FORMAT_PATTERN); + + /** + * + * @param owner + */ + public VCalendarAction(CalendarConnector owner) { + super(owner); + } + + /** + * Constructor + * + * @param owner + * The owner who trigger this kinds of events + * @param rpc + * The CalendarRpc which is used for executing actions + * @param key + * The unique action key which identifies this particular action + */ + public VCalendarAction(CalendarConnector owner, CalendarServerRpc rpc, + String key) { + this(owner); + this.rpc = rpc; + actionKey = key; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.terminal.gwt.client.ui.Action#execute() + */ + @Override + public void execute() { + String startDate = dateformat_datetime.format(actionStartDate); + String endDate = dateformat_datetime.format(actionEndDate); + + if (event == null) { + rpc.actionOnEmptyCell(actionKey.split("-")[0], startDate, endDate); + } else { + rpc.actionOnEvent(actionKey.split("-")[0], startDate, endDate, + event.getIndex()); + } + + owner.getClient().getContextMenu().hide(); + } + + /** + * Get the date and time when the action starts + * + * @return + */ + public Date getActionStartDate() { + return actionStartDate; + } + + /** + * Set the date when the actions start + * + * @param actionStartDate + * The date and time when the action starts + */ + public void setActionStartDate(Date actionStartDate) { + this.actionStartDate = actionStartDate; + } + + /** + * Get the date and time when the action ends + * + * @return + */ + public Date getActionEndDate() { + return actionEndDate; + } + + /** + * Set the date and time when the action ends + * + * @param actionEndDate + * The date and time when the action ends + */ + public void setActionEndDate(Date actionEndDate) { + this.actionEndDate = actionEndDate; + } + + public CalendarEvent getEvent() { + return event; + } + + public void setEvent(CalendarEvent event) { + this.event = event; + } + +} diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/CalendarDay.java b/client/src/com/vaadin/client/ui/calendar/schedule/CalendarDay.java new file mode 100644 index 0000000000..ca176c08c1 --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/CalendarDay.java @@ -0,0 +1,55 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +/** + * Utility class used to represent a day when updating views. Only used + * internally. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +public class CalendarDay { + private String date; + private String localizedDateFormat; + private int dayOfWeek; + private int week; + + public CalendarDay(String date, String localizedDateFormat, int dayOfWeek, + int week) { + super(); + this.date = date; + this.localizedDateFormat = localizedDateFormat; + this.dayOfWeek = dayOfWeek; + this.week = week; + } + + public String getDate() { + return date; + } + + public String getLocalizedDateFormat() { + return localizedDateFormat; + } + + public int getDayOfWeek() { + return dayOfWeek; + } + + public int getWeek() { + return week; + } +} diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/CalendarEvent.java b/client/src/com/vaadin/client/ui/calendar/schedule/CalendarEvent.java new file mode 100644 index 0000000000..e2c06d41ea --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/CalendarEvent.java @@ -0,0 +1,313 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import java.util.Date; + +import com.google.gwt.i18n.client.DateTimeFormat; +import com.vaadin.shared.ui.calendar.DateConstants; + +/** + * A client side implementation of a calendar event + * + * @since 7.1 + * @author Vaadin Ltd. + */ +public class CalendarEvent { + private int index; + private String caption; + private Date start, end; + private String styleName; + private Date startTime, endTime; + private String description; + private int slotIndex = -1; + private boolean format24h; + + DateTimeFormat dateformat_date = DateTimeFormat.getFormat("h:mm a"); + DateTimeFormat dateformat_date24 = DateTimeFormat.getFormat("H:mm"); + private boolean allDay; + + /** + * @see com.vaadin.addon.calendar.event.CalendarEvent#getStyleName() + */ + public String getStyleName() { + return styleName; + } + + /** + * @see com.vaadin.addon.calendar.event.CalendarEvent#getStart() + */ + public Date getStart() { + return start; + } + + /** + * @see com.vaadin.addon.calendar.event.CalendarEvent#getStyleName() + * @param style + */ + public void setStyleName(String style) { + styleName = style; + } + + /** + * @see com.vaadin.addon.calendar.event.CalendarEvent#getStart() + * @param start + */ + public void setStart(Date start) { + this.start = start; + } + + /** + * @see com.vaadin.addon.calendar.event.CalendarEvent#getEnd() + * @return + */ + public Date getEnd() { + return end; + } + + /** + * @see com.vaadin.addon.calendar.event.CalendarEvent#getEnd() + * @param end + */ + public void setEnd(Date end) { + this.end = end; + } + + /** + * Returns the start time of the event + * + * @return Time embedded in the {@link Date} object + */ + public Date getStartTime() { + return startTime; + } + + /** + * Set the start time of the event + * + * @param startTime + * The time of the event. Use the time fields in the {@link Date} + * object + */ + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + /** + * Get the end time of the event + * + * @return Time embedded in the {@link Date} object + */ + public Date getEndTime() { + return endTime; + } + + /** + * Set the end time of the event + * + * @param endTime + * Time embedded in the {@link Date} object + */ + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + /** + * Get the (server side) index of the event + * + * @return + */ + public int getIndex() { + return index; + } + + /** + * Get the index of the slot where the event in rendered + * + * @return + */ + public int getSlotIndex() { + return slotIndex; + } + + /** + * Set the index of the slot where the event in rendered + * + * @param index + * The index of the slot + */ + public void setSlotIndex(int index) { + slotIndex = index; + } + + /** + * Set the (server side) index of the event + * + * @param index + * The index + */ + public void setIndex(int index) { + this.index = index; + } + + /** + * Get the caption of the event. The caption is the text displayed in the + * calendar on the event. + * + * @return + */ + public String getCaption() { + return caption; + } + + /** + * Set the caption of the event. The caption is the text displayed in the + * calendar on the event. + * + * @param caption + * The visible caption of the event + */ + public void setCaption(String caption) { + this.caption = caption; + } + + /** + * Get the description of the event. The description is the text displayed + * when hoovering over the event with the mouse + * + * @return + */ + public String getDescription() { + return description; + } + + /** + * Set the description of the event. The description is the text displayed + * when hoovering over the event with the mouse + * + * @param description + */ + public void setDescription(String description) { + this.description = description; + } + + /** + * Does the event use the 24h time format + * + * @param format24h + * True if it uses the 24h format, false if it uses the 12h time + * format + */ + public void setFormat24h(boolean format24h) { + this.format24h = format24h; + } + + /** + * Is the event an all day event. + * + * @param allDay + * True if the event should be rendered all day + */ + public void setAllDay(boolean allDay) { + this.allDay = allDay; + } + + /** + * Is the event an all day event. + * + * @return + */ + public boolean isAllDay() { + return allDay; + } + + /** + * Get the time as a formatted string + * + * @return + */ + public String getTimeAsText() { + if (format24h) { + return dateformat_date24.format(startTime); + } else { + return dateformat_date.format(startTime); + } + } + + /** + * Get the amount of milliseconds between the start and end of the event + * + * @return + */ + public long getRangeInMilliseconds() { + return getEndTime().getTime() - getStartTime().getTime(); + } + + /** + * Get the amount of minutes between the start and end of the event + * + * @return + */ + public long getRangeInMinutes() { + return (getRangeInMilliseconds() / DateConstants.MINUTEINMILLIS); + } + + /** + * Get the amount of minutes for the event on a specific day. This is useful + * if the event spans several days. + * + * @param targetDay + * The date to check + * @return + */ + public long getRangeInMinutesForDay(Date targetDay) { + if (isTimeOnDifferentDays()) { + // Time range is on different days. Calculate the second day's + // range. + long range = (getEndTime().getTime() - getEnd().getTime()) + / DateConstants.MINUTEINMILLIS; + + if (getEnd().compareTo(targetDay) != 0) { + // Calculate first day's range. + return getRangeInMinutes() - range; + } + + return range; + } else { + return getRangeInMinutes(); + } + } + + /** + * Does the event span several days + * + * @return + */ + @SuppressWarnings("deprecation") + public boolean isTimeOnDifferentDays() { + if (getEndTime().getTime() - getStart().getTime() > DateConstants.DAYINMILLIS) { + return true; + } + + if (getStart().compareTo(getEnd()) != 0) { + if (getEndTime().getHours() == 0 && getEndTime().getMinutes() == 0) { + return false; + } + return true; + } + return false; + } +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DateCell.java b/client/src/com/vaadin/client/ui/calendar/schedule/DateCell.java new file mode 100644 index 0000000000..516447153e --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/DateCell.java @@ -0,0 +1,810 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.NativeEvent; +import com.google.gwt.dom.client.Node; +import com.google.gwt.dom.client.NodeList; +import com.google.gwt.dom.client.Style.Display; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.event.dom.client.ContextMenuEvent; +import com.google.gwt.event.dom.client.ContextMenuHandler; +import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.event.dom.client.KeyDownEvent; +import com.google.gwt.event.dom.client.KeyDownHandler; +import com.google.gwt.event.dom.client.MouseDownEvent; +import com.google.gwt.event.dom.client.MouseDownHandler; +import com.google.gwt.event.dom.client.MouseMoveEvent; +import com.google.gwt.event.dom.client.MouseMoveHandler; +import com.google.gwt.event.dom.client.MouseUpEvent; +import com.google.gwt.event.dom.client.MouseUpHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.Util; + +public class DateCell extends FocusableComplexPanel implements + MouseDownHandler, MouseMoveHandler, MouseUpHandler, KeyDownHandler, + ContextMenuHandler { + private static final String DRAGEMPHASISSTYLE = " dragemphasis"; + private Date date; + private int width; + private int eventRangeStart = -1; + private int eventRangeStop = -1; + final WeekGrid weekgrid; + private boolean disabled = false; + private int height; + private final Element[] slotElements; + private final List<DateCellSlot> slots = new ArrayList<DateCell.DateCellSlot>(); + private int[] slotElementHeights; + private int startingSlotHeight; + private Date today; + private Element todaybar; + private final List<HandlerRegistration> handlers; + private final int numberOfSlots; + private final int firstHour; + private final int lastHour; + + public class DateCellSlot extends Widget { + + private final DateCell cell; + + private final Date from; + + private final Date to; + + public DateCellSlot(DateCell cell, Date from, Date to) { + setElement(DOM.createDiv()); + getElement().setInnerHTML(" "); + this.cell = cell; + this.from = from; + this.to = to; + } + + public Date getFrom() { + return from; + } + + public Date getTo() { + return to; + } + + public DateCell getParentCell() { + return cell; + } + } + + public DateCell(WeekGrid parent, Date date) { + weekgrid = parent; + Element mainElement = DOM.createDiv(); + setElement(mainElement); + makeFocusable(); + setDate(date); + + addStyleName("v-calendar-day-times"); + + handlers = new LinkedList<HandlerRegistration>(); + + // 2 slots / hour + firstHour = weekgrid.getFirstHour(); + lastHour = weekgrid.getLastHour(); + numberOfSlots = (lastHour - firstHour + 1) * 2; + long slotTime = Math.round(((lastHour - firstHour + 1) * 3600000.0) + / numberOfSlots); + + slotElements = new Element[numberOfSlots]; + slotElementHeights = new int[numberOfSlots]; + + slots.clear(); + long start = getDate().getTime() + firstHour * 3600000; + long end = start + slotTime; + for (int i = 0; i < numberOfSlots; i++) { + DateCellSlot slot = new DateCellSlot(this, new Date(start), + new Date(end)); + if (i % 2 == 0) { + slot.setStyleName("v-datecellslot-even"); + } else { + slot.setStyleName("v-datecellslot"); + } + Event.sinkEvents(slot.getElement(), Event.MOUSEEVENTS); + mainElement.appendChild(slot.getElement()); + slotElements[i] = slot.getElement(); + slots.add(slot); + start = end; + end = start + slotTime; + } + + // Sink events for tooltip handling + Event.sinkEvents(mainElement, Event.MOUSEEVENTS); + } + + public int getFirstHour() { + return firstHour; + } + + public int getLastHour() { + return lastHour; + } + + @Override + protected void onAttach() { + super.onAttach(); + + handlers.add(addHandler(this, MouseDownEvent.getType())); + handlers.add(addHandler(this, MouseUpEvent.getType())); + handlers.add(addHandler(this, MouseMoveEvent.getType())); + handlers.add(addDomHandler(this, ContextMenuEvent.getType())); + handlers.add(addKeyDownHandler(this)); + } + + @Override + protected void onDetach() { + for (HandlerRegistration handler : handlers) { + handler.removeHandler(); + } + handlers.clear(); + + super.onDetach(); + } + + public int getSlotIndex(Element slotElement) { + for (int i = 0; i < slotElements.length; i++) { + if (slotElement == slotElements[i]) { + return i; + } + } + + throw new IllegalArgumentException("Element not found in this DateCell"); + } + + public DateCellSlot getSlot(int index) { + return slots.get(index); + } + + public int getNumberOfSlots() { + return numberOfSlots; + } + + public void setTimeBarWidth(int timebarWidth) { + todaybar.getStyle().setWidth(timebarWidth, Unit.PX); + } + + /** + * @param isHorizontalSized + * if true, this DateCell is sized with CSS and not via + * {@link #setWidthPX(int)} + */ + public void setHorizontalSized(boolean isHorizontalSized) { + if (isHorizontalSized) { + addStyleDependentName("Hsized"); + + width = getOffsetWidth() + - Util.measureHorizontalBorder(getElement()); + recalculateEventWidths(); + } else { + removeStyleDependentName("Hsized"); + } + } + + /** + * @param isVerticalSized + * if true, this DateCell is sized with CSS and not via + * {@link #setHeightPX(int)} + */ + public void setVerticalSized(boolean isVerticalSized) { + if (isVerticalSized) { + addStyleDependentName("Vsized"); + + // recalc heights&size for events. all other height sizes come + // from css + startingSlotHeight = slotElements[0].getOffsetHeight(); + recalculateEventPositions(); + + if (isToday()) { + recalculateTimeBarPosition(); + } + + } else { + removeStyleDependentName("Vsized"); + } + } + + public void setDate(Date date) { + this.date = date; + } + + public void setWidthPX(int cellWidth) { + width = cellWidth; + setWidth(cellWidth + "px"); + recalculateEventWidths(); + } + + public void setHeightPX(int height, int[] cellHeights) { + this.height = height; + slotElementHeights = cellHeights; + setHeight(height + "px"); + recalculateCellHeights(); + recalculateEventPositions(); + if (today != null) { + recalculateTimeBarPosition(); + } + } + + // date methods are not deprecated in GWT + @SuppressWarnings("deprecation") + private void recalculateTimeBarPosition() { + int h = today.getHours(); + int m = today.getMinutes(); + if (h >= firstHour && h <= lastHour) { + int pixelTop = weekgrid.getPixelTopFor(m + 60 * h); + todaybar.getStyle().clearDisplay(); + todaybar.getStyle().setTop(pixelTop, Unit.PX); + } else { + todaybar.getStyle().setDisplay(Display.NONE); + } + } + + private void recalculateEventPositions() { + for (int i = 0; i < getWidgetCount(); i++) { + DateCellDayEvent dayEvent = (DateCellDayEvent) getWidget(i); + updatePositionFor(dayEvent, getDate(), dayEvent.getCalendarEvent()); + } + } + + public void recalculateEventWidths() { + List<DateCellGroup> groups = new ArrayList<DateCellGroup>(); + + int count = getWidgetCount(); + + List<Integer> handled = new ArrayList<Integer>(); + + // Iterate through all events and group them. Events that overlaps + // with each other, are added to the same group. + for (int i = 0; i < count; i++) { + if (handled.contains(i)) { + continue; + } + + DateCellGroup curGroup = getOverlappingEvents(i); + handled.addAll(curGroup.getItems()); + + boolean newGroup = true; + // No need to check other groups, if size equals the count + if (curGroup.getItems().size() != count) { + // Check other groups. When the whole group overlaps with + // other group, the group is merged to the other. + for (DateCellGroup g : groups) { + + if (WeekGridMinuteTimeRange.doesOverlap( + curGroup.getDateRange(), g.getDateRange())) { + newGroup = false; + updateGroup(g, curGroup); + } + } + } else { + if (newGroup) { + groups.add(curGroup); + } + break; + } + + if (newGroup) { + groups.add(curGroup); + } + } + + drawDayEvents(groups); + } + + private void recalculateCellHeights() { + startingSlotHeight = height / numberOfSlots; + + for (int i = 0; i < slotElements.length; i++) { + slotElements[i].getStyle() + .setHeight(slotElementHeights[i], Unit.PX); + } + + Iterator<Widget> it = iterator(); + while (it.hasNext()) { + Widget child = it.next(); + if (child instanceof DateCellDayEvent) { + ((DateCellDayEvent) child).setSlotHeightInPX(getSlotHeight()); + } + + } + } + + public int getSlotHeight() { + return startingSlotHeight; + } + + public int getSlotBorder() { + return Util + .measureVerticalBorder((com.google.gwt.user.client.Element) slotElements[0]); + } + + private void drawDayEvents(List<DateCellGroup> groups) { + for (DateCellGroup g : groups) { + int col = 0; + int colCount = 0; + List<Integer> order = new ArrayList<Integer>(); + Map<Integer, Integer> columns = new HashMap<Integer, Integer>(); + for (Integer eventIndex : g.getItems()) { + DateCellDayEvent d = (DateCellDayEvent) getWidget(eventIndex); + d.setMoveWidth(width); + + int freeSpaceCol = findFreeColumnSpaceOnLeft( + new WeekGridMinuteTimeRange(d.getCalendarEvent() + .getStartTime(), d.getCalendarEvent() + .getEndTime()), order, columns); + if (freeSpaceCol >= 0) { + col = freeSpaceCol; + columns.put(eventIndex, col); + int newOrderindex = 0; + for (Integer i : order) { + if (columns.get(i) >= col) { + newOrderindex = order.indexOf(i); + break; + } + } + order.add(newOrderindex, eventIndex); + } else { + // New column + col = colCount++; + columns.put(eventIndex, col); + order.add(eventIndex); + } + } + + // Update widths and left position + int eventWidth = (width / colCount); + for (Integer index : g.getItems()) { + DateCellDayEvent d = (DateCellDayEvent) getWidget(index); + d.getElement() + .getStyle() + .setMarginLeft((eventWidth * columns.get(index)), + Unit.PX); + d.setWidth(eventWidth + "px"); + d.setSlotHeightInPX(getSlotHeight()); + } + } + } + + private int findFreeColumnSpaceOnLeft(WeekGridMinuteTimeRange dateRange, + List<Integer> order, Map<Integer, Integer> columns) { + int freeSpot = -1; + int skipIndex = -1; + for (Integer eventIndex : order) { + int col = columns.get(eventIndex); + if (col == skipIndex) { + continue; + } + + if (freeSpot != -1 && freeSpot != col) { + // Free spot found + return freeSpot; + } + + DateCellDayEvent d = (DateCellDayEvent) getWidget(eventIndex); + WeekGridMinuteTimeRange nextRange = new WeekGridMinuteTimeRange(d + .getCalendarEvent().getStartTime(), d.getCalendarEvent() + .getEndTime()); + + if (WeekGridMinuteTimeRange.doesOverlap(dateRange, nextRange)) { + skipIndex = col; + freeSpot = -1; + } else { + freeSpot = col; + } + } + + return freeSpot; + } + + /* Update top and bottom date range values. Add new index to the group. */ + private void updateGroup(DateCellGroup targetGroup, DateCellGroup byGroup) { + Date newStart = targetGroup.getStart(); + Date newEnd = targetGroup.getEnd(); + if (byGroup.getStart().before(targetGroup.getStart())) { + newStart = byGroup.getEnd(); + } + if (byGroup.getStart().after(targetGroup.getEnd())) { + newStart = byGroup.getStart(); + } + + targetGroup.setDateRange(new WeekGridMinuteTimeRange(newStart, newEnd)); + + for (Integer index : byGroup.getItems()) { + if (!targetGroup.getItems().contains(index)) { + targetGroup.add(index); + } + } + } + + /** + * Returns all overlapping DayEvent indexes in the Group. Including the + * target. + * + * @param targetIndex + * Index of DayEvent in the current DateCell widget. + * @return Group that contains all Overlapping DayEvent indexes + */ + public DateCellGroup getOverlappingEvents(int targetIndex) { + DateCellGroup g = new DateCellGroup(targetIndex); + + int count = getWidgetCount(); + DateCellDayEvent target = (DateCellDayEvent) getWidget(targetIndex); + WeekGridMinuteTimeRange targetRange = new WeekGridMinuteTimeRange( + target.getCalendarEvent().getStartTime(), target + .getCalendarEvent().getEndTime()); + Date groupStart = targetRange.getStart(); + Date groupEnd = targetRange.getEnd(); + + for (int i = 0; i < count; i++) { + if (targetIndex == i) { + continue; + } + + DateCellDayEvent d = (DateCellDayEvent) getWidget(i); + WeekGridMinuteTimeRange nextRange = new WeekGridMinuteTimeRange(d + .getCalendarEvent().getStartTime(), d.getCalendarEvent() + .getEndTime()); + if (WeekGridMinuteTimeRange.doesOverlap(targetRange, nextRange)) { + g.add(i); + + // Update top & bottom values to the greatest + if (nextRange.getStart().before(targetRange.getStart())) { + groupStart = targetRange.getStart(); + } + if (nextRange.getEnd().after(targetRange.getEnd())) { + groupEnd = targetRange.getEnd(); + } + } + } + + g.setDateRange(new WeekGridMinuteTimeRange(groupStart, groupEnd)); + return g; + } + + public Date getDate() { + return date; + } + + public void addEvent(Date targetDay, CalendarEvent calendarEvent) { + Element main = getElement(); + DateCellDayEvent dayEvent = new DateCellDayEvent(this, weekgrid, + calendarEvent); + dayEvent.setSlotHeightInPX(getSlotHeight()); + dayEvent.setDisabled(isDisabled()); + + if (startingSlotHeight > 0) { + updatePositionFor(dayEvent, targetDay, calendarEvent); + } + + add(dayEvent, (com.google.gwt.user.client.Element) main); + } + + // date methods are not deprecated in GWT + @SuppressWarnings("deprecation") + private void updatePositionFor(DateCellDayEvent dayEvent, Date targetDay, + CalendarEvent calendarEvent) { + if (canDisplay(calendarEvent)) { + + dayEvent.getElement().getStyle().clearDisplay(); + + Date fromDt = calendarEvent.getStartTime(); + int h = fromDt.getHours(); + int m = fromDt.getMinutes(); + long range = calendarEvent.getRangeInMinutesForDay(targetDay); + + boolean onDifferentDays = calendarEvent.isTimeOnDifferentDays(); + if (onDifferentDays) { + if (calendarEvent.getStart().compareTo(targetDay) != 0) { + // Current day slot is for the end date. Lets fix also + // the + // start & end times. + h = 0; + m = 0; + } + } + + int startFromMinutes = (h * 60) + m; + dayEvent.updatePosition(startFromMinutes, range); + + } else { + dayEvent.getElement().getStyle().setDisplay(Display.NONE); + } + } + + public void addEvent(DateCellDayEvent dayEvent) { + Element main = getElement(); + int index = 0; + List<CalendarEvent> events = new ArrayList<CalendarEvent>(); + + // events are the only widgets in this panel + // slots are just elements + for (; index < getWidgetCount(); index++) { + DateCellDayEvent dc = (DateCellDayEvent) getWidget(index); + dc.setDisabled(isDisabled()); + events.add(dc.getCalendarEvent()); + } + events.add(dayEvent.getCalendarEvent()); + + index = 0; + for (CalendarEvent e : weekgrid.getCalendar().sortEventsByDuration( + events)) { + if (e.equals(dayEvent.getCalendarEvent())) { + break; + } + index++; + } + this.insert(dayEvent, (com.google.gwt.user.client.Element) main, index, + true); + } + + public void removeEvent(DateCellDayEvent dayEvent) { + remove(dayEvent); + } + + /** + * + * @param event + * @return + */ + // Date methods not deprecated in GWT + @SuppressWarnings("deprecation") + private boolean canDisplay(CalendarEvent event) { + Date eventStart = event.getStartTime(); + Date eventEnd = event.getEndTime(); + + int eventStartHours = eventStart.getHours(); + int eventEndHours = eventEnd.getHours(); + + return (eventStartHours <= lastHour) && (eventEndHours >= firstHour); + } + + @Override + public void onKeyDown(KeyDownEvent event) { + int keycode = event.getNativeEvent().getKeyCode(); + if (keycode == KeyCodes.KEY_ESCAPE && eventRangeStart > -1) { + cancelRangeSelect(); + } + } + + @Override + public void onMouseDown(MouseDownEvent event) { + if (event.getNativeButton() == NativeEvent.BUTTON_LEFT) { + Element e = Element.as(event.getNativeEvent().getEventTarget()); + if (e.getClassName().contains("reserved") || isDisabled() + || !weekgrid.getParentCalendar().isRangeSelectAllowed()) { + eventRangeStart = -1; + } else { + eventRangeStart = event.getY(); + eventRangeStop = eventRangeStart; + Event.setCapture(getElement()); + setFocus(true); + } + } + } + + @Override + @SuppressWarnings("deprecation") + public void onMouseUp(MouseUpEvent event) { + if (event.getNativeButton() != NativeEvent.BUTTON_LEFT) { + return; + } + Event.releaseCapture(getElement()); + setFocus(false); + int dragDistance = Math.abs(eventRangeStart - event.getY()); + if (dragDistance > 0 && eventRangeStart >= 0) { + Element main = getElement(); + if (eventRangeStart > eventRangeStop) { + if (eventRangeStop <= -1) { + eventRangeStop = 0; + } + int temp = eventRangeStart; + eventRangeStart = eventRangeStop; + eventRangeStop = temp; + } + + NodeList<Node> nodes = main.getChildNodes(); + + int slotStart = -1; + int slotEnd = -1; + + // iterate over all child nodes, until we find first the start, + // and then the end + for (int i = 0; i < nodes.getLength(); i++) { + Element element = (Element) nodes.getItem(i); + boolean isRangeElement = element.getClassName().contains( + "v-daterange"); + + if (isRangeElement && slotStart == -1) { + slotStart = i; + slotEnd = i; // to catch one-slot selections + + } else if (isRangeElement) { + slotEnd = i; + + } else if (slotStart != -1 && slotEnd != -1) { + break; + } + } + + clearSelectionRange(); + + int startMinutes = firstHour * 60 + slotStart * 30; + int endMinutes = (firstHour * 60) + (slotEnd + 1) * 30; + Date currentDate = getDate(); + String yr = (currentDate.getYear() + 1900) + "-" + + (currentDate.getMonth() + 1) + "-" + + currentDate.getDate(); + if (weekgrid.getCalendar().getRangeSelectListener() != null) { + weekgrid.getCalendar() + .getRangeSelectListener() + .rangeSelected( + yr + ":" + startMinutes + ":" + endMinutes); + } + eventRangeStart = -1; + } else { + // Click event + eventRangeStart = -1; + cancelRangeSelect(); + + } + } + + @Override + public void onMouseMove(MouseMoveEvent event) { + if (event.getNativeButton() != NativeEvent.BUTTON_LEFT) { + return; + } + + if (eventRangeStart >= 0) { + int newY = event.getY(); + int fromY = 0; + int toY = 0; + if (newY < eventRangeStart) { + fromY = newY; + toY = eventRangeStart; + } else { + fromY = eventRangeStart; + toY = newY; + } + Element main = getElement(); + eventRangeStop = newY; + NodeList<Node> nodes = main.getChildNodes(); + for (int i = 0; i < nodes.getLength(); i++) { + Element c = (Element) nodes.getItem(i); + + if (todaybar != c) { + + int elemStart = c.getOffsetTop(); + int elemStop = elemStart + getSlotHeight(); + if (elemStart >= fromY && elemStart <= toY) { + c.addClassName("v-daterange"); + } else if (elemStop >= fromY && elemStop <= toY) { + c.addClassName("v-daterange"); + } else if (elemStop >= fromY && elemStart <= toY) { + c.addClassName("v-daterange"); + } else { + c.removeClassName("v-daterange"); + } + } + } + } + + event.preventDefault(); + } + + public void cancelRangeSelect() { + Event.releaseCapture(getElement()); + setFocus(false); + + clearSelectionRange(); + } + + private void clearSelectionRange() { + if (eventRangeStart > -1) { + // clear all "selected" class names + Element main = getElement(); + NodeList<Node> nodes = main.getChildNodes(); + + for (int i = 0; i <= 47; i++) { + Element c = (Element) nodes.getItem(i); + if (c == null) { + continue; + } + c.removeClassName("v-daterange"); + } + + eventRangeStart = -1; + } + } + + public void setToday(Date today, int width) { + this.today = today; + addStyleDependentName("today"); + Element lastChild = (Element) getElement().getLastChild(); + if (lastChild.getClassName().equals("v-calendar-current-time")) { + todaybar = lastChild; + } else { + todaybar = DOM.createDiv(); + todaybar.setClassName("v-calendar-current-time"); + getElement().appendChild(todaybar); + } + + if (width != -1) { + todaybar.getStyle().setWidth(width, Unit.PX); + } + + // position is calculated later, when we know the cell heights + } + + public Element getTodaybarElement() { + return todaybar; + } + + public void setDisabled(boolean disabled) { + this.disabled = disabled; + } + + public boolean isDisabled() { + return disabled; + } + + public void setDateColor(String styleName) { + this.setStyleName("v-calendar-datecell " + styleName); + } + + public boolean isToday() { + return today != null; + } + + public void addEmphasisStyle(com.google.gwt.user.client.Element elementOver) { + String originalStylename = getStyleName(elementOver); + setStyleName(elementOver, originalStylename + DRAGEMPHASISSTYLE); + } + + public void removeEmphasisStyle( + com.google.gwt.user.client.Element elementOver) { + String originalStylename = getStyleName(elementOver); + setStyleName( + elementOver, + originalStylename.substring(0, originalStylename.length() + - DRAGEMPHASISSTYLE.length())); + } + + @Override + public void onContextMenu(ContextMenuEvent event) { + if (weekgrid.getCalendar().getMouseEventListener() != null) { + event.preventDefault(); + event.stopPropagation(); + weekgrid.getCalendar().getMouseEventListener() + .contextMenu(event, DateCell.this); + } + } +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DateCellContainer.java b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellContainer.java new file mode 100644 index 0000000000..04e6bb7df6 --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellContainer.java @@ -0,0 +1,117 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import java.util.Date; + +import com.google.gwt.event.dom.client.MouseDownEvent; +import com.google.gwt.event.dom.client.MouseDownHandler; +import com.google.gwt.event.dom.client.MouseUpEvent; +import com.google.gwt.event.dom.client.MouseUpHandler; +import com.google.gwt.user.client.ui.FlowPanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.Util; +import com.vaadin.client.ui.VCalendar; + +/** + * Internally used class by the Calendar + * + * since 7.1 + */ +public class DateCellContainer extends FlowPanel implements MouseDownHandler, + MouseUpHandler { + + private Date date; + + private Widget clickTargetWidget; + + private VCalendar calendar; + + private static int borderWidth = -1; + + public DateCellContainer() { + setStylePrimaryName("v-calendar-datecell"); + } + + public static int measureBorderWidth(DateCellContainer dc) { + if (borderWidth == -1) { + borderWidth = Util.measureHorizontalBorder(dc.getElement()); + } + return borderWidth; + } + + public void setCalendar(VCalendar calendar) { + this.calendar = calendar; + } + + public void setDate(Date date) { + this.date = date; + } + + public Date getDate() { + return date; + } + + public boolean hasEvent(int slotIndex) { + return hasDateCell(slotIndex) + && ((WeeklyLongEventsDateCell) getChildren().get(slotIndex)) + .getEvent() != null; + } + + public boolean hasDateCell(int slotIndex) { + return (getChildren().size() - 1) >= slotIndex; + } + + public WeeklyLongEventsDateCell getDateCell(int slotIndex) { + if (!hasDateCell(slotIndex)) { + addEmptyEventCells(slotIndex - (getChildren().size() - 1)); + } + return (WeeklyLongEventsDateCell) getChildren().get(slotIndex); + } + + public void addEmptyEventCells(int eventCount) { + for (int i = 0; i < eventCount; i++) { + addEmptyEventCell(); + } + } + + public void addEmptyEventCell() { + WeeklyLongEventsDateCell dateCell = new WeeklyLongEventsDateCell(); + dateCell.addMouseDownHandler(this); + dateCell.addMouseUpHandler(this); + add(dateCell); + } + + @Override + public void onMouseDown(MouseDownEvent event) { + clickTargetWidget = (Widget) event.getSource(); + + event.stopPropagation(); + } + + @Override + public void onMouseUp(MouseUpEvent event) { + if (event.getSource() == clickTargetWidget + && clickTargetWidget instanceof WeeklyLongEventsDateCell + && !calendar.isDisabledOrReadOnly()) { + CalendarEvent calendarEvent = ((WeeklyLongEventsDateCell) clickTargetWidget) + .getEvent(); + if (calendar.getEventClickListener() != null) { + calendar.getEventClickListener().eventClick(calendarEvent); + } + } + } +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java new file mode 100644 index 0000000000..c56566bf25 --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellDayEvent.java @@ -0,0 +1,639 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.EventTarget; +import com.google.gwt.dom.client.NativeEvent; +import com.google.gwt.dom.client.Style; +import com.google.gwt.dom.client.Style.Position; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.event.dom.client.ContextMenuEvent; +import com.google.gwt.event.dom.client.ContextMenuHandler; +import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.event.dom.client.KeyDownEvent; +import com.google.gwt.event.dom.client.KeyDownHandler; +import com.google.gwt.event.dom.client.MouseDownEvent; +import com.google.gwt.event.dom.client.MouseDownHandler; +import com.google.gwt.event.dom.client.MouseMoveEvent; +import com.google.gwt.event.dom.client.MouseMoveHandler; +import com.google.gwt.event.dom.client.MouseUpEvent; +import com.google.gwt.event.dom.client.MouseUpHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.vaadin.client.Util; +import com.vaadin.client.ui.VCalendar; +import com.vaadin.shared.ui.calendar.DateConstants; + +/** + * Internally used by the calendar + * + * @since 7.1 + */ +public class DateCellDayEvent extends FocusableHTML implements + MouseDownHandler, MouseUpHandler, MouseMoveHandler, KeyDownHandler, + ContextMenuHandler, HasTooltipKey { + + private final DateCell dateCell; + private Element caption = null; + private final Element eventContent; + private CalendarEvent calendarEvent = null; + private HandlerRegistration moveRegistration; + private int startY = -1; + private int startX = -1; + private String moveWidth; + public static final int halfHourInMilliSeconds = 1800 * 1000; + private Date startDatetimeFrom; + private Date startDatetimeTo; + private boolean mouseMoveStarted; + private int top; + private int startYrelative; + private int startXrelative; + private boolean disabled; + private final WeekGrid weekGrid; + private com.google.gwt.user.client.Element topResizeBar; + private com.google.gwt.user.client.Element bottomResizeBar; + private Element clickTarget; + private final Integer eventIndex; + private int slotHeight; + private final List<HandlerRegistration> handlers; + private boolean mouseMoveCanceled; + + public DateCellDayEvent(DateCell dateCell, WeekGrid parent, + CalendarEvent event) { + super(); + this.dateCell = dateCell; + + handlers = new LinkedList<HandlerRegistration>(); + + setStylePrimaryName("v-calendar-event"); + setCalendarEvent(event); + + weekGrid = parent; + + Style s = getElement().getStyle(); + if (event.getStyleName().length() > 0) { + addStyleDependentName(event.getStyleName()); + } + s.setPosition(Position.ABSOLUTE); + + caption = DOM.createDiv(); + caption.addClassName("v-calendar-event-caption"); + getElement().appendChild(caption); + + eventContent = DOM.createDiv(); + eventContent.addClassName("v-calendar-event-content"); + getElement().appendChild(eventContent); + + VCalendar calendar = weekGrid.getCalendar(); + if (weekGrid.getCalendar().isEventResizeAllowed()) { + topResizeBar = DOM.createDiv(); + bottomResizeBar = DOM.createDiv(); + + topResizeBar.addClassName("v-calendar-event-resizetop"); + bottomResizeBar.addClassName("v-calendar-event-resizebottom"); + + getElement().appendChild(topResizeBar); + getElement().appendChild(bottomResizeBar); + } + + eventIndex = event.getIndex(); + } + + @Override + protected void onAttach() { + super.onAttach(); + handlers.add(addMouseDownHandler(this)); + handlers.add(addMouseUpHandler(this)); + handlers.add(addKeyDownHandler(this)); + handlers.add(addDomHandler(this, ContextMenuEvent.getType())); + } + + @Override + protected void onDetach() { + for (HandlerRegistration handler : handlers) { + handler.removeHandler(); + } + handlers.clear(); + super.onDetach(); + } + + public void setSlotHeightInPX(int slotHeight) { + this.slotHeight = slotHeight; + } + + public void updatePosition(long startFromMinutes, long durationInMinutes) { + if (startFromMinutes < 0) { + startFromMinutes = 0; + } + top = weekGrid.getPixelTopFor((int) startFromMinutes); + + getElement().getStyle().setTop(top, Unit.PX); + if (durationInMinutes > 0) { + int heightMinutes = weekGrid.getPixelLengthFor( + (int) startFromMinutes, (int) durationInMinutes); + setHeight(heightMinutes); + } else { + setHeight(-1); + } + + boolean multiRowCaption = (durationInMinutes > 30); + updateCaptions(multiRowCaption); + } + + public int getTop() { + return top; + } + + public void setMoveWidth(int width) { + moveWidth = width + "px"; + } + + public void setHeight(int h) { + if (h == -1) { + getElement().getStyle().setProperty("height", ""); + eventContent.getStyle().setProperty("height", ""); + } else { + getElement().getStyle().setHeight(h, Unit.PX); + // FIXME measure the border height (2px) from the DOM + eventContent.getStyle().setHeight(h - 2, Unit.PX); + } + } + + /** + * @param bigMode + * If false, event is so small that caption must be in time-row + */ + private void updateCaptions(boolean bigMode) { + String separator = bigMode ? "<br />" : ": "; + caption.setInnerHTML("<span>" + calendarEvent.getTimeAsText() + + "</span>" + separator + + Util.escapeHTML(calendarEvent.getCaption())); + eventContent.setInnerHTML(""); + } + + @Override + public void onKeyDown(KeyDownEvent event) { + int keycode = event.getNativeEvent().getKeyCode(); + if (keycode == KeyCodes.KEY_ESCAPE && mouseMoveStarted) { + cancelMouseMove(); + } + } + + @Override + public void onMouseDown(MouseDownEvent event) { + startX = event.getClientX(); + startY = event.getClientY(); + if (isDisabled() || event.getNativeButton() != NativeEvent.BUTTON_LEFT) { + return; + } + + clickTarget = Element.as(event.getNativeEvent().getEventTarget()); + mouseMoveCanceled = false; + + if (weekGrid.getCalendar().isEventMoveAllowed() || clickTargetsResize()) { + moveRegistration = addMouseMoveHandler(this); + setFocus(true); + try { + startYrelative = (int) ((double) event.getRelativeY(caption) % slotHeight); + startXrelative = (event.getRelativeX(weekGrid.getElement()) - weekGrid.timebar + .getOffsetWidth()) % getDateCellWidth(); + } catch (Exception e) { + GWT.log("Exception calculating relative start position", e); + } + mouseMoveStarted = false; + Style s = getElement().getStyle(); + s.setZIndex(1000); + startDatetimeFrom = (Date) calendarEvent.getStartTime().clone(); + startDatetimeTo = (Date) calendarEvent.getEndTime().clone(); + Event.setCapture(getElement()); + } + + // make sure the right cursor is always displayed + if (clickTargetsResize()) { + addGlobalResizeStyle(); + } + + /* + * We need to stop the event propagation or else the WeekGrid range + * select will kick in + */ + event.stopPropagation(); + event.preventDefault(); + } + + @Override + public void onMouseUp(MouseUpEvent event) { + if (mouseMoveCanceled) { + return; + } + + Event.releaseCapture(getElement()); + setFocus(false); + if (moveRegistration != null) { + moveRegistration.removeHandler(); + moveRegistration = null; + } + int endX = event.getClientX(); + int endY = event.getClientY(); + int xDiff = startX - endX; + int yDiff = startY - endY; + startX = -1; + startY = -1; + mouseMoveStarted = false; + Style s = getElement().getStyle(); + s.setZIndex(1); + if (!clickTargetsResize()) { + // check if mouse has moved over threshold of 3 pixels + boolean mouseMoved = (xDiff < -3 || xDiff > 3 || yDiff < -3 || yDiff > 3); + + if (!weekGrid.getCalendar().isDisabledOrReadOnly() && mouseMoved) { + // Event Move: + // - calendar must be enabled + // - calendar must not be in read-only mode + weekGrid.eventMoved(this); + } else if (!weekGrid.getCalendar().isDisabled()) { + // Event Click: + // - calendar must be enabled (read-only is allowed) + EventTarget et = event.getNativeEvent().getEventTarget(); + Element e = Element.as(et); + if (e == caption || e == eventContent + || e.getParentElement() == caption) { + if (weekGrid.getCalendar().getEventClickListener() != null) { + weekGrid.getCalendar().getEventClickListener() + .eventClick(calendarEvent); + } + } + } + + } else { // click targeted resize bar + removeGlobalResizeStyle(); + if (weekGrid.getCalendar().getEventResizeListener() != null) { + weekGrid.getCalendar().getEventResizeListener() + .eventResized(calendarEvent); + } + } + } + + @Override + @SuppressWarnings("deprecation") + public void onMouseMove(MouseMoveEvent event) { + if (startY < 0 && startX < 0) { + return; + } + if (isDisabled()) { + Event.releaseCapture(getElement()); + mouseMoveStarted = false; + startY = -1; + startX = -1; + removeGlobalResizeStyle(); + return; + } + int currentY = event.getClientY(); + int currentX = event.getClientX(); + int moveY = (currentY - startY); + int moveX = (currentX - startX); + if ((moveY < 5 && moveY > -6) && (moveX < 5 && moveX > -6)) { + return; + } + if (!mouseMoveStarted) { + setWidth(moveWidth); + getElement().getStyle().setMarginLeft(0, Unit.PX); + mouseMoveStarted = true; + } + + HorizontalPanel parent = (HorizontalPanel) getParent().getParent(); + int relativeX = event.getRelativeX(parent.getElement()) + - weekGrid.timebar.getOffsetWidth(); + int halfHourDiff = 0; + if (moveY > 0) { + halfHourDiff = (startYrelative + moveY) / slotHeight; + } else { + halfHourDiff = (moveY - startYrelative) / slotHeight; + } + + int dateCellWidth = getDateCellWidth(); + long dayDiff = 0; + if (moveX >= 0) { + dayDiff = (startXrelative + moveX) / dateCellWidth; + } else { + dayDiff = (moveX - (dateCellWidth - startXrelative)) + / dateCellWidth; + } + + int dayOffset = relativeX / dateCellWidth; + + // sanity check for right side overflow + int dateCellCount = weekGrid.getDateCellCount(); + if (dayOffset >= dateCellCount) { + dayOffset--; + dayDiff--; + } + + int dayOffsetPx = calculateDateCellOffsetPx(dayOffset) + + weekGrid.timebar.getOffsetWidth(); + + GWT.log("DateCellWidth: " + dateCellWidth + " dayDiff: " + dayDiff + + " dayOffset: " + dayOffset + " dayOffsetPx: " + dayOffsetPx + + " startXrelative: " + startXrelative + " moveX: " + moveX); + + if (relativeX < 0 || relativeX >= getDatesWidth()) { + return; + } + + Style s = getElement().getStyle(); + + Date from = calendarEvent.getStartTime(); + Date to = calendarEvent.getEndTime(); + long duration = to.getTime() - from.getTime(); + + if (!clickTargetsResize() + && weekGrid.getCalendar().isEventMoveAllowed()) { + long daysMs = dayDiff * DateConstants.DAYINMILLIS; + from.setTime(startDatetimeFrom.getTime() + daysMs); + from.setTime(from.getTime() + + ((long) halfHourInMilliSeconds * halfHourDiff)); + to.setTime((from.getTime() + duration)); + + calendarEvent.setStartTime(from); + calendarEvent.setEndTime(to); + calendarEvent.setStart(new Date(from.getTime())); + calendarEvent.setEnd(new Date(to.getTime())); + + // Set new position for the event + long startFromMinutes = (from.getHours() * 60) + from.getMinutes(); + long range = calendarEvent.getRangeInMinutes(); + startFromMinutes = calculateStartFromMinute(startFromMinutes, from, + to, dayOffsetPx); + if (startFromMinutes < 0) { + range += startFromMinutes; + } + updatePosition(startFromMinutes, range); + + s.setLeft(dayOffsetPx, Unit.PX); + + if (weekGrid.getDateCellWidths() != null) { + s.setWidth(weekGrid.getDateCellWidths()[dayOffset], Unit.PX); + } else { + setWidth(moveWidth); + } + + } else if (clickTarget == topResizeBar) { + long oldStartTime = startDatetimeFrom.getTime(); + long newStartTime = oldStartTime + + ((long) halfHourInMilliSeconds * halfHourDiff); + + if (!isTimeRangeTooSmall(newStartTime, startDatetimeTo.getTime())) { + newStartTime = startDatetimeTo.getTime() - getMinTimeRange(); + } + + from.setTime(newStartTime); + + calendarEvent.setStartTime(from); + calendarEvent.setStart(new Date(from.getTime())); + + // Set new position for the event + long startFromMinutes = (from.getHours() * 60) + from.getMinutes(); + long range = calendarEvent.getRangeInMinutes(); + + updatePosition(startFromMinutes, range); + + } else if (clickTarget == bottomResizeBar) { + long oldEndTime = startDatetimeTo.getTime(); + long newEndTime = oldEndTime + + ((long) halfHourInMilliSeconds * halfHourDiff); + + if (!isTimeRangeTooSmall(startDatetimeFrom.getTime(), newEndTime)) { + newEndTime = startDatetimeFrom.getTime() + getMinTimeRange(); + } + + to.setTime(newEndTime); + + calendarEvent.setEndTime(to); + calendarEvent.setEnd(new Date(to.getTime())); + + // Set new position for the event + long startFromMinutes = (startDatetimeFrom.getHours() * 60) + + startDatetimeFrom.getMinutes(); + long range = calendarEvent.getRangeInMinutes(); + startFromMinutes = calculateStartFromMinute(startFromMinutes, from, + to, dayOffsetPx); + if (startFromMinutes < 0) { + range += startFromMinutes; + } + updatePosition(startFromMinutes, range); + } + } + + private void cancelMouseMove() { + mouseMoveCanceled = true; + + // reset and remove everything related to the event handling + Event.releaseCapture(getElement()); + setFocus(false); + + if (moveRegistration != null) { + moveRegistration.removeHandler(); + moveRegistration = null; + } + + mouseMoveStarted = false; + removeGlobalResizeStyle(); + + Style s = getElement().getStyle(); + s.setZIndex(1); + + // reset the position of the event + int dateCellWidth = getDateCellWidth(); + int dayOffset = startXrelative / dateCellWidth; + s.clearLeft(); + + calendarEvent.setStartTime(startDatetimeFrom); + calendarEvent.setEndTime(startDatetimeTo); + + long startFromMinutes = (startDatetimeFrom.getHours() * 60) + + startDatetimeFrom.getMinutes(); + long range = calendarEvent.getRangeInMinutes(); + + startFromMinutes = calculateStartFromMinute(startFromMinutes, + startDatetimeFrom, startDatetimeTo, dayOffset); + if (startFromMinutes < 0) { + range += startFromMinutes; + } + + updatePosition(startFromMinutes, range); + + startY = -1; + startX = -1; + + // to reset the event width + ((DateCell) getParent()).recalculateEventWidths(); + } + + // date methods are not deprecated in GWT + @SuppressWarnings("deprecation") + private long calculateStartFromMinute(long startFromMinutes, Date from, + Date to, int dayOffset) { + boolean eventStartAtDifferentDay = from.getDate() != to.getDate(); + if (eventStartAtDifferentDay) { + long minutesOnPrevDay = (getTargetDateByCurrentPosition(dayOffset) + .getTime() - from.getTime()) / DateConstants.MINUTEINMILLIS; + startFromMinutes = -1 * minutesOnPrevDay; + } + + return startFromMinutes; + } + + /** + * @param dateOffset + * @return the amount of pixels the given date is from the left side + */ + private int calculateDateCellOffsetPx(int dateOffset) { + int dateCellOffset = 0; + int[] dateWidths = weekGrid.getDateCellWidths(); + + if (dateWidths != null) { + for (int i = 0; i < dateOffset; i++) { + dateCellOffset += dateWidths[i] + 1; + } + } else { + dateCellOffset = dateOffset * weekGrid.getDateCellWidth(); + } + + return dateCellOffset; + } + + /** + * Check if the given time range is too small for events + * + * @param start + * @param end + * @return + */ + private boolean isTimeRangeTooSmall(long start, long end) { + return (end - start) >= getMinTimeRange(); + } + + /** + * @return the minimum amount of ms that an event must last when resized + */ + private long getMinTimeRange() { + return DateConstants.MINUTEINMILLIS * 30; + } + + /** + * Build the string for sending resize events to server + * + * @param event + * @return + */ + private String buildResizeString(CalendarEvent event) { + StringBuilder buffer = new StringBuilder(); + buffer.append(event.getIndex()); + buffer.append(","); + buffer.append(DateUtil.formatClientSideDate(event.getStart())); + buffer.append("-"); + buffer.append(DateUtil.formatClientSideTime(event.getStartTime())); + buffer.append(","); + buffer.append(DateUtil.formatClientSideDate(event.getEnd())); + buffer.append("-"); + buffer.append(DateUtil.formatClientSideTime(event.getEndTime())); + + return buffer.toString(); + } + + private Date getTargetDateByCurrentPosition(int left) { + DateCell newParent = (DateCell) weekGrid.content + .getWidget((left / getDateCellWidth()) + 1); + Date targetDate = newParent.getDate(); + return targetDate; + } + + private int getDateCellWidth() { + return weekGrid.getDateCellWidth(); + } + + /* Returns total width of all date cells. */ + private int getDatesWidth() { + if (weekGrid.width == -1) { + // Undefined width. Needs to be calculated by the known cell + // widths. + int count = weekGrid.content.getWidgetCount() - 1; + return count * getDateCellWidth(); + } + + return weekGrid.getInternalWidth(); + } + + /** + * @return true if the current mouse movement is resizing + */ + private boolean clickTargetsResize() { + return weekGrid.getCalendar().isEventResizeAllowed() + && (clickTarget == topResizeBar || clickTarget == bottomResizeBar); + } + + private void addGlobalResizeStyle() { + if (clickTarget == topResizeBar) { + weekGrid.getCalendar().addStyleDependentName("nresize"); + } else if (clickTarget == bottomResizeBar) { + weekGrid.getCalendar().addStyleDependentName("sresize"); + } + } + + private void removeGlobalResizeStyle() { + weekGrid.getCalendar().removeStyleDependentName("nresize"); + weekGrid.getCalendar().removeStyleDependentName("sresize"); + } + + public void setCalendarEvent(CalendarEvent calendarEvent) { + this.calendarEvent = calendarEvent; + } + + public CalendarEvent getCalendarEvent() { + return calendarEvent; + } + + public void setDisabled(boolean disabled) { + this.disabled = disabled; + } + + public boolean isDisabled() { + return disabled; + } + + @Override + public void onContextMenu(ContextMenuEvent event) { + if (dateCell.weekgrid.getCalendar().getMouseEventListener() != null) { + event.preventDefault(); + event.stopPropagation(); + dateCell.weekgrid.getCalendar().getMouseEventListener() + .contextMenu(event, this); + } + } + + @Override + public Object getTooltipKey() { + return eventIndex; + } +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DateCellGroup.java b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellGroup.java new file mode 100644 index 0000000000..79276eab7b --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/DateCellGroup.java @@ -0,0 +1,59 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +/** + * Internally used by the calendar + * + * @since 7.1 + */ +public class DateCellGroup { + private WeekGridMinuteTimeRange range; + private final List<Integer> items; + + public DateCellGroup(Integer index) { + items = new ArrayList<Integer>(); + items.add(index); + } + + public WeekGridMinuteTimeRange getDateRange() { + return range; + } + + public Date getStart() { + return range.getStart(); + } + + public Date getEnd() { + return range.getEnd(); + } + + public void setDateRange(WeekGridMinuteTimeRange range) { + this.range = range; + } + + public List<Integer> getItems() { + return items; + } + + public void add(Integer index) { + items.add(index); + } +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DateUtil.java b/client/src/com/vaadin/client/ui/calendar/schedule/DateUtil.java new file mode 100644 index 0000000000..84726327e2 --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/DateUtil.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import java.util.Date; + +import com.google.gwt.i18n.client.DateTimeFormat; +import com.vaadin.shared.ui.calendar.DateConstants; + +/** + * Utility class for {@link Date} operations + * + * @since 7.1 + * @author Vaadin Ltd. + */ +public class DateUtil { + + /** + * Checks if dates are same day without checking datetimes. + * + * @param date1 + * @param date2 + * @return + */ + @SuppressWarnings("deprecation") + public static boolean compareDate(Date date1, Date date2) { + if (date1.getDate() == date2.getDate() + && date1.getYear() == date2.getYear() + && date1.getMonth() == date2.getMonth()) { + return true; + } + return false; + } + + /** + * @param date + * the date to format + * + * @return given Date as String, for communicating to server-side + */ + public static String formatClientSideDate(Date date) { + DateTimeFormat dateformat_date = DateTimeFormat + .getFormat(DateConstants.CLIENT_DATE_FORMAT); + return dateformat_date.format(date); + } + + /** + * @param date + * the date to format + * @return given Date as String, for communicating to server-side + */ + public static String formatClientSideTime(Date date) { + DateTimeFormat dateformat_date = DateTimeFormat + .getFormat(DateConstants.CLIENT_TIME_FORMAT); + return dateformat_date.format(date); + } +} diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/DayToolbar.java b/client/src/com/vaadin/client/ui/calendar/schedule/DayToolbar.java new file mode 100644 index 0000000000..6233e8111e --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/DayToolbar.java @@ -0,0 +1,181 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import java.util.Iterator; + +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.ui.VCalendar; + +/** + * + * @since 7.1 + * @author Vaadin Ltd. + * + */ +public class DayToolbar extends HorizontalPanel implements ClickHandler { + private int width = 0; + protected static final int MARGINLEFT = 50; + protected static final int MARGINRIGHT = 20; + protected Button backLabel; + protected Button nextLabel; + private boolean verticalSized; + private boolean horizontalSized; + private VCalendar calendar; + + public DayToolbar(VCalendar vcalendar) { + calendar = vcalendar; + + setStylePrimaryName("v-calendar-header-week"); + backLabel = new Button(); + backLabel.setStylePrimaryName("v-calendar-back"); + nextLabel = new Button(); + nextLabel.addClickHandler(this); + nextLabel.setStylePrimaryName("v-calendar-next"); + backLabel.addClickHandler(this); + setBorderWidth(0); + setSpacing(0); + } + + public void setWidthPX(int width) { + this.width = (width - MARGINLEFT) - MARGINRIGHT; + // super.setWidth(this.width + "px"); + if (getWidgetCount() == 0) { + return; + } + updateCellWidths(); + } + + public void updateCellWidths() { + int count = getWidgetCount(); + if (count > 0) { + setCellWidth(backLabel, MARGINLEFT + "px"); + setCellWidth(nextLabel, MARGINRIGHT + "px"); + setCellHorizontalAlignment(nextLabel, ALIGN_RIGHT); + int cellw = width / (count - 2); + int remain = width % (count - 2); + int cellw2 = cellw + 1; + if (cellw > 0) { + int[] cellWidths = VCalendar + .distributeSize(width, count - 2, 0); + for (int i = 1; i < count - 1; i++) { + Widget widget = getWidget(i); + // if (remain > 0) { + // setCellWidth(widget, cellw2 + "px"); + // remain--; + // } else { + // setCellWidth(widget, cellw + "px"); + // } + setCellWidth(widget, cellWidths[i - 1] + "px"); + widget.setWidth(cellWidths[i - 1] + "px"); + } + } + } + } + + public void add(String dayName, final String date, + String localized_date_format, String extraClass) { + Label l = new Label(dayName + " " + localized_date_format); + l.setStylePrimaryName("v-calendar-header-day"); + + if (extraClass != null) { + l.addStyleDependentName(extraClass); + } + + if (verticalSized) { + l.addStyleDependentName("Vsized"); + } + if (horizontalSized) { + l.addStyleDependentName("Hsized"); + } + + l.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + if (calendar.getDateClickListener() != null) { + calendar.getDateClickListener().dateClick(date); + } + } + }); + + add(l); + } + + public void addBackButton() { + if (!calendar.isBackwardNavigationEnabled()) { + nextLabel.getElement().getStyle().setHeight(0, Unit.PX); + } + add(backLabel); + } + + public void addNextButton() { + if (!calendar.isForwardNavigationEnabled()) { + backLabel.getElement().getStyle().setHeight(0, Unit.PX); + } + add(nextLabel); + } + + @Override + public void onClick(ClickEvent event) { + if (!calendar.isDisabledOrReadOnly()) { + if (event.getSource() == nextLabel) { + if (calendar.getForwardListener() != null) { + calendar.getForwardListener().forward(); + } + } else if (event.getSource() == backLabel) { + if (calendar.getBackwardListener() != null) { + calendar.getBackwardListener().backward(); + } + } + } + } + + public void setVerticalSized(boolean sized) { + verticalSized = sized; + updateDayLabelSizedStyleNames(); + } + + public void setHorizontalSized(boolean sized) { + horizontalSized = sized; + updateDayLabelSizedStyleNames(); + } + + private void updateDayLabelSizedStyleNames() { + Iterator<Widget> it = iterator(); + while (it.hasNext()) { + updateWidgetSizedStyleName(it.next()); + } + } + + private void updateWidgetSizedStyleName(Widget w) { + if (verticalSized) { + w.addStyleDependentName("Vsized"); + } else { + w.removeStyleDependentName("VSized"); + } + if (horizontalSized) { + w.addStyleDependentName("Hsized"); + } else { + w.removeStyleDependentName("HSized"); + } + } +} diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/FocusableComplexPanel.java b/client/src/com/vaadin/client/ui/calendar/schedule/FocusableComplexPanel.java new file mode 100644 index 0000000000..6b42caec10 --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/FocusableComplexPanel.java @@ -0,0 +1,122 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import com.google.gwt.event.dom.client.BlurEvent; +import com.google.gwt.event.dom.client.BlurHandler; +import com.google.gwt.event.dom.client.FocusEvent; +import com.google.gwt.event.dom.client.FocusHandler; +import com.google.gwt.event.dom.client.HasBlurHandlers; +import com.google.gwt.event.dom.client.HasFocusHandlers; +import com.google.gwt.event.dom.client.HasKeyDownHandlers; +import com.google.gwt.event.dom.client.HasKeyPressHandlers; +import com.google.gwt.event.dom.client.KeyDownEvent; +import com.google.gwt.event.dom.client.KeyDownHandler; +import com.google.gwt.event.dom.client.KeyPressEvent; +import com.google.gwt.event.dom.client.KeyPressHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.user.client.ui.ComplexPanel; +import com.google.gwt.user.client.ui.impl.FocusImpl; +import com.vaadin.client.Focusable; + +/** + * A ComplexPanel that can be focused + * + * @since 7.1 + * @author Vaadin Ltd. + * + */ +public class FocusableComplexPanel extends ComplexPanel implements + HasFocusHandlers, HasBlurHandlers, HasKeyDownHandlers, + HasKeyPressHandlers, Focusable { + + protected void makeFocusable() { + // make focusable, as we don't need access key magic we don't need to + // use FocusImpl.createFocusable + getElement().setTabIndex(0); + } + + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.event.dom.client.HasFocusHandlers#addFocusHandler(com. + * google.gwt.event.dom.client.FocusHandler) + */ + @Override + public HandlerRegistration addFocusHandler(FocusHandler handler) { + return addDomHandler(handler, FocusEvent.getType()); + } + + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.event.dom.client.HasBlurHandlers#addBlurHandler(com.google + * .gwt.event.dom.client.BlurHandler) + */ + @Override + public HandlerRegistration addBlurHandler(BlurHandler handler) { + return addDomHandler(handler, BlurEvent.getType()); + } + + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.event.dom.client.HasKeyDownHandlers#addKeyDownHandler( + * com.google.gwt.event.dom.client.KeyDownHandler) + */ + @Override + public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) { + return addDomHandler(handler, KeyDownEvent.getType()); + } + + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.event.dom.client.HasKeyPressHandlers#addKeyPressHandler + * (com.google.gwt.event.dom.client.KeyPressHandler) + */ + @Override + public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) { + return addDomHandler(handler, KeyPressEvent.getType()); + } + + /** + * Sets/Removes the keyboard focus to the panel. + * + * @param focus + * If set to true then the focus is moved to the panel, if set to + * false the focus is removed + */ + public void setFocus(boolean focus) { + if (focus) { + FocusImpl.getFocusImplForPanel().focus(getElement()); + } else { + FocusImpl.getFocusImplForPanel().blur(getElement()); + } + } + + /** + * Focus the panel + */ + @Override + public void focus() { + setFocus(true); + } +} diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/FocusableGrid.java b/client/src/com/vaadin/client/ui/calendar/schedule/FocusableGrid.java new file mode 100644 index 0000000000..b40f1c3652 --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/FocusableGrid.java @@ -0,0 +1,134 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import com.google.gwt.event.dom.client.BlurEvent; +import com.google.gwt.event.dom.client.BlurHandler; +import com.google.gwt.event.dom.client.FocusEvent; +import com.google.gwt.event.dom.client.FocusHandler; +import com.google.gwt.event.dom.client.HasBlurHandlers; +import com.google.gwt.event.dom.client.HasFocusHandlers; +import com.google.gwt.event.dom.client.HasKeyDownHandlers; +import com.google.gwt.event.dom.client.HasKeyPressHandlers; +import com.google.gwt.event.dom.client.KeyDownEvent; +import com.google.gwt.event.dom.client.KeyDownHandler; +import com.google.gwt.event.dom.client.KeyPressEvent; +import com.google.gwt.event.dom.client.KeyPressHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.user.client.ui.Grid; +import com.google.gwt.user.client.ui.impl.FocusImpl; +import com.vaadin.client.Focusable; + +/** + * A Grid that can be focused + * + * @since 7.1 + * @author Vaadin Ltd. + * + */ +public class FocusableGrid extends Grid implements HasFocusHandlers, + HasBlurHandlers, HasKeyDownHandlers, HasKeyPressHandlers, Focusable { + + /** + * Constructor + */ + public FocusableGrid() { + super(); + makeFocusable(); + } + + public FocusableGrid(int rows, int columns) { + super(rows, columns); + makeFocusable(); + } + + protected void makeFocusable() { + // make focusable, as we don't need access key magic we don't need to + // use FocusImpl.createFocusable + getElement().setTabIndex(0); + } + + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.event.dom.client.HasFocusHandlers#addFocusHandler(com. + * google.gwt.event.dom.client.FocusHandler) + */ + @Override + public HandlerRegistration addFocusHandler(FocusHandler handler) { + return addDomHandler(handler, FocusEvent.getType()); + } + + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.event.dom.client.HasBlurHandlers#addBlurHandler(com.google + * .gwt.event.dom.client.BlurHandler) + */ + @Override + public HandlerRegistration addBlurHandler(BlurHandler handler) { + return addDomHandler(handler, BlurEvent.getType()); + } + + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.event.dom.client.HasKeyDownHandlers#addKeyDownHandler( + * com.google.gwt.event.dom.client.KeyDownHandler) + */ + @Override + public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) { + return addDomHandler(handler, KeyDownEvent.getType()); + } + + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.event.dom.client.HasKeyPressHandlers#addKeyPressHandler + * (com.google.gwt.event.dom.client.KeyPressHandler) + */ + @Override + public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) { + return addDomHandler(handler, KeyPressEvent.getType()); + } + + /** + * Sets/Removes the keyboard focus to the panel. + * + * @param focus + * If set to true then the focus is moved to the panel, if set to + * false the focus is removed + */ + public void setFocus(boolean focus) { + if (focus) { + FocusImpl.getFocusImplForPanel().focus(getElement()); + } else { + FocusImpl.getFocusImplForPanel().blur(getElement()); + } + } + + /** + * Focus the panel + */ + @Override + public void focus() { + setFocus(true); + } +} diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/FocusableHTML.java b/client/src/com/vaadin/client/ui/calendar/schedule/FocusableHTML.java new file mode 100644 index 0000000000..31d810608a --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/FocusableHTML.java @@ -0,0 +1,124 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import com.google.gwt.event.dom.client.BlurEvent; +import com.google.gwt.event.dom.client.BlurHandler; +import com.google.gwt.event.dom.client.FocusEvent; +import com.google.gwt.event.dom.client.FocusHandler; +import com.google.gwt.event.dom.client.HasBlurHandlers; +import com.google.gwt.event.dom.client.HasFocusHandlers; +import com.google.gwt.event.dom.client.HasKeyDownHandlers; +import com.google.gwt.event.dom.client.HasKeyPressHandlers; +import com.google.gwt.event.dom.client.KeyDownEvent; +import com.google.gwt.event.dom.client.KeyDownHandler; +import com.google.gwt.event.dom.client.KeyPressEvent; +import com.google.gwt.event.dom.client.KeyPressHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.impl.FocusImpl; +import com.vaadin.client.Focusable; + +/** + * A HTML widget that can be focused + * + * @since 7.1 + * @author Vaadin Ltd. + * + */ +public class FocusableHTML extends HTML implements HasFocusHandlers, + HasBlurHandlers, HasKeyDownHandlers, HasKeyPressHandlers, Focusable { + + /** + * Constructor + */ + public FocusableHTML() { + // make focusable, as we don't need access key magic we don't need to + // use FocusImpl.createFocusable + getElement().setTabIndex(0); + } + + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.event.dom.client.HasFocusHandlers#addFocusHandler(com. + * google.gwt.event.dom.client.FocusHandler) + */ + @Override + public HandlerRegistration addFocusHandler(FocusHandler handler) { + return addDomHandler(handler, FocusEvent.getType()); + } + + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.event.dom.client.HasBlurHandlers#addBlurHandler(com.google + * .gwt.event.dom.client.BlurHandler) + */ + @Override + public HandlerRegistration addBlurHandler(BlurHandler handler) { + return addDomHandler(handler, BlurEvent.getType()); + } + + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.event.dom.client.HasKeyDownHandlers#addKeyDownHandler( + * com.google.gwt.event.dom.client.KeyDownHandler) + */ + @Override + public HandlerRegistration addKeyDownHandler(KeyDownHandler handler) { + return addDomHandler(handler, KeyDownEvent.getType()); + } + + /* + * (non-Javadoc) + * + * @see + * com.google.gwt.event.dom.client.HasKeyPressHandlers#addKeyPressHandler + * (com.google.gwt.event.dom.client.KeyPressHandler) + */ + @Override + public HandlerRegistration addKeyPressHandler(KeyPressHandler handler) { + return addDomHandler(handler, KeyPressEvent.getType()); + } + + /** + * Sets/Removes the keyboard focus to the panel. + * + * @param focus + * If set to true then the focus is moved to the panel, if set to + * false the focus is removed + */ + public void setFocus(boolean focus) { + if (focus) { + FocusImpl.getFocusImplForPanel().focus(getElement()); + } else { + FocusImpl.getFocusImplForPanel().blur(getElement()); + } + } + + /** + * Focus the panel + */ + @Override + public void focus() { + setFocus(true); + } +} diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/HasTooltipKey.java b/client/src/com/vaadin/client/ui/calendar/schedule/HasTooltipKey.java new file mode 100644 index 0000000000..5827068840 --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/HasTooltipKey.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +/** + * For Calendar client-side internal use only. + * + * @since 7.1 + * @author Vaadin Ltd. + * + */ +public interface HasTooltipKey { + /** + * Gets the key associated for the Widget implementing this interface. This + * key is used for getting a tooltip title identified by the key + * + * @return the tooltip key + */ + Object getTooltipKey(); +} diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/MonthEventLabel.java b/client/src/com/vaadin/client/ui/calendar/schedule/MonthEventLabel.java new file mode 100644 index 0000000000..b7f6ee7a3c --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/MonthEventLabel.java @@ -0,0 +1,142 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import java.util.Date; + +import com.google.gwt.user.client.ui.HTML; +import com.vaadin.client.ui.VCalendar; + +/** + * The label in a month cell + * + * @since 7.1 + */ +public class MonthEventLabel extends HTML implements HasTooltipKey { + + private static final String STYLENAME = "v-calendar-event"; + + private boolean timeSpecificEvent = false; + private Integer eventIndex; + private VCalendar calendar; + private String caption; + private Date time; + + /** + * Default constructor + */ + public MonthEventLabel() { + setStylePrimaryName(STYLENAME); + } + + /** + * Set the time of the event label + * + * @param date + * The date object that specifies the time + */ + public void setTime(Date date) { + time = date; + renderCaption(); + } + + /** + * Set the caption of the event label + * + * @param caption + * The caption string, can be HTML + */ + public void setCaption(String caption) { + this.caption = caption; + renderCaption(); + } + + /** + * Renders the caption in the DIV element + */ + private void renderCaption() { + StringBuilder html = new StringBuilder(); + if (caption != null && time != null) { + html.append("<span class=\"" + STYLENAME + "-time\">"); + html.append(calendar.getTimeFormat().format(time)); + html.append("</span> "); + html.append(caption); + } else if (caption != null) { + html.append(caption); + } else if (time != null) { + html.append("<span class=\"" + STYLENAME + "-time\">"); + html.append(calendar.getTimeFormat().format(time)); + html.append("</span>"); + } + super.setHTML(html.toString()); + } + + /** + * Set the (server side) index of the event + * + * @param index + * The integer index + */ + public void setEventIndex(int index) { + eventIndex = index; + } + + /** + * Set the Calendar instance this label belongs to + * + * @param calendar + * The calendar instance + */ + public void setCalendar(VCalendar calendar) { + this.calendar = calendar; + } + + /** + * Is the event bound to a specific time + * + * @return + */ + public boolean isTimeSpecificEvent() { + return timeSpecificEvent; + } + + /** + * Is the event bound to a specific time + * + * @param timeSpecificEvent + * True if the event is bound to a time, false if it is only + * bound to the day + */ + public void setTimeSpecificEvent(boolean timeSpecificEvent) { + this.timeSpecificEvent = timeSpecificEvent; + } + + /* + * (non-Javadoc) + * + * @see com.google.gwt.user.client.ui.HTML#setHTML(java.lang.String) + */ + @Override + public void setHTML(String html) { + throw new UnsupportedOperationException( + "Use setCaption() and setTime() instead"); + } + + @Override + public Object getTooltipKey() { + return eventIndex; + } +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/MonthGrid.java b/client/src/com/vaadin/client/ui/calendar/schedule/MonthGrid.java new file mode 100644 index 0000000000..df9bc42d2a --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/MonthGrid.java @@ -0,0 +1,216 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import java.util.Date; + +import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.event.dom.client.KeyDownEvent; +import com.google.gwt.event.dom.client.KeyDownHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.vaadin.client.ui.VCalendar; + +/** + * + * @since 7.1 + * @author Vaadin Ltd. + * + */ +public class MonthGrid extends FocusableGrid implements KeyDownHandler { + + private SimpleDayCell selectionStart; + private SimpleDayCell selectionEnd; + private final VCalendar calendar; + private boolean rangeSelectDisabled; + private boolean disabled; + private boolean enabled = true; + private final HandlerRegistration keyDownHandler; + + public MonthGrid(VCalendar parent, int rows, int columns) { + super(rows, columns); + calendar = parent; + setCellSpacing(0); + setCellPadding(0); + setStylePrimaryName("v-calendar-month"); + + keyDownHandler = addKeyDownHandler(this); + } + + @Override + protected void onUnload() { + keyDownHandler.removeHandler(); + super.onUnload(); + } + + public void setSelectionEnd(SimpleDayCell simpleDayCell) { + selectionEnd = simpleDayCell; + updateSelection(); + } + + public void setSelectionStart(SimpleDayCell simpleDayCell) { + if (!rangeSelectDisabled && isEnabled()) { + selectionStart = simpleDayCell; + setFocus(true); + } + + } + + private void updateSelection() { + if (selectionStart == null) { + return; + } + if (selectionStart != null && selectionEnd != null) { + Date startDate = selectionStart.getDate(); + Date endDate = selectionEnd.getDate(); + for (int row = 0; row < getRowCount(); row++) { + for (int cell = 0; cell < getCellCount(row); cell++) { + SimpleDayCell sdc = (SimpleDayCell) getWidget(row, cell); + if (sdc == null) { + return; + } + Date d = sdc.getDate(); + if (startDate.compareTo(d) <= 0 + && endDate.compareTo(d) >= 0) { + sdc.addStyleDependentName("selected"); + } else if (startDate.compareTo(d) >= 0 + && endDate.compareTo(d) <= 0) { + sdc.addStyleDependentName("selected"); + } else { + sdc.removeStyleDependentName("selected"); + } + } + } + } + } + + public void setSelectionReady() { + if (selectionStart != null && selectionEnd != null) { + String value = ""; + Date startDate = selectionStart.getDate(); + Date endDate = selectionEnd.getDate(); + if (startDate.compareTo(endDate) > 0) { + Date temp = startDate; + startDate = endDate; + endDate = temp; + } + + if (calendar.getRangeSelectListener() != null) { + value = calendar.getDateFormat().format(startDate) + "TO" + + calendar.getDateFormat().format(endDate); + calendar.getRangeSelectListener().rangeSelected(value); + } + selectionStart = null; + selectionEnd = null; + setFocus(false); + } + } + + public void cancelRangeSelection() { + if (selectionStart != null && selectionEnd != null) { + for (int row = 0; row < getRowCount(); row++) { + for (int cell = 0; cell < getCellCount(row); cell++) { + SimpleDayCell sdc = (SimpleDayCell) getWidget(row, cell); + if (sdc == null) { + return; + } + sdc.removeStyleDependentName("selected"); + } + } + } + setFocus(false); + selectionStart = null; + } + + public void updateCellSizes(int totalWidthPX, int totalHeightPX) { + boolean setHeight = totalHeightPX > 0; + boolean setWidth = totalWidthPX > 0; + int rows = getRowCount(); + int cells = getCellCount(0); + int cellWidth = (totalWidthPX / cells) - 1; + int widthRemainder = totalWidthPX % cells; + // Division for cells might not be even. Distribute it evenly to + // will whole space. + int heightPX = totalHeightPX; + int cellHeight = heightPX / rows; + int heightRemainder = heightPX % rows; + + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cells; j++) { + SimpleDayCell sdc = (SimpleDayCell) getWidget(i, j); + + if (setWidth) { + if (widthRemainder > 0) { + sdc.setWidth(cellWidth + 1 + "px"); + widthRemainder--; + + } else { + sdc.setWidth(cellWidth + "px"); + } + } + + if (setHeight) { + if (heightRemainder > 0) { + sdc.setHeightPX(cellHeight + 1, true); + + } else { + sdc.setHeightPX(cellHeight, true); + } + } else { + sdc.setHeightPX(-1, true); + } + } + heightRemainder--; + } + } + + /** + * Disable or enable possibility to select ranges + */ + public void setRangeSelect(boolean b) { + rangeSelectDisabled = !b; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public boolean isEnabled() { + return enabled; + } + + @Override + public void onKeyDown(KeyDownEvent event) { + int keycode = event.getNativeKeyCode(); + if (KeyCodes.KEY_ESCAPE == keycode && selectionStart != null) { + cancelRangeSelection(); + } + } + + public int getDayCellIndex(SimpleDayCell dayCell) { + int rows = getRowCount(); + int cells = getCellCount(0); + for (int i = 0; i < rows; i++) { + for (int j = 0; j < cells; j++) { + SimpleDayCell sdc = (SimpleDayCell) getWidget(i, j); + if (dayCell == sdc) { + return i * cells + j; + } + } + } + + return -1; + } +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayCell.java b/client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayCell.java new file mode 100644 index 0000000000..a2bd008d01 --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayCell.java @@ -0,0 +1,701 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import java.util.Date; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.NativeEvent; +import com.google.gwt.event.dom.client.KeyCodes; +import com.google.gwt.event.dom.client.KeyDownEvent; +import com.google.gwt.event.dom.client.KeyDownHandler; +import com.google.gwt.event.dom.client.MouseDownEvent; +import com.google.gwt.event.dom.client.MouseDownHandler; +import com.google.gwt.event.dom.client.MouseMoveEvent; +import com.google.gwt.event.dom.client.MouseMoveHandler; +import com.google.gwt.event.dom.client.MouseOverEvent; +import com.google.gwt.event.dom.client.MouseOverHandler; +import com.google.gwt.event.dom.client.MouseUpEvent; +import com.google.gwt.event.dom.client.MouseUpHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.ui.FocusableFlowPanel; +import com.vaadin.client.ui.VCalendar; +import com.vaadin.shared.ui.calendar.DateConstants; + +/** + * A class representing a single cell within the calendar in month-view + * + * @since 7.1 + * @author Vaadin Ltd. + */ +public class SimpleDayCell extends FocusableFlowPanel implements + MouseUpHandler, MouseDownHandler, MouseOverHandler, MouseMoveHandler { + + private static int BOTTOMSPACERHEIGHT = -1; + private static int EVENTHEIGHT = -1; + private static final int BORDERPADDINGSIZE = 1; + + private final VCalendar calendar; + private Date date; + private int intHeight; + private final HTML bottomspacer; + private final Label caption; + private final CalendarEvent[] events = new CalendarEvent[10]; + private final int cell; + private final int row; + private boolean monthNameVisible; + private HandlerRegistration mouseUpRegistration; + private HandlerRegistration mouseDownRegistration; + private HandlerRegistration mouseOverRegistration; + private boolean monthEventMouseDown; + private boolean labelMouseDown; + private int eventCount = 0; + + private int startX = -1; + private int startY = -1; + private int startYrelative; + private int startXrelative; + private Date startDateFrom; + private Date startDateTo; + private int prevDayDiff = 0; + private int prevWeekDiff = 0; + private HandlerRegistration moveRegistration; + private CalendarEvent moveEvent; + private Widget clickedWidget; + private HandlerRegistration bottomSpacerMouseDownHandler; + private boolean scrollable = false; + private boolean eventCanceled; + private MonthGrid monthGrid; + private HandlerRegistration keyDownHandler; + + public SimpleDayCell(VCalendar calendar, int row, int cell) { + this.calendar = calendar; + this.row = row; + this.cell = cell; + setStylePrimaryName("v-calendar-month-day"); + caption = new Label(); + bottomspacer = new HTML(); + bottomspacer.setStyleName("v-calendar-bottom-spacer-empty"); + bottomspacer.setWidth(3 + "em"); + caption.setStyleName("v-calendar-day-number"); + add(caption); + add(bottomspacer); + caption.addMouseDownHandler(this); + caption.addMouseUpHandler(this); + } + + @Override + public void onLoad() { + BOTTOMSPACERHEIGHT = bottomspacer.getOffsetHeight(); + EVENTHEIGHT = BOTTOMSPACERHEIGHT; + } + + public void setMonthGrid(MonthGrid monthGrid) { + this.monthGrid = monthGrid; + } + + public MonthGrid getMonthGrid() { + return monthGrid; + } + + @SuppressWarnings("deprecation") + public void setDate(Date date) { + int dateOfMonth = date.getDate(); + if (monthNameVisible) { + caption.setText(dateOfMonth + " " + + calendar.getMonthNames()[date.getMonth()]); + } else { + caption.setText("" + dateOfMonth); + } + this.date = date; + } + + public Date getDate() { + return date; + } + + public void reDraw(boolean clear) { + setHeightPX(intHeight + BORDERPADDINGSIZE, clear); + } + + /* + * Events and whole cell content are drawn by this method. By the + * clear-argument, you can choose to clear all old content. Notice that + * clearing will also remove all element's event handlers. + */ + public void setHeightPX(int px, boolean clear) { + // measure from DOM if needed + if (px < 0) { + intHeight = getOffsetHeight() - BORDERPADDINGSIZE; + } else { + intHeight = px - BORDERPADDINGSIZE; + } + + // Couldn't measure height or it ended up negative. Don't bother + // continuing + if (intHeight == -1) { + return; + } + + if (clear) { + while (getWidgetCount() > 1) { + remove(1); + } + } + + // How many events can be shown in UI + int slots = 0; + if (scrollable) { + for (int i = 0; i < events.length; i++) { + if (events[i] != null) { + slots = i + 1; + } + } + setHeight(intHeight + "px"); // Fixed height + } else { + // Dynamic height by the content + DOM.removeElementAttribute(getElement(), "height"); + slots = (intHeight - caption.getOffsetHeight() - BOTTOMSPACERHEIGHT) + / EVENTHEIGHT; + if (slots > 10) { + slots = 10; + } + } + + updateEvents(slots, clear); + + } + + public void updateEvents(int slots, boolean clear) { + int eventsAdded = 0; + + for (int i = 0; i < slots; i++) { + CalendarEvent e = events[i]; + if (e == null) { + // Empty slot + HTML slot = new HTML(); + slot.setStyleName("v-calendar-spacer"); + if (!clear) { + remove(i + 1); + insert(slot, i + 1); + } else { + add(slot); + } + } else { + // Event slot + eventsAdded++; + if (!clear) { + Widget w = getWidget(i + 1); + if (!(w instanceof MonthEventLabel)) { + remove(i + 1); + insert(createMonthEventLabel(e), i + 1); + } + } else { + add(createMonthEventLabel(e)); + } + } + } + + int remainingSpace = intHeight + - ((slots * EVENTHEIGHT) + BOTTOMSPACERHEIGHT + caption + .getOffsetHeight()); + int newHeight = remainingSpace + BOTTOMSPACERHEIGHT; + if (newHeight < 0) { + newHeight = EVENTHEIGHT; + } + bottomspacer.setHeight(newHeight + "px"); + + if (clear) { + add(bottomspacer); + } + + int more = eventCount - eventsAdded; + if (more > 0) { + if (bottomSpacerMouseDownHandler == null) { + bottomSpacerMouseDownHandler = bottomspacer + .addMouseDownHandler(this); + } + bottomspacer.setStyleName("v-calendar-bottom-spacer"); + bottomspacer.setText("+ " + more); + } else { + if (!scrollable && bottomSpacerMouseDownHandler != null) { + bottomSpacerMouseDownHandler.removeHandler(); + bottomSpacerMouseDownHandler = null; + } + + if (scrollable) { + bottomspacer.setText("[ - ]"); + } else { + bottomspacer.setStyleName("v-calendar-bottom-spacer-empty"); + bottomspacer.setText(""); + } + } + } + + private MonthEventLabel createMonthEventLabel(CalendarEvent e) { + long rangeInMillis = e.getRangeInMilliseconds(); + boolean timeEvent = rangeInMillis <= DateConstants.DAYINMILLIS + && !e.isAllDay(); + Date fromDatetime = e.getStartTime(); + + // Create a new MonthEventLabel + MonthEventLabel eventDiv = new MonthEventLabel(); + eventDiv.addStyleDependentName("month"); + eventDiv.addMouseDownHandler(this); + eventDiv.addMouseUpHandler(this); + eventDiv.setCalendar(calendar); + eventDiv.setEventIndex(e.getIndex()); + + if (timeEvent) { + eventDiv.setTimeSpecificEvent(true); + if (e.getStyleName() != null) { + eventDiv.addStyleDependentName(e.getStyleName()); + } + eventDiv.setCaption(e.getCaption()); + eventDiv.setTime(fromDatetime); + + } else { + eventDiv.setTimeSpecificEvent(false); + Date from = e.getStart(); + Date to = e.getEnd(); + if (e.getStyleName().length() > 0) { + eventDiv.addStyleName("month-event " + e.getStyleName()); + } else { + eventDiv.addStyleName("month-event"); + } + int fromCompareToDate = from.compareTo(date); + int toCompareToDate = to.compareTo(date); + eventDiv.addStyleDependentName("all-day"); + if (fromCompareToDate == 0) { + eventDiv.addStyleDependentName("start"); + eventDiv.setCaption(e.getCaption()); + + } else if (fromCompareToDate < 0 && cell == 0) { + eventDiv.addStyleDependentName("continued-from"); + eventDiv.setCaption(e.getCaption()); + } + if (toCompareToDate == 0) { + eventDiv.addStyleDependentName("end"); + } else if (toCompareToDate > 0 + && (cell + 1) == getMonthGrid().getCellCount(row)) { + eventDiv.addStyleDependentName("continued-to"); + } + if (e.getStyleName() != null) { + eventDiv.addStyleDependentName(e.getStyleName() + "-all-day"); + } + } + + return eventDiv; + } + + private void setUnlimitedCellHeight() { + scrollable = true; + addStyleDependentName("scrollable"); + } + + private void setLimitedCellHeight() { + scrollable = false; + removeStyleDependentName("scrollable"); + } + + public void addCalendarEvent(CalendarEvent e) { + eventCount++; + int slot = e.getSlotIndex(); + if (slot == -1) { + for (int i = 0; i < events.length; i++) { + if (events[i] == null) { + events[i] = e; + e.setSlotIndex(i); + break; + } + } + } else { + events[slot] = e; + } + } + + @SuppressWarnings("deprecation") + public void setMonthNameVisible(boolean b) { + monthNameVisible = b; + int dateOfMonth = date.getDate(); + caption.setText(dateOfMonth + " " + + calendar.getMonthNames()[date.getMonth()]); + } + + public HandlerRegistration addMouseMoveHandler(MouseMoveHandler handler) { + return addDomHandler(handler, MouseMoveEvent.getType()); + } + + @Override + protected void onAttach() { + super.onAttach(); + mouseUpRegistration = addDomHandler(this, MouseUpEvent.getType()); + mouseDownRegistration = addDomHandler(this, MouseDownEvent.getType()); + mouseOverRegistration = addDomHandler(this, MouseOverEvent.getType()); + } + + @Override + protected void onDetach() { + mouseUpRegistration.removeHandler(); + mouseDownRegistration.removeHandler(); + mouseOverRegistration.removeHandler(); + super.onDetach(); + } + + @Override + public void onMouseUp(MouseUpEvent event) { + if (event.getNativeButton() != NativeEvent.BUTTON_LEFT) { + return; + } + + Widget w = (Widget) event.getSource(); + if (moveRegistration != null) { + Event.releaseCapture(getElement()); + moveRegistration.removeHandler(); + moveRegistration = null; + keyDownHandler.removeHandler(); + keyDownHandler = null; + } + + if (w == bottomspacer && monthEventMouseDown) { + GWT.log("Mouse up over bottomspacer"); + + } else if (clickedWidget instanceof MonthEventLabel + && monthEventMouseDown) { + MonthEventLabel mel = (MonthEventLabel) clickedWidget; + + int endX = event.getClientX(); + int endY = event.getClientY(); + int xDiff = startX - endX; + int yDiff = startY - endY; + startX = -1; + startY = -1; + prevDayDiff = 0; + prevWeekDiff = 0; + + if (!mel.isTimeSpecificEvent() + && (xDiff < -3 || xDiff > 3 || yDiff < -3 || yDiff > 3)) { + eventMoved(moveEvent); + + } else if (calendar.getEventClickListener() != null) { + CalendarEvent e = getEventByWidget(mel); + calendar.getEventClickListener().eventClick(e); + } + + moveEvent = null; + } else if (w == this) { + getMonthGrid().setSelectionReady(); + + } else if (w instanceof Label && labelMouseDown) { + String clickedDate = calendar.getDateFormat().format(date); + if (calendar.getDateClickListener() != null) { + calendar.getDateClickListener().dateClick(clickedDate); + } + } + monthEventMouseDown = false; + labelMouseDown = false; + clickedWidget = null; + } + + @Override + public void onMouseDown(MouseDownEvent event) { + if (calendar.isDisabled() + || event.getNativeButton() != NativeEvent.BUTTON_LEFT) { + return; + } + + Widget w = (Widget) event.getSource(); + clickedWidget = w; + + if (w instanceof MonthEventLabel) { + // event clicks should be allowed even when read-only + monthEventMouseDown = true; + + if (w instanceof MonthEventLabel) { + startCalendarEventDrag(event, (MonthEventLabel) w); + } + } else if (!calendar.isReadOnly()) { + // these are not allowed when in read-only + if (w == bottomspacer) { + if (scrollable) { + setLimitedCellHeight(); + } else { + setUnlimitedCellHeight(); + } + reDraw(true); + + } else if (w == this && !scrollable) { + MonthGrid grid = getMonthGrid(); + if (grid.isEnabled() && calendar.isRangeSelectAllowed()) { + grid.setSelectionStart(this); + grid.setSelectionEnd(this); + } + } else if (w instanceof Label) { + labelMouseDown = true; + } + } + + event.stopPropagation(); + event.preventDefault(); + } + + @Override + public void onMouseOver(MouseOverEvent event) { + event.preventDefault(); + getMonthGrid().setSelectionEnd(this); + } + + @Override + public void onMouseMove(MouseMoveEvent event) { + if (clickedWidget instanceof MonthEventLabel && !monthEventMouseDown + || (startY < 0 && startX < 0)) { + return; + } + + MonthEventLabel w = (MonthEventLabel) clickedWidget; + + if (calendar.isDisabledOrReadOnly()) { + Event.releaseCapture(getElement()); + monthEventMouseDown = false; + startY = -1; + startX = -1; + return; + } + + int currentY = event.getClientY(); + int currentX = event.getClientX(); + int moveY = (currentY - startY); + int moveX = (currentX - startX); + if ((moveY < 5 && moveY > -6) && (moveX < 5 && moveX > -6)) { + return; + } + + int dateCellWidth = getWidth(); + int dateCellHeigth = getHeigth(); + + Element parent = getMonthGrid().getElement(); + int relativeX = event.getRelativeX(parent); + int relativeY = event.getRelativeY(parent); + int weekDiff = 0; + if (moveY > 0) { + weekDiff = (startYrelative + moveY) / dateCellHeigth; + } else { + weekDiff = (moveY - (dateCellHeigth - startYrelative)) + / dateCellHeigth; + } + + int dayDiff = 0; + if (moveX >= 0) { + dayDiff = (startXrelative + moveX) / dateCellWidth; + } else { + dayDiff = (moveX - (dateCellWidth - startXrelative)) + / dateCellWidth; + } + // Check boundaries + if (relativeY < 0 + || relativeY >= (calendar.getMonthGrid().getRowCount() * dateCellHeigth) + || relativeX < 0 + || relativeX >= (calendar.getMonthGrid().getColumnCount() * dateCellWidth)) { + return; + } + + GWT.log("Event moving delta: " + weekDiff + " weeks " + dayDiff + + " days" + " (" + getCell() + "," + getRow() + ")"); + + CalendarEvent e = moveEvent; + if (e == null) { + e = getEventByWidget(w); + } + + Date from = e.getStart(); + Date to = e.getEnd(); + long duration = to.getTime() - from.getTime(); + + long daysMs = dayDiff * DateConstants.DAYINMILLIS; + long weeksMs = weekDiff * DateConstants.WEEKINMILLIS; + from.setTime(startDateFrom.getTime() + weeksMs + daysMs); + to.setTime((from.getTime() + duration)); + e.setStart(from); + e.setEnd(to); + e.setStartTime(new Date(from.getTime())); + e.setEndTime(new Date(to.getTime())); + + updateDragPosition(w, dayDiff, weekDiff); + } + + private void eventMoved(CalendarEvent e) { + calendar.updateEventToMonthGrid(e); + if (calendar.getEventMovedListener() != null) { + calendar.getEventMovedListener().eventMoved(e); + } + } + + public void startCalendarEventDrag(MouseDownEvent event, + final MonthEventLabel w) { + if (w.isTimeSpecificEvent()) { + return; + } + + moveRegistration = addMouseMoveHandler(this); + startX = event.getClientX(); + startY = event.getClientY(); + startYrelative = event.getRelativeY(w.getParent().getElement()) + % getHeigth(); + startXrelative = event.getRelativeX(w.getParent().getElement()) + % getWidth(); + + CalendarEvent e = getEventByWidget(w); + startDateFrom = (Date) e.getStart().clone(); + startDateTo = (Date) e.getEnd().clone(); + + Event.setCapture(getElement()); + keyDownHandler = addKeyDownHandler(new KeyDownHandler() { + + @Override + public void onKeyDown(KeyDownEvent event) { + if (event.getNativeKeyCode() == KeyCodes.KEY_ESCAPE) { + cancelEventDrag(w); + } + } + + }); + + focus(); + + GWT.log("Start drag"); + } + + protected void cancelEventDrag(MonthEventLabel w) { + if (moveRegistration != null) { + // reset position + if (moveEvent == null) { + moveEvent = getEventByWidget(w); + } + + moveEvent.setStart(startDateFrom); + moveEvent.setEnd(startDateTo); + calendar.updateEventToMonthGrid(moveEvent); + + // reset drag-related properties + Event.releaseCapture(getElement()); + moveRegistration.removeHandler(); + moveRegistration = null; + keyDownHandler.removeHandler(); + keyDownHandler = null; + setFocus(false); + monthEventMouseDown = false; + startY = -1; + startX = -1; + moveEvent = null; + labelMouseDown = false; + clickedWidget = null; + } + } + + public void updateDragPosition(MonthEventLabel w, int dayDiff, int weekDiff) { + // Draw event to its new position only when position has changed + if (dayDiff == prevDayDiff && weekDiff == prevWeekDiff) { + return; + } + + prevDayDiff = dayDiff; + prevWeekDiff = weekDiff; + + if (moveEvent == null) { + moveEvent = getEventByWidget(w); + } + + calendar.updateEventToMonthGrid(moveEvent); + } + + public int getRow() { + return row; + } + + public int getCell() { + return cell; + } + + public int getHeigth() { + return intHeight + BORDERPADDINGSIZE; + } + + public int getWidth() { + return getOffsetWidth() - BORDERPADDINGSIZE; + } + + public void setToday(boolean today) { + if (today) { + addStyleDependentName("today"); + } else { + removeStyleDependentName("today"); + } + } + + public boolean removeEvent(CalendarEvent targetEvent, + boolean reDrawImmediately) { + int slot = targetEvent.getSlotIndex(); + if (slot < 0) { + return false; + } + + CalendarEvent e = getCalendarEvent(slot); + if (targetEvent.equals(e)) { + events[slot] = null; + eventCount--; + if (reDrawImmediately) { + reDraw(moveEvent == null); + } + return true; + } + return false; + } + + private CalendarEvent getEventByWidget(MonthEventLabel eventWidget) { + int index = getWidgetIndex(eventWidget); + return getCalendarEvent(index - 1); + } + + public CalendarEvent getCalendarEvent(int i) { + return events[i]; + } + + public CalendarEvent[] getEvents() { + return events; + } + + public int getEventCount() { + return eventCount; + } + + public CalendarEvent getMoveEvent() { + return moveEvent; + } + + public void addEmphasisStyle() { + addStyleDependentName("dragemphasis"); + } + + public void removeEmphasisStyle() { + removeStyleDependentName("dragemphasis"); + } +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayToolbar.java b/client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayToolbar.java new file mode 100644 index 0000000000..fc75136b93 --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/SimpleDayToolbar.java @@ -0,0 +1,97 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.Widget; + +/** + * + * @since 7.1.0 + * @author Vaadin Ltd. + * + */ +public class SimpleDayToolbar extends HorizontalPanel { + private int width = 0; + private boolean isWidthUndefined = false; + + public SimpleDayToolbar() { + setStylePrimaryName("v-calendar-header-month"); + } + + public void setDayNames(String[] dayNames) { + clear(); + for (int i = 0; i < dayNames.length; i++) { + Label l = new Label(dayNames[i]); + l.setStylePrimaryName("v-calendar-header-day"); + add(l); + } + updateCellWidth(); + } + + public void setWidthPX(int width) { + this.width = width; + + setWidthUndefined(width == -1); + + if (!isWidthUndefined()) { + super.setWidth(this.width + "px"); + if (getWidgetCount() == 0) { + return; + } + } + updateCellWidth(); + } + + private boolean isWidthUndefined() { + return isWidthUndefined; + } + + private void setWidthUndefined(boolean isWidthUndefined) { + this.isWidthUndefined = isWidthUndefined; + + if (isWidthUndefined) { + addStyleDependentName("Hsized"); + + } else { + removeStyleDependentName("Hsized"); + } + } + + private void updateCellWidth() { + int cellw = -1; + int widgetCount = getWidgetCount(); + if (widgetCount <= 0) { + return; + } + if (isWidthUndefined()) { + Widget widget = getWidget(0); + String w = widget.getElement().getStyle().getWidth(); + if (w.length() > 2) { + cellw = Integer.parseInt(w.substring(0, w.length() - 2)); + } + } else { + cellw = width / getWidgetCount(); + } + if (cellw > 0) { + for (int i = 0; i < getWidgetCount(); i++) { + Widget widget = getWidget(i); + setCellWidth(widget, cellw + "px"); + } + } + } +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/SimpleWeekToolbar.java b/client/src/com/vaadin/client/ui/calendar/schedule/SimpleWeekToolbar.java new file mode 100644 index 0000000000..59902811cd --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/SimpleWeekToolbar.java @@ -0,0 +1,109 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.user.client.ui.FlexTable; +import com.vaadin.client.ui.VCalendar; + +/** + * + * @since 7.1 + * @author Vaadin Ltd. + * + */ +public class SimpleWeekToolbar extends FlexTable implements ClickHandler { + private int height; + private VCalendar calendar; + private boolean isHeightUndefined; + + public SimpleWeekToolbar(VCalendar parent) { + calendar = parent; + setCellSpacing(0); + setCellPadding(0); + setStyleName("v-calendar-week-numbers"); + } + + public void addWeek(int week, int year) { + WeekLabel l = new WeekLabel(week + "", week, year); + l.addClickHandler(this); + int rowCount = getRowCount(); + insertRow(rowCount); + setWidget(rowCount, 0, l); + updateCellHeights(); + } + + public void updateCellHeights() { + if (!isHeightUndefined()) { + int rowCount = getRowCount(); + if (rowCount == 0) { + return; + } + int cellheight = (height / rowCount) - 1; + int remainder = height % rowCount; + if (cellheight < 0) { + cellheight = 0; + } + for (int i = 0; i < rowCount; i++) { + if (remainder > 0) { + getWidget(i, 0).setHeight(cellheight + 1 + "px"); + } else { + getWidget(i, 0).setHeight(cellheight + "px"); + } + getWidget(i, 0).getElement().getStyle() + .setProperty("lineHeight", cellheight + "px"); + remainder--; + } + } else { + for (int i = 0; i < getRowCount(); i++) { + getWidget(i, 0).setHeight(""); + getWidget(i, 0).getElement().getStyle() + .setProperty("lineHeight", ""); + } + } + } + + public void setHeightPX(int intHeight) { + setHeightUndefined(intHeight == -1); + height = intHeight; + updateCellHeights(); + } + + public boolean isHeightUndefined() { + return isHeightUndefined; + } + + public void setHeightUndefined(boolean isHeightUndefined) { + this.isHeightUndefined = isHeightUndefined; + + if (isHeightUndefined) { + addStyleDependentName("Vsized"); + + } else { + removeStyleDependentName("Vsized"); + } + } + + @Override + public void onClick(ClickEvent event) { + WeekLabel wl = (WeekLabel) event.getSource(); + if (calendar.getWeekClickListener() != null) { + calendar.getWeekClickListener().weekClick( + wl.getYear() + "w" + wl.getWeek()); + } + } +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/WeekGrid.java b/client/src/com/vaadin/client/ui/calendar/schedule/WeekGrid.java new file mode 100644 index 0000000000..450ea29549 --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/WeekGrid.java @@ -0,0 +1,678 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import java.util.Arrays; +import java.util.Date; + +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.Style; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.event.dom.client.ScrollEvent; +import com.google.gwt.event.dom.client.ScrollHandler; +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.ui.HTML; +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.Panel; +import com.google.gwt.user.client.ui.ScrollPanel; +import com.google.gwt.user.client.ui.SimplePanel; +import com.google.gwt.user.client.ui.Widget; +import com.vaadin.client.DateTimeService; +import com.vaadin.client.Util; +import com.vaadin.client.ui.VCalendar; + +/** + * + * @since 7.1 + * @author Vaadin Ltd. + * + */ +public class WeekGrid extends SimplePanel { + + int width = 0; + private int height = 0; + final HorizontalPanel content; + private VCalendar calendar; + private boolean disabled; + final Timebar timebar; + private Panel wrapper; + private boolean verticalScrollEnabled; + private boolean horizontalScrollEnabled; + private int[] cellHeights; + private final int slotInMinutes = 30; + private int dateCellBorder; + private DateCell dateCellOfToday; + private int[] cellWidths; + private int firstHour; + private int lastHour; + + public WeekGrid(VCalendar parent, boolean format24h) { + setCalendar(parent); + content = new HorizontalPanel(); + timebar = new Timebar(format24h); + content.add(timebar); + + wrapper = new SimplePanel(); + wrapper.setStylePrimaryName("v-calendar-week-wrapper"); + wrapper.add(content); + + setWidget(wrapper); + } + + private void setVerticalScroll(boolean isVerticalScrollEnabled) { + if (isVerticalScrollEnabled && !(isVerticalScrollable())) { + verticalScrollEnabled = true; + horizontalScrollEnabled = false; + wrapper.remove(content); + + final ScrollPanel scrollPanel = new ScrollPanel(); + scrollPanel.setStylePrimaryName("v-calendar-week-wrapper"); + scrollPanel.setWidget(content); + + scrollPanel.addScrollHandler(new ScrollHandler() { + @Override + public void onScroll(ScrollEvent event) { + if (calendar.getScrollListener() != null) { + calendar.getScrollListener().scroll( + scrollPanel.getVerticalScrollPosition()); + } + } + }); + + setWidget(scrollPanel); + wrapper = scrollPanel; + + } else if (!isVerticalScrollEnabled && (isVerticalScrollable())) { + verticalScrollEnabled = false; + horizontalScrollEnabled = false; + wrapper.remove(content); + + SimplePanel simplePanel = new SimplePanel(); + simplePanel.setStylePrimaryName("v-calendar-week-wrapper"); + simplePanel.setWidget(content); + + setWidget(simplePanel); + wrapper = simplePanel; + } + } + + public void setVerticalScrollPosition(int verticalScrollPosition) { + if (isVerticalScrollable()) { + ((ScrollPanel) wrapper) + .setVerticalScrollPosition(verticalScrollPosition); + } + } + + public int getInternalWidth() { + return width; + } + + public void addDate(Date d) { + final DateCell dc = new DateCell(this, d); + dc.setDisabled(isDisabled()); + dc.setHorizontalSized(isHorizontalScrollable() || width < 0); + dc.setVerticalSized(isVerticalScrollable()); + content.add(dc); + } + + /** + * @param dateCell + * @return get the index of the given date cell in this week, starting from + * 0 + */ + public int getDateCellIndex(DateCell dateCell) { + return content.getWidgetIndex(dateCell) - 1; + } + + /** + * @return get the slot border in pixels + */ + public int getDateSlotBorder() { + return ((DateCell) content.getWidget(1)).getSlotBorder(); + } + + private boolean isVerticalScrollable() { + return verticalScrollEnabled; + } + + private boolean isHorizontalScrollable() { + return horizontalScrollEnabled; + } + + public void setWidthPX(int width) { + if (isHorizontalScrollable()) { + updateCellWidths(); + + // Otherwise the scroll wrapper is somehow too narrow = horizontal + // scroll + wrapper.setWidth(content.getOffsetWidth() + + Util.getNativeScrollbarSize() + "px"); + + this.width = content.getOffsetWidth() - timebar.getOffsetWidth(); + + } else { + this.width = (width == -1) ? width : width + - timebar.getOffsetWidth(); + + if (isVerticalScrollable() && width != -1) { + this.width = this.width - Util.getNativeScrollbarSize(); + } + updateCellWidths(); + } + } + + public void setHeightPX(int intHeight) { + height = intHeight; + + setVerticalScroll(height <= -1); + + // if not scrollable, use any height given + if (!isVerticalScrollable() && height > 0) { + + content.setHeight(height + "px"); + setHeight(height + "px"); + wrapper.setHeight(height + "px"); + wrapper.removeStyleDependentName("Vsized"); + updateCellHeights(); + timebar.setCellHeights(cellHeights); + timebar.setHeightPX(height); + + } else if (isVerticalScrollable()) { + updateCellHeights(); + wrapper.addStyleDependentName("Vsized"); + timebar.setCellHeights(cellHeights); + timebar.setHeightPX(height); + } + } + + public void clearDates() { + while (content.getWidgetCount() > 1) { + content.remove(1); + } + + dateCellOfToday = null; + } + + /** + * @return true if this weekgrid contains a date that is today + */ + public boolean hasToday() { + return dateCellOfToday != null; + } + + public void updateCellWidths() { + if (!isHorizontalScrollable() && width != -1) { + int count = content.getWidgetCount(); + int datesWidth = width; + if (datesWidth > 0 && count > 1) { + cellWidths = VCalendar + .distributeSize(datesWidth, count - 1, -1); + + for (int i = 1; i < count; i++) { + DateCell dc = (DateCell) content.getWidget(i); + dc.setHorizontalSized(isHorizontalScrollable() || width < 0); + dc.setWidthPX(cellWidths[i - 1]); + if (dc.isToday()) { + dc.setTimeBarWidth(getOffsetWidth()); + } + } + } + + } else { + int count = content.getWidgetCount(); + if (count > 1) { + for (int i = 1; i < count; i++) { + DateCell dc = (DateCell) content.getWidget(i); + dc.setHorizontalSized(isHorizontalScrollable() || width < 0); + } + } + } + } + + /** + * @return an int-array containing the widths of the cells (days) + */ + public int[] getDateCellWidths() { + return cellWidths; + } + + public void updateCellHeights() { + if (!isVerticalScrollable()) { + int count = content.getWidgetCount(); + if (count > 1) { + DateCell first = (DateCell) content.getWidget(1); + dateCellBorder = first.getSlotBorder(); + cellHeights = VCalendar.distributeSize(height, + first.getNumberOfSlots(), -dateCellBorder); + for (int i = 1; i < count; i++) { + DateCell dc = (DateCell) content.getWidget(i); + dc.setHeightPX(height, cellHeights); + } + } + + } else { + int count = content.getWidgetCount(); + if (count > 1) { + DateCell first = (DateCell) content.getWidget(1); + dateCellBorder = first.getSlotBorder(); + int dateHeight = (first.getOffsetHeight() / first + .getNumberOfSlots()) - dateCellBorder; + cellHeights = new int[48]; + Arrays.fill(cellHeights, dateHeight); + + for (int i = 1; i < count; i++) { + DateCell dc = (DateCell) content.getWidget(i); + dc.setVerticalSized(isVerticalScrollable()); + } + } + } + } + + public void addEvent(CalendarEvent e) { + int dateCount = content.getWidgetCount(); + Date from = e.getStart(); + Date toTime = e.getEndTime(); + for (int i = 1; i < dateCount; i++) { + DateCell dc = (DateCell) content.getWidget(i); + Date dcDate = dc.getDate(); + int comp = dcDate.compareTo(from); + int comp2 = dcDate.compareTo(toTime); + if (comp >= 0 + && comp2 < 0 + || (comp == 0 && comp2 == 0 && VCalendar + .isZeroLengthMidnightEvent(e))) { + // Same event may be over two DateCells if event's date + // range floats over one day. It can't float over two days, + // because event which range is over 24 hours, will be handled + // as a "fullDay" event. + dc.addEvent(dcDate, e); + } + } + } + + public int getPixelLengthFor(int startFromMinutes, int durationInMinutes) { + int pixelLength = 0; + int currentSlot = 0; + + int firstHourInMinutes = firstHour * 60; + + if (firstHourInMinutes > startFromMinutes) { + startFromMinutes = 0; + } else { + startFromMinutes -= firstHourInMinutes; + } + + // calculate full slots to event + int slotsTillEvent = startFromMinutes / slotInMinutes; + int startOverFlowTime = slotInMinutes + - (startFromMinutes % slotInMinutes); + if (startOverFlowTime == slotInMinutes) { + startOverFlowTime = 0; + currentSlot = slotsTillEvent; + } else { + currentSlot = slotsTillEvent + 1; + } + + int durationInSlots = 0; + int endOverFlowTime = 0; + + if (startOverFlowTime > 0) { + durationInSlots = (durationInMinutes - startOverFlowTime) + / slotInMinutes; + endOverFlowTime = (durationInMinutes - startOverFlowTime) + % slotInMinutes; + + } else { + durationInSlots = durationInMinutes / slotInMinutes; + endOverFlowTime = durationInMinutes % slotInMinutes; + } + + // calculate slot overflow at start + if (startOverFlowTime > 0 && currentSlot < cellHeights.length) { + int lastSlotHeight = cellHeights[currentSlot] + dateCellBorder; + pixelLength += (int) (((double) lastSlotHeight / (double) slotInMinutes) * startOverFlowTime); + } + + // calculate length in full slots + int lastFullSlot = currentSlot + durationInSlots; + for (; currentSlot < lastFullSlot && currentSlot < cellHeights.length; currentSlot++) { + pixelLength += cellHeights[currentSlot] + dateCellBorder; + } + + // calculate overflow at end + if (endOverFlowTime > 0 && currentSlot < cellHeights.length) { + int lastSlotHeight = cellHeights[currentSlot] + dateCellBorder; + pixelLength += (int) (((double) lastSlotHeight / (double) slotInMinutes) * endOverFlowTime); + } + + // reduce possible underflow at end + if (endOverFlowTime < 0) { + int lastSlotHeight = cellHeights[currentSlot] + dateCellBorder; + pixelLength += (int) (((double) lastSlotHeight / (double) slotInMinutes) * endOverFlowTime); + } + + return pixelLength; + } + + public int getPixelTopFor(int startFromMinutes) { + int pixelsToTop = 0; + int slotIndex = 0; + + int firstHourInMinutes = firstHour * 60; + + if (firstHourInMinutes > startFromMinutes) { + startFromMinutes = 0; + } else { + startFromMinutes -= firstHourInMinutes; + } + + // calculate full slots to event + int slotsTillEvent = startFromMinutes / slotInMinutes; + int overFlowTime = startFromMinutes % slotInMinutes; + if (slotsTillEvent > 0) { + for (slotIndex = 0; slotIndex < slotsTillEvent; slotIndex++) { + pixelsToTop += cellHeights[slotIndex] + dateCellBorder; + } + } + + // calculate lengths less than one slot + if (overFlowTime > 0) { + int lastSlotHeight = cellHeights[slotIndex] + dateCellBorder; + pixelsToTop += ((double) lastSlotHeight / (double) slotInMinutes) + * overFlowTime; + } + + return pixelsToTop; + } + + public void eventMoved(DateCellDayEvent dayEvent) { + Style s = dayEvent.getElement().getStyle(); + int left = Integer.parseInt(s.getLeft().substring(0, + s.getLeft().length() - 2)); + DateCell previousParent = (DateCell) dayEvent.getParent(); + DateCell newParent = (DateCell) content + .getWidget((left / getDateCellWidth()) + 1); + CalendarEvent se = dayEvent.getCalendarEvent(); + previousParent.removeEvent(dayEvent); + newParent.addEvent(dayEvent); + if (!previousParent.equals(newParent)) { + previousParent.recalculateEventWidths(); + } + newParent.recalculateEventWidths(); + if (calendar.getEventMovedListener() != null) { + calendar.getEventMovedListener().eventMoved(se); + } + } + + public void setToday(Date todayDate, Date todayTimestamp) { + int count = content.getWidgetCount(); + if (count > 1) { + for (int i = 1; i < count; i++) { + DateCell dc = (DateCell) content.getWidget(i); + if (dc.getDate().getTime() == todayDate.getTime()) { + if (isVerticalScrollable()) { + dc.setToday(todayTimestamp, -1); + } else { + dc.setToday(todayTimestamp, getOffsetWidth()); + } + } + dateCellOfToday = dc; + } + } + } + + public DateCell getDateCellOfToday() { + return dateCellOfToday; + } + + public void setDisabled(boolean disabled) { + this.disabled = disabled; + } + + public boolean isDisabled() { + return disabled; + } + + public Timebar getTimeBar() { + return timebar; + } + + public void setDateColor(Date when, Date to, String styleName) { + int dateCount = content.getWidgetCount(); + for (int i = 1; i < dateCount; i++) { + DateCell dc = (DateCell) content.getWidget(i); + Date dcDate = dc.getDate(); + int comp = dcDate.compareTo(when); + int comp2 = dcDate.compareTo(to); + if (comp >= 0 && comp2 <= 0) { + dc.setDateColor(styleName); + } + } + } + + /** + * @param calendar + * the calendar to set + */ + public void setCalendar(VCalendar calendar) { + this.calendar = calendar; + } + + /** + * @return the calendar + */ + public VCalendar getCalendar() { + return calendar; + } + + /** + * Get width of the single date cell + * + * @return Date cell width + */ + public int getDateCellWidth() { + int count = content.getWidgetCount() - 1; + int cellWidth = -1; + if (count <= 0) { + return cellWidth; + } + + if (width == -1) { + Widget firstWidget = content.getWidget(1); + cellWidth = firstWidget.getElement().getOffsetWidth(); + } else { + cellWidth = getInternalWidth() / count; + } + return cellWidth; + } + + /** + * @return the number of day cells in this week + */ + public int getDateCellCount() { + return content.getWidgetCount() - 1; + } + + public void setFirstHour(int firstHour) { + this.firstHour = firstHour; + timebar.setFirstHour(firstHour); + } + + public void setLastHour(int lastHour) { + this.lastHour = lastHour; + timebar.setLastHour(lastHour); + } + + public int getFirstHour() { + return firstHour; + } + + public int getLastHour() { + return lastHour; + } + + public static class Timebar extends HTML { + + private static final int[] timesFor12h = { 12, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11 }; + + private int height; + + private final int verticalPadding = 7; // FIXME measure this from DOM + + private int[] slotCellHeights; + + private int firstHour; + + private int lastHour; + + public Timebar(boolean format24h) { + createTimeBar(format24h); + } + + public void setLastHour(int lastHour) { + this.lastHour = lastHour; + } + + public void setFirstHour(int firstHour) { + this.firstHour = firstHour; + + } + + public void setCellHeights(int[] cellHeights) { + slotCellHeights = cellHeights; + } + + private void createTimeBar(boolean format24h) { + setStylePrimaryName("v-calendar-times"); + + // Fist "time" is empty + Element e = DOM.createDiv(); + setStyleName(e, "v-calendar-time"); + e.setInnerText(""); + getElement().appendChild(e); + + DateTimeService dts = new DateTimeService(); + + if (format24h) { + for (int i = firstHour + 1; i <= lastHour; i++) { + e = DOM.createDiv(); + setStyleName(e, "v-calendar-time"); + String delimiter = dts.getClockDelimeter(); + e.setInnerHTML("<span>" + i + "</span>" + delimiter + "00"); + getElement().appendChild(e); + } + } else { + // FIXME Use dts.getAmPmStrings(); and make sure that + // DateTimeService has a some Locale set. + String[] ampm = new String[] { "AM", "PM" }; + + int amStop = (lastHour < 11) ? lastHour : 11; + int pmStart = (firstHour > 11) ? firstHour % 11 : 0; + + if (firstHour < 12) { + for (int i = firstHour + 1; i <= amStop; i++) { + e = DOM.createDiv(); + setStyleName(e, "v-calendar-time"); + e.setInnerHTML("<span>" + timesFor12h[i] + "</span>" + + " " + ampm[0]); + getElement().appendChild(e); + } + } + + if (lastHour > 11) { + for (int i = pmStart; i < lastHour - 11; i++) { + e = DOM.createDiv(); + setStyleName(e, "v-calendar-time"); + e.setInnerHTML("<span>" + timesFor12h[i] + "</span>" + + " " + ampm[1]); + getElement().appendChild(e); + } + } + } + } + + public void updateTimeBar(boolean format24h) { + clear(); + createTimeBar(format24h); + } + + private void clear() { + while (getElement().getChildCount() > 0) { + getElement().removeChild(getElement().getChild(0)); + } + } + + public void setHeightPX(int pixelHeight) { + height = pixelHeight; + + if (pixelHeight > -1) { + // as the negative margins on children pulls the whole element + // upwards, we must compensate. otherwise the element would be + // too short + super.setHeight((height + verticalPadding) + "px"); + removeStyleDependentName("Vsized"); + updateChildHeights(); + + } else { + addStyleDependentName("Vsized"); + updateChildHeights(); + } + } + + private void updateChildHeights() { + int childCount = getElement().getChildCount(); + + if (height != -1) { + + // 23 hours + first is empty + // we try to adjust the height of time labels to the distributed + // heights of the time slots + int hoursPerDay = lastHour - firstHour + 1; + + int slotsPerHour = slotCellHeights.length / hoursPerDay; + int[] cellHeights = new int[slotCellHeights.length + / slotsPerHour]; + + int slotHeightPosition = 0; + for (int i = 0; i < cellHeights.length; i++) { + for (int j = slotHeightPosition; j < slotHeightPosition + + slotsPerHour; j++) { + cellHeights[i] += slotCellHeights[j] + 1; + // 1px more for borders + // FIXME measure from DOM + } + slotHeightPosition += slotsPerHour; + } + + for (int i = 0; i < childCount; i++) { + Element e = (Element) getElement().getChild(i); + e.getStyle().setHeight(cellHeights[i], Unit.PX); + } + + } else { + for (int i = 0; i < childCount; i++) { + Element e = (Element) getElement().getChild(i); + e.getStyle().setProperty("height", ""); + } + } + } + } + + public VCalendar getParentCalendar() { + return calendar; + } +} diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/WeekGridMinuteTimeRange.java b/client/src/com/vaadin/client/ui/calendar/schedule/WeekGridMinuteTimeRange.java new file mode 100644 index 0000000000..e634735be7 --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/WeekGridMinuteTimeRange.java @@ -0,0 +1,62 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import java.util.Date; + +/** + * Internally used by the calendar + * + * @since 7.1 + */ +public class WeekGridMinuteTimeRange { + private final Date start; + private final Date end; + + /** + * Creates a Date time range between start and end date. Drops seconds from + * the range. + * + * @param start + * Start time of the range + * @param end + * End time of the range + * @param clearSeconds + * Boolean Indicates, if seconds should be dropped from the range + * start and end + */ + public WeekGridMinuteTimeRange(Date start, Date end) { + this.start = new Date(start.getTime()); + this.end = new Date(end.getTime()); + this.start.setSeconds(0); + this.end.setSeconds(0); + } + + public Date getStart() { + return start; + } + + public Date getEnd() { + return end; + } + + public static boolean doesOverlap(WeekGridMinuteTimeRange a, + WeekGridMinuteTimeRange b) { + boolean overlaps = a.getStart().compareTo(b.getEnd()) < 0 + && a.getEnd().compareTo(b.getStart()) > 0; + return overlaps; + } +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/WeekLabel.java b/client/src/com/vaadin/client/ui/calendar/schedule/WeekLabel.java new file mode 100644 index 0000000000..bde8675435 --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/WeekLabel.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import com.google.gwt.user.client.ui.Label; + +/** + * A label in the {@link SimpleWeekToolbar} + * + * @since 7.1 + */ +public class WeekLabel extends Label { + private int week; + private int year; + + public WeekLabel(String string, int week2, int year2) { + super(string); + setStylePrimaryName("v-calendar-week-number"); + week = week2; + year = year2; + } + + public int getWeek() { + return week; + } + + public void setWeek(int week) { + this.week = week; + } + + public int getYear() { + return year; + } + + public void setYear(int year) { + this.year = year; + } +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEvents.java b/client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEvents.java new file mode 100644 index 0000000000..f7c5c0dac4 --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEvents.java @@ -0,0 +1,185 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import java.util.Date; +import java.util.List; + +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.vaadin.client.ui.VCalendar; + +/** + * + * @since 7.1 + * @author Vaadin Ltd. + * + */ +public class WeeklyLongEvents extends HorizontalPanel implements HasTooltipKey { + + public static final int EVENT_HEIGTH = 15; + + public static final int EVENT_MARGIN = 1; + + private int rowCount = 0; + + private VCalendar calendar; + + private boolean undefinedWidth; + + public WeeklyLongEvents(VCalendar calendar) { + setStylePrimaryName("v-calendar-weekly-longevents"); + this.calendar = calendar; + } + + public void addDate(Date d) { + DateCellContainer dcc = new DateCellContainer(); + dcc.setDate(d); + dcc.setCalendar(calendar); + add(dcc); + } + + public void setWidthPX(int width) { + if (getWidgetCount() == 0) { + return; + } + undefinedWidth = (width < 0); + + updateCellWidths(); + } + + public void addEvents(List<CalendarEvent> events) { + for (CalendarEvent e : events) { + addEvent(e); + } + } + + public void addEvent(CalendarEvent calendarEvent) { + updateEventSlot(calendarEvent); + + int dateCount = getWidgetCount(); + Date from = calendarEvent.getStart(); + Date to = calendarEvent.getEnd(); + boolean started = false; + for (int i = 0; i < dateCount; i++) { + DateCellContainer dc = (DateCellContainer) getWidget(i); + Date dcDate = dc.getDate(); + int comp = dcDate.compareTo(from); + int comp2 = dcDate.compareTo(to); + WeeklyLongEventsDateCell eventLabel = dc.getDateCell(calendarEvent + .getSlotIndex()); + eventLabel.setStylePrimaryName("v-calendar-event"); + if (comp >= 0 && comp2 <= 0) { + eventLabel.setEvent(calendarEvent); + eventLabel.setCalendar(calendar); + + eventLabel.addStyleDependentName("all-day"); + if (comp == 0) { + eventLabel.addStyleDependentName("start"); + } + if (comp2 == 0) { + eventLabel.addStyleDependentName("end"); + } + if (!started && comp > 0 && comp2 <= 0) { + eventLabel.addStyleDependentName("continued-from"); + } else if (i == (dateCount - 1)) { + eventLabel.addStyleDependentName("continued-to"); + } + final String extraStyle = calendarEvent.getStyleName(); + if (extraStyle != null && extraStyle.length() > 0) { + eventLabel.addStyleDependentName(extraStyle + "-all-day"); + } + if (!started) { + eventLabel.setText(calendarEvent.getCaption()); + started = true; + } + } + } + } + + private void updateEventSlot(CalendarEvent e) { + boolean foundFreeSlot = false; + int slot = 0; + while (!foundFreeSlot) { + if (isSlotFree(slot, e.getStart(), e.getEnd())) { + e.setSlotIndex(slot); + foundFreeSlot = true; + + } else { + slot++; + } + } + } + + private boolean isSlotFree(int slot, Date start, Date end) { + int dateCount = getWidgetCount(); + + // Go over all dates this week + for (int i = 0; i < dateCount; i++) { + DateCellContainer dc = (DateCellContainer) getWidget(i); + Date dcDate = dc.getDate(); + int comp = dcDate.compareTo(start); + int comp2 = dcDate.compareTo(end); + + // check if the date is in the range we need + if (comp >= 0 && comp2 <= 0) { + + // check if the slot is taken + if (dc.hasEvent(slot)) { + return false; + } + } + } + + return true; + } + + public int getRowCount() { + return rowCount; + } + + public void updateCellWidths() { + int cells = getWidgetCount(); + if (cells <= 0) { + return; + } + + int cellWidth = -1; + + // if width is undefined, use the width of the first cell + // otherwise use distributed sizes + if (undefinedWidth) { + cellWidth = calendar.getWeekGrid().getDateCellWidth() + - calendar.getWeekGrid().getDateSlotBorder(); + } + + for (int i = 0; i < cells; i++) { + DateCellContainer dc = (DateCellContainer) getWidget(i); + + if (undefinedWidth) { + dc.setWidth(cellWidth + "px"); + + } else { + dc.setWidth(calendar.getWeekGrid().getDateCellWidths()[i] + + "px"); + } + } + } + + @Override + public String getTooltipKey() { + return null; + } +} diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEventsDateCell.java b/client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEventsDateCell.java new file mode 100644 index 0000000000..a97d352e81 --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/WeeklyLongEventsDateCell.java @@ -0,0 +1,67 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule; + +import java.util.Date; + +import com.google.gwt.user.client.ui.HTML; +import com.vaadin.client.ui.VCalendar; + +/** + * Represents a cell used in {@link WeeklyLongEvents} + * + * @since 7.1 + */ +public class WeeklyLongEventsDateCell extends HTML implements HasTooltipKey { + private Date date; + private CalendarEvent calendarEvent; + private VCalendar calendar; + + public WeeklyLongEventsDateCell() { + } + + public void setDate(Date date) { + this.date = date; + } + + public Date getDate() { + return date; + } + + public void setEvent(CalendarEvent event) { + calendarEvent = event; + } + + public CalendarEvent getEvent() { + return calendarEvent; + } + + public void setCalendar(VCalendar calendar) { + this.calendar = calendar; + } + + public VCalendar getCalendar() { + return calendar; + } + + @Override + public Object getTooltipKey() { + if (calendarEvent != null) { + return calendarEvent.getIndex(); + } + return null; + } +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarDropHandler.java b/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarDropHandler.java new file mode 100644 index 0000000000..aab9ca9c38 --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarDropHandler.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule.dd; + +import com.vaadin.client.ApplicationConnection; +import com.vaadin.client.ui.calendar.CalendarConnector; +import com.vaadin.client.ui.dd.VAbstractDropHandler; + +/** + * Abstract base class for calendar drop handlers. + * + * @since 7.1 + * @author Vaadin Ltd. + * + */ +public abstract class CalendarDropHandler extends VAbstractDropHandler { + + protected CalendarConnector calendarConnector; + + /** + * Set the calendar instance + * + * @param calendarPaintable + */ + public void setConnector(CalendarConnector calendarConnector) { + this.calendarConnector = calendarConnector; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#getConnector() + */ + @Override + public CalendarConnector getConnector() { + return calendarConnector; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.ui.dd.VDropHandler#getApplicationConnection + * () + */ + @Override + public ApplicationConnection getApplicationConnection() { + return calendarConnector.getClient(); + } + +} diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarMonthDropHandler.java b/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarMonthDropHandler.java new file mode 100644 index 0000000000..913477ee14 --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarMonthDropHandler.java @@ -0,0 +1,167 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule.dd; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.vaadin.client.Util; +import com.vaadin.client.ui.calendar.schedule.SimpleDayCell; +import com.vaadin.client.ui.dd.VAcceptCallback; +import com.vaadin.client.ui.dd.VDragEvent; + +/** + * Handles DD when the monthly view is showing in the Calendar. In the monthly + * view, drops are only allowed in the the day cells. Only the day index is + * included in the drop details sent to the server. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +public class CalendarMonthDropHandler extends CalendarDropHandler { + + private Element currentTargetElement; + private SimpleDayCell currentTargetDay; + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#dragAccepted + * (com.vaadin.terminal.gwt.client.ui.dd.VDragEvent) + */ + @Override + protected void dragAccepted(VDragEvent drag) { + deEmphasis(); + currentTargetElement = drag.getElementOver(); + currentTargetDay = Util.findWidget(currentTargetElement, + SimpleDayCell.class); + emphasis(); + } + + /** + * Removed the emphasis CSS style name from the currently emphasized day + */ + private void deEmphasis() { + if (currentTargetElement != null && currentTargetDay != null) { + currentTargetDay.removeEmphasisStyle(); + currentTargetElement = null; + } + } + + /** + * Add CSS style name for the currently emphasized day + */ + private void emphasis() { + if (currentTargetElement != null && currentTargetDay != null) { + currentTargetDay.addEmphasisStyle(); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#dragOver(com + * .vaadin.terminal.gwt.client.ui.dd.VDragEvent) + */ + @Override + public void dragOver(final VDragEvent drag) { + if (isLocationValid(drag.getElementOver())) { + validate(new VAcceptCallback() { + @Override + public void accepted(VDragEvent event) { + dragAccepted(drag); + } + }, drag); + } + } + + /** + * Checks if the one can perform a drop in a element + * + * @param elementOver + * The element to check + * @return + */ + private boolean isLocationValid( + com.google.gwt.user.client.Element elementOver) { + com.google.gwt.user.client.Element monthGridElement = calendarConnector + .getWidget().getMonthGrid().getElement(); + + // drops are not allowed in: + // - weekday header + // - week number bart + return DOM.isOrHasChild(monthGridElement, elementOver); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#dragEnter(com + * .vaadin.terminal.gwt.client.ui.dd.VDragEvent) + */ + @Override + public void dragEnter(VDragEvent drag) { + // NOOP, we determine drag acceptance in dragOver + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#drop(com.vaadin + * .terminal.gwt.client.ui.dd.VDragEvent) + */ + @Override + public boolean drop(VDragEvent drag) { + if (isLocationValid(drag.getElementOver())) { + updateDropDetails(drag); + deEmphasis(); + return super.drop(drag); + + } else { + deEmphasis(); + return false; + } + } + + /** + * Updates the drop details sent to the server + * + * @param drag + * The drag event + */ + private void updateDropDetails(VDragEvent drag) { + int dayIndex = calendarConnector.getWidget().getMonthGrid() + .getDayCellIndex(currentTargetDay); + + drag.getDropDetails().put("dropDayIndex", dayIndex); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#dragLeave(com + * .vaadin.terminal.gwt.client.ui.dd.VDragEvent) + */ + @Override + public void dragLeave(VDragEvent drag) { + deEmphasis(); + super.dragLeave(drag); + } +} diff --git a/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarWeekDropHandler.java b/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarWeekDropHandler.java new file mode 100644 index 0000000000..0ea683dc3c --- /dev/null +++ b/client/src/com/vaadin/client/ui/calendar/schedule/dd/CalendarWeekDropHandler.java @@ -0,0 +1,182 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.client.ui.calendar.schedule.dd; + +import com.google.gwt.user.client.DOM; +import com.google.gwt.user.client.Element; +import com.vaadin.client.Util; +import com.vaadin.client.ui.calendar.schedule.DateCell; +import com.vaadin.client.ui.calendar.schedule.DateCellDayEvent; +import com.vaadin.client.ui.dd.VAcceptCallback; +import com.vaadin.client.ui.dd.VDragEvent; + +/** + * Handles DD when the weekly view is showing in the Calendar. In the weekly + * view, drops are only allowed in the the time slots for each day. The slot + * index and the day index are included in the drop details sent to the server. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +public class CalendarWeekDropHandler extends CalendarDropHandler { + + private com.google.gwt.user.client.Element currentTargetElement; + private DateCell currentTargetDay; + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#dragAccepted + * (com.vaadin.terminal.gwt.client.ui.dd.VDragEvent) + */ + @Override + protected void dragAccepted(VDragEvent drag) { + deEmphasis(); + currentTargetElement = drag.getElementOver(); + currentTargetDay = Util + .findWidget(currentTargetElement, DateCell.class); + emphasis(); + } + + /** + * Removes the CSS style name from the emphasized element + */ + private void deEmphasis() { + if (currentTargetElement != null) { + currentTargetDay.removeEmphasisStyle(currentTargetElement); + currentTargetElement = null; + } + } + + /** + * Add a CSS stylen name to current target element + */ + private void emphasis() { + currentTargetDay.addEmphasisStyle(currentTargetElement); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#dragOver(com + * .vaadin.terminal.gwt.client.ui.dd.VDragEvent) + */ + @Override + public void dragOver(final VDragEvent drag) { + if (isLocationValid(drag.getElementOver())) { + validate(new VAcceptCallback() { + @Override + public void accepted(VDragEvent event) { + dragAccepted(drag); + } + }, drag); + } + } + + /** + * Checks if the location is a valid drop location + * + * @param elementOver + * The element to check + * @return + */ + private boolean isLocationValid( + com.google.gwt.user.client.Element elementOver) { + com.google.gwt.user.client.Element weekGridElement = calendarConnector + .getWidget().getWeekGrid().getElement(); + com.google.gwt.user.client.Element timeBarElement = calendarConnector + .getWidget().getWeekGrid().getTimeBar().getElement(); + + com.google.gwt.user.client.Element todayBarElement = null; + if (calendarConnector.getWidget().getWeekGrid().hasToday()) { + todayBarElement = (Element) calendarConnector.getWidget() + .getWeekGrid().getDateCellOfToday().getTodaybarElement(); + } + + // drops are not allowed in: + // - weekday header + // - allday event list + // - todaybar + // - timebar + // - events + return DOM.isOrHasChild(weekGridElement, elementOver) + && !DOM.isOrHasChild(timeBarElement, elementOver) + && todayBarElement != elementOver + && (Util.findWidget(elementOver, DateCellDayEvent.class) == null); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#dragEnter(com + * .vaadin.terminal.gwt.client.ui.dd.VDragEvent) + */ + @Override + public void dragEnter(VDragEvent drag) { + // NOOP, we determine drag acceptance in dragOver + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#drop(com.vaadin + * .terminal.gwt.client.ui.dd.VDragEvent) + */ + @Override + public boolean drop(VDragEvent drag) { + if (isLocationValid(drag.getElementOver())) { + updateDropDetails(drag); + deEmphasis(); + return super.drop(drag); + + } else { + deEmphasis(); + return false; + } + } + + /** + * Update the drop details sent to the server + * + * @param drag + * The drag event + */ + private void updateDropDetails(VDragEvent drag) { + int slotIndex = currentTargetDay.getSlotIndex(currentTargetElement); + int dayIndex = calendarConnector.getWidget().getWeekGrid() + .getDateCellIndex(currentTargetDay); + + drag.getDropDetails().put("dropDayIndex", dayIndex); + drag.getDropDetails().put("dropSlotIndex", slotIndex); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.terminal.gwt.client.ui.dd.VAbstractDropHandler#dragLeave(com + * .vaadin.terminal.gwt.client.ui.dd.VDragEvent) + */ + @Override + public void dragLeave(VDragEvent drag) { + deEmphasis(); + super.dragLeave(drag); + } +} diff --git a/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java b/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java index 772419e730..85e4e5ee8b 100644 --- a/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java +++ b/client/src/com/vaadin/client/ui/checkbox/CheckBoxConnector.java @@ -69,6 +69,8 @@ public class CheckBoxConnector extends AbstractFieldConnector implements blurHandlerRegistration); if (null != getState().errorMessage) { + getWidget().setAriaInvalid(true); + if (getWidget().errorIndicatorElement == null) { getWidget().errorIndicatorElement = DOM.createSpan(); getWidget().errorIndicatorElement.setInnerHTML(" "); @@ -85,8 +87,11 @@ public class CheckBoxConnector extends AbstractFieldConnector implements } else if (getWidget().errorIndicatorElement != null) { DOM.setStyleAttribute(getWidget().errorIndicatorElement, "display", "none"); + + getWidget().setAriaInvalid(false); } + getWidget().setAriaRequired(isRequired()); if (isReadOnly()) { getWidget().setEnabled(false); } diff --git a/client/src/com/vaadin/client/ui/datefield/InlineDateFieldConnector.java b/client/src/com/vaadin/client/ui/datefield/InlineDateFieldConnector.java index beff3eaa72..2fb40b3cdb 100644 --- a/client/src/com/vaadin/client/ui/datefield/InlineDateFieldConnector.java +++ b/client/src/com/vaadin/client/ui/datefield/InlineDateFieldConnector.java @@ -114,6 +114,8 @@ public class InlineDateFieldConnector extends AbstractDateFieldConnector { public void onStateChanged(StateChangeEvent stateChangeEvent) { super.onStateChanged(stateChangeEvent); getWidget().setTabIndex(getState().tabIndex); + getWidget().calendarPanel.setRangeStart(getState().rangeStart); + getWidget().calendarPanel.setRangeEnd(getState().rangeEnd); } @Override diff --git a/client/src/com/vaadin/client/ui/datefield/PopupDateFieldConnector.java b/client/src/com/vaadin/client/ui/datefield/PopupDateFieldConnector.java index 7246c27b6b..b3bb481658 100644 --- a/client/src/com/vaadin/client/ui/datefield/PopupDateFieldConnector.java +++ b/client/src/com/vaadin/client/ui/datefield/PopupDateFieldConnector.java @@ -119,6 +119,8 @@ public class PopupDateFieldConnector extends TextualDateConnector { + "-button-readonly"); } + getWidget().setDescriptionForAssistiveDevices( + getState().descriptionForAssistiveDevices); getWidget().calendarToggle.setEnabled(true); } @@ -136,6 +138,8 @@ public class PopupDateFieldConnector extends TextualDateConnector { public void onStateChanged(StateChangeEvent stateChangeEvent) { super.onStateChanged(stateChangeEvent); getWidget().setTextFieldEnabled(getState().textFieldEnabled); + getWidget().setRangeStart(getState().rangeStart); + getWidget().setRangeEnd(getState().rangeEnd); } @Override diff --git a/client/src/com/vaadin/client/ui/form/FormConnector.java b/client/src/com/vaadin/client/ui/form/FormConnector.java index 22277b6974..acd0e917fc 100644 --- a/client/src/com/vaadin/client/ui/form/FormConnector.java +++ b/client/src/com/vaadin/client/ui/form/FormConnector.java @@ -231,4 +231,9 @@ public class FormConnector extends AbstractComponentContainerConnector // as a part of the actual layout return null; } + + @Override + public boolean hasTooltip() { + return false; + } } diff --git a/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java b/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java index 1a952959f3..c65f689f7a 100644 --- a/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/formlayout/FormLayoutConnector.java @@ -141,4 +141,13 @@ public class FormLayoutConnector extends AbstractLayoutConnector { return info; } + @Override + public boolean hasTooltip() { + /* + * Tooltips are fetched from child connectors -> there's no quick way of + * checking whether there might a tooltip hiding somewhere + */ + return true; + } + } diff --git a/client/src/com/vaadin/client/ui/menubar/MenuBarConnector.java b/client/src/com/vaadin/client/ui/menubar/MenuBarConnector.java index d8ca73a401..3e22ebb05b 100644 --- a/client/src/com/vaadin/client/ui/menubar/MenuBarConnector.java +++ b/client/src/com/vaadin/client/ui/menubar/MenuBarConnector.java @@ -209,4 +209,14 @@ public class MenuBarConnector extends AbstractComponentConnector implements return info; } + + @Override + public boolean hasTooltip() { + /* + * Item tooltips are not processed until updateFromUIDL, so we can't be + * sure that there are no tooltips during onStateChange when this method + * is used. + */ + return true; + } } diff --git a/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java b/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java index 50de8e0936..cb6ad25e97 100644 --- a/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java +++ b/client/src/com/vaadin/client/ui/orderedlayout/AbstractOrderedLayoutConnector.java @@ -31,6 +31,7 @@ import com.vaadin.client.communication.StateChangeEvent.StateChangeHandler; import com.vaadin.client.ui.AbstractFieldConnector; import com.vaadin.client.ui.AbstractLayoutConnector; import com.vaadin.client.ui.LayoutClickEventHandler; +import com.vaadin.client.ui.aria.AriaHelper; import com.vaadin.client.ui.layout.ElementResizeEvent; import com.vaadin.client.ui.layout.ElementResizeListener; import com.vaadin.shared.AbstractFieldState; @@ -258,6 +259,10 @@ public abstract class AbstractOrderedLayoutConnector extends slot.setCaption(caption, iconUrlString, styles, error, showError, required, enabled); + AriaHelper.handleInputRequired(child.getWidget(), required); + AriaHelper.handleInputInvalid(child.getWidget(), showError); + AriaHelper.bindCaption(child.getWidget(), slot.getCaptionElement()); + if (slot.hasCaption()) { CaptionPosition pos = slot.getCaptionPosition(); getLayoutManager().addElementResizeListener( diff --git a/client/src/com/vaadin/client/ui/orderedlayout/Slot.java b/client/src/com/vaadin/client/ui/orderedlayout/Slot.java index 5fab131c65..00ff5bbc5a 100644 --- a/client/src/com/vaadin/client/ui/orderedlayout/Slot.java +++ b/client/src/com/vaadin/client/ui/orderedlayout/Slot.java @@ -18,6 +18,7 @@ package com.vaadin.client.ui.orderedlayout; import java.util.List; +import com.google.gwt.aria.client.Roles; import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; @@ -507,6 +508,11 @@ public final class Slot extends SimplePanel { // character) requiredIcon.setInnerHTML("*"); requiredIcon.setClassName("v-required-field-indicator"); + + // The star should not be read by the screen reader, as it is + // purely visual. Required state is set at the element level for + // the screen reader. + Roles.getTextboxRole().setAriaHiddenState(requiredIcon, true); } caption.appendChild(requiredIcon); } else if (requiredIcon != null) { diff --git a/client/src/com/vaadin/client/ui/table/TableConnector.java b/client/src/com/vaadin/client/ui/table/TableConnector.java index 6e50bab9ab..36587ffe4d 100644 --- a/client/src/com/vaadin/client/ui/table/TableConnector.java +++ b/client/src/com/vaadin/client/ui/table/TableConnector.java @@ -395,6 +395,16 @@ public class TableConnector extends AbstractHasComponentsConnector implements } @Override + public boolean hasTooltip() { + /* + * Tooltips for individual rows and cells are not processed until + * updateFromUIDL, so we can't be sure that there are no tooltips during + * onStateChange when this method is used. + */ + return true; + } + + @Override public void onConnectorHierarchyChange( ConnectorHierarchyChangeEvent connectorHierarchyChangeEvent) { // TODO Move code from updateFromUIDL to this method diff --git a/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java b/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java index f1ad5e792a..04a514738d 100644 --- a/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java +++ b/client/src/com/vaadin/client/ui/tabsheet/TabsheetConnector.java @@ -138,6 +138,16 @@ public class TabsheetConnector extends TabsheetBaseConnector implements } @Override + public boolean hasTooltip() { + /* + * Tab tooltips are not processed until updateFromUIDL, so we can't be + * sure that there are no tooltips during onStateChange when this method + * is used. + */ + return true; + } + + @Override public void onConnectorHierarchyChange( ConnectorHierarchyChangeEvent connectorHierarchyChangeEvent) { // TODO Move code from updateFromUIDL to this method diff --git a/client/src/com/vaadin/client/ui/tree/TreeConnector.java b/client/src/com/vaadin/client/ui/tree/TreeConnector.java index d6d1cef8cc..ef016c31b7 100644 --- a/client/src/com/vaadin/client/ui/tree/TreeConnector.java +++ b/client/src/com/vaadin/client/ui/tree/TreeConnector.java @@ -19,6 +19,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import com.google.gwt.aria.client.Roles; import com.google.gwt.dom.client.Element; import com.vaadin.client.ApplicationConnection; import com.vaadin.client.BrowserInfo; @@ -26,6 +27,7 @@ import com.vaadin.client.Paintable; import com.vaadin.client.TooltipInfo; import com.vaadin.client.UIDL; import com.vaadin.client.Util; +import com.vaadin.client.VConsole; import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractComponentConnector; import com.vaadin.client.ui.VTree; @@ -93,7 +95,7 @@ public class TreeConnector extends AbstractComponentConnector implements } childTree = getWidget().new TreeNode(); getConnection().getVTooltip().connectHandlersToWidget(childTree); - updateNodeFromUIDL(childTree, childUidl); + updateNodeFromUIDL(childTree, childUidl, 1); getWidget().body.add(childTree); childTree.addStyleDependentName("root"); childTree.childNodeContainer.addStyleDependentName("root"); @@ -108,6 +110,9 @@ public class TreeConnector extends AbstractComponentConnector implements getWidget().isMultiselect = "multi".equals(selectMode); if (getWidget().isMultiselect) { + Roles.getTreeRole().setAriaMultiselectableProperty( + getWidget().getElement(), true); + if (BrowserInfo.get().isTouchDevice()) { // Always use the simple mode for touch devices that do not have // shift/ctrl keys (#8595) @@ -116,6 +121,9 @@ public class TreeConnector extends AbstractComponentConnector implements getWidget().multiSelectMode = MultiSelectMode.valueOf(uidl .getStringAttribute("multiselectmode")); } + } else { + Roles.getTreeRole().setAriaMultiselectableProperty( + getWidget().getElement(), false); } getWidget().selectedIds = uidl.getStringArrayVariableAsSet("selected"); @@ -169,7 +177,18 @@ public class TreeConnector extends AbstractComponentConnector implements // expanding node happened server side rootNode.setState(true, false); } - renderChildNodes(rootNode, (Iterator) uidl.getChildIterator()); + String levelPropertyString = Roles.getTreeitemRole() + .getAriaLevelProperty(rootNode.getElement()); + int levelProperty; + try { + levelProperty = Integer.valueOf(levelPropertyString); + } catch (NumberFormatException e) { + levelProperty = 1; + VConsole.error(e); + } + + renderChildNodes(rootNode, (Iterator) uidl.getChildIterator(), + levelProperty + 1); } } @@ -196,7 +215,10 @@ public class TreeConnector extends AbstractComponentConnector implements } - public void updateNodeFromUIDL(TreeNode treeNode, UIDL uidl) { + public void updateNodeFromUIDL(TreeNode treeNode, UIDL uidl, int level) { + Roles.getTreeitemRole().setAriaLevelProperty(treeNode.getElement(), + level); + String nodeKey = uidl.getStringAttribute("key"); treeNode.setText(uidl .getStringAttribute(TreeConstants.ATTRIBUTE_NODE_CAPTION)); @@ -212,7 +234,8 @@ public class TreeConnector extends AbstractComponentConnector implements if (uidl.getChildCount() == 0) { treeNode.childNodeContainer.setVisible(false); } else { - renderChildNodes(treeNode, (Iterator) uidl.getChildIterator()); + renderChildNodes(treeNode, (Iterator) uidl.getChildIterator(), + level + 1); treeNode.childrenLoaded = true; } } else { @@ -239,11 +262,14 @@ public class TreeConnector extends AbstractComponentConnector implements getWidget().selectedIds.add(nodeKey); } - treeNode.setIcon(uidl - .getStringAttribute(TreeConstants.ATTRIBUTE_NODE_ICON)); + String iconUrl = uidl + .getStringAttribute(TreeConstants.ATTRIBUTE_NODE_ICON); + String iconAltText = uidl + .getStringAttribute(TreeConstants.ATTRIBUTE_NODE_ICON_ALT); + treeNode.setIcon(iconUrl, iconAltText); } - void renderChildNodes(TreeNode containerNode, Iterator<UIDL> i) { + void renderChildNodes(TreeNode containerNode, Iterator<UIDL> i, int level) { containerNode.childNodeContainer.clear(); containerNode.childNodeContainer.setVisible(true); while (i.hasNext()) { @@ -256,7 +282,7 @@ public class TreeConnector extends AbstractComponentConnector implements } final TreeNode childTree = getWidget().new TreeNode(); getConnection().getVTooltip().connectHandlersToWidget(childTree); - updateNodeFromUIDL(childTree, childUidl); + updateNodeFromUIDL(childTree, childUidl, level); containerNode.childNodeContainer.add(childTree); if (!i.hasNext()) { childTree @@ -306,4 +332,14 @@ public class TreeConnector extends AbstractComponentConnector implements return info; } + @Override + public boolean hasTooltip() { + /* + * Item tooltips are not processed until updateFromUIDL, so we can't be + * sure that there are no tooltips during onStateChange when this method + * is used. + */ + return true; + } + } diff --git a/client/src/com/vaadin/client/ui/ui/UIConnector.java b/client/src/com/vaadin/client/ui/ui/UIConnector.java index 593aa0d793..643d687f1d 100644 --- a/client/src/com/vaadin/client/ui/ui/UIConnector.java +++ b/client/src/com/vaadin/client/ui/ui/UIConnector.java @@ -21,9 +21,13 @@ import java.util.List; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.HeadElement; +import com.google.gwt.dom.client.LinkElement; import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Position; +import com.google.gwt.dom.client.StyleInjector; import com.google.gwt.event.dom.client.ScrollEvent; import com.google.gwt.event.dom.client.ScrollHandler; import com.google.gwt.event.logical.shared.ResizeEvent; @@ -34,6 +38,7 @@ import com.google.gwt.user.client.DOM; import com.google.gwt.user.client.Element; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.History; +import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.RootPanel; import com.google.gwt.user.client.ui.Widget; @@ -45,7 +50,6 @@ import com.vaadin.client.ConnectorHierarchyChangeEvent; import com.vaadin.client.ConnectorMap; import com.vaadin.client.Focusable; import com.vaadin.client.Paintable; -import com.vaadin.client.TooltipInfo; import com.vaadin.client.UIDL; import com.vaadin.client.VConsole; import com.vaadin.client.communication.StateChangeEvent; @@ -57,12 +61,16 @@ import com.vaadin.client.ui.VNotification; import com.vaadin.client.ui.VUI; import com.vaadin.client.ui.layout.MayScrollChildren; import com.vaadin.client.ui.window.WindowConnector; +import com.vaadin.server.Page.Styles; import com.vaadin.shared.MouseEventDetails; +import com.vaadin.shared.communication.MethodInvocation; import com.vaadin.shared.ui.ComponentStateUtil; import com.vaadin.shared.ui.Connect; import com.vaadin.shared.ui.Connect.LoadStyle; import com.vaadin.shared.ui.ui.PageClientRpc; +import com.vaadin.shared.ui.ui.PageState; import com.vaadin.shared.ui.ui.ScrollClientRpc; +import com.vaadin.shared.ui.ui.UIClientRpc; import com.vaadin.shared.ui.ui.UIConstants; import com.vaadin.shared.ui.ui.UIServerRpc; import com.vaadin.shared.ui.ui.UIState; @@ -91,6 +99,12 @@ public class UIConnector extends AbstractSingleComponentContainerConnector public void setTitle(String title) { com.google.gwt.user.client.Window.setTitle(title); } + + @Override + public void reload() { + Window.Location.reload(); + + } }); registerRpc(ScrollClientRpc.class, new ScrollClientRpc() { @Override @@ -103,13 +117,29 @@ public class UIConnector extends AbstractSingleComponentContainerConnector getWidget().getElement().setScrollLeft(scrollLeft); } }); + registerRpc(UIClientRpc.class, new UIClientRpc() { + @Override + public void uiClosed(final boolean sessionExpired) { + Scheduler.get().scheduleDeferred(new ScheduledCommand() { + @Override + public void execute() { + if (sessionExpired) { + getConnection().showSessionExpiredError(null); + } else { + getState().enabled = false; + updateEnabledState(getState().enabled); + } + } + }); + } + }); getWidget().addResizeHandler(new ResizeHandler() { @Override public void onResize(ResizeEvent event) { getRpcProxy(UIServerRpc.class).resize(event.getHeight(), event.getWidth(), Window.getClientWidth(), Window.getClientHeight()); - if (getState().immediate) { + if (getState().immediate || getPageState().hasResizeListeners) { getConnection().sendPendingVariableChanges(); } } @@ -257,6 +287,8 @@ public class UIConnector extends AbstractSingleComponentContainerConnector final UIDL notification = (UIDL) it.next(); VNotification.showNotification(client, notification); } + } else if (tag == "css-injections") { + injectCSS(childUidl); } } @@ -326,6 +358,47 @@ public class UIConnector extends AbstractSingleComponentContainerConnector getWidget().rendering = false; } + /** + * Reads CSS strings and resources injected by {@link Styles#inject} from + * the UIDL stream. + * + * @param uidl + * The uidl which contains "css-resource" and "css-string" tags + */ + private void injectCSS(UIDL uidl) { + + final HeadElement head = HeadElement.as(Document.get() + .getElementsByTagName(HeadElement.TAG).getItem(0)); + + /* + * Search the UIDL stream for CSS resources and strings to be injected. + */ + for (Iterator<?> it = uidl.getChildIterator(); it.hasNext();) { + UIDL cssInjectionsUidl = (UIDL) it.next(); + + // Check if we have resources to inject + if (cssInjectionsUidl.getTag().equals("css-resource")) { + String url = getWidget().connection + .translateVaadinUri(cssInjectionsUidl + .getStringAttribute("url")); + LinkElement link = LinkElement.as(DOM + .createElement(LinkElement.TAG)); + link.setRel("stylesheet"); + link.setHref(url); + link.setType("text/css"); + head.appendChild(link); + + // Check if we have CSS string to inject + } else if (cssInjectionsUidl.getTag().equals("css-string")) { + for (Iterator<?> it2 = cssInjectionsUidl.getChildIterator(); it2 + .hasNext();) { + StyleInjector.injectAtEnd((String) it2.next()); + StyleInjector.flush(); + } + } + } + } + public void init(String rootPanelId, ApplicationConnection applicationConnection) { DOM.sinkEvents(getWidget().getElement(), Event.ONKEYDOWN @@ -339,8 +412,6 @@ public class UIConnector extends AbstractSingleComponentContainerConnector String themeName = applicationConnection.getConfiguration() .getThemeName(); - // Remove chars that are not suitable for style names - themeName = themeName.replaceAll("[^a-zA-Z0-9]", ""); root.addStyleName(themeName); root.add(getWidget()); @@ -368,6 +439,8 @@ public class UIConnector extends AbstractSingleComponentContainerConnector }; + private Timer pollTimer = null; + @Override public void updateCaption(ComponentConnector component) { // NOP The main view never draws caption for its layout @@ -432,6 +505,23 @@ public class UIConnector extends AbstractSingleComponentContainerConnector return (UIState) super.getState(); } + /** + * Returns the state of the Page associated with the UI. + * <p> + * Note that state is considered an internal part of the connector. You + * should not rely on the state object outside of the connector who owns it. + * If you depend on the state of other connectors you should use their + * public API instead of their state object directly. The page state might + * not be an independent state object but can be embedded in UI state. + * </p> + * + * @since 7.1 + * @return state object of the page + */ + public PageState getPageState() { + return getState().pageState; + } + @Override public void onConnectorHierarchyChange(ConnectorHierarchyChangeEvent event) { ComponentConnector oldChild = null; @@ -477,13 +567,13 @@ public class UIConnector extends AbstractSingleComponentContainerConnector } @Override - public TooltipInfo getTooltipInfo(com.google.gwt.dom.client.Element element) { + public boolean hasTooltip() { /* - * Override method to make AbstractComponentConnector.hasTooltip() - * return true so there's a top level handler that takes care of hiding - * tooltips whenever the mouse is moved somewhere else. + * Always return true so there's always top level tooltip handler that + * takes care of hiding tooltips whenever the mouse is moved somewhere + * else. */ - return super.getTooltipInfo(element); + return true; } /** @@ -505,4 +595,74 @@ public class UIConnector extends AbstractSingleComponentContainerConnector } }); } + + @Override + public void onStateChanged(StateChangeEvent stateChangeEvent) { + super.onStateChanged(stateChangeEvent); + if (stateChangeEvent.hasPropertyChanged("tooltipConfiguration")) { + getConnection().getVTooltip().setCloseTimeout( + getState().tooltipConfiguration.closeTimeout); + getConnection().getVTooltip().setOpenDelay( + getState().tooltipConfiguration.openDelay); + getConnection().getVTooltip().setQuickOpenDelay( + getState().tooltipConfiguration.quickOpenDelay); + getConnection().getVTooltip().setQuickOpenTimeout( + getState().tooltipConfiguration.quickOpenTimeout); + getConnection().getVTooltip().setMaxWidth( + getState().tooltipConfiguration.maxWidth); + } + + if (stateChangeEvent + .hasPropertyChanged("loadingIndicatorConfiguration")) { + getConnection().getLoadingIndicator().setFirstDelay( + getState().loadingIndicatorConfiguration.firstDelay); + getConnection().getLoadingIndicator().setSecondDelay( + getState().loadingIndicatorConfiguration.secondDelay); + getConnection().getLoadingIndicator().setThirdDelay( + getState().loadingIndicatorConfiguration.thirdDelay); + } + + if (stateChangeEvent.hasPropertyChanged("pollInterval")) { + configurePolling(); + } + + if (stateChangeEvent.hasPropertyChanged("pushMode")) { + getConnection().setPushEnabled(getState().pushMode.isEnabled()); + } + + if (stateChangeEvent.hasPropertyChanged("overlayContainerLabel")) { + getConnection().setOverlayContainerLabel( + getState().overlayContainerLabel); + } + } + + private void configurePolling() { + if (pollTimer != null) { + pollTimer.cancel(); + pollTimer = null; + } + if (getState().pollInterval >= 0) { + pollTimer = new Timer() { + @Override + public void run() { + /* + * Verify that polling has not recently been canceled. This + * is needed because Timer.cancel() does not always work + * properly in IE 8 until GWT issue 8101 has been fixed. + */ + if (pollTimer != null) { + getRpcProxy(UIServerRpc.class).poll(); + // Send changes even though poll is @Delayed + getConnection().sendPendingVariableChanges(); + } + } + }; + pollTimer.scheduleRepeating(getState().pollInterval); + } else { + // Ensure no more polls are sent as polling has been disabled + getConnection().removePendingInvocations( + new MethodInvocation(getConnectorId(), UIServerRpc.class + .getName(), "poll")); + } + } } diff --git a/client/src/com/vaadin/client/ui/window/WindowConnector.java b/client/src/com/vaadin/client/ui/window/WindowConnector.java index 8cfc25a9dc..90311e30ad 100644 --- a/client/src/com/vaadin/client/ui/window/WindowConnector.java +++ b/client/src/com/vaadin/client/ui/window/WindowConnector.java @@ -20,7 +20,10 @@ import com.google.gwt.dom.client.NativeEvent; import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Position; import com.google.gwt.dom.client.Style.Unit; -import com.google.gwt.user.client.DOM; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.dom.client.DoubleClickEvent; +import com.google.gwt.event.dom.client.DoubleClickHandler; import com.google.gwt.user.client.Event; import com.google.gwt.user.client.Window; import com.vaadin.client.ApplicationConnection; @@ -31,6 +34,7 @@ import com.vaadin.client.LayoutManager; import com.vaadin.client.Paintable; import com.vaadin.client.UIDL; import com.vaadin.client.Util; +import com.vaadin.client.communication.StateChangeEvent; import com.vaadin.client.ui.AbstractSingleComponentContainerConnector; import com.vaadin.client.ui.ClickEventHandler; import com.vaadin.client.ui.PostLayoutListener; @@ -41,6 +45,7 @@ import com.vaadin.client.ui.VWindow; import com.vaadin.client.ui.layout.MayScrollChildren; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.ui.Connect; +import com.vaadin.shared.ui.window.WindowMode; import com.vaadin.shared.ui.window.WindowServerRpc; import com.vaadin.shared.ui.window.WindowState; @@ -57,7 +62,32 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector } }; - boolean minWidthChecked = false; + abstract class WindowEventHandler implements ClickHandler, + DoubleClickHandler { + } + + private WindowEventHandler maximizeRestoreClickHandler = new WindowEventHandler() { + + @Override + public void onClick(ClickEvent event) { + final Element target = event.getNativeEvent().getEventTarget() + .cast(); + if (target == getWidget().maximizeRestoreBox) { + // Click on maximize/restore box + onMaximizeRestore(); + } + } + + @Override + public void onDoubleClick(DoubleClickEvent event) { + final Element target = event.getNativeEvent().getEventTarget() + .cast(); + if (getWidget().header.isOrHasChild(target)) { + // Double click on header + onMaximizeRestore(); + } + } + }; @Override public boolean delegateCaptionHandling() { @@ -68,12 +98,18 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector protected void init() { super.init(); + VWindow window = getWidget(); + getLayoutManager().registerDependency(this, - getWidget().contentPanel.getElement()); - getLayoutManager().registerDependency(this, getWidget().header); - getLayoutManager().registerDependency(this, getWidget().footer); + window.contentPanel.getElement()); + getLayoutManager().registerDependency(this, window.header); + getLayoutManager().registerDependency(this, window.footer); - getWidget().setOwner(getConnection().getUIConnector().getWidget()); + window.addHandler(maximizeRestoreClickHandler, ClickEvent.getType()); + window.addHandler(maximizeRestoreClickHandler, + DoubleClickEvent.getType()); + + window.setOwner(getConnection().getUIConnector().getWidget()); } @Override @@ -87,109 +123,46 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector @Override public void updateFromUIDL(UIDL uidl, ApplicationConnection client) { - getWidget().id = getConnectorId(); - getWidget().client = client; - - // Workaround needed for Testing Tools (GWT generates window DOM - // slightly different in different browsers). - DOM.setElementProperty(getWidget().closeBox, "id", getConnectorId() - + "_window_close"); - if (isRealUpdate(uidl)) { - if (getState().modal != getWidget().vaadinModality) { - getWidget().setVaadinModality(!getWidget().vaadinModality); - } - if (!getWidget().isAttached()) { - getWidget().setVisible(false); // hide until - // possible centering - getWidget().show(); - } - if (getState().resizable != getWidget().resizable) { - getWidget().setResizable(getState().resizable); - } - getWidget().resizeLazy = getState().resizeLazy; + VWindow window = getWidget(); + String connectorId = getConnectorId(); - getWidget().setDraggable(getState().draggable); + window.id = getConnectorId(); + window.client = client; - // Caption must be set before required header size is measured. If - // the caption attribute is missing the caption should be cleared. - String iconURL = null; - if (getIcon() != null) { - iconURL = getIcon(); - } - getWidget().setCaption(getState().caption, iconURL); - } + // Workaround needed for Testing Tools (GWT generates window DOM + // slightly different in different browsers). + window.closeBox.setId(connectorId + "_window_close"); + window.maximizeRestoreBox + .setId(connectorId + "_window_maximizerestore"); - getWidget().visibilityChangesDisabled = true; + window.visibilityChangesDisabled = true; if (!isRealUpdate(uidl)) { return; } - getWidget().visibilityChangesDisabled = false; - - clickEventHandler.handleEventHandlerRegistration(); - - getWidget().immediate = getState().immediate; - - getWidget().setClosable(!isReadOnly()); - - // Initialize the position form UIDL - int positionx = getState().positionX; - int positiony = getState().positionY; - if (positionx >= 0 || positiony >= 0) { - if (positionx < 0) { - positionx = 0; - } - if (positiony < 0) { - positiony = 0; - } - getWidget().setPopupPosition(positionx, positiony); - } - - int childIndex = 0; + window.visibilityChangesDisabled = false; // we may have actions for (int i = 0; i < uidl.getChildCount(); i++) { UIDL childUidl = uidl.getChildUIDL(i); if (childUidl.getTag().equals("actions")) { - if (getWidget().shortcutHandler == null) { - getWidget().shortcutHandler = new ShortcutActionHandler( - getConnectorId(), client); + if (window.shortcutHandler == null) { + window.shortcutHandler = new ShortcutActionHandler( + connectorId, client); } - getWidget().shortcutHandler.updateActionMap(childUidl); + window.shortcutHandler.updateActionMap(childUidl); } } - // setting scrollposition must happen after children is rendered - getWidget().contentPanel.setScrollPosition(getState().scrollTop); - getWidget().contentPanel - .setHorizontalScrollPosition(getState().scrollLeft); - - // Center this window on screen if requested - // This had to be here because we might not know the content size before - // everything is painted into the window - - // centered is this is unset on move/resize - getWidget().centered = getState().centered; - getWidget().setVisible(true); - - // ensure window is not larger than browser window - if (getWidget().getOffsetWidth() > Window.getClientWidth()) { - getWidget().setWidth(Window.getClientWidth() + "px"); - } - if (getWidget().getOffsetHeight() > Window.getClientHeight()) { - getWidget().setHeight(Window.getClientHeight() + "px"); - } - if (uidl.hasAttribute("bringToFront")) { /* * Focus as a side-effect. Will be overridden by * ApplicationConnection if another component was focused by the * server side. */ - getWidget().contentPanel.focus(); - getWidget().bringToFrontSequence = uidl - .getIntAttribute("bringToFront"); + window.contentPanel.focus(); + window.bringToFrontSequence = uidl.getIntAttribute("bringToFront"); VWindow.deferOrdering(); } } @@ -224,26 +197,6 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector boolean hasContent = (content != null); Element contentElement = window.contentPanel.getElement(); - if (!minWidthChecked) { - boolean needsMinWidth = !isUndefinedWidth() || !hasContent - || content.isRelativeWidth(); - int minWidth = window.getMinWidth(); - if (needsMinWidth && lm.getInnerWidth(contentElement) < minWidth) { - minWidthChecked = true; - // Use minimum width if less than a certain size - window.setWidth(minWidth + "px"); - } - minWidthChecked = true; - } - - boolean needsMinHeight = !isUndefinedHeight() || !hasContent - || content.isRelativeHeight(); - int minHeight = window.getMinHeight(); - if (needsMinHeight && lm.getInnerHeight(contentElement) < minHeight) { - // Use minimum height if less than a certain size - window.setHeight(minHeight + "px"); - } - Style contentStyle = window.contents.getStyle(); int headerHeight = lm.getOuterHeight(window.header); @@ -291,9 +244,8 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector @Override public void postLayout() { - minWidthChecked = false; VWindow window = getWidget(); - if (window.centered) { + if (window.centered && getState().windowMode != WindowMode.MAXIMIZED) { window.center(); } window.positionOrSizeUpdated(); @@ -304,6 +256,126 @@ public class WindowConnector extends AbstractSingleComponentContainerConnector return (WindowState) super.getState(); } + @Override + public void onStateChanged(StateChangeEvent stateChangeEvent) { + super.onStateChanged(stateChangeEvent); + + VWindow window = getWidget(); + WindowState state = getState(); + + if (state.modal != window.vaadinModality) { + window.setVaadinModality(!window.vaadinModality); + } + if (!window.isAttached()) { + window.setVisible(false); // hide until possible centering + window.show(); + } + boolean resizeable = state.resizable + && state.windowMode == WindowMode.NORMAL; + window.setResizable(resizeable); + + window.resizeLazy = state.resizeLazy; + + window.setDraggable(state.draggable + && state.windowMode == WindowMode.NORMAL); + + window.updateMaximizeRestoreClassName(state.resizable, state.windowMode); + + // Caption must be set before required header size is measured. If + // the caption attribute is missing the caption should be cleared. + String iconURL = null; + if (getIcon() != null) { + iconURL = getIcon(); + } + window.setCaption(state.caption, iconURL); + + clickEventHandler.handleEventHandlerRegistration(); + + window.immediate = state.immediate; + + window.setClosable(!isReadOnly()); + // initialize position from state + updateWindowPosition(); + + // setting scrollposition must happen after children is rendered + window.contentPanel.setScrollPosition(state.scrollTop); + window.contentPanel.setHorizontalScrollPosition(state.scrollLeft); + + // Center this window on screen if requested + // This had to be here because we might not know the content size before + // everything is painted into the window + + // centered is this is unset on move/resize + window.centered = state.centered; + window.setVisible(true); + + // ensure window is not larger than browser window + if (window.getOffsetWidth() > Window.getClientWidth()) { + window.setWidth(Window.getClientWidth() + "px"); + } + if (window.getOffsetHeight() > Window.getClientHeight()) { + window.setHeight(Window.getClientHeight() + "px"); + } + } + + // Need to override default because of window mode + @Override + protected void updateComponentSize() { + if (getState().windowMode == WindowMode.NORMAL) { + super.updateComponentSize(); + } else if (getState().windowMode == WindowMode.MAXIMIZED) { + super.updateComponentSize("100%", "100%"); + } + } + + protected void updateWindowPosition() { + VWindow window = getWidget(); + WindowState state = getState(); + if (state.windowMode == WindowMode.NORMAL) { + // if centered, position handled in postLayout() + if (!state.centered + && (state.positionX >= 0 || state.positionY >= 0)) { + // If both positions are negative, then + // setWindowOrderAndPosition has already taken care of + // positioning the window so it stacks with other windows + window.setPopupPosition(state.positionX, state.positionY); + } + } else if (state.windowMode == WindowMode.MAXIMIZED) { + window.setPopupPositionNoUpdate(0, 0); + window.bringToFront(); + } + } + + protected void updateWindowMode() { + VWindow window = getWidget(); + WindowState state = getState(); + + // update draggable on widget + window.setDraggable(state.draggable + && state.windowMode == WindowMode.NORMAL); + // update resizable on widget + window.setResizable(state.resizable + && state.windowMode == WindowMode.NORMAL); + updateComponentSize(); + updateWindowPosition(); + window.updateMaximizeRestoreClassName(state.resizable, state.windowMode); + window.updateContentsSize(); + } + + protected void onMaximizeRestore() { + WindowState state = getState(); + if (state.resizable) { + if (state.windowMode == WindowMode.MAXIMIZED) { + state.windowMode = WindowMode.NORMAL; + } else { + state.windowMode = WindowMode.MAXIMIZED; + } + updateWindowMode(); + getRpcProxy(WindowServerRpc.class).windowModeChanged( + state.windowMode); + } + } + /** * Gives the WindowConnector an order number. As a side effect, moves the * window according to its order number so the windows are stacked. This diff --git a/common.xml b/common.xml index 5b464e5047..d673273a53 100644 --- a/common.xml +++ b/common.xml @@ -6,9 +6,10 @@ </tstamp> <dirname property="vaadin.basedir" file="${ant.file.common}" /> + <property name="gwt.basedir" location="${vaadin.basedir}/../gwt" /> <property file="${vaadin.basedir}/build.properties" /> - <property name="modules.to.publish.to.maven" value="shared,server,client,client-compiler,client-compiled,theme-compiler,themes" /> + <property name="modules.to.publish.to.maven" value="shared,server,client,client-compiler,client-compiled,theme-compiler,themes,push" /> <property name="modules.to.publish.to.download" value="${modules.to.publish.to.maven},all" /> <ivy:settings file="${vaadin.basedir}/ivysettings.xml" /> @@ -292,25 +293,25 @@ <mkdir dir="${classes}" /> </target> - <target name="tests.run" depends="tests.compile"> + <target name="test.run" depends="test.compile"> <fail unless="module.name" message="No module name given" /> <property name="result.dir" location="result" /> <property name="classes" location="${result.dir}/classes" /> - <property name="tests.src" location="${result.dir}/../tests/src" /> - <property name="tests.classes" location="${result.dir}/tests/classes" /> + <property name="test.src" location="${result.dir}/../tests/src" /> + <property name="test.classes" location="${result.dir}/tests/classes" /> <junit printsummary="withOutAndErr" fork="yes"> <formatter usefile="false" type="plain" /> <jvmarg value="-ea" /> - <classpath location="${tests.classes}" /> + <classpath location="${test.classes}" /> <classpath location="${classes}" /> <classpath refid="classpath.compile.custom" /> - <classpath refid="classpath.tests.dependencies" /> + <classpath refid="classpath.test.dependencies" /> <batchtest fork="yes"> - <fileset dir="${tests.src}"> + <fileset dir="${test.src}"> <exclude name="**/Abstract*" /> <exclude name="com/vaadin/tests/data/bean/*" /> <exclude name="com/vaadin/tests/util/*" /> @@ -322,26 +323,26 @@ </junit> </target> - <target name="tests.compile" description="Compiles tests" depends="compile, dependencies.tests"> + <target name="test.compile" description="Compiles tests" depends="compile, dependencies.test"> <fail unless="module.name" message="No module name given" /> <property name="result.dir" location="result" /> <property name="base.dir" location="${result.dir}/.." /> - <property name="tests.src" location="${base.dir}/tests/src" /> - <property name="tests.resources" location="${base.dir}/tests/resources" /> - <property name="tests.classes" location="${result.dir}/tests/classes" /> + <property name="test.src" location="${base.dir}/tests/src" /> + <property name="test.resources" location="${base.dir}/tests/resources" /> + <property name="test.classes" location="${result.dir}/tests/classes" /> <property name="classes" location="${result.dir}/classes" /> - <mkdir dir="${tests.classes}" /> + <mkdir dir="${test.classes}" /> - <javac srcdir="${tests.src}" destdir="${tests.classes}" source="${vaadin.java.version}" target="${vaadin.java.version}" debug="true" encoding="UTF-8" includeantruntime="false"> - <classpath refid="classpath.tests.dependencies" /> + <javac srcdir="${test.src}" destdir="${test.classes}" source="${vaadin.java.version}" target="${vaadin.java.version}" debug="true" encoding="UTF-8" includeantruntime="false"> + <classpath refid="classpath.test.dependencies" /> <classpath location="${classes}" /> - <classpath refid="classpath.tests.custom" /> + <classpath refid="classpath.test.custom" /> </javac> <!-- Copy resources --> - <copy todir="${tests.classes}" failonerror="false"> - <fileset dir="${tests.resources}" /> + <copy todir="${test.classes}" failonerror="false"> + <fileset dir="${test.resources}" /> </copy> </target> @@ -351,9 +352,9 @@ <ivy:cachepath pathid="classpath.compile.dependencies" conf="${conf}" /> </target> - <target name="dependencies.tests" description="Resolves dependencies needed by tests"> - <ivy:resolve resolveid="common" conf="tests" /> - <ivy:cachepath pathid="classpath.tests.dependencies" conf="tests" /> + <target name="dependencies.test" description="Resolves dependencies needed by test"> + <ivy:resolve resolveid="common" conf="test" /> + <ivy:cachepath pathid="classpath.test.dependencies" conf="test" /> </target> <target name="clean"> diff --git a/eclipse/Development Mode (vaadin).launch b/eclipse/Development Mode (vaadin).launch new file mode 100644 index 0000000000..483ce58233 --- /dev/null +++ b/eclipse/Development Mode (vaadin).launch @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication"> +<stringAttribute key="bad_container_name" value="\eclipse"/> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> +<listEntry value="/gwt-dev/core/src/com/google/gwt/dev/DevMode.java"/> +</listAttribute> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> +<listEntry value="1"/> +</listAttribute> +<listAttribute key="org.eclipse.jdt.launching.CLASSPATH"> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry path="3" projectName="vaadin" type="1"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/vaadin/shared/src" path="3" type="2"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/vaadin/client/src" path="3" type="2"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/vaadin/uitest/src" path="3" type="2"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry path="3" projectName="gwt-dev" type="1"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry path="3" projectName="gwt-user" type="1"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="GWT_TOOLS/lib/apache/tapestry-util-text-4.0.2.jar" path="3" type="3"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="GWT_TOOLS/lib/junit/junit-4.8.2.jar" path="3" type="3"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="GWT_TOOLS/lib/tomcat/servlet-api-2.5.jar" path="3" type="3"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="GWT_TOOLS/lib/javax/validation/validation-api-1.0.0.GA.jar" path="3" type="3"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="GWT_TOOLS/lib/javax/validation/validation-api-1.0.0.GA-sources.jar" path="3" type="3"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/gwt-dev/core/src" path="3" type="2"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/gwt-dev/core/super" path="3" type="2"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/gwt-user/core/src" path="3" type="2"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry internalArchive="/gwt-user/core/super" path="3" type="2"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=vaadin&amp;ivyXmlPath=client%2Fivy.xml&amp;confs=ide&amp;ivySettingsPath=%24%7Bworkspace_loc%3Avaadin%2Fivysettings.xml%7D&amp;loadSettingsOnDemand=false&amp;propertyFiles=" javaProject="vaadin" path="3" type="4"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=vaadin&amp;ivyXmlPath=server%2Fivy.xml&amp;confs=ide&amp;ivySettingsPath=%24%7Bworkspace_loc%3Avaadin%2Fivysettings.xml%7D&amp;loadSettingsOnDemand=false&amp;propertyFiles=" javaProject="vaadin" path="3" type="4"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=vaadin&amp;ivyXmlPath=shared%2Fivy.xml&amp;confs=ide&amp;ivySettingsPath=%24%7Bworkspace_loc%3Avaadin%2Fivysettings.xml%7D&amp;loadSettingsOnDemand=false&amp;propertyFiles=" javaProject="vaadin" path="3" type="4"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=vaadin&amp;ivyXmlPath=client-compiler%2Fivy.xml&amp;confs=ide&amp;ivySettingsPath=%24%7Bworkspace_loc%3Avaadin%2Fivysettings.xml%7D&amp;loadSettingsOnDemand=false&amp;propertyFiles=" javaProject="vaadin" path="3" type="4"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=vaadin&amp;ivyXmlPath=theme-compiler%2Fivy.xml&amp;confs=ide&amp;ivySettingsPath=%24%7Bworkspace_loc%3Avaadin%2Fivysettings.xml%7D&amp;loadSettingsOnDemand=false&amp;propertyFiles=" javaProject="vaadin" path="3" type="4"/> "/> +<listEntry value="<?xml version="1.0" encoding="UTF-8" standalone="no"?> <runtimeClasspathEntry containerPath="org.apache.ivyde.eclipse.cpcontainer.IVYDE_CONTAINER/?project=vaadin&amp;ivyXmlPath=uitest%2Fivy.xml&amp;confs=ide&amp;ivySettingsPath=%24%7Bworkspace_loc%3Avaadin%2Fivysettings.xml%7D&amp;loadSettingsOnDemand=false&amp;propertyFiles=" javaProject="vaadin" path="3" type="4"/> "/> +</listAttribute> +<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/> +<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/> +<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-noserver -war WebContent/VAADIN/widgetsets com.vaadin.terminal.gwt.DefaultWidgetSet -startupUrl http://localhost:8888 -bindAddress 0.0.0.0"/> +<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="vaadin"/> +<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx512M -XX:MaxPermSize=256M"/> +</launchConfiguration> diff --git a/eclipse/Development Server (vaadin).launch b/eclipse/Development Server (vaadin).launch new file mode 100644 index 0000000000..8f57c441ec --- /dev/null +++ b/eclipse/Development Server (vaadin).launch @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<launchConfiguration type="org.eclipse.jdt.launching.localJavaApplication"> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS"> +<listEntry value="/vaadin/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java"/> +</listAttribute> +<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES"> +<listEntry value="1"/> +</listAttribute> +<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.vaadin.launcher.DevelopmentServerLauncher"/> +<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="vaadin"/> +<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea"/> +</launchConfiguration> diff --git a/gwt-files.xml b/gwt-files.xml index 69e6c04e70..cc4b4a1e96 100644 --- a/gwt-files.xml +++ b/gwt-files.xml @@ -3,7 +3,8 @@ <project name="GWT files for Vaadin" basedir="."> <include file="common.xml" as="common" /> - <property name="gwt.lib.dir" location="${vaadin.basedir}/../gwt-libs" /> + <property name="gwt.lib.dir" location="${gwt.basedir}/build/lib" /> + <property name="gwt.eclipse.basedir" location="${gwt.basedir}/eclipse" /> <property name="gwt.user.jar" location="${gwt.lib.dir}/gwt-user.jar" /> <property name="gwt.dev.jar" location="${gwt.lib.dir}/gwt-dev.jar" /> @@ -15,11 +16,6 @@ <available file="${gwt.elemental.jar}" property="gwt.elemental.jar.found" /> <available file="${gwt.codeserver.jar}" property="gwt.codeserver.jar.found" /> - <fail unless="gwt.dev.jar.found" message="Could not find gwt-dev.jar at ${gwt.dev.jar}" /> - <fail unless="gwt.user.jar.found" message="Could not find gwt-user.jar at ${gwt.user.jar}" /> - <fail unless="gwt.elemental.jar.found" message="Could not find gwt-elemental.jar at ${gwt.elemental.jar}" /> - <fail unless="gwt.codeserver.jar.found" message="Could not find gwt-codeserver.jar at ${gtw.codeserver.jar}" /> - <property name="gwt.unpack.dir" location="${vaadin.basedir}/build/gwt" /> <property name="gwt.user.jar.files" location="${gwt.unpack.dir}/gwt-user.jar" /> @@ -28,6 +24,11 @@ <property name="gwt.codeserver.jar.files" location="${gwt.unpack.dir}/gwt-codeserver.jar" /> <target name="unpack.gwt"> + <fail unless="gwt.dev.jar.found" message="Could not find gwt-dev.jar at ${gwt.dev.jar}" /> + <fail unless="gwt.user.jar.found" message="Could not find gwt-user.jar at ${gwt.user.jar}" /> + <fail unless="gwt.elemental.jar.found" message="Could not find gwt-elemental.jar at ${gwt.elemental.jar}" /> + <fail unless="gwt.codeserver.jar.found" message="Could not find gwt-codeserver.jar at ${gwt.codeserver.jar}" /> + <delete dir="${gwt.unpack.dir}" /> <mkdir dir="${gwt.user.jar.files}" /> @@ -57,17 +58,62 @@ <!-- cssparser --> <exclude name="com/steadystate/css/**" /> <!-- Ant & AntLauncher --> - <exclude name="org/apache/tools/**"/> + <exclude name="org/apache/tools/**" /> <!-- Jetty & jetty-util --> - <exclude name="org/mortbay/**"/> + <exclude name="org/mortbay/**" /> <!-- Swing Worker--> - <exclude name="org/jdesktop/swingworker/**"/> - <!-- Apache commons codec & io & lang --> - <exclude name="org/apache/commons/codec/**"/> - <exclude name="org/apache/commons/io/**"/> - <exclude name="org/apache/commons/lang/**"/> + <exclude name="org/jdesktop/swingworker/**" /> + <!-- Apache commons codec & io & lang & collections & logging --> + <exclude name="org/apache/commons/codec/**" /> + <exclude name="org/apache/commons/io/**" /> + <exclude name="org/apache/commons/lang/**" /> + <exclude name="org/apache/commons/collections/**" /> + <exclude name="org/apache/commons/logging/**" /> <!-- apache mime4j --> - <exclude name="org/apache/james/mime4j/**"/> + <exclude name="org/apache/james/mime4j/**" /> + + <!-- client-compiler-deps --> + <exclude name="com/gargoylesoftware/" /> + <exclude name="com/google/common/" /> + <exclude name="com/google/debugging/" /> + <exclude name="com/google/gwt/dev/protobuf/" /> + <exclude name="com/google/gwt/thirdparty/debugging/" /> + <exclude name="com/google/gwt/thirdparty/javascript/" /> + <exclude name="com/google/gwt/thirdparty/mozilla/" /> + <exclude name="com/ibm/" /> + <exclude name="externs.zip" /> + <exclude name="java_cup/" /> + <exclude name="javax/annotation/" /> + <exclude name="net/sourceforge/htmlunit/" /> + <exclude name="org/apache/bcel/" /> + <exclude name="org/apache/html/" /> + <exclude name="org/apache/http/" /> + <exclude name="org/apache/NOTICE" /> + <exclude name="org/apache/regexp/" /> + <exclude name="org/apache/tapestry/" /> + <exclude name="org/apache/wml/" /> + <exclude name="org/apache/xalan/" /> + <exclude name="org/apache/xerces/" /> + <exclude name="org/apache/xml/" /> + <exclude name="org/apache/xmlcommons/" /> + <exclude name="org/apache/xpath/" /> + <exclude name="org/cyberneko/" /> + <exclude name="org/eclipse/" /> + <exclude name="org/kohsuke/" /> + <exclude name="org/w3c/" /> + <exclude name="org/xml/" /> + <exclude name="rhino_ast/" /> + <exclude name="rhinoDiff.txt" /> + <exclude name="trax/" /> + <exclude name="unicode-license.txt" /> + <exclude name="org/json" /> + + <exclude name="license/NOTICE" /> + <exclude name="license/LICENSE.dom-documentation.txt" /> + <exclude name="license/LICENSE.dom-software.txt" /> + <exclude name="license/LICENSE" /> + <exclude name="license/README.dom.txt" /> + <exclude name="license/README.sax.txt" /> <!-- Overridden in Vaadin --> <exclude name="com/google/gwt/dev/About.properties" /> @@ -105,7 +151,7 @@ <exclude name="com/google/gwt/*/shared/**" /> <exclude name="com/google/gwt/*/*/shared/**" /> <exclude name="com/google/web/bindery/*/shared/**" /> - + <!-- Used by the server, in wrong package in GWT --> <exclude name="com/google/gwt/user/client/rpc/IsSerializable.*" /> diff --git a/ivy.xml b/ivy.xml deleted file mode 100644 index f2179255d4..0000000000 --- a/ivy.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!DOCTYPE ivy-module [ - <!ENTITY server SYSTEM "server/ivy.xml"> - <!ENTITY client SYSTEM "client/ivy.xml"> - <!ENTITY clientCompiler SYSTEM "client-compiler/ivy.xml"> - <!ENTITY shared SYSTEM "shared/ivy.xml"> - <!ENTITY uitest SYSTEM "uitest/ivy.xml"> - <!ENTITY themeCompiler SYSTEM "theme-compiler/ivy.xml"> -]> -<ivy-module version="2.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd" - xmlns:m="http://ant.apache.org/ivy/maven"> - - &server; - &client; - &clientCompiler; - &shared; - &uitest; - &themeCompiler; - -</ivy-module>
\ No newline at end of file diff --git a/ivysettings.xml b/ivysettings.xml index e1e2e45b3d..288eae9036 100644 --- a/ivysettings.xml +++ b/ivysettings.xml @@ -17,7 +17,7 @@ </filesystem> <dual name="custom-smartsprites"> <filesystem name="smartsprites-ivy"> - <ivy pattern="${basedir}/ivymodule/[module]-ivy-[revision].xml" /> + <ivy pattern="${ivy.settings.dir}/theme-compiler/ivymodule/[module]-ivy-[revision].xml" /> </filesystem> <url name="smartsprites-artifact"> <artifact @@ -51,6 +51,8 @@ resolver="build-temp" /> <module organisation="com.vaadin" name="vaadin-themes" resolver="build-temp" /> + <module organisation="com.vaadin" name="vaadin-push" + resolver="build-temp" /> </modules> diff --git a/push/build.xml b/push/build.xml new file mode 100644 index 0000000000..95284e6e37 --- /dev/null +++ b/push/build.xml @@ -0,0 +1,74 @@ +<?xml version="1.0"?> + +<project name="vaadin-push" basedir="." default="publish-local" xmlns:ivy="antlib:org.apache.ivy.ant"> + <description> + Meta package which defines dependencies needed for push + </description> + <include file="../build.xml" as="vaadin" /> + <include file="../common.xml" as="common" /> + + <property name="module.name" value="vaadin-push" /> + <property name="module.symbolic" value="com.vaadin.push" /> + <property name="result.dir" location="result" /> + <property name="vaadinPush.js" location="${result.dir}/js/VAADIN/vaadinPush.js" /> + <path id="classpath.compile.custom" /> + + <union id="jar.includes"> + <fileset dir="${result.dir}/js"> + <include name="VAADIN/vaadinPush.js" /> + </fileset> + </union> + + <target name="vaadinPush.js"> + <mkdir dir="${result.dir}/js/VAADIN" /> + <property name="vaadinPush.js.output" location="${result.dir}/js/VAADIN/vaadinPush.js" /> + <property name="vaadinPush.js.combined.output" location="${result.dir}/js/VAADIN/push.combined.js" /> + + <loadfile srcfile="${vaadin.basedir}/WebContent/VAADIN/jquery-1.7.2.js" property="jquery.js.contents" /> + <loadfile srcfile="${vaadin.basedir}/WebContent/VAADIN/jquery.atmosphere.js" property="jquery.atmosphere.js.contents" /> + <loadfile srcfile="${vaadin.basedir}/WebContent/VAADIN/vaadinPush.js.tpl" property="vaadinPush.js.contents"> + <filterchain> + <replacetokens begintoken="@" endtoken="@"> + <token key="jquery.js" value="${jquery.js.contents}" /> + <token key="jquery.atmosphere.js" value="${jquery.atmosphere.js.contents}" /> + </replacetokens> + </filterchain> + </loadfile> + <echo file="${vaadinPush.js.combined.output}">${vaadinPush.js.contents}</echo> + + <!-- Minify --> + <ivy:retrieve organisation="com.yahoo.platform.yui" module="yuicompressor" revision="2.4.7" inline="true" type="jar" pattern="${result.dir}/compressor.jar" /> + <java jar="${result.dir}/compressor.jar" fork="true"> + <arg value="-v"/> + <arg value="-o"/> + <arg file="${vaadinPush.js.output}"/> + <arg file="${vaadinPush.js.combined.output}"/> + </java> + </target> + + <target name="jar" depends="vaadinPush.js"> + <property name="server.osgi.import" value="" /> + <antcall target="common.jar"> + <param name="require-bundle" value="" /> + <param name="import-package" value="${server.osgi.import}" /> + <reference torefid="extra.jar.includes" refid="jar.includes" /> + </antcall> + </target> + + <target name="publish-local" depends="jar"> + <antcall target="common.sources.jar"> + <reference torefid="extra.jar.includes" refid="jar.includes" /> + </antcall> + <antcall target="common.javadoc.jar" /> + <antcall target="common.publish-local" /> + </target> + + <target name="clean"> + <antcall target="common.clean" /> + </target> + <target name="checkstyle"> + </target> + + <target name="test" depends="checkstyle"> + </target> +</project>
\ No newline at end of file diff --git a/push/ivy.xml b/push/ivy.xml new file mode 100644 index 0000000000..153c02e256 --- /dev/null +++ b/push/ivy.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ivy-module version="2.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:noNamespaceSchemaLocation="http://ant.apache.org/ivy/schemas/ivy.xsd" + xmlns:m="http://ant.apache.org/ivy/maven"> + + <info organisation="com.vaadin" module="vaadin-push" + revision="${vaadin.version}" /> + + <configurations> + <conf name="build" /> + <conf name="build-provided" /> + <conf name="ide" visibility="private" /> + <conf name="test" visibility="private" /> + </configurations> + <publications> + <artifact type="jar" ext="jar" /> + <artifact type="source" ext="jar" m:classifier="sources" /> + <artifact type="javadoc" ext="jar" m:classifier="javadoc" /> + <artifact type="pom" ext="pom" /> + </publications> + <dependencies> + + + <!-- API DEPENDENCIES --> + + <!--Servlet API version 2.4 --> + <dependency org="javax.servlet" name="servlet-api" + rev="2.4" conf="build-provided,ide,test -> default" /> + + <!-- Atmosphere --> + <dependency org="org.atmosphere" name="atmosphere-runtime" rev="1.0.12" + conf="build,ide,test -> default"> + </dependency> + </dependencies> + +</ivy-module> diff --git a/push/src/org/atmosphere/cpr/AtmosphereFramework.java b/push/src/org/atmosphere/cpr/AtmosphereFramework.java new file mode 100644 index 0000000000..62f867fc94 --- /dev/null +++ b/push/src/org/atmosphere/cpr/AtmosphereFramework.java @@ -0,0 +1,1779 @@ +/* + * Copyright 2013 Jeanfrancois Arcand + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.atmosphere.cpr; + +import org.atmosphere.cache.UUIDBroadcasterCache; +import org.atmosphere.config.ApplicationConfiguration; +import org.atmosphere.config.AtmosphereHandlerConfig; +import org.atmosphere.config.AtmosphereHandlerProperty; +import org.atmosphere.config.FrameworkConfiguration; +import org.atmosphere.container.BlockingIOCometSupport; +import org.atmosphere.container.Tomcat7BIOSupportWithWebSocket; +import org.atmosphere.di.InjectorProvider; +import org.atmosphere.di.ServletContextHolder; +import org.atmosphere.di.ServletContextProvider; +import org.atmosphere.handler.AbstractReflectorAtmosphereHandler; +import org.atmosphere.handler.ReflectorServletProcessor; +import org.atmosphere.interceptor.AndroidAtmosphereInterceptor; +import org.atmosphere.interceptor.JSONPAtmosphereInterceptor; +import org.atmosphere.interceptor.JavaScriptProtocol; +import org.atmosphere.interceptor.OnDisconnectInterceptor; +import org.atmosphere.interceptor.SSEAtmosphereInterceptor; +import org.atmosphere.util.AtmosphereConfigReader; +import org.atmosphere.util.IntrospectionUtils; +import org.atmosphere.util.Version; +import org.atmosphere.websocket.DefaultWebSocketProcessor; +import org.atmosphere.websocket.WebSocket; +import org.atmosphere.websocket.WebSocketProtocol; +import org.atmosphere.websocket.protocol.SimpleHttpProtocol; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.servlet.Servlet; +import javax.servlet.ServletConfig; +import javax.servlet.ServletContext; +import javax.servlet.ServletException; +import java.io.File; +import java.io.FilenameFilter; +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; + +import static org.atmosphere.cpr.ApplicationConfig.ALLOW_QUERYSTRING_AS_REQUEST; +import static org.atmosphere.cpr.ApplicationConfig.ATMOSPHERE_HANDLER; +import static org.atmosphere.cpr.ApplicationConfig.ATMOSPHERE_HANDLER_MAPPING; +import static org.atmosphere.cpr.ApplicationConfig.ATMOSPHERE_HANDLER_PATH; +import static org.atmosphere.cpr.ApplicationConfig.BROADCASTER_CACHE; +import static org.atmosphere.cpr.ApplicationConfig.BROADCASTER_CLASS; +import static org.atmosphere.cpr.ApplicationConfig.BROADCASTER_FACTORY; +import static org.atmosphere.cpr.ApplicationConfig.BROADCASTER_LIFECYCLE_POLICY; +import static org.atmosphere.cpr.ApplicationConfig.BROADCAST_FILTER_CLASSES; +import static org.atmosphere.cpr.ApplicationConfig.DISABLE_ONSTATE_EVENT; +import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_ATMOSPHERE_XML; +import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_BLOCKING_COMETSUPPORT; +import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_COMET_SUPPORT; +import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_NATIVE_COMETSUPPORT; +import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_SERVLET_MAPPING; +import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_SESSION_SUPPORT; +import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_USE_STREAM; +import static org.atmosphere.cpr.ApplicationConfig.RESUME_AND_KEEPALIVE; +import static org.atmosphere.cpr.ApplicationConfig.SUSPENDED_ATMOSPHERE_RESOURCE_UUID; +import static org.atmosphere.cpr.ApplicationConfig.WEBSOCKET_PROCESSOR; +import static org.atmosphere.cpr.ApplicationConfig.WEBSOCKET_PROTOCOL; +import static org.atmosphere.cpr.ApplicationConfig.WEBSOCKET_SUPPORT; +import static org.atmosphere.cpr.FrameworkConfig.ATMOSPHERE_CONFIG; +import static org.atmosphere.cpr.FrameworkConfig.HAZELCAST_BROADCASTER; +import static org.atmosphere.cpr.FrameworkConfig.JERSEY_BROADCASTER; +import static org.atmosphere.cpr.FrameworkConfig.JERSEY_CONTAINER; +import static org.atmosphere.cpr.FrameworkConfig.JGROUPS_BROADCASTER; +import static org.atmosphere.cpr.FrameworkConfig.JMS_BROADCASTER; +import static org.atmosphere.cpr.FrameworkConfig.REDIS_BROADCASTER; +import static org.atmosphere.cpr.FrameworkConfig.WRITE_HEADERS; +import static org.atmosphere.cpr.FrameworkConfig.XMPP_BROADCASTER; +import static org.atmosphere.cpr.HeaderConfig.ATMOSPHERE_POST_BODY; +import static org.atmosphere.cpr.HeaderConfig.X_ATMOSPHERE_TRACKING_ID; +import static org.atmosphere.websocket.WebSocket.WEBSOCKET_SUSPEND; + +/** + * The {@link AtmosphereFramework} is the entry point for the framework. This class can be used to from Servlet/filter + * to dispatch {@link AtmosphereRequest} and {@link AtmosphereResponse}. The framework can also be configured using + * the setXXX method. The life cycle of this class is + * <blockquote><pre> + * AtmosphereFramework f = new AtmosphereFramework(); + * f.init(); + * f.doCometSupport(AtmosphereRequest, AtmosphereResource); + * f.destroy(); + * </pre></blockquote> + * + * @author Jeanfrancois Arcand + */ +public class AtmosphereFramework implements ServletContextProvider { + public static final String DEFAULT_ATMOSPHERE_CONFIG_PATH = "/META-INF/atmosphere.xml"; + public static final String DEFAULT_LIB_PATH = "/WEB-INF/lib/"; + public static final String MAPPING_REGEX = "[a-zA-Z0-9-&.*=@~;\\?]+"; + + protected static final Logger logger = LoggerFactory.getLogger(AtmosphereFramework.class); + + protected final List<String> broadcasterFilters = new ArrayList<String>(); + protected final List<AsyncSupportListener> asyncSupportListeners = new ArrayList<AsyncSupportListener>(); + protected final ArrayList<String> possibleComponentsCandidate = new ArrayList<String>(); + protected final HashMap<String, String> initParams = new HashMap<String, String>(); + protected final AtmosphereConfig config; + protected final AtomicBoolean isCometSupportConfigured = new AtomicBoolean(false); + protected final boolean isFilter; + protected final Map<String, AtmosphereHandlerWrapper> atmosphereHandlers = new ConcurrentHashMap<String, AtmosphereHandlerWrapper>(); + protected final ConcurrentLinkedQueue<String> broadcasterTypes = new ConcurrentLinkedQueue<String>(); + + protected boolean useNativeImplementation = false; + protected boolean useBlockingImplementation = false; + protected boolean useStreamForFlushingComments = false; + protected AsyncSupport asyncSupport; + protected String broadcasterClassName = DefaultBroadcaster.class.getName(); + protected boolean isCometSupportSpecified = false; + protected boolean isBroadcasterSpecified = false; + protected boolean isSessionSupportSpecified = false; + protected BroadcasterFactory broadcasterFactory; + protected String broadcasterFactoryClassName; + protected String broadcasterCacheClassName; + protected boolean webSocketEnabled = true; + protected String broadcasterLifeCyclePolicy = "NEVER"; + protected String webSocketProtocolClassName = SimpleHttpProtocol.class.getName(); + protected WebSocketProtocol webSocketProtocol; + protected String handlersPath = "/WEB-INF/classes/"; + protected ServletConfig servletConfig; + protected boolean autoDetectHandlers = true; + private boolean hasNewWebSocketProtocol = false; + protected String atmosphereDotXmlPath = DEFAULT_ATMOSPHERE_CONFIG_PATH; + protected final LinkedList<AtmosphereInterceptor> interceptors = new LinkedList<AtmosphereInterceptor>(); + protected boolean scanDone = false; + protected String annotationProcessorClassName = "org.atmosphere.cpr.DefaultAnnotationProcessor"; + protected final List<BroadcasterListener> broadcasterListeners = new ArrayList<BroadcasterListener>(); + protected String webSocketProcessorClassName = DefaultWebSocketProcessor.class.getName(); + protected String libPath = DEFAULT_LIB_PATH; + protected boolean isInit; + protected boolean sharedThreadPools = true; + + public static final class AtmosphereHandlerWrapper { + + public final AtmosphereHandler atmosphereHandler; + public Broadcaster broadcaster; + public String mapping; + public List<AtmosphereInterceptor> interceptors = Collections.<AtmosphereInterceptor>emptyList(); + + public AtmosphereHandlerWrapper(BroadcasterFactory broadcasterFactory, AtmosphereHandler atmosphereHandler, String mapping) { + this.atmosphereHandler = atmosphereHandler; + try { + if (broadcasterFactory != null) { + this.broadcaster = broadcasterFactory.lookup(mapping, true); + } else { + this.mapping = mapping; + } + } catch (Exception t) { + throw new RuntimeException(t); + } + } + + public AtmosphereHandlerWrapper(AtmosphereHandler atmosphereHandler, Broadcaster broadcaster) { + this.atmosphereHandler = atmosphereHandler; + this.broadcaster = broadcaster; + } + + @Override + public String toString() { + return "AtmosphereHandlerWrapper{ atmosphereHandler=" + atmosphereHandler + ", broadcaster=" + + broadcaster + " }"; + } + } + + /** + * Create an AtmosphereFramework. + */ + public AtmosphereFramework() { + this(false, true); + } + + /** + * Create an AtmosphereFramework and initialize it via {@link AtmosphereFramework#init(javax.servlet.ServletConfig)} + */ + public AtmosphereFramework(ServletConfig sc) throws ServletException { + this(false, true); + init(sc); + } + + /** + * Create an AtmosphereFramework. + * + * @param isFilter true if this instance is used as an {@link AtmosphereFilter} + */ + public AtmosphereFramework(boolean isFilter, boolean autoDetectHandlers) { + this.isFilter = isFilter; + this.autoDetectHandlers = autoDetectHandlers; + readSystemProperties(); + populateBroadcasterType(); + config = new AtmosphereConfig(this); + } + + /** + * The order of addition is quite important here. + */ + private void populateBroadcasterType() { + broadcasterTypes.add(HAZELCAST_BROADCASTER); + broadcasterTypes.add(XMPP_BROADCASTER); + broadcasterTypes.add(REDIS_BROADCASTER); + broadcasterTypes.add(JGROUPS_BROADCASTER); + broadcasterTypes.add(JMS_BROADCASTER); + } + + /** + * Add an {@link AtmosphereHandler} serviced by the {@link Servlet} + * This API is exposed to allow embedding an Atmosphere application. + * + * @param mapping The servlet mapping (servlet path) + * @param h implementation of an {@link AtmosphereHandler} + * @param l An attay of {@link AtmosphereInterceptor} + */ + public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h, List<AtmosphereInterceptor> l) { + if (!mapping.startsWith("/")) { + mapping = "/" + mapping; + } + + AtmosphereHandlerWrapper w = new AtmosphereHandlerWrapper(broadcasterFactory, h, mapping); + w.interceptors = l; + addMapping(mapping, w); + + logger.info("Installed AtmosphereHandler {} mapped to context-path: {}", h.getClass().getName(), mapping); + if (l.size() > 0) { + logger.info("Installed AtmosphereInterceptor {} mapped to AtmosphereHandler {}", l, h.getClass().getName()); + } + return this; + } + + /** + * Add an {@link AtmosphereHandler} serviced by the {@link Servlet} + * This API is exposed to allow embedding an Atmosphere application. + * + * @param mapping The servlet mapping (servlet path) + * @param h implementation of an {@link AtmosphereHandler} + */ + public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h) { + addAtmosphereHandler(mapping, h, Collections.<AtmosphereInterceptor>emptyList()); + return this; + } + + private AtmosphereFramework addMapping(String path, AtmosphereHandlerWrapper w) { + // We are using JAXRS mapping algorithm. + if (path.contains("*")) { + path = path.replace("*", MAPPING_REGEX); + } + + if (path.endsWith("/")) { + path = path + MAPPING_REGEX; + } + + InjectorProvider.getInjector().inject(w.atmosphereHandler); + atmosphereHandlers.put(path, w); + return this; + } + + /** + * Add an {@link AtmosphereHandler} serviced by the {@link Servlet} + * This API is exposed to allow embedding an Atmosphere application. + * + * @param mapping The servlet mapping (servlet path) + * @param h implementation of an {@link AtmosphereHandler} + * @param broadcasterId The {@link Broadcaster#getID} value. + * @param l An attay of {@link AtmosphereInterceptor} + */ + public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h, String broadcasterId, List<AtmosphereInterceptor> l) { + if (!mapping.startsWith("/")) { + mapping = "/" + mapping; + } + + AtmosphereHandlerWrapper w = new AtmosphereHandlerWrapper(broadcasterFactory, h, mapping); + w.broadcaster.setID(broadcasterId); + w.interceptors = l; + addMapping(mapping, w); + logger.info("Installed AtmosphereHandler {} mapped to context-path: {}", h.getClass().getName(), mapping); + if (l.size() > 0) { + logger.info("Installed AtmosphereInterceptor {} mapped to AtmosphereHandler {}", l, h.getClass().getName()); + } + return this; + } + + /** + * Add an {@link AtmosphereHandler} serviced by the {@link Servlet} + * This API is exposed to allow embedding an Atmosphere application. + * + * @param mapping The servlet mapping (servlet path) + * @param h implementation of an {@link AtmosphereHandler} + * @param broadcasterId The {@link Broadcaster#getID} value. + */ + public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h, String broadcasterId) { + addAtmosphereHandler(mapping, h, broadcasterId, Collections.<AtmosphereInterceptor>emptyList()); + return this; + } + + /** + * Add an {@link AtmosphereHandler} serviced by the {@link Servlet} + * This API is exposed to allow embedding an Atmosphere application. + * + * @param mapping The servlet mapping (servlet path) + * @param h implementation of an {@link AtmosphereHandler} + * @param broadcaster The {@link Broadcaster} associated with AtmosphereHandler. + * @param l An attay of {@link AtmosphereInterceptor} + */ + public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h, Broadcaster broadcaster, List<AtmosphereInterceptor> l) { + if (!mapping.startsWith("/")) { + mapping = "/" + mapping; + } + + AtmosphereHandlerWrapper w = new AtmosphereHandlerWrapper(h, broadcaster); + w.interceptors = l; + + addMapping(mapping, w); + logger.info("Installed AtmosphereHandler {} mapped to context-path: {}", h.getClass().getName(), mapping); + if (l.size() > 0) { + logger.info("Installed AtmosphereInterceptor {} mapped to AtmosphereHandler {}", l, h.getClass().getName()); + } + return this; + } + + /** + * Add an {@link AtmosphereHandler} serviced by the {@link Servlet} + * This API is exposed to allow embedding an Atmosphere application. + * + * @param mapping The servlet mapping (servlet path) + * @param h implementation of an {@link AtmosphereHandler} + * @param broadcaster The {@link Broadcaster} associated with AtmosphereHandler. + */ + public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h, Broadcaster broadcaster) { + addAtmosphereHandler(mapping, h, broadcaster, Collections.<AtmosphereInterceptor>emptyList()); + return this; + } + + /** + * Remove an {@link AtmosphereHandler} + * + * @param mapping the mapping used when invoking {@link #addAtmosphereHandler(String, AtmosphereHandler)}; + * @return true if removed + */ + public AtmosphereFramework removeAtmosphereHandler(String mapping) { + + if (mapping.endsWith("/")) { + mapping += MAPPING_REGEX; + } + + atmosphereHandlers.remove(mapping); + return this; + } + + /** + * Remove all {@link AtmosphereHandler} + */ + public AtmosphereFramework removeAllAtmosphereHandler() { + atmosphereHandlers.clear(); + return this; + } + + /** + * Remove all init parameters. + */ + public AtmosphereFramework removeAllInitParams() { + initParams.clear(); + return this; + } + + /** + * Add init-param like if they were defined in web.xml + * + * @param name The name + * @param value The value + */ + public AtmosphereFramework addInitParameter(String name, String value) { + initParams.put(name, value); + return this; + } + + protected void readSystemProperties() { + if (System.getProperty(PROPERTY_NATIVE_COMETSUPPORT) != null) { + useNativeImplementation = Boolean + .parseBoolean(System.getProperty(PROPERTY_NATIVE_COMETSUPPORT)); + isCometSupportSpecified = true; + } + + if (System.getProperty(PROPERTY_BLOCKING_COMETSUPPORT) != null) { + useBlockingImplementation = Boolean + .parseBoolean(System.getProperty(PROPERTY_BLOCKING_COMETSUPPORT)); + isCometSupportSpecified = true; + } + atmosphereDotXmlPath = System.getProperty(PROPERTY_ATMOSPHERE_XML, atmosphereDotXmlPath); + + if (System.getProperty(DISABLE_ONSTATE_EVENT) != null) { + initParams.put(DISABLE_ONSTATE_EVENT, System.getProperty(DISABLE_ONSTATE_EVENT)); + } + } + + /** + * Path specific container using their own property. + */ + public void patchContainer() { + System.setProperty("org.apache.catalina.STRICT_SERVLET_COMPLIANCE", "false"); + } + + /** + * Initialize the AtmosphereFramework. Invoke that method after having properly configured this class using the setter. + */ + public AtmosphereFramework init() { + try { + init(new ServletConfig() { + + @Override + public String getServletName() { + return "AtmosphereFramework"; + } + + @Override + public ServletContext getServletContext() { + return (ServletContext) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{ServletContext.class}, + new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { + logger.trace("Method {} not supported", method.getName()); + return null; + } + }); + } + + @Override + public String getInitParameter(String name) { + return initParams.get(name); + } + + @Override + public Enumeration<String> getInitParameterNames() { + return Collections.enumeration(initParams.values()); + } + }); + } catch (ServletException e) { + logger.error("", e); + } + return this; + } + + /** + * Initialize the AtmosphereFramework using the {@link ServletContext} + * + * @param sc the {@link ServletContext} + */ + public AtmosphereFramework init(final ServletConfig sc) throws ServletException { + + if (isInit) return this; + + try { + ServletContextHolder.register(this); + + ServletConfig scFacade = new ServletConfig() { + + public String getServletName() { + return sc.getServletName(); + } + + public ServletContext getServletContext() { + return sc.getServletContext(); + } + + public String getInitParameter(String name) { + String param = initParams.get(name); + if (param == null) { + return sc.getInitParameter(name); + } + return param; + } + + public Enumeration<String> getInitParameterNames() { + Enumeration en = sc.getInitParameterNames(); + while (en.hasMoreElements()) { + String name = (String) en.nextElement(); + if (!initParams.containsKey(name)) { + initParams.put(name, sc.getInitParameter(name)); + } + } + return Collections.enumeration(initParams.keySet()); + } + }; + this.servletConfig = scFacade; + asyncSupportListener(new AsyncSupportListenerAdapter()); + + autoConfigureService(scFacade.getServletContext()); + patchContainer(); + doInitParams(scFacade); + doInitParamsForWebSocket(scFacade); + configureBroadcaster(); + loadConfiguration(scFacade); + initWebSocket(); + + autoDetectContainer(); + configureWebDotXmlAtmosphereHandler(sc); + asyncSupport.init(scFacade); + initAtmosphereHandler(scFacade); + configureAtmosphereInterceptor(sc); + + if (broadcasterCacheClassName == null) { + logger.warn("No BroadcasterCache configured. Broadcasted message between client reconnection will be LOST. " + + "It is recommended to configure the {}", UUIDBroadcasterCache.class.getName()); + } else { + logger.info("Using BroadcasterCache: {}", broadcasterCacheClassName); + } + + // http://java.net/jira/browse/ATMOSPHERE-157 + if (sc.getServletContext() != null) { + sc.getServletContext().setAttribute(BroadcasterFactory.class.getName(), broadcasterFactory); + } + + for (String i : broadcasterFilters) { + logger.info("Using BroadcastFilter: {}", i); + } + + String s = config.getInitParameter(ApplicationConfig.BROADCASTER_SHARABLE_THREAD_POOLS); + if (s != null) { + sharedThreadPools = Boolean.parseBoolean(s); + } + + logger.info("Shared ExecutorService supported: {}", sharedThreadPools); + logger.info("HttpSession supported: {}", config.isSupportSession()); + logger.info("Using BroadcasterFactory: {}", broadcasterFactory.getClass().getName()); + logger.info("Using WebSocketProcessor: {}", webSocketProcessorClassName); + logger.info("Using Broadcaster: {}", broadcasterClassName); + logger.info("Atmosphere Framework {} started.", Version.getRawVersion()); + + String showSupportMessage = config.getInitParameter("org.atmosphere.cpr.showSupportMessage"); + if (showSupportMessage == null || Boolean.parseBoolean(showSupportMessage)) { + logger.info("\n\n\tFor Commercial Support, visit \n\t{} " + + "or send an email to {}\n", "http://www.async-io.org/", "support@async-io.org"); + } + } catch (Throwable t) { + logger.error("Failed to initialize Atmosphere Framework", t); + + if (t instanceof ServletException) { + throw (ServletException) t; + } + + throw new ServletException(t); + } + isInit = true; + return this; + } + + /** + * Configure the list of {@link AtmosphereInterceptor}. + * + * @param sc a ServletConfig + */ + protected void configureAtmosphereInterceptor(ServletConfig sc) { + String s = sc.getInitParameter(ApplicationConfig.ATMOSPHERE_INTERCEPTORS); + if (s != null) { + String[] list = s.split(","); + for (String a : list) { + try { + AtmosphereInterceptor ai = (AtmosphereInterceptor) Thread.currentThread().getContextClassLoader() + .loadClass(a.trim()).newInstance(); + ai.configure(config); + interceptor(ai); + } catch (InstantiationException e) { + logger.warn("", e); + } catch (IllegalAccessException e) { + logger.warn("", e); + } catch (ClassNotFoundException e) { + logger.warn("", e); + } + } + } + + s = sc.getInitParameter(ApplicationConfig.DISABLE_ATMOSPHEREINTERCEPTOR); + if (s == null) { + // OnDisconnect + interceptors.addFirst(newAInterceptor(OnDisconnectInterceptor.class)); + // ADD Tracking ID Handshake + interceptors.addFirst(newAInterceptor(JavaScriptProtocol.class)); + // ADD JSONP support + interceptors.addFirst(newAInterceptor(JSONPAtmosphereInterceptor.class)); + // Add SSE support + interceptors.addFirst(newAInterceptor(SSEAtmosphereInterceptor.class)); + // Android 2.3.x streaming support + interceptors.addFirst(newAInterceptor(AndroidAtmosphereInterceptor.class)); + logger.info("Installed Default AtmosphereInterceptor {}. " + + "Set org.atmosphere.cpr.AtmosphereInterceptor.disableDefaults in your xml to disable them.", interceptors); + } + } + + protected AtmosphereInterceptor newAInterceptor(Class<? extends AtmosphereInterceptor> a) { + AtmosphereInterceptor ai = null; + try { + ai = (AtmosphereInterceptor) getClass().getClassLoader().loadClass(a.getName()).newInstance(); + ai.configure(config); + } catch (Exception ex) { + logger.warn("", ex); + } + return ai; + } + + protected void configureWebDotXmlAtmosphereHandler(ServletConfig sc) { + String s = sc.getInitParameter(ATMOSPHERE_HANDLER); + if (s != null) { + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + try { + + String mapping = sc.getInitParameter(ATMOSPHERE_HANDLER_MAPPING); + if (mapping == null) { + mapping = "/*"; + } + addAtmosphereHandler(mapping, (AtmosphereHandler) cl.loadClass(s).newInstance()); + } catch (Exception ex) { + logger.warn("Unable to load WebSocketHandle instance", ex); + } + } + } + + protected void configureBroadcaster() { + + try { + // Check auto supported one + if (isBroadcasterSpecified == false) { + broadcasterClassName = lookupDefaultBroadcasterType(broadcasterClassName); + } + + Class<? extends Broadcaster> bc = + (Class<? extends Broadcaster>) Thread.currentThread().getContextClassLoader() + .loadClass(broadcasterClassName); + if (broadcasterFactoryClassName != null) { + broadcasterFactory = (BroadcasterFactory) Thread.currentThread().getContextClassLoader() + .loadClass(broadcasterFactoryClassName).newInstance(); + } + + if (broadcasterFactory == null) { + broadcasterFactory = new DefaultBroadcasterFactory(bc, broadcasterLifeCyclePolicy, config); + } + + for (BroadcasterListener b : broadcasterListeners) { + broadcasterFactory.addBroadcasterListener(b); + } + + BroadcasterFactory.setBroadcasterFactory(broadcasterFactory, config); + InjectorProvider.getInjector().inject(broadcasterFactory); + + Iterator<Entry<String, AtmosphereHandlerWrapper>> i = atmosphereHandlers.entrySet().iterator(); + AtmosphereHandlerWrapper w; + Entry<String, AtmosphereHandlerWrapper> e; + while (i.hasNext()) { + e = i.next(); + w = e.getValue(); + + if (w.broadcaster == null) { + w.broadcaster = broadcasterFactory.get(w.mapping); + } else { + if (broadcasterCacheClassName != null) { + BroadcasterCache cache = (BroadcasterCache) Thread.currentThread().getContextClassLoader() + .loadClass(broadcasterCacheClassName).newInstance(); + InjectorProvider.getInjector().inject(cache); + w.broadcaster.getBroadcasterConfig().setBroadcasterCache(cache); + } + } + } + } catch (Exception ex) { + logger.error("Unable to configure Broadcaster/Factory/Cache", ex); + } + } + + protected void doInitParamsForWebSocket(ServletConfig sc) { + String s = sc.getInitParameter(WEBSOCKET_SUPPORT); + if (s != null) { + webSocketEnabled = Boolean.parseBoolean(s); + sessionSupport(false); + } + s = sc.getInitParameter(WEBSOCKET_PROTOCOL); + if (s != null) { + webSocketProtocolClassName = s; + } + + s = sc.getInitParameter(WEBSOCKET_PROCESSOR); + if (s != null) { + webSocketProcessorClassName = s; + } + } + + /** + * Read init param from web.xml and apply them. + * + * @param sc {@link ServletConfig} + */ + protected void doInitParams(ServletConfig sc) { + String s = sc.getInitParameter(PROPERTY_NATIVE_COMETSUPPORT); + if (s != null) { + useNativeImplementation = Boolean.parseBoolean(s); + if (useNativeImplementation) isCometSupportSpecified = true; + } + s = sc.getInitParameter(PROPERTY_BLOCKING_COMETSUPPORT); + if (s != null) { + useBlockingImplementation = Boolean.parseBoolean(s); + if (useBlockingImplementation) isCometSupportSpecified = true; + } + s = sc.getInitParameter(PROPERTY_USE_STREAM); + if (s != null) { + useStreamForFlushingComments = Boolean.parseBoolean(s); + } + s = sc.getInitParameter(PROPERTY_COMET_SUPPORT); + if (s != null) { + asyncSupport = new DefaultAsyncSupportResolver(config).newCometSupport(s); + isCometSupportSpecified = true; + } + s = sc.getInitParameter(BROADCASTER_CLASS); + if (s != null) { + broadcasterClassName = s; + isBroadcasterSpecified = true; + } + s = sc.getInitParameter(BROADCASTER_CACHE); + if (s != null) { + broadcasterCacheClassName = s; + } + s = sc.getInitParameter(PROPERTY_SESSION_SUPPORT); + if (s != null) { + config.setSupportSession(Boolean.valueOf(s)); + isSessionSupportSpecified = true; + } + s = sc.getInitParameter(DISABLE_ONSTATE_EVENT); + if (s != null) { + initParams.put(DISABLE_ONSTATE_EVENT, s); + } else { + initParams.put(DISABLE_ONSTATE_EVENT, "false"); + } + s = sc.getInitParameter(RESUME_AND_KEEPALIVE); + if (s != null) { + initParams.put(RESUME_AND_KEEPALIVE, s); + } + s = sc.getInitParameter(BROADCAST_FILTER_CLASSES); + if (s != null) { + broadcasterFilters.addAll(Arrays.asList(s.split(","))); + logger.info("Installing BroadcastFilter class(es) {}", s); + } + s = sc.getInitParameter(BROADCASTER_LIFECYCLE_POLICY); + if (s != null) { + broadcasterLifeCyclePolicy = s; + } + s = sc.getInitParameter(BROADCASTER_FACTORY); + if (s != null) { + broadcasterFactoryClassName = s; + } + s = sc.getInitParameter(ATMOSPHERE_HANDLER_PATH); + if (s != null) { + handlersPath = s; + } + s = sc.getInitParameter(PROPERTY_ATMOSPHERE_XML); + if (s != null) { + atmosphereDotXmlPath = s; + } + } + + public void loadConfiguration(ServletConfig sc) throws ServletException { + + if (!autoDetectHandlers) return; + + try { + URL url = sc.getServletContext().getResource(handlersPath); + URLClassLoader urlC = new URLClassLoader(new URL[]{url}, + Thread.currentThread().getContextClassLoader()); + loadAtmosphereDotXml(sc.getServletContext(). + getResourceAsStream(atmosphereDotXmlPath), urlC); + + if (atmosphereHandlers.size() == 0) { + autoDetectAtmosphereHandlers(sc.getServletContext(), urlC); + + if (atmosphereHandlers.size() == 0) { + detectSupportedFramework(sc); + } + } + + autoDetectWebSocketHandler(sc.getServletContext(), urlC); + } catch (Throwable t) { + throw new ServletException(t); + } + } + + /** + * Auto-detect Jersey when no atmosphere.xml file are specified. + * + * @param sc {@link ServletConfig} + * @return true if Jersey classes are detected + * @throws ClassNotFoundException + */ + protected boolean detectSupportedFramework(ServletConfig sc) throws ClassNotFoundException, IllegalAccessException, + InstantiationException, NoSuchMethodException, InvocationTargetException { + + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + String broadcasterClassNameTmp = null; + + try { + cl.loadClass(JERSEY_CONTAINER); + + if (!isBroadcasterSpecified) { + broadcasterClassNameTmp = lookupDefaultBroadcasterType(JERSEY_BROADCASTER); + + cl.loadClass(broadcasterClassNameTmp); + } + useStreamForFlushingComments = true; + } catch (Throwable t) { + logger.trace("", t); + return false; + } + + logger.warn("Missing META-INF/atmosphere.xml but found the Jersey runtime. Starting Jersey"); + + // Jersey will handle itself the headers. + initParams.put(WRITE_HEADERS, "false"); + + ReflectorServletProcessor rsp = new ReflectorServletProcessor(); + if (broadcasterClassNameTmp != null) broadcasterClassName = broadcasterClassNameTmp; + rsp.setServletClassName(JERSEY_CONTAINER); + sessionSupport(false); + initParams.put(DISABLE_ONSTATE_EVENT, "true"); + + String mapping = sc.getInitParameter(PROPERTY_SERVLET_MAPPING); + if (mapping == null) { + mapping = "/*"; + } + Class<? extends Broadcaster> bc = (Class<? extends Broadcaster>) cl.loadClass(broadcasterClassName); + + + if (broadcasterFactory != null) { + broadcasterFactory.destroy(); + } + broadcasterFactory = new DefaultBroadcasterFactory(bc, broadcasterLifeCyclePolicy, config); + BroadcasterFactory.setBroadcasterFactory(broadcasterFactory, config); + + for (BroadcasterListener b : broadcasterListeners) { + broadcasterFactory.addBroadcasterListener(b); + } + + Broadcaster b; + + try { + b = broadcasterFactory.get(bc, mapping); + } catch (IllegalStateException ex) { + logger.warn("Two Broadcaster's named {}. Renaming the second one to {}", mapping, sc.getServletName() + mapping); + b = broadcasterFactory.get(bc, sc.getServletName() + mapping); + } + + addAtmosphereHandler(mapping, rsp, b); + return true; + } + + protected String lookupDefaultBroadcasterType(String defaultB) { + for (String b : broadcasterTypes) { + try { + Class.forName(b); + return b; + } catch (ClassNotFoundException e) { + } + } + return defaultB; + } + + protected void sessionSupport(boolean sessionSupport) { + if (!isSessionSupportSpecified) { + config.setSupportSession(sessionSupport); + } else if (!config.isSupportSession()) { + // Don't turn off session support. Once it's on, leave it on. + config.setSupportSession(sessionSupport); + } + } + + /** + * Initialize {@link AtmosphereServletProcessor} + * + * @param sc the {@link ServletConfig} + * @throws javax.servlet.ServletException + */ + public void initAtmosphereHandler(ServletConfig sc) throws ServletException { + AtmosphereHandler a; + AtmosphereHandlerWrapper w; + for (Entry<String, AtmosphereHandlerWrapper> h : atmosphereHandlers.entrySet()) { + w = h.getValue(); + a = w.atmosphereHandler; + if (a instanceof AtmosphereServletProcessor) { + ((AtmosphereServletProcessor) a).init(sc); + } + } + + if (atmosphereHandlers.size() == 0 && !SimpleHttpProtocol.class.isAssignableFrom(webSocketProtocol.getClass())) { + logger.debug("Adding a void AtmosphereHandler mapped to /* to allow WebSocket application only"); + addAtmosphereHandler("/*", new AbstractReflectorAtmosphereHandler() { + @Override + public void onRequest(AtmosphereResource r) throws IOException { + logger.debug("No AtmosphereHandler defined."); + } + + @Override + public void destroy() { + } + }); + } + } + + protected void initWebSocket() { + if (webSocketProtocol == null) { + try { + webSocketProtocol = (WebSocketProtocol) Thread.currentThread().getContextClassLoader() + .loadClass(webSocketProtocolClassName).newInstance(); + logger.info("Installed WebSocketProtocol {} ", webSocketProtocolClassName); + } catch (Exception ex) { + try { + webSocketProtocol = (WebSocketProtocol) AtmosphereFramework.class.getClassLoader() + .loadClass(webSocketProtocolClassName).newInstance(); + logger.info("Installed WebSocketProtocol {} ", webSocketProtocolClassName); + } catch (Exception ex2) { + logger.error("Cannot load the WebSocketProtocol {}", getWebSocketProtocolClassName(), ex); + webSocketProtocol = new SimpleHttpProtocol(); + } + } + } + webSocketProtocol.configure(config); + } + + public AtmosphereFramework destroy() { + if (asyncSupport != null && AsynchronousProcessor.class.isAssignableFrom(asyncSupport.getClass())) { + ((AsynchronousProcessor) asyncSupport).shutdown(); + } + + // We just need one bc to shutdown the shared thread pool + for (Entry<String, AtmosphereHandlerWrapper> entry : atmosphereHandlers.entrySet()) { + AtmosphereHandlerWrapper handlerWrapper = entry.getValue(); + handlerWrapper.atmosphereHandler.destroy(); + } + + BroadcasterFactory factory = broadcasterFactory; + if (factory != null) { + factory.destroy(); + BroadcasterFactory.factory = null; + } + WebSocketProcessorFactory.getDefault().destroy(); + return this; + } + + /** + * Load AtmosphereHandler defined under META-INF/atmosphere.xml + * + * @param stream The input stream we read from. + * @param c The classloader + */ + protected void loadAtmosphereDotXml(InputStream stream, URLClassLoader c) + throws IOException, ServletException { + + if (stream == null) { + return; + } + + AtmosphereConfigReader.getInstance().parse(config, stream); + for (AtmosphereHandlerConfig atmoHandler : config.getAtmosphereHandlerConfig()) { + try { + AtmosphereHandler handler; + + if (!ReflectorServletProcessor.class.getName().equals(atmoHandler.getClassName())) { + handler = (AtmosphereHandler) c.loadClass(atmoHandler.getClassName()).newInstance(); + } else { + handler = new ReflectorServletProcessor(); + } + + logger.info("Installed AtmosphereHandler {} mapped to context-path: {}", handler, atmoHandler.getContextRoot()); + + for (ApplicationConfiguration a : atmoHandler.getApplicationConfig()) { + initParams.put(a.getParamName(), a.getParamValue()); + } + + for (FrameworkConfiguration a : atmoHandler.getFrameworkConfig()) { + initParams.put(a.getParamName(), a.getParamValue()); + } + + for (AtmosphereHandlerProperty handlerProperty : atmoHandler.getProperties()) { + + if (handlerProperty.getValue() != null && handlerProperty.getValue().indexOf("jersey") != -1) { + initParams.put(DISABLE_ONSTATE_EVENT, "true"); + useStreamForFlushingComments = true; + broadcasterClassName = lookupDefaultBroadcasterType(JERSEY_BROADCASTER); + broadcasterFactory = null; + configureBroadcaster(); + } + + IntrospectionUtils.setProperty(handler, handlerProperty.getName(), handlerProperty.getValue()); + IntrospectionUtils.addProperty(handler, handlerProperty.getName(), handlerProperty.getValue()); + } + + sessionSupport(Boolean.valueOf(atmoHandler.getSupportSession())); + + String broadcasterClass = atmoHandler.getBroadcaster(); + Broadcaster b; + /** + * If there is more than one AtmosphereHandler defined, their Broadcaster + * may clash each other with the BroadcasterFactory. In that case we will use the + * last one defined. + */ + if (broadcasterClass != null) { + broadcasterClassName = broadcasterClass; + ClassLoader cl = Thread.currentThread().getContextClassLoader(); + Class<? extends Broadcaster> bc = (Class<? extends Broadcaster>) cl.loadClass(broadcasterClassName); + broadcasterFactory = new DefaultBroadcasterFactory(bc, broadcasterLifeCyclePolicy, config); + BroadcasterFactory.setBroadcasterFactory(broadcasterFactory, config); + } + + b = broadcasterFactory.lookup(atmoHandler.getContextRoot(), true); + + AtmosphereHandlerWrapper wrapper = new AtmosphereHandlerWrapper(handler, b); + addMapping(atmoHandler.getContextRoot(), wrapper); + + String bc = atmoHandler.getBroadcasterCache(); + if (bc != null) { + broadcasterCacheClassName = bc; + } + + if (atmoHandler.getCometSupport() != null) { + asyncSupport = (AsyncSupport) c.loadClass(atmoHandler.getCometSupport()) + .getDeclaredConstructor(new Class[]{AtmosphereConfig.class}) + .newInstance(new Object[]{config}); + } + + if (atmoHandler.getBroadcastFilterClasses() != null) { + broadcasterFilters.addAll(atmoHandler.getBroadcastFilterClasses()); + } + + List<AtmosphereInterceptor> l = new ArrayList<AtmosphereInterceptor>(); + if (atmoHandler.getAtmosphereInterceptorClasses() != null) { + for (String a : atmoHandler.getAtmosphereInterceptorClasses()) { + try { + AtmosphereInterceptor ai = (AtmosphereInterceptor) c.loadClass(a).newInstance(); + ai.configure(config); + l.add(ai); + } catch (Throwable e) { + logger.warn("", e); + } + } + } + wrapper.interceptors = l; + if (l.size() > 0) { + logger.info("Installed AtmosphereInterceptor {} mapped to AtmosphereHandler {}", l, atmoHandler.getClassName()); + } + } catch (Throwable t) { + logger.warn("Unable to load AtmosphereHandler class: " + atmoHandler.getClassName(), t); + throw new ServletException(t); + } + + } + } + + /** + * Set the {@link AsyncSupport} implementation. Make sure you don't set + * an implementation that only works on some Container. See {@link BlockingIOCometSupport} + * for an example. + * + * @param asyncSupport + */ + public AtmosphereFramework setAsyncSupport(AsyncSupport asyncSupport) { + this.asyncSupport = asyncSupport; + return this; + } + + /** + * @param asyncSupport + * @return + * @Deprecated - Use {@link #setAsyncSupport(AsyncSupport)} + */ + public AtmosphereFramework setCometSupport(AsyncSupport asyncSupport) { + return setAsyncSupport(asyncSupport); + } + + /** + * Return the current {@link AsyncSupport} + * + * @return the current {@link AsyncSupport} + */ + public AsyncSupport getAsyncSupport() { + return asyncSupport; + } + + /** + * Return the current {@link AsyncSupport} + * + * @return the current {@link AsyncSupport} + * @deprecated Use getAsyncSupport + */ + public AsyncSupport getCometSupport() { + return asyncSupport; + } + + /** + * Returns an instance of AsyncSupportResolver {@link AsyncSupportResolver} + * + * @return CometSupportResolver + */ + protected AsyncSupportResolver createAsyncSupportResolver() { + return new DefaultAsyncSupportResolver(config); + } + + + /** + * Auto detect the underlying Servlet Container we are running on. + */ + protected void autoDetectContainer() { + // Was defined in atmosphere.xml + if (getAsyncSupport() == null) { + setAsyncSupport(createAsyncSupportResolver() + .resolve(useNativeImplementation, useBlockingImplementation, webSocketEnabled)); + } + + logger.info("Atmosphere is using async support: {} running under container: {}", + getAsyncSupport().getClass().getName(), asyncSupport.getContainerName()); + } + + /** + * Auto detect instance of {@link AtmosphereHandler} in case META-INF/atmosphere.xml + * is missing. + * + * @param servletContext {@link ServletContext} + * @param classloader {@link URLClassLoader} to load the class. + * @throws java.net.MalformedURLException + * @throws java.net.URISyntaxException + */ + public void autoDetectAtmosphereHandlers(ServletContext servletContext, URLClassLoader classloader) + throws MalformedURLException, URISyntaxException { + + // If Handler has been added + if (atmosphereHandlers.size() > 0) return; + + logger.info("Auto detecting atmosphere handlers {}", handlersPath); + + String realPath = servletContext.getRealPath(handlersPath); + + // Weblogic bug + if (realPath == null) { + URL u = servletContext.getResource(handlersPath); + if (u == null) return; + realPath = u.getPath(); + } + + loadAtmosphereHandlersFromPath(classloader, realPath); + } + + public void loadAtmosphereHandlersFromPath(URLClassLoader classloader, String realPath) { + File file = new File(realPath); + + if (file.isDirectory()) { + getFiles(file); + scanDone = true; + + for (String className : possibleComponentsCandidate) { + try { + className = className.replace('\\', '/'); + className = className.replaceFirst("^.*/(WEB-INF|target)(?:/scala-[^/]+)?/(test-)?classes/(.*)\\.class", "$3").replace("/", "."); + Class<?> clazz = classloader.loadClass(className); + + if (AtmosphereHandler.class.isAssignableFrom(clazz)) { + AtmosphereHandler handler = (AtmosphereHandler) clazz.newInstance(); + InjectorProvider.getInjector().inject(handler); + addMapping("/" + handler.getClass().getSimpleName(), + new AtmosphereHandlerWrapper(broadcasterFactory, handler, "/" + handler.getClass().getSimpleName())); + logger.info("Installed AtmosphereHandler {} mapped to context-path: {}", handler, handler.getClass().getName()); + } + } catch (Throwable t) { + logger.trace("failed to load class as an AtmosphereHandler: " + className, t); + } + } + } + } + + /** + * Auto detect instance of {@link org.atmosphere.websocket.WebSocketHandler} in case META-INF/atmosphere.xml + * is missing. + * + * @param servletContext {@link ServletContext} + * @param classloader {@link URLClassLoader} to load the class. + * @throws java.net.MalformedURLException + * @throws java.net.URISyntaxException + */ + protected void autoDetectWebSocketHandler(ServletContext servletContext, URLClassLoader classloader) + throws MalformedURLException, URISyntaxException { + + if (hasNewWebSocketProtocol) return; + + logger.info("Auto detecting WebSocketHandler in {}", handlersPath); + + String realPath = servletContext.getRealPath(handlersPath); + + // Weblogic bug + if (realPath == null) { + URL u = servletContext.getResource(handlersPath); + if (u == null) return; + realPath = u.getPath(); + } + + loadWebSocketFromPath(classloader, realPath); + } + + protected void loadWebSocketFromPath(URLClassLoader classloader, String realPath) { + File file = new File(realPath); + + if (file.isDirectory()) { + getFiles(file); + scanDone = true; + + for (String className : possibleComponentsCandidate) { + try { + className = className.replace('\\', '/'); + className = className.replaceFirst("^.*/(WEB-INF|target)(?:/scala-[^/]+)?/(test-)?classes/(.*)\\.class", "$3").replace("/", "."); + Class<?> clazz = classloader.loadClass(className); + + if (WebSocketProtocol.class.isAssignableFrom(clazz)) { + webSocketProtocol = (WebSocketProtocol) clazz.newInstance(); + InjectorProvider.getInjector().inject(webSocketProtocol); + logger.info("Installed WebSocketProtocol {}", webSocketProtocol); + } + } catch (Throwable t) { + logger.trace("failed to load class as an WebSocketProtocol: " + className, t); + } + } + } + } + + + /** + * Get the list of possible candidate to load as {@link AtmosphereHandler} + * + * @param f the real path {@link File} + */ + private void getFiles(File f) { + if (scanDone) return; + + File[] files = f.listFiles(); + for (File test : files) { + if (test.isDirectory()) { + getFiles(test); + } else { + String clazz = test.getAbsolutePath(); + if (clazz.endsWith(".class")) { + possibleComponentsCandidate.add(clazz); + } + } + } + } + + /** + * Invoke the proprietary {@link AsyncSupport} + * + * @param req + * @param res + * @return an {@link Action} + * @throws IOException + * @throws ServletException + */ + public Action doCometSupport(AtmosphereRequest req, AtmosphereResponse res) throws IOException, ServletException { + req.setAttribute(BROADCASTER_FACTORY, broadcasterFactory); + req.setAttribute(PROPERTY_USE_STREAM, useStreamForFlushingComments); + req.setAttribute(BROADCASTER_CLASS, broadcasterClassName); + req.setAttribute(ATMOSPHERE_CONFIG, config); + + Action a = null; + try { + boolean skip = true; + String s = config.getInitParameter(ALLOW_QUERYSTRING_AS_REQUEST); + if (s != null) { + skip = Boolean.valueOf(s); + } + if (!skip || req.getAttribute(WEBSOCKET_SUSPEND) == null) { + Map<String, String> headers = configureQueryStringAsRequest(req); + String body = headers.remove(ATMOSPHERE_POST_BODY); + if (body != null && body.isEmpty()) { + body = null; + } + + req.headers(headers) + .method(body != null && req.getMethod().equalsIgnoreCase("GET") ? "POST" : req.getMethod()); + + if (body != null) { + req.body(body); + } + } + + s = req.getHeader(X_ATMOSPHERE_TRACKING_ID); + + // Lookup for websocket + if (s == null || s.equals("0")) { + String unique = config.getInitParameter(ApplicationConfig.UNIQUE_UUID_WEBSOCKET); + if (unique != null && Boolean.valueOf(unique)) { + s = (String) req.getAttribute(SUSPENDED_ATMOSPHERE_RESOURCE_UUID); + } + } + + if (s == null || s.equals("0")) { + s = UUID.randomUUID().toString(); + res.setHeader(X_ATMOSPHERE_TRACKING_ID, s); + } else { + // This may breaks 1.0.0 application because the WebSocket's associated AtmosphereResource will + // all have the same UUID, and retrieving the original one for WebSocket, so we don't set it at all. + // Null means it is not an HTTP request. + if (req.resource() == null) { + res.setHeader(X_ATMOSPHERE_TRACKING_ID, s); + } else if (req.getAttribute(WebSocket.WEBSOCKET_INITIATED) == null) { + // WebSocket reconnect, in case an application manually set the header + // (impossible to retrieve the headers normally with WebSocket or SSE) + res.setHeader(X_ATMOSPHERE_TRACKING_ID, s); + } + } + + if (req.getAttribute(SUSPENDED_ATMOSPHERE_RESOURCE_UUID) == null) { + req.setAttribute(SUSPENDED_ATMOSPHERE_RESOURCE_UUID, s); + } + + a = asyncSupport.service(req, res); + } catch (IllegalStateException ex) { + if (ex.getMessage() != null && (ex.getMessage().startsWith("Tomcat failed") || ex.getMessage().startsWith("JBoss failed"))) { + if (!isFilter) { + logger.warn("Failed using comet support: {}, error: {} Is the Nio or Apr Connector enabled?", asyncSupport.getClass().getName(), + ex.getMessage()); + } + logger.trace(ex.getMessage(), ex); + + AsyncSupport current = asyncSupport; + asyncSupport = asyncSupport.supportWebSocket() ? new Tomcat7BIOSupportWithWebSocket(config) : new BlockingIOCometSupport(config); + if (current instanceof AsynchronousProcessor) { + ((AsynchronousProcessor) current).shutdown(); + } + + asyncSupport.init(config.getServletConfig()); + logger.warn("Using " + asyncSupport.getClass().getName()); + + a = asyncSupport.service(req, res); + } else { + logger.error("AtmosphereFramework exception", ex); + throw ex; + } + } finally { + if (a != null) { + notify(a.type(), req, res); + } + + if (req != null && a != null && a.type() != Action.TYPE.SUSPEND) { + req.destroy(); + res.destroy(); + notify(Action.TYPE.DESTROYED, req, res); + } + } + return a; + } + + /** + * Return the default {@link Broadcaster} class name. + * + * @return the broadcasterClassName + */ + public String getDefaultBroadcasterClassName() { + return broadcasterClassName; + } + + /** + * Set the default {@link Broadcaster} class name + * + * @param bccn the broadcasterClassName to set + */ + public AtmosphereFramework setDefaultBroadcasterClassName(String bccn) { + broadcasterClassName = bccn; + return this; + } + + /** + * <tt>true</tt> if Atmosphere uses {@link AtmosphereResponse#getOutputStream()} + * by default for write operation. + * + * @return the useStreamForFlushingComments + */ + public boolean isUseStreamForFlushingComments() { + return useStreamForFlushingComments; + } + + /** + * Set to <tt>true</tt> so Atmosphere uses {@link AtmosphereResponse#getOutputStream()} + * by default for write operation. Default is false. + * + * @param useStreamForFlushingComments the useStreamForFlushingComments to set + */ + public AtmosphereFramework setUseStreamForFlushingComments(boolean useStreamForFlushingComments) { + this.useStreamForFlushingComments = useStreamForFlushingComments; + return this; + } + + /** + * Get the {@link BroadcasterFactory} which is used by Atmosphere to construct + * {@link Broadcaster} + * + * @return {@link BroadcasterFactory} + */ + public BroadcasterFactory getBroadcasterFactory() { + return broadcasterFactory; + } + + /** + * Set the {@link BroadcasterFactory} which is used by Atmosphere to construct + * {@link Broadcaster} + * + * @return {@link BroadcasterFactory} + */ + public AtmosphereFramework setBroadcasterFactory(final BroadcasterFactory broadcasterFactory) { + this.broadcasterFactory = broadcasterFactory; + configureBroadcaster(); + return this; + } + + /** + * Return the {@link org.atmosphere.cpr.BroadcasterCache} class name. + * + * @return the {@link org.atmosphere.cpr.BroadcasterCache} class name. + */ + public String getBroadcasterCacheClassName() { + return broadcasterCacheClassName; + } + + /** + * Set the {@link org.atmosphere.cpr.BroadcasterCache} class name. + * + * @param broadcasterCacheClassName + */ + public void setBroadcasterCacheClassName(String broadcasterCacheClassName) { + this.broadcasterCacheClassName = broadcasterCacheClassName; + configureBroadcaster(); + } + + /** + * Add a new Broadcaster class name AtmosphereServlet can use when initializing requests, and when + * atmosphere.xml broadcaster element is unspecified. + * + * @param broadcasterTypeString + */ + public AtmosphereFramework addBroadcasterType(String broadcasterTypeString) { + broadcasterTypes.add(broadcasterTypeString); + return this; + } + + public String getWebSocketProtocolClassName() { + return webSocketProtocolClassName; + } + + public AtmosphereFramework setWebSocketProtocolClassName(String webSocketProtocolClassName) { + hasNewWebSocketProtocol = true; + this.webSocketProtocolClassName = webSocketProtocolClassName; + return this; + } + + public Map<String, AtmosphereHandlerWrapper> getAtmosphereHandlers() { + return atmosphereHandlers; + } + + protected Map<String, String> configureQueryStringAsRequest(AtmosphereRequest request) { + Map<String, String> headers = new HashMap<String, String>(); + + Enumeration<String> e = request.getParameterNames(); + String s; + while (e.hasMoreElements()) { + s = e.nextElement(); + if (s.equalsIgnoreCase("Content-Type")) { + // Use the one set by the user first. + if (request.getContentType() == null || + !request.getContentType().equalsIgnoreCase(request.getParameter(s))) { + request.contentType(request.getParameter(s)); + } + } + headers.put(s, request.getParameter(s)); + } + logger.trace("Query String translated to headers {}", headers); + return headers; + } + + protected boolean isIECandidate(AtmosphereRequest request) { + String userAgent = request.getHeader("User-Agent"); + if (userAgent == null) return false; + + if (userAgent.contains("MSIE") || userAgent.contains(".NET")) { + // Now check the header + String transport = request.getHeader(HeaderConfig.X_ATMOSPHERE_TRANSPORT); + if (transport != null) { + return false; + } else { + return true; + } + } + return false; + } + + public WebSocketProtocol getWebSocketProtocol() { + // TODO: Spagetthi code, needs to rework. + // Make sure we initialized the WebSocketProtocol + initWebSocket(); + return webSocketProtocol; + } + + public boolean isUseNativeImplementation() { + return useNativeImplementation; + } + + public AtmosphereFramework setUseNativeImplementation(boolean useNativeImplementation) { + this.useNativeImplementation = useNativeImplementation; + return this; + } + + public boolean isUseBlockingImplementation() { + return useBlockingImplementation; + } + + public AtmosphereFramework setUseBlockingImplementation(boolean useBlockingImplementation) { + this.useBlockingImplementation = useBlockingImplementation; + return this; + } + + public String getAtmosphereDotXmlPath() { + return atmosphereDotXmlPath; + } + + public AtmosphereFramework setAtmosphereDotXmlPath(String atmosphereDotXmlPath) { + this.atmosphereDotXmlPath = atmosphereDotXmlPath; + return this; + } + + public String getHandlersPath() { + return handlersPath; + } + + public AtmosphereFramework setHandlersPath(String handlersPath) { + this.handlersPath = handlersPath; + return this; + } + + /** + * Return the location of the jars containing the application classes. Default is WEB-INF/lib + * + * @return the location of the jars containing the application classes. Default is WEB-INF/lib + */ + public String getLibPath() { + return libPath; + } + + /** + * Set the location of the jars containing the application. + * + * @param libPath the location of the jars containing the application. + * @return this + */ + public AtmosphereFramework setLibPath(String libPath) { + this.libPath = libPath; + return this; + } + + public String getWebSocketProcessorClassName() { + return webSocketProcessorClassName; + } + + public AtmosphereFramework setWebsocketProcessorClassName(String webSocketProcessorClassName) { + this.webSocketProcessorClassName = webSocketProcessorClassName; + return this; + } + + /** + * Add an {@link AtmosphereInterceptor} implementation. The adding order or {@link AtmosphereInterceptor} will be used, e.g + * the first added {@link AtmosphereInterceptor} will always be called first. + * + * @param c {@link AtmosphereInterceptor} + * @return this + */ + public AtmosphereFramework interceptor(AtmosphereInterceptor c) { + boolean found = false; + for (AtmosphereInterceptor interceptor : interceptors) { + if (interceptor.getClass().equals(c.getClass())) { + found = true; + break; + } + } + + if (!found) { + interceptors.addLast(c); + logger.info("Installed AtmosphereInterceptor {}. ", c); + } + return this; + } + + /** + * Return the list of {@link AtmosphereInterceptor} + * + * @return the list of {@link AtmosphereInterceptor} + */ + public LinkedList<AtmosphereInterceptor> interceptors() { + return interceptors; + } + + /** + * Set the {@link AnnotationProcessor} class name. + * + * @param annotationProcessorClassName the {@link AnnotationProcessor} class name. + * @return this + */ + public AtmosphereFramework annotationProcessorClassName(String annotationProcessorClassName) { + this.annotationProcessorClassName = annotationProcessorClassName; + return this; + } + + /** + * Add an {@link AsyncSupportListener} + * + * @param asyncSupportListener an {@link AsyncSupportListener} + * @return this; + */ + public AtmosphereFramework asyncSupportListener(AsyncSupportListener asyncSupportListener) { + asyncSupportListeners.add(asyncSupportListener); + return this; + } + + /** + * Return the list of an {@link AsyncSupportListener} + * + * @return + */ + public List<AsyncSupportListener> asyncSupportListeners() { + return asyncSupportListeners; + } + + /** + * Add {@link BroadcasterListener} to all created {@link Broadcaster} + */ + public AtmosphereFramework addBroadcasterListener(BroadcasterListener b) { + if (isInit) { + broadcasterFactory.addBroadcasterListener(b); + } else { + broadcasterListeners.add(b); + } + return this; + } + + /** + * Return a configured instance of {@link AtmosphereConfig} + * + * @return a configured instance of {@link AtmosphereConfig} + */ + public AtmosphereConfig getAtmosphereConfig() { + return config; + } + + @Override + public ServletContext getServletContext() { + return servletConfig.getServletContext(); + } + + public ServletConfig getServletConfig() { + return servletConfig; + } + + /** + * Return the list of {@link BroadcastFilter} + * + * @return the list of {@link BroadcastFilter + */ + public List<String> broadcasterFilters() { + return broadcasterFilters; + } + + /** + * Returns true if {@link java.util.concurrent.ExecutorService} shared amongst all components. + * + * @return true if {@link java.util.concurrent.ExecutorService} shared amongst all components. + */ + public boolean isShareExecutorServices() { + return sharedThreadPools; + } + + /** + * Set to true to have a {@link java.util.concurrent.ExecutorService} shared amongst all components. + * + * @param sharedThreadPools + * @return this + */ + public AtmosphereFramework shareExecutorServices(boolean sharedThreadPools) { + this.sharedThreadPools = sharedThreadPools; + return this; + } + + protected void autoConfigureService(ServletContext sc) throws IOException { + final ClassLoader cl = Thread.currentThread().getContextClassLoader(); + + String path = libPath != DEFAULT_LIB_PATH ? libPath : sc.getRealPath(handlersPath); + try { + AnnotationProcessor p = (AnnotationProcessor) cl.loadClass(annotationProcessorClassName).newInstance(); + logger.info("Atmosphere is using {} for processing annotation", annotationProcessorClassName); + + p.configure(this); + if (path != null) { + p.scan(new File(path)); + } + + String pathLibs = sc.getRealPath(DEFAULT_LIB_PATH); + if (pathLibs != null) { + File libFolder = new File(pathLibs); + File jars[] = libFolder.listFiles(new FilenameFilter() { + + @Override + public boolean accept(File arg0, String arg1) { + return arg1.endsWith(".jar"); + } + }); + + for (File file : jars) { + p.scan(file); + } + } + } catch (Throwable e) { + logger.debug("Atmosphere's Service Annotation Not Supported. Please add https://github.com/rmuller/infomas-asl as dependencies or your own AnnotationProcessor to support @Service"); + logger.trace("", e); + return; + } + } + + protected void notify(Action.TYPE type, AtmosphereRequest request, AtmosphereResponse response) { + for (AsyncSupportListener l : asyncSupportListeners()) { + try { + switch (type) { + case TIMEOUT: + l.onTimeout(request, response); + break; + case CANCELLED: + l.onClose(request, response); + break; + case SUSPEND: + l.onSuspend(request, response); + break; + case RESUME: + l.onSuspend(request, response); + break; + case DESTROYED: + l.onDestroyed(request, response); + break; + } + } catch (Throwable t) { + logger.warn("", t); + } + } + } +}
\ No newline at end of file diff --git a/scripts/automerge7.sh b/scripts/automerge7.sh new file mode 100755 index 0000000000..ea83b2e52c --- /dev/null +++ b/scripts/automerge7.sh @@ -0,0 +1,131 @@ +#!/bin/bash + +FROM=7.0 +TO=7.1 + +FROM_HEAD=origin/$FROM +PUSH="origin HEAD:refs/for/$TO" + +show() { + sCommit=$1 + if [ "$sCommit" == "" ] + then + echo "show() missing commit id" + exit 1 + fi + git show -s $sCommit +} +merge() { + mCommit=$1 + if [ "$mCommit" == "" ] + then + echo "merge() missing commit id" + exit 1 + fi + +# echo "merge($mCommit)" + + git merge -m "Should be overwritten by merge script" $mCommit $2 + if [ "$?" != "0" ] + then + echo "Merge failed for commit $mCommit" + echo "Manual merge is needed" + exit 2 + fi + # Add a change id using git hook + git commit --amend --no-edit + +} + +pushMerged() { +# echo "pushMerged()" + git push $PUSH + if [ "$?" != "0" ] + then + echo "Push failed!" + exit 2 + fi +} + +maybe_commit_and_push() { +# echo "maybe_commit_and_push()" + cpCommit=$1 + if [ "$cpCommit" == "" ] + then + # Nothing to merge currently + return + fi + cpCommitMsg=$2 + if [ "$cpCommitMsg" == "" ] + then + echo "Internal error, no commit message passed to maybe_commit_and_push()" + exit 1; + fi +# echo "maybe_commit_and_push: Merging $cpCommit" + merge $cpCommit + echo -e "Merge changes from $FROM_HEAD\n\n$cpCommitMsg"|git commit --amend -F - + pushMerged +} + +nothingToCommit=`git status | grep "nothing to commit"` +if [ "$nothingToCommit" == "" ] +then + git status + echo "Can not merge when there are unstaged changes." + exit 1; +fi + +git checkout $TO +git fetch + +pending=`git log $TO..$FROM_HEAD --reverse|grep "^commit "|sed "s/commit //"` + +pendingCommit= +pendingCommitMessage= +for commit in $pending +do + echo "Checking $commit..." + mergeDirective=`git log -n 1 --format=%B $commit|grep "^Merge:"|sed "s/Merge: //"` + commitMsg=`git log -n 1 --format=oneline --abbrev-commit $commit` + if [ "$mergeDirective" == "" ] + then + pendingCommit=$commit + pendingCommitMessage=$pendingCommitMessage"$commitMsg\n" + echo pendingCommitMessage: $pendingCommitMessage + elif [ "$mergeDirective" == "no" ] + then + maybe_commit_and_push $pendingCommit "$pendingCommitMessage" + pendingCommit= + pendingCommitMessage= + echo + echo "Doing a no-op merge because of Merge: no for $commit" + git log -n 1 --format=%B $commit + echo + # Do a no-op merge + git merge $commit -s ours + echo -e "No-op merge from $FROM_HEAD\n\n$commitMsg"|git commit --amend -F - + pushMerged + elif [ "$mergeDirective" == "manual" ] + then + maybe_commit_and_push $pendingCommit "$pendingCommitMessage" + pendingCommit= + pendingCommitMessage= + echo + echo "Stopping merge at $commit (merge: manual)" + echo "The following commit must be manually merged." + show $commit + exit 3 + else + maybe_commit_and_push $pendingCommit "$pendingCommitMessage" + pendingCommit= + pendingCommitMessage= + echo + echo "Commit $commit contains an unknown merge directive, Merge: $mergeDirective" + echo "Stopping merge." + show $commit + exit 3 + fi +done + +# Push any pending merges +maybe_commit_and_push $pendingCommit "$pendingCommitMessage" diff --git a/server/build.xml b/server/build.xml index 91526dd1c5..d61f412883 100644 --- a/server/build.xml +++ b/server/build.xml @@ -13,7 +13,7 @@ <property name="module.symbolic" value="com.vaadin.server" /> <property name="result.dir" value="result" /> <path id="classpath.compile.custom" /> - <path id="classpath.tests.custom" /> + <path id="classpath.test.custom" /> <union id="jar.includes"> <union refid="server.gwt.includes" /> @@ -51,8 +51,8 @@ </antcall> </target> - <target name="tests" depends="checkstyle"> - <antcall target="common.tests.run" /> + <target name="test" depends="checkstyle"> + <antcall target="common.test.run" /> </target> diff --git a/server/ivy.xml b/server/ivy.xml index 6911a7054f..46d9e4c9f5 100644 --- a/server/ivy.xml +++ b/server/ivy.xml @@ -11,7 +11,7 @@ <conf name="build" /> <conf name="build-provided" /> <conf name="ide" visibility="private" /> - <conf name="tests" visibility="private" /> + <conf name="test" visibility="private" /> </configurations> <publications> <artifact type="jar" ext="jar" /> @@ -27,47 +27,55 @@ rev="6.0.2" conf="build-provided,ide -> default" /> <!--Servlet API version 2.4 --> <dependency org="javax.servlet" name="servlet-api" - rev="2.4" conf="build-provided,ide,tests -> default" /> + rev="2.4" conf="build-provided,ide,test -> default" /> <!--Portlet API version 2.0 (JSR-286) --> <dependency org="javax.portlet" name="portlet-api" - rev="2.0" conf="build-provided,ide,tests -> default" /> + rev="2.0" conf="build-provided,ide,test -> default" /> <!-- Google App Engine --> <dependency org="com.google.appengine" name="appengine-api-1.0-sdk" - rev="1.2.1" conf="build-provided,ide,tests -> default" /> + rev="1.2.1" conf="build-provided,ide,test -> default" /> <dependency org="javax.validation" name="validation-api" - rev="1.0.0.GA" conf="build-provided,ide,tests -> default" /> + rev="1.0.0.GA" conf="build-provided,ide,test -> default" /> <!-- LIBRARY DEPENDENCIES (compile time) --> <!-- Project modules --> <dependency org="com.vaadin" name="vaadin-shared" - rev="${vaadin.version}" conf="build,tests" /> + rev="${vaadin.version}" conf="build,test->build" /> <dependency org="com.vaadin" name="vaadin-theme-compiler" - rev="${vaadin.version}" conf="build,tests" /> + rev="${vaadin.version}" conf="build,test->build" /> + <dependency org="com.vaadin" name="vaadin-push" + rev="${vaadin.version}" conf="build-provided,test->build" /> <!-- Jsoup for BootstrapHandler --> <dependency org="org.jsoup" name="jsoup" rev="1.6.3" - conf="build,ide,tests -> default" /> + conf="build,ide,test -> default" /> <!-- TESTING DEPENDENCIES --> <!-- Test frameworks & related --> <dependency org="junit" name="junit" rev="4.5" - conf="tests,ide -> default" /> + conf="test,ide -> default" /> <dependency org="org.easymock" name="easymock" rev="3.0" - conf="tests,ide-> default" transitive="true" /> + conf="test,ide-> default" transitive="true" /> <dependency org="org.hsqldb" name="hsqldb" rev="2.2.6" - conf="tests,ide -> default" /> - <dependency org="commons-io" name="commons-io" rev="1.4" - conf="tests->default" /> + conf="test,ide -> default" /> + <dependency org="commons-io" name="commons-io" rev="2.2" + conf="test->default" /> <dependency org="commons-lang" name="commons-lang" - rev="2.6" conf="tests,ide->default" /> + rev="2.6" conf="test,ide->default" /> <!-- Bean Validation implementation --> <dependency org="org.slf4j" name="slf4j-log4j12" rev="1.6.1" - conf="tests -> default" /> + conf="test -> default" /> <dependency org="org.hibernate" name="hibernate-validator" - rev="4.2.0.Final" conf="tests -> default" /> + rev="4.2.0.Final" conf="test -> default" /> + + <!-- For manual testing with PostgreSQL (see SQLTestConstants) --> + <!-- + <dependency org="postgresql" name="postgresql" + rev="9.1-901.jdbc3" conf="test,ide->default" /> + --> </dependencies> diff --git a/server/src/com/vaadin/annotations/Push.java b/server/src/com/vaadin/annotations/Push.java new file mode 100644 index 0000000000..58e70acf21 --- /dev/null +++ b/server/src/com/vaadin/annotations/Push.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.annotations; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import com.vaadin.shared.communication.PushMode; +import com.vaadin.ui.UI; + +/** + * Configures server push for a {@link UI}. Adding <code>@Push</code> to a UI + * class configures the UI for automatic push. If some other push mode is + * desired, it can be passed as a parameter, e.g. + * <code>@Push(PushMode.MANUAL)</code>. + * + * @see PushMode + * + * @author Vaadin Ltd. + * @since 7.1 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface Push { + /** + * Returns the {@link PushMode} to use for the annotated UI. The default + * push mode when this annotation is present is {@link PushMode#AUTOMATIC}. + * + * @return the push mode to use + */ + public PushMode value() default PushMode.AUTOMATIC; + +} diff --git a/server/src/com/vaadin/data/Container.java b/server/src/com/vaadin/data/Container.java index ddeac62d6d..e93db52a35 100644 --- a/server/src/com/vaadin/data/Container.java +++ b/server/src/com/vaadin/data/Container.java @@ -953,6 +953,15 @@ public interface Container extends Serializable { */ public void removeAllContainerFilters(); + /** + * Returns the filters which have been applied to the container + * + * @return A collection of filters which have been applied to the + * container. An empty collection if no filters have been + * applied. + * @since 7.1 + */ + public Collection<Filter> getContainerFilters(); } /** diff --git a/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java b/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java index 9dc6037d83..0b4e3a8049 100644 --- a/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java +++ b/server/src/com/vaadin/data/fieldgroup/BeanFieldGroup.java @@ -58,6 +58,23 @@ public class BeanFieldGroup<T> extends FieldGroup { } } + @Override + protected Object findPropertyId(java.lang.reflect.Field memberField) { + String fieldName = memberField.getName(); + Item dataSource = getItemDataSource(); + if (dataSource != null && dataSource.getItemProperty(fieldName) != null) { + return fieldName; + } else { + String minifiedFieldName = minifyFieldName(fieldName); + try { + return getFieldName(beanType, minifiedFieldName); + } catch (SecurityException e) { + } catch (NoSuchFieldException e) { + } + } + return null; + } + private static java.lang.reflect.Field getField(Class<?> cls, String propertyId) throws SecurityException, NoSuchFieldException { if (propertyId.contains(".")) { @@ -75,7 +92,7 @@ public class BeanFieldGroup<T> extends FieldGroup { } catch (NoSuchFieldException e) { // Try super classes until we reach Object Class<?> superClass = cls.getSuperclass(); - if (superClass != Object.class) { + if (superClass != null && superClass != Object.class) { return getField(superClass, propertyId); } else { throw e; @@ -84,6 +101,22 @@ public class BeanFieldGroup<T> extends FieldGroup { } } + private static String getFieldName(Class<?> cls, String propertyId) + throws SecurityException, NoSuchFieldException { + for (java.lang.reflect.Field field1 : cls.getDeclaredFields()) { + if (propertyId.equals(minifyFieldName(field1.getName()))) { + return field1.getName(); + } + } + // Try super classes until we reach Object + Class<?> superClass = cls.getSuperclass(); + if (superClass != null && superClass != Object.class) { + return getFieldName(superClass, propertyId); + } else { + throw new NoSuchFieldException(); + } + } + /** * Helper method for setting the data source directly using a bean. This * method wraps the bean in a {@link BeanItem} and calls @@ -176,4 +209,4 @@ public class BeanFieldGroup<T> extends FieldGroup { } return beanValidationImplementationAvailable; } -}
\ No newline at end of file +} diff --git a/server/src/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java b/server/src/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java index 9ced6588f5..c1e4b4933e 100644 --- a/server/src/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java +++ b/server/src/com/vaadin/data/fieldgroup/DefaultFieldGroupFieldFactory.java @@ -15,18 +15,23 @@ */ package com.vaadin.data.fieldgroup; +import java.util.Date; import java.util.EnumSet; import com.vaadin.data.Item; import com.vaadin.data.fieldgroup.FieldGroup.BindException; +import com.vaadin.ui.AbstractField; import com.vaadin.ui.AbstractSelect; import com.vaadin.ui.AbstractTextField; import com.vaadin.ui.CheckBox; import com.vaadin.ui.ComboBox; +import com.vaadin.ui.DateField; import com.vaadin.ui.Field; +import com.vaadin.ui.InlineDateField; import com.vaadin.ui.ListSelect; import com.vaadin.ui.NativeSelect; import com.vaadin.ui.OptionGroup; +import com.vaadin.ui.PopupDateField; import com.vaadin.ui.RichTextArea; import com.vaadin.ui.Table; import com.vaadin.ui.TextField; @@ -39,6 +44,8 @@ public class DefaultFieldGroupFieldFactory implements FieldGroupFieldFactory { public <T extends Field> T createField(Class<?> type, Class<T> fieldType) { if (Enum.class.isAssignableFrom(type)) { return createEnumField(type, fieldType); + } else if (Date.class.isAssignableFrom(type)) { + return createDateField(type, fieldType); } else if (Boolean.class.isAssignableFrom(type) || boolean.class.isAssignableFrom(type)) { return createBooleanField(fieldType); @@ -70,6 +77,25 @@ public class DefaultFieldGroupFieldFactory implements FieldGroupFieldFactory { return null; } + private <T extends Field> T createDateField(Class<?> type, + Class<T> fieldType) { + AbstractField field; + + if (InlineDateField.class.isAssignableFrom(fieldType)) { + field = new InlineDateField(); + } else if (DateField.class.isAssignableFrom(fieldType) + || fieldType == Field.class) { + field = new PopupDateField(); + } else if (AbstractTextField.class.isAssignableFrom(fieldType)) { + field = createAbstractTextField((Class<? extends AbstractTextField>) fieldType); + } else { + return null; + } + + field.setImmediate(true); + return (T) field; + } + protected AbstractSelect createCompatibleSelect( Class<? extends AbstractSelect> fieldType) { AbstractSelect select; diff --git a/server/src/com/vaadin/data/fieldgroup/FieldGroup.java b/server/src/com/vaadin/data/fieldgroup/FieldGroup.java index dc1fdbb78d..981aea387d 100644 --- a/server/src/com/vaadin/data/fieldgroup/FieldGroup.java +++ b/server/src/com/vaadin/data/fieldgroup/FieldGroup.java @@ -733,11 +733,12 @@ public class FieldGroup implements Serializable { * that have not been initialized. * <p> * This method processes all (Java) member fields whose type extends - * {@link Field} and that can be mapped to a property id. Property id - * mapping is done based on the field name or on a @{@link PropertyId} - * annotation on the field. Fields that are not initialized (null) are built - * using the field factory. All non-null fields for which a property id can - * be determined are bound to the property id. + * {@link Field} and that can be mapped to a property id. Property ids are + * searched in the following order: @{@link PropertyId} annotations, exact + * field name matches and the case-insensitive matching that ignores + * underscores. Fields that are not initialized (null) are built using the + * field factory. All non-null fields for which a property id can be + * determined are bound to the property id. * </p> * <p> * For example: @@ -777,11 +778,12 @@ public class FieldGroup implements Serializable { * member fields that have not been initialized. * <p> * This method processes all (Java) member fields whose type extends - * {@link Field} and that can be mapped to a property id. Property id - * mapping is done based on the field name or on a @{@link PropertyId} - * annotation on the field. Fields that are not initialized (null) are built - * using the field factory is buildFields is true. All non-null fields for - * which a property id can be determined are bound to the property id. + * {@link Field} and that can be mapped to a property id. Property ids are + * searched in the following order: @{@link PropertyId} annotations, exact + * field name matches and the case-insensitive matching that ignores + * underscores. Fields that are not initialized (null) are built using the + * field factory is buildFields is true. All non-null fields for which a + * property id can be determined are bound to the property id. * </p> * * @param objectWithMemberFields @@ -812,7 +814,16 @@ public class FieldGroup implements Serializable { // @PropertyId(propertyId) always overrides property id propertyId = propertyIdAnnotation.value(); } else { - propertyId = memberField.getName(); + try { + propertyId = findPropertyId(memberField); + } catch (SearchException e) { + // Property id was not found, skip this field + continue; + } + if (propertyId == null) { + // Property id was not found, skip this field + continue; + } } // Ensure that the property id exists @@ -873,6 +884,55 @@ public class FieldGroup implements Serializable { } } + /** + * Searches for a property id from the current itemDataSource that matches + * the given memberField. + * <p> + * If perfect match is not found, uses a case insensitive search that also + * ignores underscores. Returns null if no match is found. Throws a + * SearchException if no item data source has been set. + * </p> + * <p> + * The propertyId search logic used by + * {@link #buildAndBindMemberFields(Object, boolean) + * buildAndBindMemberFields} can easily be customized by overriding this + * method. No other changes are needed. + * </p> + * + * @param memberField + * The field an object id is searched for + * @return + */ + protected Object findPropertyId(java.lang.reflect.Field memberField) { + String fieldName = memberField.getName(); + if (getItemDataSource() == null) { + throw new SearchException( + "Property id type for field '" + + fieldName + + "' could not be determined. No item data source has been set."); + } + Item dataSource = getItemDataSource(); + if (dataSource.getItemProperty(fieldName) != null) { + return fieldName; + } else { + String minifiedFieldName = minifyFieldName(fieldName); + for (Object itemPropertyId : dataSource.getItemPropertyIds()) { + if (itemPropertyId instanceof String) { + String itemPropertyName = (String) itemPropertyId; + if (minifiedFieldName + .equals(minifyFieldName(itemPropertyName))) { + return itemPropertyName; + } + } + } + } + return null; + } + + protected static String minifyFieldName(String fieldName) { + return fieldName.toLowerCase().replace("_", ""); + } + public static class CommitException extends Exception { public CommitException() { @@ -909,6 +969,18 @@ public class FieldGroup implements Serializable { } + public static class SearchException extends RuntimeException { + + public SearchException(String message) { + super(message); + } + + public SearchException(String message, Throwable t) { + super(message, t); + } + + } + /** * Builds a field and binds it to the given property id using the field * binder. diff --git a/server/src/com/vaadin/data/util/AbstractBeanContainer.java b/server/src/com/vaadin/data/util/AbstractBeanContainer.java index db1e1afe0d..35403d6419 100644 --- a/server/src/com/vaadin/data/util/AbstractBeanContainer.java +++ b/server/src/com/vaadin/data/util/AbstractBeanContainer.java @@ -385,6 +385,26 @@ public abstract class AbstractBeanContainer<IDTYPE, BEANTYPE> extends removeFilter(filter); } + /* + * (non-Javadoc) + * + * @see com.vaadin.data.util.AbstractInMemoryContainer#hasContainerFilters() + */ + @Override + public boolean hasContainerFilters() { + return super.hasContainerFilters(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.util.AbstractInMemoryContainer#getContainerFilters() + */ + @Override + public Collection<Filter> getContainerFilters() { + return super.getContainerFilters(); + } + /** * Make this container listen to the given property provided it notifies * when its value changes. diff --git a/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java b/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java index 504b4081c1..84304431bc 100644 --- a/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java +++ b/server/src/com/vaadin/data/util/AbstractInMemoryContainer.java @@ -501,6 +501,25 @@ public abstract class AbstractInMemoryContainer<ITEMIDTYPE, PROPERTYIDCLASS, ITE } /** + * Returns true if any filters have been applied to the container. + * + * @return true if the container has filters applied, false otherwise + * @since 7.1 + */ + protected boolean hasContainerFilters() { + return !getContainerFilters().isEmpty(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Filterable#getContainerFilters() + */ + protected Collection<Filter> getContainerFilters() { + return Collections.unmodifiableCollection(filters); + } + + /** * Remove a specific container filter and re-filter the view (if necessary). * * This can be used to implement diff --git a/server/src/com/vaadin/data/util/AbstractProperty.java b/server/src/com/vaadin/data/util/AbstractProperty.java index 499421a8b4..903f2f50f2 100644 --- a/server/src/com/vaadin/data/util/AbstractProperty.java +++ b/server/src/com/vaadin/data/util/AbstractProperty.java @@ -18,7 +18,6 @@ package com.vaadin.data.util; import java.util.Collection; import java.util.Collections; import java.util.LinkedList; -import java.util.logging.Level; import java.util.logging.Logger; import com.vaadin.data.Property; @@ -71,27 +70,33 @@ public abstract class AbstractProperty<T> implements Property<T>, } /** - * Returns the value of the <code>Property</code> in human readable textual - * format. + * Returns a string representation of this object. The returned string + * representation depends on if the legacy Property toString mode is enabled + * or disabled. + * <p> + * If legacy Property toString mode is enabled, returns the value of the + * <code>Property</code> converted to a String. + * </p> + * <p> + * If legacy Property toString mode is disabled, the string representation + * has no special meaning + * </p> * - * @return String representation of the value stored in the Property - * @deprecated As of 7.0, use {@link #getValue()} instead and possibly - * toString on that + * @see LegacyPropertyHelper#isLegacyToStringEnabled() + * + * @return A string representation of the value value stored in the Property + * or a string representation of the Property object. + * @deprecated As of 7.0. To get the property value, use {@link #getValue()} + * instead (and possibly toString on that) */ @Deprecated @Override public String toString() { - getLogger() - .log(Level.WARNING, - "You are using Property.toString() instead of getValue() to get the value for a {0}." - + "This will not be supported starting from Vaadin 7.1 " - + "(your debugger might call toString() and cause this message to appear).", - getClass().getSimpleName()); - T v = getValue(); - if (v == null) { - return null; + if (!LegacyPropertyHelper.isLegacyToStringEnabled()) { + return super.toString(); + } else { + return LegacyPropertyHelper.legacyPropertyToString(this); } - return v.toString(); } /* Events */ diff --git a/server/src/com/vaadin/data/util/IndexedContainer.java b/server/src/com/vaadin/data/util/IndexedContainer.java index 1df4dd9bfb..d7bf70caf6 100644 --- a/server/src/com/vaadin/data/util/IndexedContainer.java +++ b/server/src/com/vaadin/data/util/IndexedContainer.java @@ -28,7 +28,6 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.logging.Level; import java.util.logging.Logger; import com.vaadin.data.Container; @@ -954,29 +953,32 @@ public class IndexedContainer extends } /** - * Returns the value of the Property in human readable textual format. - * The return value should be assignable to the <code>setValue</code> - * method if the Property is not in read-only mode. + * Returns a string representation of this object. The returned string + * representation depends on if the legacy Property toString mode is + * enabled or disabled. + * <p> + * If legacy Property toString mode is enabled, returns the value of the + * <code>Property</code> converted to a String. + * </p> + * <p> + * If legacy Property toString mode is disabled, the string + * representation has no special meaning + * </p> * - * @return <code>String</code> representation of the value stored in the - * Property - * @deprecated As of 7.0, use {@link #getValue()} instead and possibly - * toString on that + * @return A string representation of the value value stored in the + * Property or a string representation of the Property object. + * @deprecated As of 7.0. To get the property value, use + * {@link #getValue()} instead (and possibly toString on + * that) */ @Deprecated @Override public String toString() { - getLogger() - .log(Level.WARNING, - "You are using IndexedContainerProperty.toString() instead of getValue() to get the value for a {0}." - + " This will not be supported starting from Vaadin 7.1 " - + "(your debugger might call toString() and cause this message to appear).", - getClass().getSimpleName()); - Object v = getValue(); - if (v == null) { - return null; + if (!LegacyPropertyHelper.isLegacyToStringEnabled()) { + return super.toString(); + } else { + return LegacyPropertyHelper.legacyPropertyToString(this); } - return v.toString(); } private Logger getLogger() { @@ -1190,4 +1192,23 @@ public class IndexedContainer extends removeFilter(filter); } + /* + * (non-Javadoc) + * + * @see com.vaadin.data.util.AbstractInMemoryContainer#getContainerFilters() + */ + @Override + public boolean hasContainerFilters() { + return super.hasContainerFilters(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.util.AbstractInMemoryContainer#getContainerFilters() + */ + @Override + public Collection<Filter> getContainerFilters() { + return super.getContainerFilters(); + } } diff --git a/server/src/com/vaadin/data/util/LegacyPropertyHelper.java b/server/src/com/vaadin/data/util/LegacyPropertyHelper.java new file mode 100644 index 0000000000..0276e35dbf --- /dev/null +++ b/server/src/com/vaadin/data/util/LegacyPropertyHelper.java @@ -0,0 +1,102 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.data.util; + +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.vaadin.data.Property; +import com.vaadin.server.Constants; +import com.vaadin.server.DeploymentConfiguration.LegacyProperyToStringMode; +import com.vaadin.server.VaadinService; + +/** + * Helper class which provides methods for handling Property.toString in a + * Vaadin 6 compatible way + * + * @author Vaadin Ltd + * @since 7.1 + * @deprecated This is only used internally for backwards compatibility + */ +@Deprecated +public class LegacyPropertyHelper { + + /** + * Returns the property value converted to a String. + * + * @param p + * The property + * @return A string representation of the property value, compatible with + * how Property implementations in Vaadin 6 do it + */ + public static String legacyPropertyToString(Property p) { + maybeLogLegacyPropertyToStringWarning(p); + Object value = p.getValue(); + if (value == null) { + return null; + } + return value.toString(); + } + + public static void maybeLogLegacyPropertyToStringWarning(Property p) { + if (!logLegacyToStringWarning()) { + return; + } + + getLogger().log(Level.WARNING, + Constants.WARNING_LEGACY_PROPERTY_TOSTRING, + p.getClass().getName()); + } + + /** + * Checks if legacy Property.toString() implementation is enabled. The + * legacy Property.toString() will return the value of the property somehow + * converted to a String. If the legacy mode is disabled, toString() will + * return super.toString(). + * <p> + * The legacy toString mode can be toggled using the + * "legacyPropertyToString" init parameter + * </p> + * + * @return true if legacy Property.toString() mode is enabled, false + * otherwise + */ + public static boolean isLegacyToStringEnabled() { + if (VaadinService.getCurrent() == null) { + // This should really not happen but we need to handle it somehow. + // IF it happens it seems more safe to use the legacy mode and log. + return true; + } + return VaadinService.getCurrent().getDeploymentConfiguration() + .getLegacyPropertyToStringMode().useLegacyMode(); + } + + private static boolean logLegacyToStringWarning() { + if (VaadinService.getCurrent() == null) { + // This should really not happen but we need to handle it somehow. + // IF it happens it seems more safe to use the legacy mode and log. + return true; + } + return VaadinService.getCurrent().getDeploymentConfiguration() + .getLegacyPropertyToStringMode() == LegacyProperyToStringMode.WARNING; + + } + + private static Logger getLogger() { + return Logger.getLogger(LegacyPropertyHelper.class.getName()); + } + +} diff --git a/server/src/com/vaadin/data/util/sqlcontainer/ColumnProperty.java b/server/src/com/vaadin/data/util/sqlcontainer/ColumnProperty.java index 378372d044..d8448a2b50 100644 --- a/server/src/com/vaadin/data/util/sqlcontainer/ColumnProperty.java +++ b/server/src/com/vaadin/data/util/sqlcontainer/ColumnProperty.java @@ -18,10 +18,10 @@ package com.vaadin.data.util.sqlcontainer; import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; -import java.util.logging.Level; import java.util.logging.Logger; import com.vaadin.data.Property; +import com.vaadin.data.util.LegacyPropertyHelper; import com.vaadin.data.util.converter.Converter.ConversionException; /** @@ -255,26 +255,33 @@ final public class ColumnProperty implements Property { } /** - * Returns the value of the Property in human readable textual format. + * Returns a string representation of this object. The returned string + * representation depends on if the legacy Property toString mode is enabled + * or disabled. + * <p> + * If legacy Property toString mode is enabled, returns the value of this + * <code>Property</code> converted to a String. + * </p> + * <p> + * If legacy Property toString mode is disabled, the string representation + * has no special meaning + * </p> * - * @see java.lang.Object#toString() - * @deprecated As of 7.0, use {@link #getValue()} instead and possibly - * toString on that + * @see LegacyPropertyHelper#isLegacyToStringEnabled() + * + * @return A string representation of the value value stored in the Property + * or a string representation of the Property object. + * @deprecated As of 7.0. To get the property value, use {@link #getValue()} + * instead (and possibly toString on that) */ @Deprecated @Override public String toString() { - getLogger() - .log(Level.WARNING, - "You are using ColumnProperty.toString() instead of getValue() to get the value for a {0}. " - + "This will not be supported starting from Vaadin 7.1 (your debugger might call toString() " - + "and cause this message to appear).", - getClass().getSimpleName()); - Object v = getValue(); - if (v == null) { - return null; + if (!LegacyPropertyHelper.isLegacyToStringEnabled()) { + return super.toString(); + } else { + return LegacyPropertyHelper.legacyPropertyToString(this); } - return v.toString(); } private static Logger getLogger() { diff --git a/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java b/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java index 64c16b2798..987466cdb7 100644 --- a/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java +++ b/server/src/com/vaadin/data/util/sqlcontainer/SQLContainer.java @@ -590,13 +590,32 @@ public class SQLContainer implements Container, Container.Filterable, /** * {@inheritDoc} */ - @Override public void removeAllContainerFilters() { filters.clear(); refresh(); } + /** + * Returns true if any filters have been applied to the container. + * + * @return true if the container has filters applied, false otherwise + * @since 7.1 + */ + public boolean hasContainerFilters() { + return !getContainerFilters().isEmpty(); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.data.Container.Filterable#getContainerFilters() + */ + @Override + public Collection<Filter> getContainerFilters() { + return Collections.unmodifiableCollection(filters); + } + /**********************************************/ /** Methods from interface Container.Indexed **/ /**********************************************/ @@ -1818,4 +1837,5 @@ public class SQLContainer implements Container, Container.Filterable, private static final Logger getLogger() { return Logger.getLogger(SQLContainer.class.getName()); } + } diff --git a/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java b/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java index caed5526e3..39c8365076 100644 --- a/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java +++ b/server/src/com/vaadin/data/util/sqlcontainer/query/TableQuery.java @@ -50,9 +50,23 @@ import com.vaadin.data.util.sqlcontainer.query.generator.StatementHelper; public class TableQuery extends AbstractTransactionalQuery implements QueryDelegate, QueryDelegate.RowIdChangeNotifier { - /** Table name, primary key column name(s) and version column name */ + /** + * Table name (without catalog or schema information). + */ private String tableName; + private String catalogName; + private String schemaName; + /** + * Cached concatenated version of the table name. + */ + private String fullTableName; + /** + * Primary key column name(s) in the table. + */ private List<String> primaryKeyColumns; + /** + * Version column name in the table. + */ private String versionColumn; /** Currently set Filters and OrderBys */ @@ -70,15 +84,15 @@ public class TableQuery extends AbstractTransactionalQuery implements /** Set to true to output generated SQL Queries to System.out */ private final boolean debug = false; - /** Prevent no-parameters instantiation of TableQuery */ - @SuppressWarnings("unused") - private TableQuery() { - } - /** * Creates a new TableQuery using the given connection pool, SQL generator * and table name to fetch the data from. All parameters must be non-null. * + * The table name must be a simple name with no catalog or schema + * information. If those are needed, use + * {@link #TableQuery(String, String, String, JDBCConnectionPool, SQLGenerator)} + * . + * * @param tableName * Name of the database table to connect to * @param connectionPool @@ -88,15 +102,30 @@ public class TableQuery extends AbstractTransactionalQuery implements */ public TableQuery(String tableName, JDBCConnectionPool connectionPool, SQLGenerator sqlGenerator) { - super(connectionPool); - if (tableName == null || tableName.trim().length() < 1 - || connectionPool == null || sqlGenerator == null) { - throw new IllegalArgumentException( - "All parameters must be non-null and a table name must be given."); - } - this.tableName = tableName; - this.sqlGenerator = sqlGenerator; - fetchMetaData(); + this(null, null, tableName, connectionPool, sqlGenerator); + } + + /** + * Creates a new TableQuery using the given connection pool, SQL generator + * and table name to fetch the data from. Catalog and schema names can be + * null, all other parameters must be non-null. + * + * @param catalogName + * Name of the database catalog (can be null) + * @param schemaName + * Name of the database schema (can be null) + * @param tableName + * Name of the database table to connect to + * @param connectionPool + * Connection pool for accessing the database + * @param sqlGenerator + * SQL query generator implementation + * @since 7.1 + */ + public TableQuery(String catalogName, String schemaName, String tableName, + JDBCConnectionPool connectionPool, SQLGenerator sqlGenerator) { + this(catalogName, schemaName, tableName, connectionPool, sqlGenerator, + true); } /** @@ -104,6 +133,11 @@ public class TableQuery extends AbstractTransactionalQuery implements * to fetch the data from. All parameters must be non-null. The default SQL * generator will be used for queries. * + * The table name must be a simple name with no catalog or schema + * information. If those are needed, use + * {@link #TableQuery(String, String, String, JDBCConnectionPool, SQLGenerator)} + * . + * * @param tableName * Name of the database table to connect to * @param connectionPool @@ -113,6 +147,48 @@ public class TableQuery extends AbstractTransactionalQuery implements this(tableName, connectionPool, new DefaultSQLGenerator()); } + /** + * Creates a new TableQuery using the given connection pool, SQL generator + * and table name to fetch the data from. Catalog and schema names can be + * null, all other parameters must be non-null. + * + * @param catalogName + * Name of the database catalog (can be null) + * @param schemaName + * Name of the database schema (can be null) + * @param tableName + * Name of the database table to connect to + * @param connectionPool + * Connection pool for accessing the database + * @param sqlGenerator + * SQL query generator implementation + * @param escapeNames + * true to escape special characters in catalog, schema and table + * names, false to use the names as-is + * @since 7.1 + */ + protected TableQuery(String catalogName, String schemaName, + String tableName, JDBCConnectionPool connectionPool, + SQLGenerator sqlGenerator, boolean escapeNames) { + super(connectionPool); + if (tableName == null || tableName.trim().length() < 1 + || connectionPool == null || sqlGenerator == null) { + throw new IllegalArgumentException( + "Table name, connection pool and SQL generator parameters must be non-null and non-empty."); + } + if (escapeNames) { + this.catalogName = SQLUtil.escapeSQL(catalogName); + this.schemaName = SQLUtil.escapeSQL(schemaName); + this.tableName = SQLUtil.escapeSQL(tableName); + } else { + this.catalogName = catalogName; + this.schemaName = schemaName; + this.tableName = tableName; + } + this.sqlGenerator = sqlGenerator; + fetchMetaData(); + } + /* * (non-Javadoc) * @@ -121,8 +197,8 @@ public class TableQuery extends AbstractTransactionalQuery implements @Override public int getCount() throws SQLException { getLogger().log(Level.FINE, "Fetching count..."); - StatementHelper sh = sqlGenerator.generateSelectQuery(tableName, - filters, null, 0, 0, "COUNT(*)"); + StatementHelper sh = sqlGenerator.generateSelectQuery( + getFullTableName(), filters, null, 0, 0, "COUNT(*)"); boolean shouldCloseTransaction = false; if (!isInTransaction()) { shouldCloseTransaction = true; @@ -167,11 +243,11 @@ public class TableQuery extends AbstractTransactionalQuery implements for (int i = 0; i < primaryKeyColumns.size(); i++) { ob.add(new OrderBy(primaryKeyColumns.get(i), true)); } - sh = sqlGenerator.generateSelectQuery(tableName, filters, ob, - offset, pagelength, null); + sh = sqlGenerator.generateSelectQuery(getFullTableName(), filters, + ob, offset, pagelength, null); } else { - sh = sqlGenerator.generateSelectQuery(tableName, filters, orderBys, - offset, pagelength, null); + sh = sqlGenerator.generateSelectQuery(getFullTableName(), filters, + orderBys, offset, pagelength, null); } return executeQuery(sh); } @@ -204,11 +280,11 @@ public class TableQuery extends AbstractTransactionalQuery implements int result = 0; if (row.getId() instanceof TemporaryRowId) { setVersionColumnFlagInProperty(row); - sh = sqlGenerator.generateInsertQuery(tableName, row); + sh = sqlGenerator.generateInsertQuery(getFullTableName(), row); result = executeUpdateReturnKeys(sh, row); } else { setVersionColumnFlagInProperty(row); - sh = sqlGenerator.generateUpdateQuery(tableName, row); + sh = sqlGenerator.generateUpdateQuery(getFullTableName(), row); result = executeUpdate(sh); } if (versionColumn != null && result == 0) { @@ -244,7 +320,8 @@ public class TableQuery extends AbstractTransactionalQuery implements /* Set version column, if one is provided */ setVersionColumnFlagInProperty(row); /* Generate query */ - StatementHelper sh = sqlGenerator.generateInsertQuery(tableName, row); + StatementHelper sh = sqlGenerator.generateInsertQuery( + getFullTableName(), row); Connection connection = null; PreparedStatement pstmt = null; ResultSet generatedKeys = null; @@ -371,10 +448,61 @@ public class TableQuery extends AbstractTransactionalQuery implements versionColumn = column; } + /** + * Returns the table name for the query without catalog and schema + * information. + * + * @return table name, not null + */ public String getTableName() { return tableName; } + /** + * Returns the catalog name for the query. + * + * @return catalog name, can be null + * @since 7.1 + */ + public String getCatalogName() { + return catalogName; + } + + /** + * Returns the catalog name for the query. + * + * @return catalog name, can be null + * @since 7.1 + */ + public String getSchemaName() { + return schemaName; + } + + /** + * Returns the complete table name obtained by concatenation of the catalog + * and schema names (if any) and the table name. + * + * This method can be overridden if customization is needed. + * + * @return table name in the form it should be used in query and update + * statements + * @since 7.1 + */ + protected String getFullTableName() { + if (fullTableName == null) { + StringBuilder sb = new StringBuilder(); + if (catalogName != null) { + sb.append(catalogName).append("."); + } + if (schemaName != null) { + sb.append(schemaName).append("."); + } + sb.append(tableName); + fullTableName = sb.toString(); + } + return fullTableName; + } + public SQLGenerator getSqlGenerator() { return sqlGenerator; } @@ -480,22 +608,28 @@ public class TableQuery extends AbstractTransactionalQuery implements connection = getConnection(); DatabaseMetaData dbmd = connection.getMetaData(); if (dbmd != null) { - tableName = SQLUtil.escapeSQL(tableName); - tables = dbmd.getTables(null, null, tableName, null); + tables = dbmd.getTables(catalogName, schemaName, tableName, + null); if (!tables.next()) { - tables = dbmd.getTables(null, null, + String catalog = (catalogName != null) ? catalogName + .toUpperCase() : null; + String schema = (schemaName != null) ? schemaName + .toUpperCase() : null; + tables = dbmd.getTables(catalog, schema, tableName.toUpperCase(), null); if (!tables.next()) { throw new IllegalArgumentException( "Table with the name \"" - + tableName + + getFullTableName() + "\" was not found. Check your database contents."); } else { + catalogName = catalog; + schemaName = schema; tableName = tableName.toUpperCase(); } } tables.close(); - rs = dbmd.getPrimaryKeys(null, null, tableName); + rs = dbmd.getPrimaryKeys(catalogName, schemaName, tableName); List<String> names = new ArrayList<String>(); while (rs.next()) { names.add(rs.getString("COLUMN_NAME")); @@ -507,7 +641,7 @@ public class TableQuery extends AbstractTransactionalQuery implements if (primaryKeyColumns == null || primaryKeyColumns.isEmpty()) { throw new IllegalArgumentException( "Primary key constraints have not been defined for the table \"" - + tableName + + getFullTableName() + "\". Use FreeFormQuery to access this table."); } for (String colName : primaryKeyColumns) { @@ -592,7 +726,7 @@ public class TableQuery extends AbstractTransactionalQuery implements getLogger().log(Level.FINE, "Removing row with id: {0}", row.getId().getId()[0]); } - if (executeUpdate(sqlGenerator.generateDeleteQuery(getTableName(), + if (executeUpdate(sqlGenerator.generateDeleteQuery(getFullTableName(), primaryKeyColumns, versionColumn, row)) == 1) { return true; } @@ -622,8 +756,8 @@ public class TableQuery extends AbstractTransactionalQuery implements filtersAndKeys.add(new Equal(colName, keys[ix])); ix++; } - StatementHelper sh = sqlGenerator.generateSelectQuery(tableName, - filtersAndKeys, orderBys, 0, 0, "*"); + StatementHelper sh = sqlGenerator.generateSelectQuery( + getFullTableName(), filtersAndKeys, orderBys, 0, 0, "*"); boolean shouldCloseTransaction = false; if (!isInTransaction()) { diff --git a/server/src/com/vaadin/server/AbstractClientConnector.java b/server/src/com/vaadin/server/AbstractClientConnector.java index cf579585ea..e998b8ed55 100644 --- a/server/src/com/vaadin/server/AbstractClientConnector.java +++ b/server/src/com/vaadin/server/AbstractClientConnector.java @@ -134,6 +134,7 @@ public abstract class AbstractClientConnector implements ClientConnector, /* Documentation copied from interface */ @Override public void markAsDirty() { + assert getSession() == null || getSession().hasLock() : "Session must be locked when markAsDirty() is called"; UI uI = getUI(); if (uI != null) { uI.getConnectorTracker().markDirty(this); @@ -218,6 +219,8 @@ public abstract class AbstractClientConnector implements ClientConnector, * @see #getState() */ protected SharedState getState(boolean markAsDirty) { + assert getSession() == null || getSession().hasLock() : "Session must be locked when getState() is called"; + if (null == sharedState) { sharedState = createState(); } @@ -233,7 +236,7 @@ public abstract class AbstractClientConnector implements ClientConnector, @Override public JSONObject encodeState() throws JSONException { - return AbstractCommunicationManager.encodeState(this, getState()); + return LegacyCommunicationManager.encodeState(this, getState()); } /** @@ -642,17 +645,22 @@ public abstract class AbstractClientConnector implements ClientConnector, @Override public boolean handleConnectorRequest(VaadinRequest request, VaadinResponse response, String path) throws IOException { + DownloadStream stream = null; String[] parts = path.split("/", 2); String key = parts[0]; - ConnectorResource resource = (ConnectorResource) getResource(key); - if (resource != null) { - DownloadStream stream = resource.getStream(); - stream.writeResponse(request, response); - return true; - } else { - return false; + getSession().lock(); + try { + ConnectorResource resource = (ConnectorResource) getResource(key); + if (resource == null) { + return false; + } + stream = resource.getStream(); + } finally { + getSession().unlock(); } + stream.writeResponse(request, response); + return true; } /** diff --git a/server/src/com/vaadin/server/AbstractCommunicationManager.java b/server/src/com/vaadin/server/AbstractCommunicationManager.java deleted file mode 100644 index 17bbbda737..0000000000 --- a/server/src/com/vaadin/server/AbstractCommunicationManager.java +++ /dev/null @@ -1,2881 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.server; - -import java.io.BufferedWriter; -import java.io.ByteArrayOutputStream; -import java.io.CharArrayWriter; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.io.Serializable; -import java.io.StringWriter; -import java.lang.reflect.Type; -import java.net.URI; -import java.net.URISyntaxException; -import java.security.GeneralSecurityException; -import java.text.CharacterIterator; -import java.text.DateFormat; -import java.text.DateFormatSymbols; -import java.text.SimpleDateFormat; -import java.text.StringCharacterIterator; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collection; -import java.util.Collections; -import java.util.Comparator; -import java.util.GregorianCalendar; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.servlet.http.HttpServletResponse; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import com.vaadin.annotations.JavaScript; -import com.vaadin.annotations.PreserveOnRefresh; -import com.vaadin.annotations.StyleSheet; -import com.vaadin.server.ClientConnector.ConnectorErrorEvent; -import com.vaadin.server.ComponentSizeValidator.InvalidLayout; -import com.vaadin.server.ServerRpcManager.RpcInvocationException; -import com.vaadin.server.StreamVariable.StreamingEndEvent; -import com.vaadin.server.StreamVariable.StreamingErrorEvent; -import com.vaadin.shared.ApplicationConstants; -import com.vaadin.shared.Connector; -import com.vaadin.shared.JavaScriptConnectorState; -import com.vaadin.shared.Version; -import com.vaadin.shared.communication.LegacyChangeVariablesInvocation; -import com.vaadin.shared.communication.MethodInvocation; -import com.vaadin.shared.communication.ServerRpc; -import com.vaadin.shared.communication.SharedState; -import com.vaadin.shared.communication.UidlValue; -import com.vaadin.shared.ui.ui.UIConstants; -import com.vaadin.ui.Component; -import com.vaadin.ui.ConnectorTracker; -import com.vaadin.ui.HasComponents; -import com.vaadin.ui.LegacyComponent; -import com.vaadin.ui.SelectiveRenderer; -import com.vaadin.ui.UI; -import com.vaadin.ui.Window; - -/** - * This is a common base class for the server-side implementations of the - * communication system between the client code (compiled with GWT into - * JavaScript) and the server side components. Its client side counterpart is - * {@link com.vaadin.client.ApplicationConnection}. - * <p> - * TODO Document better! - * - * @deprecated As of 7.0. Will likely change or be removed in a future version - */ -@Deprecated -@SuppressWarnings("serial") -public abstract class AbstractCommunicationManager implements Serializable { - - private static final String DASHDASH = "--"; - - private static final RequestHandler UNSUPPORTED_BROWSER_HANDLER = new UnsupportedBrowserHandler(); - - private static final RequestHandler CONNECTOR_RESOURCE_HANDLER = new ConnectorResourceHandler(); - - /** - * TODO Document me! - * - * @author peholmst - * - * @deprecated As of 7.0. Will likely change or be removed in a future - * version - */ - @Deprecated - public interface Callback extends Serializable { - - public void criticalNotification(VaadinRequest request, - VaadinResponse response, String cap, String msg, - String details, String outOfSyncURL) throws IOException; - } - - static class UploadInterruptedException extends Exception { - public UploadInterruptedException() { - super("Upload interrupted by other thread"); - } - } - - // flag used in the request to indicate that the security token should be - // written to the response - private static final String WRITE_SECURITY_TOKEN_FLAG = "writeSecurityToken"; - - /* Variable records indexes */ - public static final char VAR_BURST_SEPARATOR = '\u001d'; - - public static final char VAR_ESCAPE_CHARACTER = '\u001b'; - - private final HashMap<Integer, ClientCache> uiToClientCache = new HashMap<Integer, ClientCache>(); - - private static final int MAX_BUFFER_SIZE = 64 * 1024; - - /* Same as in apache commons file upload library that was previously used. */ - private static final int MAX_UPLOAD_BUFFER_SIZE = 4 * 1024; - - /** - * The session this communication manager is used for - */ - private final VaadinSession session; - - private List<String> locales; - - private int pendingLocalesIndex; - - private int timeoutInterval = -1; - - private DragAndDropService dragAndDropService; - - private String requestThemeName; - - private int maxInactiveInterval; - - private ClientConnector highlightedConnector; - - private Map<String, Class<?>> publishedFileContexts = new HashMap<String, Class<?>>(); - - /** - * TODO New constructor - document me! - * - * @param session - */ - public AbstractCommunicationManager(VaadinSession session) { - this.session = session; - session.addRequestHandler(getBootstrapHandler()); - session.addRequestHandler(UNSUPPORTED_BROWSER_HANDLER); - session.addRequestHandler(CONNECTOR_RESOURCE_HANDLER); - requireLocale(session.getLocale().toString()); - } - - protected VaadinSession getSession() { - return session; - } - - private static final int LF = "\n".getBytes()[0]; - - private static final String CRLF = "\r\n"; - - private static final String UTF8 = "UTF-8"; - - private static String readLine(InputStream stream) throws IOException { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - int readByte = stream.read(); - while (readByte != LF) { - bout.write(readByte); - readByte = stream.read(); - } - byte[] bytes = bout.toByteArray(); - return new String(bytes, 0, bytes.length - 1, UTF8); - } - - /** - * Method used to stream content from a multipart request (either from - * servlet or portlet request) to given StreamVariable - * - * - * @param request - * @param response - * @param streamVariable - * @param owner - * @param boundary - * @throws IOException - */ - protected void doHandleSimpleMultipartFileUpload(VaadinRequest request, - VaadinResponse response, StreamVariable streamVariable, - String variableName, ClientConnector owner, String boundary) - throws IOException { - // multipart parsing, supports only one file for request, but that is - // fine for our current terminal - - final InputStream inputStream = request.getInputStream(); - - int contentLength = request.getContentLength(); - - boolean atStart = false; - boolean firstFileFieldFound = false; - - String rawfilename = "unknown"; - String rawMimeType = "application/octet-stream"; - - /* - * Read the stream until the actual file starts (empty line). Read - * filename and content type from multipart headers. - */ - while (!atStart) { - String readLine = readLine(inputStream); - contentLength -= (readLine.getBytes(UTF8).length + CRLF.length()); - if (readLine.startsWith("Content-Disposition:") - && readLine.indexOf("filename=") > 0) { - rawfilename = readLine.replaceAll(".*filename=", ""); - char quote = rawfilename.charAt(0); - rawfilename = rawfilename.substring(1); - rawfilename = rawfilename.substring(0, - rawfilename.indexOf(quote)); - firstFileFieldFound = true; - } else if (firstFileFieldFound && readLine.equals("")) { - atStart = true; - } else if (readLine.startsWith("Content-Type")) { - rawMimeType = readLine.split(": ")[1]; - } - } - - contentLength -= (boundary.length() + CRLF.length() + 2 - * DASHDASH.length() + CRLF.length()); - - /* - * Reads bytes from the underlying stream. Compares the read bytes to - * the boundary string and returns -1 if met. - * - * The matching happens so that if the read byte equals to the first - * char of boundary string, the stream goes to "buffering mode". In - * buffering mode bytes are read until the character does not match the - * corresponding from boundary string or the full boundary string is - * found. - * - * Note, if this is someday needed elsewhere, don't shoot yourself to - * foot and split to a top level helper class. - */ - InputStream simpleMultiPartReader = new SimpleMultiPartInputStream( - inputStream, boundary); - - /* - * Should report only the filename even if the browser sends the path - */ - final String filename = removePath(rawfilename); - final String mimeType = rawMimeType; - - try { - // TODO Shouldn't this check connectorEnabled? - if (owner == null) { - throw new UploadException( - "File upload ignored because the connector for the stream variable was not found"); - } - if (owner instanceof Component) { - if (((Component) owner).isReadOnly()) { - throw new UploadException( - "Warning: file upload ignored because the componente was read-only"); - } - } - boolean forgetVariable = streamToReceiver(simpleMultiPartReader, - streamVariable, filename, mimeType, contentLength); - if (forgetVariable) { - cleanStreamVariable(owner, variableName); - } - } catch (Exception e) { - session.lock(); - try { - handleConnectorRelatedException(owner, e); - } finally { - session.unlock(); - } - } - sendUploadResponse(request, response); - - } - - /** - * Used to stream plain file post (aka XHR2.post(File)) - * - * @param request - * @param response - * @param streamVariable - * @param owner - * @param contentLength - * @throws IOException - */ - protected void doHandleXhrFilePost(VaadinRequest request, - VaadinResponse response, StreamVariable streamVariable, - String variableName, ClientConnector owner, int contentLength) - throws IOException { - - // These are unknown in filexhr ATM, maybe add to Accept header that - // is accessible in portlets - final String filename = "unknown"; - final String mimeType = filename; - final InputStream stream = request.getInputStream(); - try { - /* - * safe cast as in GWT terminal all variable owners are expected to - * be components. - */ - Component component = (Component) owner; - if (component.isReadOnly()) { - throw new UploadException( - "Warning: file upload ignored because the component was read-only"); - } - boolean forgetVariable = streamToReceiver(stream, streamVariable, - filename, mimeType, contentLength); - if (forgetVariable) { - cleanStreamVariable(owner, variableName); - } - } catch (Exception e) { - session.lock(); - try { - handleConnectorRelatedException(owner, e); - } finally { - session.unlock(); - } - } - sendUploadResponse(request, response); - } - - /** - * @param in - * @param streamVariable - * @param filename - * @param type - * @param contentLength - * @return true if the streamvariable has informed that the terminal can - * forget this variable - * @throws UploadException - */ - protected final boolean streamToReceiver(final InputStream in, - StreamVariable streamVariable, String filename, String type, - int contentLength) throws UploadException { - if (streamVariable == null) { - throw new IllegalStateException( - "StreamVariable for the post not found"); - } - - final VaadinSession session = getSession(); - - OutputStream out = null; - int totalBytes = 0; - StreamingStartEventImpl startedEvent = new StreamingStartEventImpl( - filename, type, contentLength); - try { - boolean listenProgress; - session.lock(); - try { - streamVariable.streamingStarted(startedEvent); - out = streamVariable.getOutputStream(); - listenProgress = streamVariable.listenProgress(); - } finally { - session.unlock(); - } - - // Gets the output target stream - if (out == null) { - throw new NoOutputStreamException(); - } - - if (null == in) { - // No file, for instance non-existent filename in html upload - throw new NoInputStreamException(); - } - - final byte buffer[] = new byte[MAX_UPLOAD_BUFFER_SIZE]; - int bytesReadToBuffer = 0; - while ((bytesReadToBuffer = in.read(buffer)) > 0) { - out.write(buffer, 0, bytesReadToBuffer); - totalBytes += bytesReadToBuffer; - if (listenProgress) { - // update progress if listener set and contentLength - // received - session.lock(); - try { - StreamingProgressEventImpl progressEvent = new StreamingProgressEventImpl( - filename, type, contentLength, totalBytes); - streamVariable.onProgress(progressEvent); - } finally { - session.unlock(); - } - } - if (streamVariable.isInterrupted()) { - throw new UploadInterruptedException(); - } - } - - // upload successful - out.close(); - StreamingEndEvent event = new StreamingEndEventImpl(filename, type, - totalBytes); - session.lock(); - try { - streamVariable.streamingFinished(event); - } finally { - session.unlock(); - } - - } catch (UploadInterruptedException e) { - // Download interrupted by application code - tryToCloseStream(out); - StreamingErrorEvent event = new StreamingErrorEventImpl(filename, - type, contentLength, totalBytes, e); - session.lock(); - try { - streamVariable.streamingFailed(event); - } finally { - session.unlock(); - } - // Note, we are not throwing interrupted exception forward as it is - // not a terminal level error like all other exception. - } catch (final Exception e) { - tryToCloseStream(out); - session.lock(); - try { - StreamingErrorEvent event = new StreamingErrorEventImpl( - filename, type, contentLength, totalBytes, e); - streamVariable.streamingFailed(event); - // throw exception for terminal to be handled (to be passed to - // terminalErrorHandler) - throw new UploadException(e); - } finally { - session.unlock(); - } - } - return startedEvent.isDisposed(); - } - - static void tryToCloseStream(OutputStream out) { - try { - // try to close output stream (e.g. file handle) - if (out != null) { - out.close(); - } - } catch (IOException e1) { - // NOP - } - } - - /** - * Removes any possible path information from the filename and returns the - * filename. Separators / and \\ are used. - * - * @param name - * @return - */ - private static String removePath(String filename) { - if (filename != null) { - filename = filename.replaceAll("^.*[/\\\\]", ""); - } - - return filename; - } - - /** - * TODO document - * - * @param request - * @param response - * @throws IOException - */ - protected void sendUploadResponse(VaadinRequest request, - VaadinResponse response) throws IOException { - response.setContentType("text/html"); - final OutputStream out = response.getOutputStream(); - final PrintWriter outWriter = new PrintWriter(new BufferedWriter( - new OutputStreamWriter(out, "UTF-8"))); - outWriter.print("<html><body>download handled</body></html>"); - outWriter.flush(); - out.close(); - } - - /** - * Internally process a UIDL request from the client. - * - * This method calls - * {@link #handleVariables(VaadinRequest, VaadinResponse, Callback, VaadinSession, UI)} - * to process any changes to variables by the client and then repaints - * affected components using {@link #paintAfterVariableChanges()}. - * - * Also, some cleanup is done when a request arrives for an session that has - * already been closed. - * - * The method handleUidlRequest(...) in subclasses should call this method. - * - * TODO better documentation - * - * @param request - * @param response - * @param callback - * @param uI - * target window for the UIDL request, can be null if target not - * found - * @throws IOException - * @throws InvalidUIDLSecurityKeyException - * @throws JSONException - */ - public void handleUidlRequest(VaadinRequest request, - VaadinResponse response, Callback callback, UI uI) - throws IOException, InvalidUIDLSecurityKeyException, JSONException { - - checkWidgetsetVersion(request); - requestThemeName = request.getParameter("theme"); - maxInactiveInterval = request.getWrappedSession() - .getMaxInactiveInterval(); - // repaint requested or session has timed out and new one is created - boolean repaintAll; - final OutputStream out; - - repaintAll = (request - .getParameter(ApplicationConstants.URL_PARAMETER_REPAINT_ALL) != null); - // || (request.getSession().isNew()); FIXME What the h*ll is this?? - out = response.getOutputStream(); - - boolean analyzeLayouts = false; - if (repaintAll) { - // analyzing can be done only with repaintAll - analyzeLayouts = (request - .getParameter(ApplicationConstants.PARAM_ANALYZE_LAYOUTS) != null); - - String pid = request - .getParameter(ApplicationConstants.PARAM_HIGHLIGHT_CONNECTOR); - if (pid != null) { - highlightedConnector = uI.getConnectorTracker().getConnector( - pid); - highlightConnector(highlightedConnector); - } - } - - final PrintWriter outWriter = new PrintWriter(new BufferedWriter( - new OutputStreamWriter(out, "UTF-8"))); - - // The rest of the process is synchronized with the session - // in order to guarantee that no parallel variable handling is - // made - session.lock(); - try { - - // Verify that there's an UI - if (uI == null) { - // This should not happen, no windows exists but - // session is still open. - getLogger().warning("Could not get UI for session"); - return; - } - - session.setLastRequestTimestamp(System.currentTimeMillis()); - - // Change all variables based on request parameters - if (!handleVariables(request, response, callback, session, uI)) { - - // var inconsistency; the client is probably out-of-sync - SystemMessages ci = response.getService().getSystemMessages( - uI.getLocale(), request); - String msg = ci.getOutOfSyncMessage(); - String cap = ci.getOutOfSyncCaption(); - if (msg != null || cap != null) { - callback.criticalNotification(request, response, cap, msg, - null, ci.getOutOfSyncURL()); - // will reload page after this - return; - } - // No message to show, let's just repaint all. - repaintAll = true; - } - - paintAfterVariableChanges(request, response, callback, repaintAll, - outWriter, uI, analyzeLayouts); - postPaint(uI); - } finally { - session.unlock(); - } - - outWriter.close(); - requestThemeName = null; - } - - /** - * Checks that the version reported by the client (widgetset) matches that - * of the server. - * - * @param request - */ - private void checkWidgetsetVersion(VaadinRequest request) { - String widgetsetVersion = request.getParameter("v-wsver"); - if (widgetsetVersion == null) { - // Only check when the widgetset version is reported. It is reported - // in the first UIDL request (not the initial request as it is a - // plain GET /) - return; - } - - if (!Version.getFullVersion().equals(widgetsetVersion)) { - getLogger().warning( - String.format(Constants.WIDGETSET_MISMATCH_INFO, - Version.getFullVersion(), widgetsetVersion)); - } - } - - /** - * Method called after the paint phase while still being synchronized on the - * session - * - * @param uI - * - */ - protected void postPaint(UI uI) { - // Remove connectors that have been detached from the session during - // handling of the request - uI.getConnectorTracker().cleanConnectorMap(); - } - - protected void highlightConnector(ClientConnector highlightedConnector) { - StringBuilder sb = new StringBuilder(); - sb.append("*** Debug details of a connector: *** \n"); - sb.append("Type: "); - sb.append(highlightedConnector.getClass().getName()); - sb.append("\nId:"); - sb.append(highlightedConnector.getConnectorId()); - if (highlightedConnector instanceof Component) { - Component component = (Component) highlightedConnector; - if (component.getCaption() != null) { - sb.append("\nCaption:"); - sb.append(component.getCaption()); - } - } - printHighlightedConnectorHierarchy(sb, highlightedConnector); - getLogger().info(sb.toString()); - } - - protected void printHighlightedConnectorHierarchy(StringBuilder sb, - ClientConnector connector) { - LinkedList<ClientConnector> h = new LinkedList<ClientConnector>(); - h.add(connector); - ClientConnector parent = connector.getParent(); - while (parent != null) { - h.addFirst(parent); - parent = parent.getParent(); - } - - sb.append("\nConnector hierarchy:\n"); - VaadinSession session2 = connector.getUI().getSession(); - sb.append(session2.getClass().getName()); - sb.append("("); - sb.append(session2.getClass().getSimpleName()); - sb.append(".java"); - sb.append(":1)"); - int l = 1; - for (ClientConnector connector2 : h) { - sb.append("\n"); - for (int i = 0; i < l; i++) { - sb.append(" "); - } - l++; - Class<? extends ClientConnector> connectorClass = connector2 - .getClass(); - Class<?> topClass = connectorClass; - while (topClass.getEnclosingClass() != null) { - topClass = topClass.getEnclosingClass(); - } - sb.append(connectorClass.getName()); - sb.append("("); - sb.append(topClass.getSimpleName()); - sb.append(".java:1)"); - } - } - - /** - * TODO document - * - * @param request - * @param response - * @param callback - * @param repaintAll - * @param outWriter - * @param window - * @param analyzeLayouts - * @throws PaintException - * @throws IOException - * @throws JSONException - */ - private void paintAfterVariableChanges(VaadinRequest request, - VaadinResponse response, Callback callback, boolean repaintAll, - final PrintWriter outWriter, UI uI, boolean analyzeLayouts) - throws PaintException, IOException, JSONException { - openJsonMessage(outWriter, response); - - // security key - Object writeSecurityTokenFlag = request - .getAttribute(WRITE_SECURITY_TOKEN_FLAG); - - if (writeSecurityTokenFlag != null) { - outWriter.print(getSecurityKeyUIDL(request)); - } - - writeUidlResponse(request, repaintAll, outWriter, uI, analyzeLayouts); - - closeJsonMessage(outWriter); - - outWriter.close(); - - } - - /** - * Gets the security key (and generates one if needed) as UIDL. - * - * @param request - * @return the security key UIDL or "" if the feature is turned off - */ - public String getSecurityKeyUIDL(VaadinRequest request) { - final String seckey = getSecurityKey(request); - if (seckey != null) { - return "\"" + ApplicationConstants.UIDL_SECURITY_TOKEN_ID + "\":\"" - + seckey + "\","; - } else { - return ""; - } - } - - /** - * Gets the security key (and generates one if needed). - * - * @param request - * @return the security key - */ - protected String getSecurityKey(VaadinRequest request) { - String seckey = null; - WrappedSession session = request.getWrappedSession(); - seckey = (String) session - .getAttribute(ApplicationConstants.UIDL_SECURITY_TOKEN_ID); - if (seckey == null) { - seckey = UUID.randomUUID().toString(); - session.setAttribute(ApplicationConstants.UIDL_SECURITY_TOKEN_ID, - seckey); - } - - return seckey; - } - - @SuppressWarnings("unchecked") - public void writeUidlResponse(VaadinRequest request, boolean repaintAll, - final PrintWriter outWriter, UI ui, boolean analyzeLayouts) - throws PaintException, JSONException { - ArrayList<ClientConnector> dirtyVisibleConnectors = new ArrayList<ClientConnector>(); - VaadinSession session = ui.getSession(); - // Paints components - ConnectorTracker uiConnectorTracker = ui.getConnectorTracker(); - getLogger().log(Level.FINE, "* Creating response to client"); - if (repaintAll) { - getClientCache(ui).clear(); - uiConnectorTracker.markAllConnectorsDirty(); - uiConnectorTracker.markAllClientSidesUninitialized(); - - // Reset sent locales - locales = null; - requireLocale(session.getLocale().toString()); - } - - dirtyVisibleConnectors - .addAll(getDirtyVisibleConnectors(uiConnectorTracker)); - - getLogger().log(Level.FINE, "Found {0} dirty connectors to paint", - dirtyVisibleConnectors.size()); - for (ClientConnector connector : dirtyVisibleConnectors) { - boolean initialized = uiConnectorTracker - .isClientSideInitialized(connector); - connector.beforeClientResponse(!initialized); - } - - uiConnectorTracker.setWritingResponse(true); - try { - outWriter.print("\"changes\":["); - - List<InvalidLayout> invalidComponentRelativeSizes = null; - - JsonPaintTarget paintTarget = new JsonPaintTarget(this, outWriter, - !repaintAll); - legacyPaint(paintTarget, dirtyVisibleConnectors); - - if (analyzeLayouts) { - invalidComponentRelativeSizes = ComponentSizeValidator - .validateComponentRelativeSizes(ui.getContent(), null, - null); - - // Also check any existing subwindows - if (ui.getWindows() != null) { - for (Window subWindow : ui.getWindows()) { - invalidComponentRelativeSizes = ComponentSizeValidator - .validateComponentRelativeSizes( - subWindow.getContent(), - invalidComponentRelativeSizes, null); - } - } - } - - paintTarget.close(); - outWriter.print("], "); // close changes - - // send shared state to client - - // for now, send the complete state of all modified and new - // components - - // Ideally, all this would be sent before "changes", but that causes - // complications with legacy components that create sub-components - // in their paint phase. Nevertheless, this will be processed on the - // client after component creation but before legacy UIDL - // processing. - JSONObject sharedStates = new JSONObject(); - for (ClientConnector connector : dirtyVisibleConnectors) { - // encode and send shared state - try { - JSONObject stateJson = connector.encodeState(); - - if (stateJson != null && stateJson.length() != 0) { - sharedStates.put(connector.getConnectorId(), stateJson); - } - } catch (JSONException e) { - throw new PaintException( - "Failed to serialize shared state for connector " - + connector.getClass().getName() + " (" - + connector.getConnectorId() + "): " - + e.getMessage(), e); - } - } - outWriter.print("\"state\":"); - outWriter.append(sharedStates.toString()); - outWriter.print(", "); // close states - - // TODO This should be optimized. The type only needs to be - // sent once for each connector id + on refresh. Use the same cache - // as - // widget mapping - - JSONObject connectorTypes = new JSONObject(); - for (ClientConnector connector : dirtyVisibleConnectors) { - String connectorType = paintTarget.getTag(connector); - try { - connectorTypes.put(connector.getConnectorId(), - connectorType); - } catch (JSONException e) { - throw new PaintException( - "Failed to send connector type for connector " - + connector.getConnectorId() + ": " - + e.getMessage(), e); - } - } - outWriter.print("\"types\":"); - outWriter.append(connectorTypes.toString()); - outWriter.print(", "); // close states - - // Send update hierarchy information to the client. - - // This could be optimized aswell to send only info if hierarchy has - // actually changed. Much like with the shared state. Note though - // that an empty hierarchy is information aswell (e.g. change from 1 - // child to 0 children) - - outWriter.print("\"hierarchy\":"); - - JSONObject hierarchyInfo = new JSONObject(); - for (ClientConnector connector : dirtyVisibleConnectors) { - String connectorId = connector.getConnectorId(); - JSONArray children = new JSONArray(); - - for (ClientConnector child : AbstractClientConnector - .getAllChildrenIterable(connector)) { - if (isConnectorVisibleToClient(child)) { - children.put(child.getConnectorId()); - } - } - try { - hierarchyInfo.put(connectorId, children); - } catch (JSONException e) { - throw new PaintException( - "Failed to send hierarchy information about " - + connectorId + " to the client: " - + e.getMessage(), e); - } - } - outWriter.append(hierarchyInfo.toString()); - outWriter.print(", "); // close hierarchy - - uiConnectorTracker.markAllConnectorsClean(); - - // send server to client RPC calls for components in the UI, in call - // order - - // collect RPC calls from components in the UI in the order in - // which they were performed, remove the calls from components - - LinkedList<ClientConnector> rpcPendingQueue = new LinkedList<ClientConnector>( - dirtyVisibleConnectors); - List<ClientMethodInvocation> pendingInvocations = collectPendingRpcCalls(dirtyVisibleConnectors); - - JSONArray rpcCalls = new JSONArray(); - for (ClientMethodInvocation invocation : pendingInvocations) { - // add invocation to rpcCalls - try { - JSONArray invocationJson = new JSONArray(); - invocationJson.put(invocation.getConnector() - .getConnectorId()); - invocationJson.put(invocation.getInterfaceName()); - invocationJson.put(invocation.getMethodName()); - JSONArray paramJson = new JSONArray(); - for (int i = 0; i < invocation.getParameterTypes().length; ++i) { - Type parameterType = invocation.getParameterTypes()[i]; - Object referenceParameter = null; - // TODO Use default values for RPC parameter types - // if (!JsonCodec.isInternalType(parameterType)) { - // try { - // referenceParameter = parameterType.newInstance(); - // } catch (Exception e) { - // logger.log(Level.WARNING, - // "Error creating reference object for parameter of type " - // + parameterType.getName()); - // } - // } - EncodeResult encodeResult = JsonCodec.encode( - invocation.getParameters()[i], - referenceParameter, parameterType, - ui.getConnectorTracker()); - paramJson.put(encodeResult.getEncodedValue()); - } - invocationJson.put(paramJson); - rpcCalls.put(invocationJson); - } catch (JSONException e) { - throw new PaintException( - "Failed to serialize RPC method call parameters for connector " - + invocation.getConnector() - .getConnectorId() + " method " - + invocation.getInterfaceName() + "." - + invocation.getMethodName() + ": " - + e.getMessage(), e); - } - - } - - if (rpcCalls.length() > 0) { - outWriter.print("\"rpc\" : "); - outWriter.append(rpcCalls.toString()); - outWriter.print(", "); // close rpc - } - - outWriter.print("\"meta\" : {"); - boolean metaOpen = false; - - if (repaintAll) { - metaOpen = true; - outWriter.write("\"repaintAll\":true"); - if (analyzeLayouts) { - outWriter.write(", \"invalidLayouts\":"); - outWriter.write("["); - if (invalidComponentRelativeSizes != null) { - boolean first = true; - for (InvalidLayout invalidLayout : invalidComponentRelativeSizes) { - if (!first) { - outWriter.write(","); - } else { - first = false; - } - invalidLayout.reportErrors(outWriter, this, - System.err); - } - } - outWriter.write("]"); - } - if (highlightedConnector != null) { - outWriter.write(", \"hl\":\""); - outWriter.write(highlightedConnector.getConnectorId()); - outWriter.write("\""); - highlightedConnector = null; - } - } - - SystemMessages ci = request.getService().getSystemMessages( - ui.getLocale(), request); - - // meta instruction for client to enable auto-forward to - // sessionExpiredURL after timer expires. - if (ci != null && ci.getSessionExpiredMessage() == null - && ci.getSessionExpiredCaption() == null - && ci.isSessionExpiredNotificationEnabled()) { - int newTimeoutInterval = getTimeoutInterval(); - if (repaintAll || (timeoutInterval != newTimeoutInterval)) { - String escapedURL = ci.getSessionExpiredURL() == null ? "" - : ci.getSessionExpiredURL().replace("/", "\\/"); - if (metaOpen) { - outWriter.write(","); - } - outWriter.write("\"timedRedirect\":{\"interval\":" - + (newTimeoutInterval + 15) + ",\"url\":\"" - + escapedURL + "\"}"); - metaOpen = true; - } - timeoutInterval = newTimeoutInterval; - } - - outWriter.print("}, \"resources\" : {"); - - // Precache custom layouts - - // TODO We should only precache the layouts that are not - // cached already (plagiate from usedPaintableTypes) - int resourceIndex = 0; - for (final Iterator<Object> i = paintTarget.getUsedResources() - .iterator(); i.hasNext();) { - final String resource = (String) i.next(); - InputStream is = null; - try { - is = getThemeResourceAsStream(ui, getTheme(ui), resource); - } catch (final Exception e) { - // FIXME: Handle exception - getLogger().log(Level.FINER, - "Failed to get theme resource stream.", e); - } - if (is != null) { - - outWriter.print((resourceIndex++ > 0 ? ", " : "") + "\"" - + resource + "\" : "); - final StringBuffer layout = new StringBuffer(); - - try { - final InputStreamReader r = new InputStreamReader(is, - "UTF-8"); - final char[] buffer = new char[20000]; - int charsRead = 0; - while ((charsRead = r.read(buffer)) > 0) { - layout.append(buffer, 0, charsRead); - } - r.close(); - } catch (final java.io.IOException e) { - // FIXME: Handle exception - getLogger().log(Level.INFO, "Resource transfer failed", - e); - } - outWriter.print("\"" - + JsonPaintTarget.escapeJSON(layout.toString()) - + "\""); - } else { - // FIXME: Handle exception - getLogger().severe("CustomLayout not found: " + resource); - } - } - outWriter.print("}"); - - Collection<Class<? extends ClientConnector>> usedClientConnectors = paintTarget - .getUsedClientConnectors(); - boolean typeMappingsOpen = false; - ClientCache clientCache = getClientCache(ui); - - List<Class<? extends ClientConnector>> newConnectorTypes = new ArrayList<Class<? extends ClientConnector>>(); - - for (Class<? extends ClientConnector> class1 : usedClientConnectors) { - if (clientCache.cache(class1)) { - // client does not know the mapping key for this type, send - // mapping to client - newConnectorTypes.add(class1); - - if (!typeMappingsOpen) { - typeMappingsOpen = true; - outWriter.print(", \"typeMappings\" : { "); - } else { - outWriter.print(" , "); - } - String canonicalName = class1.getCanonicalName(); - outWriter.print("\""); - outWriter.print(canonicalName); - outWriter.print("\" : "); - outWriter.print(getTagForType(class1)); - } - } - if (typeMappingsOpen) { - outWriter.print(" }"); - } - - boolean typeInheritanceMapOpen = false; - if (typeMappingsOpen) { - // send the whole type inheritance map if any new mappings - for (Class<? extends ClientConnector> class1 : usedClientConnectors) { - if (!ClientConnector.class.isAssignableFrom(class1 - .getSuperclass())) { - continue; - } - if (!typeInheritanceMapOpen) { - typeInheritanceMapOpen = true; - outWriter.print(", \"typeInheritanceMap\" : { "); - } else { - outWriter.print(" , "); - } - outWriter.print("\""); - outWriter.print(getTagForType(class1)); - outWriter.print("\" : "); - outWriter - .print(getTagForType((Class<? extends ClientConnector>) class1 - .getSuperclass())); - } - if (typeInheritanceMapOpen) { - outWriter.print(" }"); - } - } - - /* - * Ensure super classes come before sub classes to get script - * dependency order right. Sub class @JavaScript might assume that - * - * @JavaScript defined by super class is already loaded. - */ - Collections.sort(newConnectorTypes, new Comparator<Class<?>>() { - @Override - public int compare(Class<?> o1, Class<?> o2) { - // TODO optimize using Class.isAssignableFrom? - return hierarchyDepth(o1) - hierarchyDepth(o2); - } - - private int hierarchyDepth(Class<?> type) { - if (type == Object.class) { - return 0; - } else { - return hierarchyDepth(type.getSuperclass()) + 1; - } - } - }); - - List<String> scriptDependencies = new ArrayList<String>(); - List<String> styleDependencies = new ArrayList<String>(); - - for (Class<? extends ClientConnector> class1 : newConnectorTypes) { - JavaScript jsAnnotation = class1 - .getAnnotation(JavaScript.class); - if (jsAnnotation != null) { - for (String uri : jsAnnotation.value()) { - scriptDependencies.add(registerDependency(uri, class1)); - } - } - - StyleSheet styleAnnotation = class1 - .getAnnotation(StyleSheet.class); - if (styleAnnotation != null) { - for (String uri : styleAnnotation.value()) { - styleDependencies.add(registerDependency(uri, class1)); - } - } - } - - // Include script dependencies in output if there are any - if (!scriptDependencies.isEmpty()) { - outWriter.print(", \"scriptDependencies\": " - + new JSONArray(scriptDependencies).toString()); - } - - // Include style dependencies in output if there are any - if (!styleDependencies.isEmpty()) { - outWriter.print(", \"styleDependencies\": " - + new JSONArray(styleDependencies).toString()); - } - - // add any pending locale definitions requested by the client - printLocaleDeclarations(outWriter); - - if (dragAndDropService != null) { - dragAndDropService.printJSONResponse(outWriter); - } - - for (ClientConnector connector : dirtyVisibleConnectors) { - uiConnectorTracker.markClientSideInitialized(connector); - } - - assert (uiConnectorTracker.getDirtyConnectors().isEmpty()) : "Connectors have been marked as dirty during the end of the paint phase. This is most certainly not intended."; - - writePerformanceData(outWriter); - } finally { - uiConnectorTracker.setWritingResponse(false); - } - } - - public static JSONObject encodeState(ClientConnector connector, - SharedState state) throws JSONException { - UI uI = connector.getUI(); - ConnectorTracker connectorTracker = uI.getConnectorTracker(); - Class<? extends SharedState> stateType = connector.getStateType(); - Object diffState = connectorTracker.getDiffState(connector); - boolean supportsDiffState = !JavaScriptConnectorState.class - .isAssignableFrom(stateType); - if (diffState == null && supportsDiffState) { - // Use an empty state object as reference for full - // repaints - - try { - SharedState referenceState = stateType.newInstance(); - EncodeResult encodeResult = JsonCodec.encode(referenceState, - null, stateType, uI.getConnectorTracker()); - diffState = encodeResult.getEncodedValue(); - } catch (Exception e) { - getLogger() - .log(Level.WARNING, - "Error creating reference object for state of type {0}", - stateType.getName()); - } - } - EncodeResult encodeResult = JsonCodec.encode(state, diffState, - stateType, uI.getConnectorTracker()); - if (supportsDiffState) { - connectorTracker.setDiffState(connector, - (JSONObject) encodeResult.getEncodedValue()); - } - return (JSONObject) encodeResult.getDiff(); - } - - /** - * Resolves a dependency URI, registering the URI with this - * {@code AbstractCommunicationManager} if needed and returns a fully - * qualified URI. - */ - private String registerDependency(String resourceUri, Class<?> context) { - try { - URI uri = new URI(resourceUri); - String protocol = uri.getScheme(); - - if (ApplicationConstants.PUBLISHED_PROTOCOL_NAME.equals(protocol)) { - // Strip initial slash - String resourceName = uri.getPath().substring(1); - return registerPublishedFile(resourceName, context); - } - - if (protocol != null || uri.getHost() != null) { - return resourceUri; - } - - // Bare path interpreted as published file - return registerPublishedFile(resourceUri, context); - } catch (URISyntaxException e) { - getLogger().log(Level.WARNING, - "Could not parse resource url " + resourceUri, e); - return resourceUri; - } - } - - private String registerPublishedFile(String name, Class<?> context) { - synchronized (publishedFileContexts) { - // Add to map of names accepted by servePublishedFile - if (publishedFileContexts.containsKey(name)) { - Class<?> oldContext = publishedFileContexts.get(name); - if (oldContext != context) { - getLogger() - .log(Level.WARNING, - "{0} published by both {1} and {2}. File from {2} will be used.", - new Object[] { name, context, oldContext }); - } - } else { - publishedFileContexts.put(name, context); - } - } - - return ApplicationConstants.PUBLISHED_PROTOCOL_PREFIX + "/" + name; - } - - /** - * Adds the performance timing data (used by TestBench 3) to the UIDL - * response. - */ - private void writePerformanceData(final PrintWriter outWriter) { - outWriter.write(String.format(", \"timings\":[%d, %d]", - session.getCumulativeRequestDuration(), - session.getLastRequestDuration())); - } - - private void legacyPaint(PaintTarget paintTarget, - ArrayList<ClientConnector> dirtyVisibleConnectors) - throws PaintException { - List<LegacyComponent> legacyComponents = new ArrayList<LegacyComponent>(); - for (Connector connector : dirtyVisibleConnectors) { - // All Components that want to use paintContent must implement - // LegacyComponent - if (connector instanceof LegacyComponent) { - legacyComponents.add((LegacyComponent) connector); - } - } - sortByHierarchy((List) legacyComponents); - for (LegacyComponent c : legacyComponents) { - if (getLogger().isLoggable(Level.FINE)) { - getLogger().log( - Level.FINE, - "Painting LegacyComponent {0}@{1}", - new Object[] { c.getClass().getName(), - Integer.toHexString(c.hashCode()) }); - } - paintTarget.startTag("change"); - final String pid = c.getConnectorId(); - paintTarget.addAttribute("pid", pid); - LegacyPaint.paint(c, paintTarget); - paintTarget.endTag("change"); - } - - } - - private void sortByHierarchy(List<Component> paintables) { - // Vaadin 6 requires parents to be painted before children as component - // containers rely on that their updateFromUIDL method has been called - // before children start calling e.g. updateCaption - Collections.sort(paintables, new Comparator<Component>() { - - @Override - public int compare(Component c1, Component c2) { - int depth1 = 0; - while (c1.getParent() != null) { - depth1++; - c1 = c1.getParent(); - } - int depth2 = 0; - while (c2.getParent() != null) { - depth2++; - c2 = c2.getParent(); - } - if (depth1 < depth2) { - return -1; - } - if (depth1 > depth2) { - return 1; - } - return 0; - } - }); - - } - - private ClientCache getClientCache(UI uI) { - Integer uiId = Integer.valueOf(uI.getUIId()); - ClientCache cache = uiToClientCache.get(uiId); - if (cache == null) { - cache = new ClientCache(); - uiToClientCache.put(uiId, cache); - } - return cache; - } - - /** - * Checks if the connector is visible in context. For Components, - * {@link #isComponentVisibleToClient(Component)} is used. For other types - * of connectors, the contextual visibility of its first Component ancestor - * is used. If no Component ancestor is found, the connector is not visible. - * - * @param connector - * The connector to check - * @return <code>true</code> if the connector is visible to the client, - * <code>false</code> otherwise - */ - public static boolean isConnectorVisibleToClient(ClientConnector connector) { - if (connector instanceof Component) { - return isComponentVisibleToClient((Component) connector); - } else { - ClientConnector parent = connector.getParent(); - if (parent == null) { - return false; - } else { - return isConnectorVisibleToClient(parent); - } - } - } - - /** - * Checks if the component should be visible to the client. Returns false if - * the child should not be sent to the client, true otherwise. - * - * @param child - * The child to check - * @return true if the child is visible to the client, false otherwise - */ - public static boolean isComponentVisibleToClient(Component child) { - if (!child.isVisible()) { - return false; - } - HasComponents parent = child.getParent(); - - if (parent instanceof SelectiveRenderer) { - if (!((SelectiveRenderer) parent).isRendered(child)) { - return false; - } - } - - if (parent != null) { - return isComponentVisibleToClient(parent); - } else { - if (child instanceof UI) { - // UI has no parent and visibility was checked above - return true; - } else { - // Component which is not attached to any UI - return false; - } - } - } - - private static class NullIterator<E> implements Iterator<E> { - - @Override - public boolean hasNext() { - return false; - } - - @Override - public E next() { - return null; - } - - @Override - public void remove() { - } - - } - - /** - * Collects all pending RPC calls from listed {@link ClientConnector}s and - * clears their RPC queues. - * - * @param rpcPendingQueue - * list of {@link ClientConnector} of interest - * @return ordered list of pending RPC calls - */ - private List<ClientMethodInvocation> collectPendingRpcCalls( - List<ClientConnector> rpcPendingQueue) { - List<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>(); - for (ClientConnector connector : rpcPendingQueue) { - List<ClientMethodInvocation> paintablePendingRpc = connector - .retrievePendingRpcCalls(); - if (null != paintablePendingRpc && !paintablePendingRpc.isEmpty()) { - List<ClientMethodInvocation> oldPendingRpc = pendingInvocations; - int totalCalls = pendingInvocations.size() - + paintablePendingRpc.size(); - pendingInvocations = new ArrayList<ClientMethodInvocation>( - totalCalls); - - // merge two ordered comparable lists - for (int destIndex = 0, oldIndex = 0, paintableIndex = 0; destIndex < totalCalls; destIndex++) { - if (paintableIndex >= paintablePendingRpc.size() - || (oldIndex < oldPendingRpc.size() && ((Comparable<ClientMethodInvocation>) oldPendingRpc - .get(oldIndex)) - .compareTo(paintablePendingRpc - .get(paintableIndex)) <= 0)) { - pendingInvocations.add(oldPendingRpc.get(oldIndex++)); - } else { - pendingInvocations.add(paintablePendingRpc - .get(paintableIndex++)); - } - } - } - } - return pendingInvocations; - } - - protected abstract InputStream getThemeResourceAsStream(UI uI, - String themeName, String resource); - - private int getTimeoutInterval() { - return maxInactiveInterval; - } - - private String getTheme(UI uI) { - String themeName = uI.getTheme(); - String requestThemeName = getRequestTheme(); - - if (requestThemeName != null) { - themeName = requestThemeName; - } - if (themeName == null) { - themeName = VaadinServlet.getDefaultTheme(); - } - return themeName; - } - - private String getRequestTheme() { - return requestThemeName; - } - - /** - * Returns false if the cross site request forgery protection is turned off. - * - * @param session - * @return false if the XSRF is turned off, true otherwise - */ - public boolean isXSRFEnabled(VaadinSession session) { - return session.getConfiguration().isXsrfProtectionEnabled(); - } - - /** - * TODO document - * - * If this method returns false, something was submitted that we did not - * expect; this is probably due to the client being out-of-sync and sending - * variable changes for non-existing pids - * - * @return true if successful, false if there was an inconsistency - */ - private boolean handleVariables(VaadinRequest request, - VaadinResponse response, Callback callback, VaadinSession session, - UI uI) throws IOException, InvalidUIDLSecurityKeyException, - JSONException { - boolean success = true; - - String changes = getRequestPayload(request); - if (changes != null) { - - // Manage bursts one by one - final String[] bursts = changes.split(String - .valueOf(VAR_BURST_SEPARATOR)); - - // Security: double cookie submission pattern unless disabled by - // property - if (isXSRFEnabled(session)) { - if (bursts.length == 1 && "init".equals(bursts[0])) { - // init request; don't handle any variables, key sent in - // response. - request.setAttribute(WRITE_SECURITY_TOKEN_FLAG, true); - return true; - } else { - // ApplicationServlet has stored the security token in the - // session; check that it matched the one sent in the UIDL - String sessId = (String) request - .getWrappedSession() - .getAttribute( - ApplicationConstants.UIDL_SECURITY_TOKEN_ID); - - if (sessId == null || !sessId.equals(bursts[0])) { - throw new InvalidUIDLSecurityKeyException( - "Security key mismatch"); - } - } - - } - - for (int bi = 1; bi < bursts.length; bi++) { - // unescape any encoded separator characters in the burst - final String burst = unescapeBurst(bursts[bi]); - success &= handleBurst(request, uI, burst); - - // In case that there were multiple bursts, we know that this is - // a special synchronous case for closing window. Thus we are - // not interested in sending any UIDL changes back to client. - // Still we must clear component tree between bursts to ensure - // that no removed components are updated. The painting after - // the last burst is handled normally by the calling method. - if (bi < bursts.length - 1) { - - // We will be discarding all changes - final PrintWriter outWriter = new PrintWriter( - new CharArrayWriter()); - - paintAfterVariableChanges(request, response, callback, - true, outWriter, uI, false); - - } - - } - } - /* - * Note that we ignore inconsistencies while handling unload request. - * The client can't remove invalid variable changes from the burst, and - * we don't have the required logic implemented on the server side. E.g. - * a component is removed in a previous burst. - */ - return success; - } - - /** - * Processes a message burst received from the client. - * - * A burst can contain any number of RPC calls, including legacy variable - * change calls that are processed separately. - * - * Consecutive changes to the value of the same variable are combined and - * changeVariables() is only called once for them. This preserves the Vaadin - * 6 semantics for components and add-ons that do not use Vaadin 7 RPC - * directly. - * - * @param source - * @param uI - * the UI receiving the burst - * @param burst - * the content of the burst as a String to be parsed - * @return true if the processing of the burst was successful and there were - * no messages to non-existent components - */ - public boolean handleBurst(VaadinRequest source, UI uI, final String burst) { - boolean success = true; - try { - Set<Connector> enabledConnectors = new HashSet<Connector>(); - - List<MethodInvocation> invocations = parseInvocations( - uI.getConnectorTracker(), burst); - for (MethodInvocation invocation : invocations) { - final ClientConnector connector = getConnector(uI, - invocation.getConnectorId()); - - if (connector != null && connector.isConnectorEnabled()) { - enabledConnectors.add(connector); - } - } - - for (int i = 0; i < invocations.size(); i++) { - MethodInvocation invocation = invocations.get(i); - - final ClientConnector connector = getConnector(uI, - invocation.getConnectorId()); - if (connector == null) { - getLogger() - .log(Level.WARNING, - "Received RPC call for unknown connector with id {0} (tried to invoke {1}.{2})", - new Object[] { invocation.getConnectorId(), - invocation.getInterfaceName(), - invocation.getMethodName() }); - continue; - } - - if (!enabledConnectors.contains(connector)) { - - if (invocation instanceof LegacyChangeVariablesInvocation) { - LegacyChangeVariablesInvocation legacyInvocation = (LegacyChangeVariablesInvocation) invocation; - // TODO convert window close to a separate RPC call and - // handle above - not a variable change - - // Handle special case where window-close is called - // after the window has been removed from the - // application or the application has closed - Map<String, Object> changes = legacyInvocation - .getVariableChanges(); - if (changes.size() == 1 && changes.containsKey("close") - && Boolean.TRUE.equals(changes.get("close"))) { - // Silently ignore this - continue; - } - } - - // Connector is disabled, log a warning and move to the next - String msg = "Ignoring RPC call for disabled connector " - + connector.getClass().getName(); - if (connector instanceof Component) { - String caption = ((Component) connector).getCaption(); - if (caption != null) { - msg += ", caption=" + caption; - } - } - getLogger().warning(msg); - continue; - } - - if (invocation instanceof ServerRpcMethodInvocation) { - try { - ServerRpcManager.applyInvocation(connector, - (ServerRpcMethodInvocation) invocation); - } catch (RpcInvocationException e) { - handleConnectorRelatedException(connector, e); - } - } else { - - // All code below is for legacy variable changes - LegacyChangeVariablesInvocation legacyInvocation = (LegacyChangeVariablesInvocation) invocation; - Map<String, Object> changes = legacyInvocation - .getVariableChanges(); - try { - if (connector instanceof VariableOwner) { - changeVariables(source, (VariableOwner) connector, - changes); - } else { - throw new IllegalStateException( - "Received legacy variable change for " - + connector.getClass().getName() - + " (" - + connector.getConnectorId() - + ") which is not a VariableOwner. The client-side connector sent these legacy varaibles: " - + changes.keySet()); - } - } catch (Exception e) { - handleConnectorRelatedException(connector, e); - } - } - } - } catch (JSONException e) { - getLogger().log(Level.WARNING, - "Unable to parse RPC call from the client: {0}", - e.getMessage()); - // TODO or return success = false? - throw new RuntimeException(e); - } - - return success; - } - - /** - * Handles an exception that occurred when processing Rpc calls or a file - * upload. - * - * @param ui - * The UI where the exception occured - * @param throwable - * The exception - * @param connector - * The Rpc target - */ - private void handleConnectorRelatedException(ClientConnector connector, - Throwable throwable) { - ErrorEvent errorEvent = new ConnectorErrorEvent(connector, throwable); - ErrorHandler handler = ErrorEvent.findErrorHandler(connector); - handler.error(errorEvent); - } - - /** - * Parse a message burst from the client into a list of MethodInvocation - * instances. - * - * @param connectorTracker - * The ConnectorTracker used to lookup connectors - * @param burst - * message string (JSON) - * @return list of MethodInvocation to perform - * @throws JSONException - */ - private List<MethodInvocation> parseInvocations( - ConnectorTracker connectorTracker, final String burst) - throws JSONException { - JSONArray invocationsJson = new JSONArray(burst); - - ArrayList<MethodInvocation> invocations = new ArrayList<MethodInvocation>(); - - MethodInvocation previousInvocation = null; - // parse JSON to MethodInvocations - for (int i = 0; i < invocationsJson.length(); ++i) { - - JSONArray invocationJson = invocationsJson.getJSONArray(i); - - MethodInvocation invocation = parseInvocation(invocationJson, - previousInvocation, connectorTracker); - if (invocation != null) { - // Can be null if the invocation was a legacy invocation and it - // was merged with the previous one or if the invocation was - // rejected because of an error. - invocations.add(invocation); - previousInvocation = invocation; - } - } - return invocations; - } - - private MethodInvocation parseInvocation(JSONArray invocationJson, - MethodInvocation previousInvocation, - ConnectorTracker connectorTracker) throws JSONException { - String connectorId = invocationJson.getString(0); - String interfaceName = invocationJson.getString(1); - String methodName = invocationJson.getString(2); - - if (connectorTracker.getConnector(connectorId) == null - && !connectorId - .equals(ApplicationConstants.DRAG_AND_DROP_CONNECTOR_ID)) { - getLogger() - .log(Level.WARNING, - "RPC call to " - + interfaceName - + "." - + methodName - + " received for connector " - + connectorId - + " but no such connector could be found. Resynchronizing client."); - // This is likely an out of sync issue (client tries to update a - // connector which is not present). Force resync. - connectorTracker.markAllConnectorsDirty(); - return null; - } - - JSONArray parametersJson = invocationJson.getJSONArray(3); - - if (LegacyChangeVariablesInvocation.isLegacyVariableChange( - interfaceName, methodName)) { - if (!(previousInvocation instanceof LegacyChangeVariablesInvocation)) { - previousInvocation = null; - } - - return parseLegacyChangeVariablesInvocation(connectorId, - interfaceName, methodName, - (LegacyChangeVariablesInvocation) previousInvocation, - parametersJson, connectorTracker); - } else { - return parseServerRpcInvocation(connectorId, interfaceName, - methodName, parametersJson, connectorTracker); - } - - } - - private LegacyChangeVariablesInvocation parseLegacyChangeVariablesInvocation( - String connectorId, String interfaceName, String methodName, - LegacyChangeVariablesInvocation previousInvocation, - JSONArray parametersJson, ConnectorTracker connectorTracker) - throws JSONException { - if (parametersJson.length() != 2) { - throw new JSONException( - "Invalid parameters in legacy change variables call. Expected 2, was " - + parametersJson.length()); - } - String variableName = parametersJson.getString(0); - UidlValue uidlValue = (UidlValue) JsonCodec.decodeInternalType( - UidlValue.class, true, parametersJson.get(1), connectorTracker); - - Object value = uidlValue.getValue(); - - if (previousInvocation != null - && previousInvocation.getConnectorId().equals(connectorId)) { - previousInvocation.setVariableChange(variableName, value); - return null; - } else { - return new LegacyChangeVariablesInvocation(connectorId, - variableName, value); - } - } - - private ServerRpcMethodInvocation parseServerRpcInvocation( - String connectorId, String interfaceName, String methodName, - JSONArray parametersJson, ConnectorTracker connectorTracker) - throws JSONException { - ClientConnector connector = connectorTracker.getConnector(connectorId); - - ServerRpcManager<?> rpcManager = connector.getRpcManager(interfaceName); - if (rpcManager == null) { - /* - * Security: Don't even decode the json parameters if no RpcManager - * corresponding to the received method invocation has been - * registered. - */ - getLogger() - .log(Level.WARNING, - "Ignoring RPC call to {0}.{1} in connector {2} ({3}) as no RPC implementation is regsitered", - new Object[] { interfaceName, methodName, - connector.getClass().getName(), connectorId }); - return null; - } - - // Use interface from RpcManager instead of loading the class based on - // the string name to avoid problems with OSGi - Class<? extends ServerRpc> rpcInterface = rpcManager.getRpcInterface(); - - ServerRpcMethodInvocation invocation = new ServerRpcMethodInvocation( - connectorId, rpcInterface, methodName, parametersJson.length()); - - Object[] parameters = new Object[parametersJson.length()]; - Type[] declaredRpcMethodParameterTypes = invocation.getMethod() - .getGenericParameterTypes(); - - for (int j = 0; j < parametersJson.length(); ++j) { - Object parameterValue = parametersJson.get(j); - Type parameterType = declaredRpcMethodParameterTypes[j]; - parameters[j] = JsonCodec.decodeInternalOrCustomType(parameterType, - parameterValue, connectorTracker); - } - invocation.setParameters(parameters); - return invocation; - } - - protected void changeVariables(Object source, final VariableOwner owner, - Map<String, Object> m) { - owner.changeVariables(source, m); - } - - protected ClientConnector getConnector(UI uI, String connectorId) { - ClientConnector c = uI.getConnectorTracker().getConnector(connectorId); - if (c == null - && connectorId.equals(getDragAndDropService().getConnectorId())) { - return getDragAndDropService(); - } - - return c; - } - - private DragAndDropService getDragAndDropService() { - if (dragAndDropService == null) { - dragAndDropService = new DragAndDropService(this); - } - return dragAndDropService; - } - - /** - * Reads the request data from the Request and returns it converted to an - * UTF-8 string. - * - * @param request - * @return - * @throws IOException - */ - protected String getRequestPayload(VaadinRequest request) - throws IOException { - - int requestLength = request.getContentLength(); - if (requestLength == 0) { - return null; - } - - ByteArrayOutputStream bout = requestLength <= 0 ? new ByteArrayOutputStream() - : new ByteArrayOutputStream(requestLength); - - InputStream inputStream = request.getInputStream(); - byte[] buffer = new byte[MAX_BUFFER_SIZE]; - - while (true) { - int read = inputStream.read(buffer); - if (read == -1) { - break; - } - bout.write(buffer, 0, read); - } - String result = new String(bout.toByteArray(), "utf-8"); - - return result; - } - - /** - * Unescape encoded burst separator characters in a burst received from the - * client. This protects from separator injection attacks. - * - * @param encodedValue - * to decode - * @return decoded value - */ - protected String unescapeBurst(String encodedValue) { - final StringBuilder result = new StringBuilder(); - final StringCharacterIterator iterator = new StringCharacterIterator( - encodedValue); - char character = iterator.current(); - while (character != CharacterIterator.DONE) { - if (VAR_ESCAPE_CHARACTER == character) { - character = iterator.next(); - switch (character) { - case VAR_ESCAPE_CHARACTER + 0x30: - // escaped escape character - result.append(VAR_ESCAPE_CHARACTER); - break; - case VAR_BURST_SEPARATOR + 0x30: - // +0x30 makes these letters for easier reading - result.append((char) (character - 0x30)); - break; - case CharacterIterator.DONE: - // error - throw new RuntimeException( - "Communication error: Unexpected end of message"); - default: - // other escaped character - probably a client-server - // version mismatch - throw new RuntimeException( - "Invalid escaped character from the client - check that the widgetset and server versions match"); - } - } else { - // not a special character - add it to the result as is - result.append(character); - } - character = iterator.next(); - } - return result.toString(); - } - - /** - * Prints the queued (pending) locale definitions to a {@link PrintWriter} - * in a (UIDL) format that can be sent to the client and used there in - * formatting dates, times etc. - * - * @param outWriter - */ - private void printLocaleDeclarations(PrintWriter outWriter) { - /* - * ----------------------------- Sending Locale sensitive date - * ----------------------------- - */ - - // Send locale informations to client - outWriter.print(", \"locales\":["); - for (; pendingLocalesIndex < locales.size(); pendingLocalesIndex++) { - - final Locale l = generateLocale(locales.get(pendingLocalesIndex)); - // Locale name - outWriter.print("{\"name\":\"" + l.toString() + "\","); - - /* - * Month names (both short and full) - */ - final DateFormatSymbols dfs = new DateFormatSymbols(l); - final String[] short_months = dfs.getShortMonths(); - final String[] months = dfs.getMonths(); - outWriter.print("\"smn\":[\"" - + // ShortMonthNames - short_months[0] + "\",\"" + short_months[1] + "\",\"" - + short_months[2] + "\",\"" + short_months[3] + "\",\"" - + short_months[4] + "\",\"" + short_months[5] + "\",\"" - + short_months[6] + "\",\"" + short_months[7] + "\",\"" - + short_months[8] + "\",\"" + short_months[9] + "\",\"" - + short_months[10] + "\",\"" + short_months[11] + "\"" - + "],"); - outWriter.print("\"mn\":[\"" - + // MonthNames - months[0] + "\",\"" + months[1] + "\",\"" + months[2] - + "\",\"" + months[3] + "\",\"" + months[4] + "\",\"" - + months[5] + "\",\"" + months[6] + "\",\"" + months[7] - + "\",\"" + months[8] + "\",\"" + months[9] + "\",\"" - + months[10] + "\",\"" + months[11] + "\"" + "],"); - - /* - * Weekday names (both short and full) - */ - final String[] short_days = dfs.getShortWeekdays(); - final String[] days = dfs.getWeekdays(); - outWriter.print("\"sdn\":[\"" - + // ShortDayNames - short_days[1] + "\",\"" + short_days[2] + "\",\"" - + short_days[3] + "\",\"" + short_days[4] + "\",\"" - + short_days[5] + "\",\"" + short_days[6] + "\",\"" - + short_days[7] + "\"" + "],"); - outWriter.print("\"dn\":[\"" - + // DayNames - days[1] + "\",\"" + days[2] + "\",\"" + days[3] + "\",\"" - + days[4] + "\",\"" + days[5] + "\",\"" + days[6] + "\",\"" - + days[7] + "\"" + "],"); - - /* - * First day of week (0 = sunday, 1 = monday) - */ - final Calendar cal = new GregorianCalendar(l); - outWriter.print("\"fdow\":" + (cal.getFirstDayOfWeek() - 1) + ","); - - /* - * Date formatting (MM/DD/YYYY etc.) - */ - - DateFormat dateFormat = DateFormat.getDateTimeInstance( - DateFormat.SHORT, DateFormat.SHORT, l); - if (!(dateFormat instanceof SimpleDateFormat)) { - getLogger().log(Level.WARNING, - "Unable to get default date pattern for locale {0}", l); - dateFormat = new SimpleDateFormat(); - } - final String df = ((SimpleDateFormat) dateFormat).toPattern(); - - int timeStart = df.indexOf("H"); - if (timeStart < 0) { - timeStart = df.indexOf("h"); - } - final int ampm_first = df.indexOf("a"); - // E.g. in Korean locale AM/PM is before h:mm - // TODO should take that into consideration on client-side as well, - // now always h:mm a - if (ampm_first > 0 && ampm_first < timeStart) { - timeStart = ampm_first; - } - // Hebrew locale has time before the date - final boolean timeFirst = timeStart == 0; - String dateformat; - if (timeFirst) { - int dateStart = df.indexOf(' '); - if (ampm_first > dateStart) { - dateStart = df.indexOf(' ', ampm_first); - } - dateformat = df.substring(dateStart + 1); - } else { - dateformat = df.substring(0, timeStart - 1); - } - - outWriter.print("\"df\":\"" + dateformat.trim() + "\","); - - /* - * Time formatting (24 or 12 hour clock and AM/PM suffixes) - */ - final String timeformat = df.substring(timeStart, df.length()); - /* - * Doesn't return second or milliseconds. - * - * We use timeformat to determine 12/24-hour clock - */ - final boolean twelve_hour_clock = timeformat.indexOf("a") > -1; - // TODO there are other possibilities as well, like 'h' in french - // (ignore them, too complicated) - final String hour_min_delimiter = timeformat.indexOf(".") > -1 ? "." - : ":"; - // outWriter.print("\"tf\":\"" + timeformat + "\","); - outWriter.print("\"thc\":" + twelve_hour_clock + ","); - outWriter.print("\"hmd\":\"" + hour_min_delimiter + "\""); - if (twelve_hour_clock) { - final String[] ampm = dfs.getAmPmStrings(); - outWriter.print(",\"ampm\":[\"" + ampm[0] + "\",\"" + ampm[1] - + "\"]"); - } - outWriter.print("}"); - if (pendingLocalesIndex < locales.size() - 1) { - outWriter.print(","); - } - } - outWriter.print("]"); // Close locales - } - - protected void closeJsonMessage(PrintWriter outWriter) { - outWriter.print("}]"); - } - - /** - * Writes the opening of JSON message to be sent to client. - * - * @param outWriter - * @param response - */ - protected void openJsonMessage(PrintWriter outWriter, - VaadinResponse response) { - // Sets the response type - response.setContentType("application/json; charset=UTF-8"); - // some dirt to prevent cross site scripting - outWriter.print("for(;;);[{"); - } - - /** - * Returns dirty components which are in given window. Components in an - * invisible subtrees are omitted. - * - * @param w - * UI window for which dirty components is to be fetched - * @return - */ - private ArrayList<ClientConnector> getDirtyVisibleConnectors( - ConnectorTracker connectorTracker) { - ArrayList<ClientConnector> dirtyConnectors = new ArrayList<ClientConnector>(); - for (ClientConnector c : connectorTracker.getDirtyConnectors()) { - if (isConnectorVisibleToClient(c)) { - dirtyConnectors.add(c); - } - } - - return dirtyConnectors; - } - - /** - * Queues a locale to be sent to the client (browser) for date and time - * entry etc. All locale specific information is derived from server-side - * {@link Locale} instances and sent to the client when needed, eliminating - * the need to use the {@link Locale} class and all the framework behind it - * on the client. - * - * @see Locale#toString() - * - * @param value - */ - public void requireLocale(String value) { - if (locales == null) { - locales = new ArrayList<String>(); - locales.add(session.getLocale().toString()); - pendingLocalesIndex = 0; - } - if (!locales.contains(value)) { - locales.add(value); - } - } - - /** - * Constructs a {@link Locale} instance to be sent to the client based on a - * short locale description string. - * - * @see #requireLocale(String) - * - * @param value - * @return - */ - private Locale generateLocale(String value) { - final String[] temp = value.split("_"); - if (temp.length == 1) { - return new Locale(temp[0]); - } else if (temp.length == 2) { - return new Locale(temp[0], temp[1]); - } else { - return new Locale(temp[0], temp[1], temp[2]); - } - } - - protected class InvalidUIDLSecurityKeyException extends - GeneralSecurityException { - - InvalidUIDLSecurityKeyException(String message) { - super(message); - } - - } - - private final HashMap<Class<? extends ClientConnector>, Integer> typeToKey = new HashMap<Class<? extends ClientConnector>, Integer>(); - private int nextTypeKey = 0; - - private BootstrapHandler bootstrapHandler; - - String getTagForType(Class<? extends ClientConnector> class1) { - Integer id = typeToKey.get(class1); - if (id == null) { - id = nextTypeKey++; - typeToKey.put(class1, id); - if (getLogger().isLoggable(Level.FINE)) { - getLogger().log(Level.FINE, "Mapping {0} to {1}", - new Object[] { class1.getName(), id }); - } - } - return id.toString(); - } - - /** - * Helper class for terminal to keep track of data that client is expected - * to know. - * - * TODO make customlayout templates (from theme) to be cached here. - */ - class ClientCache implements Serializable { - - private final Set<Object> res = new HashSet<Object>(); - - /** - * - * @param paintable - * @return true if the given class was added to cache - */ - boolean cache(Object object) { - return res.add(object); - } - - public void clear() { - res.clear(); - } - - } - - public String getStreamVariableTargetUrl(ClientConnector owner, - String name, StreamVariable value) { - /* - * We will use the same APP/* URI space as ApplicationResources but - * prefix url with UPLOAD - * - * eg. APP/UPLOAD/[UIID]/[PID]/[NAME]/[SECKEY] - * - * SECKEY is created on each paint to make URL's unpredictable (to - * prevent CSRF attacks). - * - * NAME and PID from URI forms a key to fetch StreamVariable when - * handling post - */ - String paintableId = owner.getConnectorId(); - UI ui = owner.getUI(); - int uiId = ui.getUIId(); - String key = uiId + "/" + paintableId + "/" + name; - - ConnectorTracker connectorTracker = ui.getConnectorTracker(); - connectorTracker.addStreamVariable(paintableId, name, value); - String seckey = connectorTracker.getSeckey(value); - - return ApplicationConstants.APP_PROTOCOL_PREFIX - + ServletPortletHelper.UPLOAD_URL_PREFIX + key + "/" + seckey; - - } - - public void cleanStreamVariable(ClientConnector owner, String name) { - owner.getUI().getConnectorTracker() - .cleanStreamVariable(owner.getConnectorId(), name); - } - - /** - * Gets the bootstrap handler that should be used for generating the pages - * bootstrapping applications for this communication manager. - * - * @return the bootstrap handler to use - */ - private BootstrapHandler getBootstrapHandler() { - if (bootstrapHandler == null) { - bootstrapHandler = createBootstrapHandler(); - } - - return bootstrapHandler; - } - - /** - * @return - * - * @deprecated As of 7.0. Will likely change or be removed in a future - * version - */ - @Deprecated - protected abstract BootstrapHandler createBootstrapHandler(); - - /** - * Handles a request by passing it to each registered {@link RequestHandler} - * in turn until one produces a response. This method is used for requests - * that have not been handled by any specific functionality in the terminal - * implementation (e.g. {@link VaadinServlet}). - * <p> - * The request handlers are invoked in the revere order in which they were - * added to the session until a response has been produced. This means that - * the most recently added handler is used first and the first request - * handler that was added to the session is invoked towards the end unless - * any previous handler has already produced a response. - * </p> - * - * @param request - * the Vaadin request to get information from - * @param response - * the response to which data can be written - * @return returns <code>true</code> if a {@link RequestHandler} has - * produced a response and <code>false</code> if no response has - * been written. - * @throws IOException - * if a handler throws an exception - * - * @see VaadinSession#addRequestHandler(RequestHandler) - * @see RequestHandler - * - * @since 7.0 - */ - protected boolean handleOtherRequest(VaadinRequest request, - VaadinResponse response) throws IOException { - // Use a copy to avoid ConcurrentModificationException - for (RequestHandler handler : new ArrayList<RequestHandler>( - session.getRequestHandlers())) { - if (handler.handleRequest(session, request, response)) { - return true; - } - } - // If not handled - return false; - } - - public void handleBrowserDetailsRequest(VaadinRequest request, - VaadinResponse response, VaadinSession session) throws IOException { - - session.lock(); - - try { - assert UI.getCurrent() == null; - - response.setContentType("application/json; charset=UTF-8"); - - UI uI = getBrowserDetailsUI(request, session); - - JSONObject params = new JSONObject(); - params.put(UIConstants.UI_ID_PARAMETER, uI.getUIId()); - String initialUIDL = getInitialUIDL(request, uI); - params.put("uidl", initialUIDL); - - // NOTE! GateIn requires, for some weird reason, getOutputStream - // to be used instead of getWriter() (it seems to interpret - // application/json as a binary content type) - final OutputStream out = response.getOutputStream(); - final PrintWriter outWriter = new PrintWriter(new BufferedWriter( - new OutputStreamWriter(out, "UTF-8"))); - - outWriter.write(params.toString()); - // NOTE GateIn requires the buffers to be flushed to work - outWriter.flush(); - out.flush(); - } catch (JSONException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } finally { - session.unlock(); - } - } - - private UI getBrowserDetailsUI(VaadinRequest request, VaadinSession session) { - VaadinService vaadinService = request.getService(); - - List<UIProvider> uiProviders = session.getUIProviders(); - - UIClassSelectionEvent classSelectionEvent = new UIClassSelectionEvent( - request); - - UIProvider provider = null; - Class<? extends UI> uiClass = null; - for (UIProvider p : uiProviders) { - // Check for existing LegacyWindow - if (p instanceof LegacyApplicationUIProvider) { - LegacyApplicationUIProvider legacyProvider = (LegacyApplicationUIProvider) p; - - UI existingUi = legacyProvider - .getExistingUI(classSelectionEvent); - if (existingUi != null) { - reinitUI(existingUi, request); - return existingUi; - } - } - - uiClass = p.getUIClass(classSelectionEvent); - if (uiClass != null) { - provider = p; - break; - } - } - - if (provider == null || uiClass == null) { - return null; - } - - // Check for an existing UI based on window.name - - // Special parameter sent by vaadinBootstrap.js - String windowName = request.getParameter("v-wn"); - - Map<String, Integer> retainOnRefreshUIs = session - .getPreserveOnRefreshUIs(); - if (windowName != null && !retainOnRefreshUIs.isEmpty()) { - // Check for a known UI - - Integer retainedUIId = retainOnRefreshUIs.get(windowName); - - if (retainedUIId != null) { - UI retainedUI = session.getUIById(retainedUIId.intValue()); - if (uiClass.isInstance(retainedUI)) { - reinitUI(retainedUI, request); - return retainedUI; - } else { - getLogger().log( - Level.INFO, - "Not using retained UI in {0} because retained UI was of type {1}" - + " but {2} is expected for the request.", - new Object[] { windowName, retainedUI.getClass(), - uiClass }); - } - } - } - - // No existing UI found - go on by creating and initializing one - - Integer uiId = Integer.valueOf(session.getNextUIid()); - - // Explicit Class.cast to detect if the UIProvider does something - // unexpected - UICreateEvent event = new UICreateEvent(request, uiClass, uiId); - UI ui = uiClass.cast(provider.createInstance(event)); - - // Initialize some fields for a newly created UI - if (ui.getSession() != session) { - // Session already set for LegacyWindow - ui.setSession(session); - } - - // Set thread local here so it is available in init - UI.setCurrent(ui); - - ui.doInit(request, uiId.intValue()); - - session.addUI(ui); - - // Remember if it should be remembered - if (vaadinService.preserveUIOnRefresh(provider, event)) { - // Remember this UI - if (windowName == null) { - getLogger() - .log(Level.WARNING, - "There is no window.name available for UI {0} that should be preserved.", - uiClass); - } else { - session.getPreserveOnRefreshUIs().put(windowName, uiId); - } - } - - return ui; - } - - /** - * Updates a UI that has already been initialized but is now loaded again, - * e.g. because of {@link PreserveOnRefresh}. - * - * @param ui - * @param request - */ - private void reinitUI(UI ui, VaadinRequest request) { - UI.setCurrent(ui); - - // Fire fragment change if the fragment has changed - String location = request.getParameter("v-loc"); - if (location != null) { - ui.getPage().updateLocation(location); - } - } - - /** - * Generates the initial UIDL message that can e.g. be included in a html - * page to avoid a separate round trip just for getting the UIDL. - * - * @param request - * the request that caused the initialization - * @param uI - * the UI for which the UIDL should be generated - * @return a string with the initial UIDL message - * @throws PaintException - * if an exception occurs while painting - * @throws JSONException - * if an exception occurs while encoding output - */ - protected String getInitialUIDL(VaadinRequest request, UI uI) - throws PaintException, JSONException { - // TODO maybe unify writeUidlResponse()? - StringWriter sWriter = new StringWriter(); - PrintWriter pWriter = new PrintWriter(sWriter); - pWriter.print("{"); - if (isXSRFEnabled(uI.getSession())) { - pWriter.print(getSecurityKeyUIDL(request)); - } - writeUidlResponse(request, true, pWriter, uI, false); - pWriter.print("}"); - String initialUIDL = sWriter.toString(); - getLogger().log(Level.FINE, "Initial UIDL:{0}", initialUIDL); - return initialUIDL; - } - - /** - * Serve a connector resource from the classpath if the resource has - * previously been registered by calling - * {@link #registerPublishedFile(String, Class)}. Sending arbitrary files - * from the classpath is prevented by only accepting resource names that - * have explicitly been registered. Resources can currently only be - * registered by including a {@link JavaScript} or {@link StyleSheet} - * annotation on a Connector class. - * - * @param request - * @param response - * - * @throws IOException - */ - public void servePublishedFile(VaadinRequest request, - VaadinResponse response) throws IOException { - - String pathInfo = request.getPathInfo(); - // + 2 to also remove beginning and ending slashes - String fileName = pathInfo - .substring(ApplicationConstants.PUBLISHED_FILE_PATH.length() + 2); - - final String mimetype = response.getService().getMimeType(fileName); - - // Security check: avoid accidentally serving from the UI of the - // classpath instead of relative to the context class - if (fileName.startsWith("/")) { - getLogger().log(Level.WARNING, - "Published file request starting with / rejected: {0}", - fileName); - response.sendError(HttpServletResponse.SC_NOT_FOUND, fileName); - return; - } - - // Check that the resource name has been registered - Class<?> context; - synchronized (publishedFileContexts) { - context = publishedFileContexts.get(fileName); - } - - // Security check: don't serve resource if the name hasn't been - // registered in the map - if (context == null) { - getLogger() - .log(Level.WARNING, - "Rejecting published file request for file that has not been published: {0}", - fileName); - response.sendError(HttpServletResponse.SC_NOT_FOUND, fileName); - return; - } - - // Resolve file relative to the location of the context class - InputStream in = context.getResourceAsStream(fileName); - if (in == null) { - getLogger() - .log(Level.WARNING, - "{0} published by {1} not found. Verify that the file {2}/{3} is available on the classpath.", - new Object[] { - fileName, - context.getName(), - context.getPackage().getName() - .replace('.', '/'), fileName }); - response.sendError(HttpServletResponse.SC_NOT_FOUND, fileName); - return; - } - - // TODO Check and set cache headers - - OutputStream out = null; - try { - if (mimetype != null) { - response.setContentType(mimetype); - } - - out = response.getOutputStream(); - - final byte[] buffer = new byte[Constants.DEFAULT_BUFFER_SIZE]; - - int bytesRead = 0; - while ((bytesRead = in.read(buffer)) > 0) { - out.write(buffer, 0, bytesRead); - } - out.flush(); - } finally { - try { - in.close(); - } catch (Exception e) { - // Do nothing - } - if (out != null) { - try { - out.close(); - } catch (Exception e) { - // Do nothing - } - } - } - } - - /** - * Handles file upload request submitted via Upload component. - * - * @param UI - * The UI for this request - * - * @see #getStreamVariableTargetUrl(ReceiverOwner, String, StreamVariable) - * - * @param request - * @param response - * @throws IOException - * @throws InvalidUIDLSecurityKeyException - */ - public void handleFileUpload(VaadinSession session, VaadinRequest request, - VaadinResponse response) throws IOException, - InvalidUIDLSecurityKeyException { - - /* - * URI pattern: APP/UPLOAD/[UIID]/[PID]/[NAME]/[SECKEY] See - * #createReceiverUrl - */ - - String pathInfo = request.getPathInfo(); - // strip away part until the data we are interested starts - int startOfData = pathInfo - .indexOf(ServletPortletHelper.UPLOAD_URL_PREFIX) - + ServletPortletHelper.UPLOAD_URL_PREFIX.length(); - String uppUri = pathInfo.substring(startOfData); - String[] parts = uppUri.split("/", 4); // 0= UIid, 1 = cid, 2= name, 3 - // = sec key - String uiId = parts[0]; - String connectorId = parts[1]; - String variableName = parts[2]; - UI uI = session.getUIById(Integer.parseInt(uiId)); - UI.setCurrent(uI); - - StreamVariable streamVariable = uI.getConnectorTracker() - .getStreamVariable(connectorId, variableName); - String secKey = uI.getConnectorTracker().getSeckey(streamVariable); - if (secKey.equals(parts[3])) { - - ClientConnector source = getConnector(uI, connectorId); - String contentType = request.getContentType(); - if (contentType.contains("boundary")) { - // Multipart requests contain boundary string - doHandleSimpleMultipartFileUpload(request, response, - streamVariable, variableName, source, - contentType.split("boundary=")[1]); - } else { - // if boundary string does not exist, the posted file is from - // XHR2.post(File) - doHandleXhrFilePost(request, response, streamVariable, - variableName, source, request.getContentLength()); - } - } else { - throw new InvalidUIDLSecurityKeyException( - "Security key in upload post did not match!"); - } - - } - - /** - * Handles a heartbeat request. Heartbeat requests are periodically sent by - * the client-side to inform the server that the UI sending the heartbeat is - * still alive (the browser window is open, the connection is up) even when - * there are no UIDL requests for a prolonged period of time. UIs that do - * not receive either heartbeat or UIDL requests are eventually removed from - * the session and garbage collected. - * - * @param request - * @param response - * @param session - * @throws IOException - */ - public void handleHeartbeatRequest(VaadinRequest request, - VaadinResponse response, VaadinSession session) throws IOException { - UI ui = null; - try { - int uiId = Integer.parseInt(request - .getParameter(UIConstants.UI_ID_PARAMETER)); - ui = session.getUIById(uiId); - } catch (NumberFormatException nfe) { - // null-check below handles this as well - } - if (ui != null) { - ui.setLastHeartbeatTimestamp(System.currentTimeMillis()); - // Ensure that the browser does not cache heartbeat responses. - // iOS 6 Safari requires this (#10370) - response.setHeader("Cache-Control", "no-cache"); - } else { - response.sendError(HttpServletResponse.SC_NOT_FOUND, "UI not found"); - } - } - - /** - * Stream that extracts content from another stream until the boundary - * string is encountered. - * - * Public only for unit tests, should be considered private for all other - * purposes. - */ - public static class SimpleMultiPartInputStream extends InputStream { - - /** - * Counter of how many characters have been matched to boundary string - * from the stream - */ - int matchedCount = -1; - - /** - * Used as pointer when returning bytes after partly matched boundary - * string. - */ - int curBoundaryIndex = 0; - /** - * The byte found after a "promising start for boundary" - */ - private int bufferedByte = -1; - private boolean atTheEnd = false; - - private final char[] boundary; - - private final InputStream realInputStream; - - public SimpleMultiPartInputStream(InputStream realInputStream, - String boundaryString) { - boundary = (CRLF + DASHDASH + boundaryString).toCharArray(); - this.realInputStream = realInputStream; - } - - @Override - public int read() throws IOException { - if (atTheEnd) { - // End boundary reached, nothing more to read - return -1; - } else if (bufferedByte >= 0) { - /* Purge partially matched boundary if there was such */ - return getBuffered(); - } else if (matchedCount != -1) { - /* - * Special case where last "failed" matching ended with first - * character from boundary string - */ - return matchForBoundary(); - } else { - int fromActualStream = realInputStream.read(); - if (fromActualStream == -1) { - // unexpected end of stream - throw new IOException( - "The multipart stream ended unexpectedly"); - } - if (boundary[0] == fromActualStream) { - /* - * If matches the first character in boundary string, start - * checking if the boundary is fetched. - */ - return matchForBoundary(); - } - return fromActualStream; - } - } - - /** - * Reads the input to expect a boundary string. Expects that the first - * character has already been matched. - * - * @return -1 if the boundary was matched, else returns the first byte - * from boundary - * @throws IOException - */ - private int matchForBoundary() throws IOException { - matchedCount = 0; - /* - * Going to "buffered mode". Read until full boundary match or a - * different character. - */ - while (true) { - matchedCount++; - if (matchedCount == boundary.length) { - /* - * The whole boundary matched so we have reached the end of - * file - */ - atTheEnd = true; - return -1; - } - int fromActualStream = realInputStream.read(); - if (fromActualStream != boundary[matchedCount]) { - /* - * Did not find full boundary, cache the mismatching byte - * and start returning the partially matched boundary. - */ - bufferedByte = fromActualStream; - return getBuffered(); - } - } - } - - /** - * Returns the partly matched boundary string and the byte following - * that. - * - * @return - * @throws IOException - */ - private int getBuffered() throws IOException { - int b; - if (matchedCount == 0) { - // The boundary has been returned, return the buffered byte. - b = bufferedByte; - bufferedByte = -1; - matchedCount = -1; - } else { - b = boundary[curBoundaryIndex++]; - if (curBoundaryIndex == matchedCount) { - // The full boundary has been returned, remaining is the - // char that did not match the boundary. - - curBoundaryIndex = 0; - if (bufferedByte != boundary[0]) { - /* - * next call for getBuffered will return the - * bufferedByte that came after the partial boundary - * match - */ - matchedCount = 0; - } else { - /* - * Special case where buffered byte again matches the - * boundaryString. This could be the start of the real - * end boundary. - */ - matchedCount = 0; - bufferedByte = -1; - } - } - } - if (b == -1) { - throw new IOException("The multipart stream ended unexpectedly"); - } - return b; - } - } - - private static final Logger getLogger() { - return Logger.getLogger(AbstractCommunicationManager.class.getName()); - } -} diff --git a/server/src/com/vaadin/server/BootstrapHandler.java b/server/src/com/vaadin/server/BootstrapHandler.java index 403fefc0e1..dddfb385a6 100644 --- a/server/src/com/vaadin/server/BootstrapHandler.java +++ b/server/src/com/vaadin/server/BootstrapHandler.java @@ -41,6 +41,7 @@ import org.jsoup.parser.Tag; import com.vaadin.shared.ApplicationConstants; import com.vaadin.shared.Version; +import com.vaadin.shared.communication.PushMode; import com.vaadin.ui.UI; /** @@ -51,7 +52,14 @@ import com.vaadin.ui.UI; * @deprecated As of 7.0. Will likely change or be removed in a future version */ @Deprecated -public abstract class BootstrapHandler implements RequestHandler { +public abstract class BootstrapHandler extends SynchronizedRequestHandler { + + /** + * Parameter that is added to the UI init request if the session has already + * been restarted when generating the bootstrap HTML and ?restartApplication + * should thus be ignored when handling the UI init request. + */ + public static final String IGNORE_RESTART_PARAM = "ignoreRestart"; protected class BootstrapContext implements Serializable { @@ -61,6 +69,7 @@ public abstract class BootstrapHandler implements RequestHandler { private String widgetsetName; private String themeName; private String appId; + private PushMode pushMode; public BootstrapContext(VaadinResponse response, BootstrapFragmentResponse bootstrapResponse) { @@ -98,6 +107,30 @@ public abstract class BootstrapHandler implements RequestHandler { return themeName; } + public PushMode getPushMode() { + if (pushMode == null) { + UICreateEvent event = new UICreateEvent(getRequest(), + getUIClass()); + + pushMode = getBootstrapResponse().getUIProvider().getPushMode( + event); + if (pushMode == null) { + pushMode = getRequest().getService() + .getDeploymentConfiguration().getPushMode(); + } + + if (pushMode.isEnabled() + && !getRequest().getService().ensurePushAvailable()) { + /* + * Fall back if not supported (ensurePushAvailable will log + * information to the developer the first time this happens) + */ + pushMode = PushMode.DISABLED; + } + } + return pushMode; + } + public String getAppId() { if (appId == null) { appId = getRequest().getService().getMainDivId(getSession(), @@ -113,10 +146,19 @@ public abstract class BootstrapHandler implements RequestHandler { } @Override - public boolean handleRequest(VaadinSession session, VaadinRequest request, - VaadinResponse response) throws IOException { + public boolean synchronizedHandleRequest(VaadinSession session, + VaadinRequest request, VaadinResponse response) throws IOException { + if (ServletPortletHelper.isAppRequest(request)) { + // We do not want to handle /APP requests here, instead let it fall + // through and produce a 404 + return false; + } try { + // Update WebBrowser here only to make WebBrowser information + // available in init for LegacyApplications + session.getBrowser().updateRequestDetails(request); + List<UIProvider> uiProviders = session.getUIProviders(); UIClassSelectionEvent classSelectionEvent = new UIClassSelectionEvent( @@ -241,11 +283,9 @@ public abstract class BootstrapHandler implements RequestHandler { /* * Enable Chrome Frame in all versions of IE if installed. - * - * Claim IE10 support to avoid using compatibility mode. */ head.appendElement("meta").attr("http-equiv", "X-UA-Compatible") - .attr("content", "IE=9;chrome=1"); + .attr("content", "IE=10;chrome=1"); String title = response.getUIProvider().getPageTitle( new UICreateEvent(context.getRequest(), context.getUIClass())); @@ -334,8 +374,8 @@ public abstract class BootstrapHandler implements RequestHandler { VaadinRequest request = context.getRequest(); VaadinService vaadinService = request.getService(); - String staticFileLocation = vaadinService - .getStaticFileLocation(request); + String vaadinLocation = vaadinService.getStaticFileLocation(request) + + "/VAADIN/"; fragmentNodes .add(new Element(Tag.valueOf("iframe"), "") @@ -345,8 +385,14 @@ public abstract class BootstrapHandler implements RequestHandler { "position:absolute;width:0;height:0;border:0;overflow:hidden") .attr("src", "javascript:false")); - String bootstrapLocation = staticFileLocation - + "/VAADIN/vaadinBootstrap.js"; + if (context.getPushMode().isEnabled()) { + // Load client-side dependencies for push support + fragmentNodes.add(new Element(Tag.valueOf("script"), "").attr( + "type", "text/javascript").attr("src", + vaadinLocation + ApplicationConstants.VAADIN_PUSH_JS)); + } + + String bootstrapLocation = vaadinLocation + "vaadinBootstrap.js"; fragmentNodes.add(new Element(Tag.valueOf("script"), "").attr("type", "text/javascript").attr("src", bootstrapLocation)); Element mainScriptTag = new Element(Tag.valueOf("script"), "").attr( @@ -415,6 +461,12 @@ public abstract class BootstrapHandler implements RequestHandler { appConfig.put("theme", themeName); } + // Ignore restartApplication that might be passed to UI init + if (request + .getParameter(VaadinService.URL_PARAMETER_RESTART_APPLICATION) != null) { + appConfig.put("extraParams", "&" + IGNORE_RESTART_PARAM + "=1"); + } + JSONObject versionInfo = new JSONObject(); versionInfo.put("vaadinVersion", Version.getFullVersion()); appConfig.put("versionInfo", versionInfo); diff --git a/server/src/com/vaadin/server/BrowserWindowOpener.java b/server/src/com/vaadin/server/BrowserWindowOpener.java index 8e049ca454..a6e420f89c 100644 --- a/server/src/com/vaadin/server/BrowserWindowOpener.java +++ b/server/src/com/vaadin/server/BrowserWindowOpener.java @@ -38,7 +38,8 @@ public class BrowserWindowOpener extends AbstractExtension { private final String path; private final Class<? extends UI> uiClass; - public BrowserWindowOpenerUIProvider(Class<? extends UI> uiClass, String path) { + public BrowserWindowOpenerUIProvider(Class<? extends UI> uiClass, + String path) { this.path = ensureInitialSlash(path); this.uiClass = uiClass; } diff --git a/server/src/com/vaadin/server/ClientConnector.java b/server/src/com/vaadin/server/ClientConnector.java index 5e95b18281..3b52fbc730 100644 --- a/server/src/com/vaadin/server/ClientConnector.java +++ b/server/src/com/vaadin/server/ClientConnector.java @@ -300,7 +300,7 @@ public interface ClientConnector extends Connector { /** * Called by the framework to encode the state to a JSONObject. This is * typically done by calling the static method - * {@link AbstractCommunicationManager#encodeState(ClientConnector, SharedState)} + * {@link LegacyCommunicationManager#encodeState(ClientConnector, SharedState)} * . * * @return a JSON object with the encoded connector state @@ -318,8 +318,12 @@ public interface ClientConnector extends Connector { * routed to this method with the remaining part of the requested path * available in the path parameter. * <p> - * {@link DynamicConnectorResource} can be used to easily make an - * appropriate URL available to the client-side code. + * NOTE that the session is not locked when this method is called. It is the + * responsibility of the connector to ensure that the session is locked + * while handling state or other session related data. For best performance + * the session should be unlocked before writing a large response to the + * client. + * </p> * * @param request * the request that should be handled diff --git a/server/src/com/vaadin/server/CommunicationManager.java b/server/src/com/vaadin/server/CommunicationManager.java deleted file mode 100644 index 8b3550481d..0000000000 --- a/server/src/com/vaadin/server/CommunicationManager.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.server; - -import java.io.InputStream; - -import javax.servlet.ServletContext; - -import com.vaadin.ui.UI; - -/** - * Application manager processes changes and paints for single application - * instance. - * - * This class handles applications running as servlets. - * - * @see AbstractCommunicationManager - * - * @author Vaadin Ltd. - * @since 5.0 - * - * @deprecated As of 7.0. Will likely change or be removed in a future version - */ -@Deprecated -@SuppressWarnings("serial") -public class CommunicationManager extends AbstractCommunicationManager { - - /** - * TODO New constructor - document me! - * - * @param session - */ - public CommunicationManager(VaadinSession session) { - super(session); - } - - @Override - protected BootstrapHandler createBootstrapHandler() { - return new BootstrapHandler() { - @Override - protected String getServiceUrl(BootstrapContext context) { - String pathInfo = context.getRequest().getPathInfo(); - if (pathInfo == null) { - return null; - } else { - /* - * Make a relative URL to the servlet by adding one ../ for - * each path segment in pathInfo (i.e. the part of the - * requested path that comes after the servlet mapping) - */ - return VaadinServletService - .getCancelingRelativePath(pathInfo); - } - } - - @Override - public String getThemeName(BootstrapContext context) { - String themeName = context.getRequest().getParameter( - VaadinServlet.URL_PARAMETER_THEME); - if (themeName == null) { - themeName = super.getThemeName(context); - } - return themeName; - } - }; - } - - @Override - protected InputStream getThemeResourceAsStream(UI uI, String themeName, - String resource) { - VaadinServletService service = (VaadinServletService) uI.getSession() - .getService(); - ServletContext servletContext = service.getServlet() - .getServletContext(); - return servletContext.getResourceAsStream("/" - + VaadinServlet.THEME_DIR_PATH + '/' + themeName + "/" - + resource); - } -} diff --git a/server/src/com/vaadin/server/ComponentSizeValidator.java b/server/src/com/vaadin/server/ComponentSizeValidator.java index f5e2e2fe12..27d087a2b2 100644 --- a/server/src/com/vaadin/server/ComponentSizeValidator.java +++ b/server/src/com/vaadin/server/ComponentSizeValidator.java @@ -191,7 +191,6 @@ public class ComponentSizeValidator implements Serializable { } public void reportErrors(PrintWriter clientJSON, - AbstractCommunicationManager communicationManager, PrintStream serverErrorStream) { clientJSON.write("{"); @@ -269,8 +268,7 @@ public class ComponentSizeValidator implements Serializable { } else { first = false; } - subError.reportErrors(clientJSON, communicationManager, - serverErrorStream); + subError.reportErrors(clientJSON, serverErrorStream); } clientJSON.write("]"); serverErrorStream.println("<< Sub erros"); diff --git a/server/src/com/vaadin/server/ConnectorResource.java b/server/src/com/vaadin/server/ConnectorResource.java index 8f8591e6b1..8682f8ce6f 100644 --- a/server/src/com/vaadin/server/ConnectorResource.java +++ b/server/src/com/vaadin/server/ConnectorResource.java @@ -30,6 +30,15 @@ public interface ConnectorResource extends Resource { /** * Gets resource as stream. + * <p> + * Note that this method is called while the session is locked to prevent + * race conditions but the methods in the returned {@link DownloadStream} + * are assumed to be unrelated to the VaadinSession and are called without + * holding session locks (to prevent locking the session during long file + * downloads). + * </p> + * + * @return A download stream which produces the resource content */ public DownloadStream getStream(); diff --git a/server/src/com/vaadin/server/ConnectorResourceHandler.java b/server/src/com/vaadin/server/ConnectorResourceHandler.java index 03a2fcc115..00d82988d3 100644 --- a/server/src/com/vaadin/server/ConnectorResourceHandler.java +++ b/server/src/com/vaadin/server/ConnectorResourceHandler.java @@ -16,6 +16,7 @@ package com.vaadin.server; import java.io.IOException; +import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import java.util.regex.Matcher; @@ -25,6 +26,7 @@ import javax.servlet.http.HttpServletResponse; import com.vaadin.shared.ApplicationConstants; import com.vaadin.ui.UI; +import com.vaadin.util.CurrentInstance; public class ConnectorResourceHandler implements RequestHandler { // APP/connector/[uiid]/[cid]/[filename.xyz] @@ -46,28 +48,38 @@ public class ConnectorResourceHandler implements RequestHandler { return false; } Matcher matcher = CONNECTOR_RESOURCE_PATTERN.matcher(requestPath); - if (matcher.matches()) { - String uiId = matcher.group(1); - String cid = matcher.group(2); - String key = matcher.group(3); - UI ui = session.getUIById(Integer.parseInt(uiId)); + if (!matcher.matches()) { + return false; + } + String uiId = matcher.group(1); + String cid = matcher.group(2); + String key = matcher.group(3); + + session.lock(); + UI ui; + ClientConnector connector; + try { + ui = session.getUIById(Integer.parseInt(uiId)); if (ui == null) { return error(request, response, "Ignoring connector request for no-existent root " + uiId); } - UI.setCurrent(ui); - VaadinSession.setCurrent(ui.getSession()); - - ClientConnector connector = ui.getConnectorTracker().getConnector( - cid); + connector = ui.getConnectorTracker().getConnector(cid); if (connector == null) { return error(request, response, "Ignoring connector request for no-existent connector " + cid + " in root " + uiId); } + } finally { + session.unlock(); + } + + Map<Class<?>, CurrentInstance> oldThreadLocals = CurrentInstance + .setThreadLocals(ui); + try { if (!connector.handleConnectorRequest(request, response, key)) { return error(request, response, connector.getClass() .getSimpleName() @@ -75,20 +87,11 @@ public class ConnectorResourceHandler implements RequestHandler { + connector.getConnectorId() + ") did not handle connector request for " + key); } - - return true; - } else if (requestPath.matches('/' + ApplicationConstants.APP_PATH - + "(/.*)?")) { - /* - * This should be the last request handler before we get to - * bootstrap logic. Prevent /APP requests from reaching bootstrap - * handlers to help protect the /APP name space for framework usage. - */ - return error(request, response, - "Returning 404 for /APP request not yet handled."); - } else { - return false; + } finally { + CurrentInstance.restoreThreadLocals(oldThreadLocals); } + + return true; } private static boolean error(VaadinRequest request, diff --git a/server/src/com/vaadin/server/Constants.java b/server/src/com/vaadin/server/Constants.java index a9bc3e5b9e..f8d8105286 100644 --- a/server/src/com/vaadin/server/Constants.java +++ b/server/src/com/vaadin/server/Constants.java @@ -15,6 +15,8 @@ */ package com.vaadin.server; +import com.vaadin.shared.communication.PushMode; + /** * TODO Document me! * @@ -47,6 +49,13 @@ public interface Constants { + "in web.xml. The default of 5min will be used.\n" + "==========================================================="; + static final String WARNING_PUSH_MODE_NOT_RECOGNIZED = "\n" + + "===========================================================\n" + + "WARNING: pushMode has been set to an unrecognized value\n" + + "in web.xml. The permitted values are \"disabled\", \"manual\",\n" + + "and \"automatic\". The default of \"disabled\" will be used.\n" + + "==========================================================="; + static final String WIDGETSET_MISMATCH_INFO = "\n" + "=================================================================\n" + "The widgetset in use does not seem to be built for the Vaadin\n" @@ -56,6 +65,53 @@ public interface Constants { + " Widgetset version: %s\n" + "================================================================="; + static final String REQUIRED_ATMOSPHERE_VERSION = "1.0.12"; + + static final String INVALID_ATMOSPHERE_VERSION_WARNING = "\n" + + "=================================================================\n" + + "Vaadin depends on Atomsphere {0} but version {1} was found.\n" + + "This might cause compatibility problems if push is used.\n" + + "================================================================="; + + static final String ATMOSPHERE_MISSING_ERROR = "\n" + + "=================================================================\n" + + "Atmosphere could not be loaded. When using push with Vaadin, the\n" + + "Atmosphere framework must be present on the classpath.\n" + + "If using a dependency management system, please add a dependency\n" + + "to vaadin-push.\n" + + "If managing dependencies manually, please make sure Atmosphere\n" + + REQUIRED_ATMOSPHERE_VERSION + + " is included on the classpath.\n" + + "Will fall back to using " + + PushMode.class.getSimpleName() + + "." + + PushMode.DISABLED.name() + + ".\n" + + "================================================================="; + + static final String PUSH_NOT_SUPPORTED_ERROR = "\n" + + "=================================================================\n" + + "Push is not supported for {0}\n" + + "Will fall back to using " + + PushMode.class.getSimpleName() + + "." + + PushMode.DISABLED.name() + + ".\n" + + "================================================================="; + + public static final String WARNING_LEGACY_PROPERTY_TOSTRING = "You are using toString() instead of getValue() to get the value for a Property of type {0}" + + ". This is strongly discouraged and only provided for backwards compatibility with Vaadin 6. " + + "To disable this warning message and retain the behavior, set the init parameter \"" + + Constants.SERVLET_PARAMETER_LEGACY_PROPERTY_TOSTRING + + "\" to \"true\". To disable the legacy functionality, set \"" + + Constants.SERVLET_PARAMETER_LEGACY_PROPERTY_TOSTRING + + "\" to false." + + " (Note that your debugger might call toString() and trigger this message)."; + + static final String WARNING_UNKNOWN_LEGACY_PROPERTY_TOSTRING_VALUE = "Unknown value '{0}' for parameter " + + Constants.SERVLET_PARAMETER_LEGACY_PROPERTY_TOSTRING + + ". Supported values are 'false','warning','true'"; + static final String URL_PARAMETER_THEME = "theme"; static final String SERVLET_PARAMETER_PRODUCTION_MODE = "productionMode"; @@ -63,7 +119,9 @@ public interface Constants { static final String SERVLET_PARAMETER_RESOURCE_CACHE_TIME = "resourceCacheTime"; static final String SERVLET_PARAMETER_HEARTBEAT_INTERVAL = "heartbeatInterval"; static final String SERVLET_PARAMETER_CLOSE_IDLE_SESSIONS = "closeIdleSessions"; + static final String SERVLET_PARAMETER_PUSH_MODE = "pushMode"; static final String SERVLET_PARAMETER_UI_PROVIDER = "UIProvider"; + static final String SERVLET_PARAMETER_LEGACY_PROPERTY_TOSTRING = "legacyPropertyToString"; // Configurable parameter names static final String PARAMETER_VAADIN_RESOURCES = "Resources"; diff --git a/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java b/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java index 5b0c3fe8d1..80c3644d77 100644 --- a/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java +++ b/server/src/com/vaadin/server/DefaultDeploymentConfiguration.java @@ -17,8 +17,11 @@ package com.vaadin.server; import java.util.Properties; +import java.util.logging.Level; import java.util.logging.Logger; +import com.vaadin.shared.communication.PushMode; + /** * The default implementation of {@link DeploymentConfiguration} based on a base * class for resolving system properties and a set of init parameters. @@ -33,7 +36,9 @@ public class DefaultDeploymentConfiguration implements DeploymentConfiguration { private int resourceCacheTime; private int heartbeatInterval; private boolean closeIdleSessions; + private PushMode pushMode; private final Class<?> systemPropertyBaseClass; + private LegacyProperyToStringMode legacyPropertyToStringMode; /** * Create a new deployment configuration instance. @@ -55,6 +60,27 @@ public class DefaultDeploymentConfiguration implements DeploymentConfiguration { checkResourceCacheTime(); checkHeartbeatInterval(); checkCloseIdleSessions(); + checkPushMode(); + checkLegacyPropertyToString(); + } + + private void checkLegacyPropertyToString() { + String param = getApplicationOrSystemProperty( + Constants.SERVLET_PARAMETER_LEGACY_PROPERTY_TOSTRING, "warning"); + if ("true".equals(param)) { + legacyPropertyToStringMode = LegacyProperyToStringMode.ENABLED; + } else if ("false".equals(param)) { + legacyPropertyToStringMode = LegacyProperyToStringMode.DISABLED; + } else { + if (!"warning".equals(param)) { + getLogger() + .log(Level.WARNING, + Constants.WARNING_UNKNOWN_LEGACY_PROPERTY_TOSTRING_VALUE, + param); + } + legacyPropertyToStringMode = LegacyProperyToStringMode.WARNING; + + } } @Override @@ -167,12 +193,32 @@ public class DefaultDeploymentConfiguration implements DeploymentConfiguration { return heartbeatInterval; } + /** + * {@inheritDoc} + * <p> + * The default value is false. + */ @Override public boolean isCloseIdleSessions() { return closeIdleSessions; } /** + * {@inheritDoc} + * <p> + * The default mode is {@link PushMode#DISABLED}. + */ + @Override + public PushMode getPushMode() { + return pushMode; + } + + @Override + public Properties getInitParameters() { + return initParameters; + } + + /** * Log a warning if Vaadin is not running in production mode. */ private void checkProductionMode() { @@ -231,13 +277,26 @@ public class DefaultDeploymentConfiguration implements DeploymentConfiguration { .equals("true"); } + private void checkPushMode() { + String mode = getApplicationOrSystemProperty( + Constants.SERVLET_PARAMETER_PUSH_MODE, + PushMode.DISABLED.toString()); + try { + pushMode = Enum.valueOf(PushMode.class, mode.toUpperCase()); + } catch (IllegalArgumentException e) { + getLogger().warning(Constants.WARNING_PUSH_MODE_NOT_RECOGNIZED); + pushMode = PushMode.DISABLED; + } + } + private Logger getLogger() { return Logger.getLogger(getClass().getName()); } @Override - public Properties getInitParameters() { - return initParameters; + @Deprecated + public LegacyProperyToStringMode getLegacyPropertyToStringMode() { + return legacyPropertyToStringMode; } } diff --git a/server/src/com/vaadin/server/DeploymentConfiguration.java b/server/src/com/vaadin/server/DeploymentConfiguration.java index bd4bc928f4..bf9c019b6d 100644 --- a/server/src/com/vaadin/server/DeploymentConfiguration.java +++ b/server/src/com/vaadin/server/DeploymentConfiguration.java @@ -19,6 +19,9 @@ package com.vaadin.server; import java.io.Serializable; import java.util.Properties; +import com.vaadin.data.util.AbstractProperty; +import com.vaadin.shared.communication.PushMode; + /** * A collection of properties configured at deploy time as well as a way of * accessing third party properties not explicitly supported by this class. @@ -28,6 +31,23 @@ import java.util.Properties; * @since 7.0.0 */ public interface DeploymentConfiguration extends Serializable { + + /** + * Determines the mode of the "legacyPropertyToString" parameter. + * + * @author Vaadin Ltd + * @since 7.1 + */ + @Deprecated + public enum LegacyProperyToStringMode { + DISABLED, WARNING, ENABLED; + + public boolean useLegacyMode() { + return this == WARNING || this == ENABLED; + } + + } + /** * Returns whether Vaadin is in production mode. * @@ -78,6 +98,14 @@ public interface DeploymentConfiguration extends Serializable { public boolean isCloseIdleSessions(); /** + * Returns the mode of bidirectional ("push") client-server communication + * that should be used. + * + * @return The push mode in use. + */ + public PushMode getPushMode(); + + /** * Gets the properties configured for the deployment, e.g. as init * parameters to the servlet or portlet. * @@ -101,4 +129,13 @@ public interface DeploymentConfiguration extends Serializable { public String getApplicationOrSystemProperty(String propertyName, String defaultValue); + /** + * Returns to legacy Property.toString() mode used. See + * {@link AbstractProperty#isLegacyToStringEnabled()} for more information. + * + * @return The Property.toString() mode in use. + */ + @Deprecated + public LegacyProperyToStringMode getLegacyPropertyToStringMode(); + } diff --git a/server/src/com/vaadin/server/DownloadStream.java b/server/src/com/vaadin/server/DownloadStream.java index e2f9fc5296..4e66831f1d 100644 --- a/server/src/com/vaadin/server/DownloadStream.java +++ b/server/src/com/vaadin/server/DownloadStream.java @@ -28,6 +28,11 @@ import javax.servlet.http.HttpServletResponse; /** * Downloadable stream. + * <p> + * Note that the methods in a DownloadStream are called without locking the + * session to prevent locking the session during long file downloads. If your + * DownloadStream uses anything from the session, you must handle the locking. + * </p> * * @author Vaadin Ltd. * @since 3.0 diff --git a/server/src/com/vaadin/server/DragAndDropService.java b/server/src/com/vaadin/server/DragAndDropService.java index 5a54b5ae3a..a83e83ef7f 100644 --- a/server/src/com/vaadin/server/DragAndDropService.java +++ b/server/src/com/vaadin/server/DragAndDropService.java @@ -16,7 +16,7 @@ package com.vaadin.server; import java.io.IOException; -import java.io.PrintWriter; +import java.io.Writer; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -50,13 +50,13 @@ public class DragAndDropService implements VariableOwner, ClientConnector { private DragAndDropEvent dragEvent; - private final AbstractCommunicationManager manager; + private final LegacyCommunicationManager manager; private AcceptCriterion acceptCriterion; private ErrorHandler errorHandler; - public DragAndDropService(AbstractCommunicationManager manager) { + public DragAndDropService(LegacyCommunicationManager manager) { this.manager = manager; } @@ -209,10 +209,10 @@ public class DragAndDropService implements VariableOwner, ClientConnector { return true; } - void printJSONResponse(PrintWriter outWriter) throws PaintException { + public void printJSONResponse(Writer outWriter) throws IOException { if (isDirty()) { - outWriter.print(", \"dd\":"); + outWriter.write(", \"dd\":"); JsonPaintTarget jsonPaintTarget = new JsonPaintTarget(manager, outWriter, false); diff --git a/server/src/com/vaadin/server/FileDownloader.java b/server/src/com/vaadin/server/FileDownloader.java index 7cc1fd7cc8..9b49ad8edd 100644 --- a/server/src/com/vaadin/server/FileDownloader.java +++ b/server/src/com/vaadin/server/FileDownloader.java @@ -129,10 +129,15 @@ public class FileDownloader extends AbstractExtension { // Ignore if it isn't for us return false; } + getSession().lock(); + DownloadStream stream; - Resource resource = getFileDownloadResource(); - if (resource instanceof ConnectorResource) { - DownloadStream stream = ((ConnectorResource) resource).getStream(); + try { + Resource resource = getFileDownloadResource(); + if (!(resource instanceof ConnectorResource)) { + return false; + } + stream = ((ConnectorResource) resource).getStream(); if (stream.getParameter("Content-Disposition") == null) { // Content-Disposition: attachment generally forces download @@ -140,15 +145,15 @@ public class FileDownloader extends AbstractExtension { "attachment; filename=\"" + stream.getFileName() + "\""); } - // Content-Type to block eager browser plug-ins from hijacking the - // file + // Content-Type to block eager browser plug-ins from hijacking + // the file if (isOverrideContentType()) { stream.setContentType("application/octet-stream;charset=UTF-8"); } - stream.writeResponse(request, response); - return true; - } else { - return false; + } finally { + getSession().unlock(); } + stream.writeResponse(request, response); + return true; } } diff --git a/server/src/com/vaadin/server/GAEVaadinServlet.java b/server/src/com/vaadin/server/GAEVaadinServlet.java index 0d2063d446..b4a83603b0 100644 --- a/server/src/com/vaadin/server/GAEVaadinServlet.java +++ b/server/src/com/vaadin/server/GAEVaadinServlet.java @@ -184,16 +184,14 @@ public class GAEVaadinServlet extends VaadinServlet { return; } - RequestType requestType = getRequestType(request); - - if (requestType == RequestType.STATIC_FILE) { + if (isStaticResourceRequest(request)) { // no locking needed, let superclass handle super.service(request, response); cleanSession(request); return; } - if (requestType == RequestType.APP) { + if (ServletPortletHelper.isAppRequest(request)) { // no locking needed, let superclass handle getApplicationContext(request, MemcacheServiceFactory.getMemcacheService()); @@ -205,7 +203,11 @@ public class GAEVaadinServlet extends VaadinServlet { final HttpSession session = request.getSession(getService() .requestCanCreateSession(request)); if (session == null) { - handleServiceSessionExpired(request, response); + try { + getService().handleSessionExpired(request, response); + } catch (ServiceException e) { + throw new ServletException(e); + } cleanSession(request); return; } @@ -218,19 +220,21 @@ public class GAEVaadinServlet extends VaadinServlet { // try to get lock long started = new Date().getTime(); // non-UIDL requests will try indefinitely - while (requestType != RequestType.UIDL - || new Date().getTime() - started < MAX_UIDL_WAIT_MILLISECONDS) { - locked = memcache.put(mutex, 1, Expiration.byDeltaSeconds(40), - MemcacheService.SetPolicy.ADD_ONLY_IF_NOT_PRESENT); - if (locked) { - break; - } - try { - Thread.sleep(RETRY_AFTER_MILLISECONDS); - } catch (InterruptedException e) { - getLogger().finer( - "Thread.sleep() interrupted while waiting for lock. Trying again. " - + e); + if (!ServletPortletHelper.isUIDLRequest(request)) { + while (new Date().getTime() - started < MAX_UIDL_WAIT_MILLISECONDS) { + locked = memcache.put(mutex, 1, + Expiration.byDeltaSeconds(40), + MemcacheService.SetPolicy.ADD_ONLY_IF_NOT_PRESENT); + if (locked) { + break; + } + try { + Thread.sleep(RETRY_AFTER_MILLISECONDS); + } catch (InterruptedException e) { + getLogger().finer( + "Thread.sleep() interrupted while waiting for lock. Trying again. " + + e); + } } } diff --git a/server/src/com/vaadin/server/GlobalResourceHandler.java b/server/src/com/vaadin/server/GlobalResourceHandler.java index 0fac14e20c..d411b286d0 100644 --- a/server/src/com/vaadin/server/GlobalResourceHandler.java +++ b/server/src/com/vaadin/server/GlobalResourceHandler.java @@ -31,6 +31,7 @@ import javax.servlet.http.HttpServletResponse; import com.vaadin.shared.ApplicationConstants; import com.vaadin.ui.LegacyComponent; import com.vaadin.ui.UI; +import com.vaadin.util.CurrentInstance; /** * A {@link RequestHandler} that takes care of {@link ConnectorResource}s that @@ -85,30 +86,38 @@ public class GlobalResourceHandler implements RequestHandler { return error(request, response, pathInfo + " is not a valid global resource path"); } + session.lock(); + Map<Class<?>, CurrentInstance> oldThreadLocals = null; + DownloadStream stream = null; + try { + UI ui = session.getUIById(Integer.parseInt(uiid)); + if (ui == null) { + return error(request, response, "No UI found for id " + uiid); + } + oldThreadLocals = CurrentInstance.setThreadLocals(ui); + ConnectorResource resource; + if (LEGACY_TYPE.equals(type)) { + resource = legacyResources.get(key); + } else { + return error(request, response, "Unknown global resource type " + + type + " in requested path " + pathInfo); + } - UI ui = session.getUIById(Integer.parseInt(uiid)); - if (ui == null) { - return error(request, response, "No UI found for id " + uiid); - } - UI.setCurrent(ui); - - ConnectorResource resource; - if (LEGACY_TYPE.equals(type)) { - resource = legacyResources.get(key); - } else { - return error(request, response, "Unknown global resource type " - + type + " in requested path " + pathInfo); - } - - if (resource == null) { - return error(request, response, "Global resource " + key - + " not found"); - } + if (resource == null) { + return error(request, response, "Global resource " + key + + " not found"); + } - DownloadStream stream = resource.getStream(); - if (stream == null) { - return error(request, response, "Resource " + resource - + " didn't produce any stream."); + stream = resource.getStream(); + if (stream == null) { + return error(request, response, "Resource " + resource + + " didn't produce any stream."); + } + } finally { + session.unlock(); + if (oldThreadLocals != null) { + CurrentInstance.restoreThreadLocals(oldThreadLocals); + } } stream.writeResponse(request, response); diff --git a/server/src/com/vaadin/server/JsonCodec.java b/server/src/com/vaadin/server/JsonCodec.java index 9a70efab28..d533ed99f3 100644 --- a/server/src/com/vaadin/server/JsonCodec.java +++ b/server/src/com/vaadin/server/JsonCodec.java @@ -667,7 +667,7 @@ public class JsonCodec implements Serializable { } else if (value instanceof Connector) { Connector connector = (Connector) value; if (value instanceof Component - && !(AbstractCommunicationManager + && !(LegacyCommunicationManager .isComponentVisibleToClient((Component) value))) { return encodeNull(); } @@ -871,7 +871,7 @@ public class JsonCodec implements Serializable { for (Entry<?, ?> entry : map.entrySet()) { ClientConnector key = (ClientConnector) entry.getKey(); - if (AbstractCommunicationManager.isConnectorVisibleToClient(key)) { + if (LegacyCommunicationManager.isConnectorVisibleToClient(key)) { EncodeResult encodedValue = encode(entry.getValue(), null, valueType, connectorTracker); jsonMap.put(key.getConnectorId(), diff --git a/server/src/com/vaadin/server/JsonPaintTarget.java b/server/src/com/vaadin/server/JsonPaintTarget.java index 11bfb33fe1..ca70391f64 100644 --- a/server/src/com/vaadin/server/JsonPaintTarget.java +++ b/server/src/com/vaadin/server/JsonPaintTarget.java @@ -18,6 +18,7 @@ package com.vaadin.server; import java.io.PrintWriter; import java.io.Serializable; +import java.io.Writer; import java.util.Collection; import java.util.HashSet; import java.util.Iterator; @@ -60,7 +61,7 @@ public class JsonPaintTarget implements PaintTarget { private boolean closed = false; - private final AbstractCommunicationManager manager; + private final LegacyCommunicationManager manager; private int changes = 0; @@ -86,14 +87,13 @@ public class JsonPaintTarget implements PaintTarget { * @throws PaintException * if the paint operation failed. */ - public JsonPaintTarget(AbstractCommunicationManager manager, - PrintWriter outWriter, boolean cachingRequired) - throws PaintException { + public JsonPaintTarget(LegacyCommunicationManager manager, + Writer outWriter, boolean cachingRequired) throws PaintException { this.manager = manager; // Sets the target for UIDL writing - uidlBuffer = outWriter; + uidlBuffer = new PrintWriter(outWriter); // Initialize tag-writing mOpenTags = new Stack<String>(); @@ -1007,7 +1007,7 @@ public class JsonPaintTarget implements PaintTarget { return manager.getTagForType(clientConnectorClass); } - Collection<Class<? extends ClientConnector>> getUsedClientConnectors() { + public Collection<Class<? extends ClientConnector>> getUsedClientConnectors() { return usedClientConnectors; } diff --git a/server/src/com/vaadin/server/LegacyCommunicationManager.java b/server/src/com/vaadin/server/LegacyCommunicationManager.java new file mode 100644 index 0000000000..c0194db243 --- /dev/null +++ b/server/src/com/vaadin/server/LegacyCommunicationManager.java @@ -0,0 +1,498 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Serializable; +import java.io.Writer; +import java.net.URI; +import java.net.URISyntaxException; +import java.security.GeneralSecurityException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.json.JSONException; +import org.json.JSONObject; + +import com.vaadin.server.ClientConnector.ConnectorErrorEvent; +import com.vaadin.server.communication.LocaleWriter; +import com.vaadin.shared.ApplicationConstants; +import com.vaadin.shared.JavaScriptConnectorState; +import com.vaadin.shared.communication.SharedState; +import com.vaadin.ui.Component; +import com.vaadin.ui.ConnectorTracker; +import com.vaadin.ui.HasComponents; +import com.vaadin.ui.SelectiveRenderer; +import com.vaadin.ui.UI; + +/** + * This is a common base class for the server-side implementations of the + * communication system between the client code (compiled with GWT into + * JavaScript) and the server side components. Its client side counterpart is + * {@link com.vaadin.client.ApplicationConnection}. + * <p> + * TODO Document better! + * + * @deprecated As of 7.0. Will likely change or be removed in a future version + */ +@Deprecated +@SuppressWarnings("serial") +public class LegacyCommunicationManager implements Serializable { + + // TODO Refactor (#11410) + private final HashMap<Integer, ClientCache> uiToClientCache = new HashMap<Integer, ClientCache>(); + + /** + * The session this communication manager is used for + */ + private final VaadinSession session; + + // TODO Refactor to UI shared state (#11378) + private List<String> locales; + + // TODO Move to VaadinSession (#11409) + private DragAndDropService dragAndDropService; + + // TODO Refactor (#11412) + private String requestThemeName; + + // TODO Refactor (#11413) + private Map<String, Class<?>> publishedFileContexts = new HashMap<String, Class<?>>(); + + /** + * TODO New constructor - document me! + * + * @param session + */ + public LegacyCommunicationManager(VaadinSession session) { + this.session = session; + requireLocale(session.getLocale().toString()); + } + + protected VaadinSession getSession() { + return session; + } + + /** + * @deprecated As of 7.1. See #11411. + */ + @Deprecated + public static JSONObject encodeState(ClientConnector connector, + SharedState state) throws JSONException { + UI uI = connector.getUI(); + ConnectorTracker connectorTracker = uI.getConnectorTracker(); + Class<? extends SharedState> stateType = connector.getStateType(); + Object diffState = connectorTracker.getDiffState(connector); + boolean supportsDiffState = !JavaScriptConnectorState.class + .isAssignableFrom(stateType); + if (diffState == null && supportsDiffState) { + // Use an empty state object as reference for full + // repaints + + try { + SharedState referenceState = stateType.newInstance(); + EncodeResult encodeResult = JsonCodec.encode(referenceState, + null, stateType, uI.getConnectorTracker()); + diffState = encodeResult.getEncodedValue(); + } catch (Exception e) { + getLogger() + .log(Level.WARNING, + "Error creating reference object for state of type {0}", + stateType.getName()); + } + } + EncodeResult encodeResult = JsonCodec.encode(state, diffState, + stateType, uI.getConnectorTracker()); + if (supportsDiffState) { + connectorTracker.setDiffState(connector, + (JSONObject) encodeResult.getEncodedValue()); + } + return (JSONObject) encodeResult.getDiff(); + } + + /** + * Resolves a dependency URI, registering the URI with this + * {@code LegacyCommunicationManager} if needed and returns a fully + * qualified URI. + * + * @deprecated As of 7.1. See #11413. + */ + @Deprecated + public String registerDependency(String resourceUri, Class<?> context) { + try { + URI uri = new URI(resourceUri); + String protocol = uri.getScheme(); + + if (ApplicationConstants.PUBLISHED_PROTOCOL_NAME.equals(protocol)) { + // Strip initial slash + String resourceName = uri.getPath().substring(1); + return registerPublishedFile(resourceName, context); + } + + if (protocol != null || uri.getHost() != null) { + return resourceUri; + } + + // Bare path interpreted as published file + return registerPublishedFile(resourceUri, context); + } catch (URISyntaxException e) { + getLogger().log(Level.WARNING, + "Could not parse resource url " + resourceUri, e); + return resourceUri; + } + } + + /** + * @deprecated As of 7.1. See #11413. + */ + @Deprecated + public Map<String, Class<?>> getDependencies() { + return publishedFileContexts; + } + + private String registerPublishedFile(String name, Class<?> context) { + // Add to map of names accepted by servePublishedFile + if (publishedFileContexts.containsKey(name)) { + Class<?> oldContext = publishedFileContexts.get(name); + if (oldContext != context) { + getLogger() + .log(Level.WARNING, + "{0} published by both {1} and {2}. File from {2} will be used.", + new Object[] { name, context, oldContext }); + } + } else { + publishedFileContexts.put(name, context); + } + + return ApplicationConstants.PUBLISHED_PROTOCOL_PREFIX + "/" + name; + } + + /** + * @deprecated As of 7.1. See #11410. + */ + @Deprecated + public ClientCache getClientCache(UI uI) { + Integer uiId = Integer.valueOf(uI.getUIId()); + ClientCache cache = uiToClientCache.get(uiId); + if (cache == null) { + cache = new ClientCache(); + uiToClientCache.put(uiId, cache); + } + return cache; + } + + /** + * Checks if the connector is visible in context. For Components, + * {@link #isComponentVisibleToClient(Component)} is used. For other types + * of connectors, the contextual visibility of its first Component ancestor + * is used. If no Component ancestor is found, the connector is not visible. + * + * @deprecated As of 7.1. See #11411. + * + * @param connector + * The connector to check + * @return <code>true</code> if the connector is visible to the client, + * <code>false</code> otherwise + */ + @Deprecated + public static boolean isConnectorVisibleToClient(ClientConnector connector) { + if (connector instanceof Component) { + return isComponentVisibleToClient((Component) connector); + } else { + ClientConnector parent = connector.getParent(); + if (parent == null) { + return false; + } else { + return isConnectorVisibleToClient(parent); + } + } + } + + /** + * Checks if the component should be visible to the client. Returns false if + * the child should not be sent to the client, true otherwise. + * + * @deprecated As of 7.1. See #11411. + * + * @param child + * The child to check + * @return true if the child is visible to the client, false otherwise + */ + @Deprecated + public static boolean isComponentVisibleToClient(Component child) { + if (!child.isVisible()) { + return false; + } + HasComponents parent = child.getParent(); + + if (parent instanceof SelectiveRenderer) { + if (!((SelectiveRenderer) parent).isRendered(child)) { + return false; + } + } + + if (parent != null) { + return isComponentVisibleToClient(parent); + } else { + if (child instanceof UI) { + // UI has no parent and visibility was checked above + return true; + } else { + // Component which is not attached to any UI + return false; + } + } + } + + /** + * @deprecated As of 7.1. See #11412. + */ + @Deprecated + public String getTheme(UI uI) { + String themeName = uI.getTheme(); + String requestThemeName = getRequestTheme(); + + if (requestThemeName != null) { + themeName = requestThemeName; + } + if (themeName == null) { + themeName = VaadinServlet.getDefaultTheme(); + } + return themeName; + } + + private String getRequestTheme() { + return requestThemeName; + } + + /** + * @deprecated As of 7.1. See #11411. + */ + @Deprecated + public ClientConnector getConnector(UI uI, String connectorId) { + ClientConnector c = uI.getConnectorTracker().getConnector(connectorId); + if (c == null + && connectorId.equals(getDragAndDropService().getConnectorId())) { + return getDragAndDropService(); + } + + return c; + } + + /** + * @deprecated As of 7.1. See #11409. + */ + @Deprecated + public DragAndDropService getDragAndDropService() { + if (dragAndDropService == null) { + dragAndDropService = new DragAndDropService(this); + } + return dragAndDropService; + } + + /** + * Prints the queued (pending) locale definitions to a {@link PrintWriter} + * in a (UIDL) format that can be sent to the client and used there in + * formatting dates, times etc. + * + * @deprecated As of 7.1. See #11378. + * + * @param outWriter + */ + @Deprecated + public void printLocaleDeclarations(Writer writer) throws IOException { + new LocaleWriter().write(locales, writer); + } + + /** + * Queues a locale to be sent to the client (browser) for date and time + * entry etc. All locale specific information is derived from server-side + * {@link Locale} instances and sent to the client when needed, eliminating + * the need to use the {@link Locale} class and all the framework behind it + * on the client. + * + * @deprecated As of 7.1. See #11378. + * + * @see Locale#toString() + * + * @param value + */ + @Deprecated + public void requireLocale(String value) { + if (locales == null) { + locales = new ArrayList<String>(); + locales.add(session.getLocale().toString()); + } + if (!locales.contains(value)) { + locales.add(value); + } + } + + /** + * @deprecated As of 7.1. See #11378. + */ + @Deprecated + public void resetLocales() { + locales = null; + } + + /** + * @deprecated As of 7.1. Will be removed in the future. + */ + @Deprecated + public static class InvalidUIDLSecurityKeyException extends + GeneralSecurityException { + + public InvalidUIDLSecurityKeyException(String message) { + super(message); + } + } + + private final HashMap<Class<? extends ClientConnector>, Integer> typeToKey = new HashMap<Class<? extends ClientConnector>, Integer>(); + private int nextTypeKey = 0; + + private BootstrapHandler bootstrapHandler; + + /** + * @deprecated As of 7.1. Will be removed in the future. + */ + @Deprecated + public String getTagForType(Class<? extends ClientConnector> class1) { + Integer id = typeToKey.get(class1); + if (id == null) { + id = nextTypeKey++; + typeToKey.put(class1, id); + if (getLogger().isLoggable(Level.FINE)) { + getLogger().log(Level.FINE, "Mapping {0} to {1}", + new Object[] { class1.getName(), id }); + } + } + return id.toString(); + } + + /** + * Helper class for terminal to keep track of data that client is expected + * to know. + * + * TODO make customlayout templates (from theme) to be cached here. + * + * @deprecated As of 7.1. See #11410. + */ + @Deprecated + public class ClientCache implements Serializable { + + private final Set<Object> res = new HashSet<Object>(); + + /** + * + * @param paintable + * @return true if the given class was added to cache + */ + public boolean cache(Object object) { + return res.add(object); + } + + public void clear() { + res.clear(); + } + + } + + /** + * @deprecated As of 7.1. See #11411. + */ + @Deprecated + public String getStreamVariableTargetUrl(ClientConnector owner, + String name, StreamVariable value) { + /* + * We will use the same APP/* URI space as ApplicationResources but + * prefix url with UPLOAD + * + * eg. APP/UPLOAD/[UIID]/[PID]/[NAME]/[SECKEY] + * + * SECKEY is created on each paint to make URL's unpredictable (to + * prevent CSRF attacks). + * + * NAME and PID from URI forms a key to fetch StreamVariable when + * handling post + */ + String paintableId = owner.getConnectorId(); + UI ui = owner.getUI(); + int uiId = ui.getUIId(); + String key = uiId + "/" + paintableId + "/" + name; + + ConnectorTracker connectorTracker = ui.getConnectorTracker(); + connectorTracker.addStreamVariable(paintableId, name, value); + String seckey = connectorTracker.getSeckey(value); + + return ApplicationConstants.APP_PROTOCOL_PREFIX + + ServletPortletHelper.UPLOAD_URL_PREFIX + key + "/" + seckey; + + } + + /** + * Handles an exception that occurred when processing RPC calls or a file + * upload. + * + * @deprecated As of 7.1. See #11411. + * + * @param ui + * The UI where the exception occured + * @param throwable + * The exception + * @param connector + * The Rpc target + */ + @Deprecated + public void handleConnectorRelatedException(ClientConnector connector, + Throwable throwable) { + ErrorEvent errorEvent = new ConnectorErrorEvent(connector, throwable); + ErrorHandler handler = ErrorEvent.findErrorHandler(connector); + handler.error(errorEvent); + } + + /** + * Requests that the given UI should be fully re-rendered on the client + * side. + * + * @since 7.1 + * @deprecated. As of 7.1. Should be refactored once locales are fixed + * (#11378) + */ + @Deprecated + public void repaintAll(UI ui) { + getClientCache(ui).clear(); + ui.getConnectorTracker().markAllConnectorsDirty(); + ui.getConnectorTracker().markAllClientSidesUninitialized(); + + // Reset sent locales + resetLocales(); + requireLocale(session.getLocale().toString()); + } + + private static final Logger getLogger() { + return Logger.getLogger(LegacyCommunicationManager.class.getName()); + } +} diff --git a/server/src/com/vaadin/server/LegacyPaint.java b/server/src/com/vaadin/server/LegacyPaint.java index 09477aaf3e..8d59dfd5ea 100644 --- a/server/src/com/vaadin/server/LegacyPaint.java +++ b/server/src/com/vaadin/server/LegacyPaint.java @@ -50,7 +50,7 @@ public class LegacyPaint implements Serializable { public static void paint(Component component, PaintTarget target) throws PaintException { // Only paint content of visible components. - if (!AbstractCommunicationManager.isComponentVisibleToClient(component)) { + if (!LegacyCommunicationManager.isComponentVisibleToClient(component)) { return; } diff --git a/server/src/com/vaadin/server/Page.java b/server/src/com/vaadin/server/Page.java index 8737b478c3..d4c16fe7f7 100644 --- a/server/src/com/vaadin/server/Page.java +++ b/server/src/com/vaadin/server/Page.java @@ -21,14 +21,18 @@ import java.lang.reflect.Method; import java.net.URI; import java.net.URISyntaxException; import java.util.EventObject; +import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; +import java.util.Map; import com.vaadin.event.EventRouter; import com.vaadin.shared.ui.BorderStyle; import com.vaadin.shared.ui.ui.PageClientRpc; +import com.vaadin.shared.ui.ui.PageState; import com.vaadin.shared.ui.ui.UIConstants; +import com.vaadin.shared.ui.ui.UIState; import com.vaadin.ui.JavaScript; import com.vaadin.ui.LegacyWindow; import com.vaadin.ui.Link; @@ -218,7 +222,7 @@ public class Page implements Serializable { } } - private static final Method BROWSWER_RESIZE_METHOD = ReflectTools + private static final Method BROWSER_RESIZE_METHOD = ReflectTools .findMethod(BrowserWindowResizeListener.class, "browserWindowResized", BrowserWindowResizeEvent.class); @@ -303,6 +307,102 @@ public class Page implements Serializable { } } + /** + * Contains dynamically injected styles injected in the HTML document at + * runtime. + * + * @since 7.1 + */ + public static class Styles implements Serializable { + + private final Map<Integer, String> stringInjections = new HashMap<Integer, String>(); + + private final Map<Integer, Resource> resourceInjections = new HashMap<Integer, Resource>(); + + // The combined injection counter between both string and resource + // injections. Used as the key for the injection maps + private int injectionCounter = 0; + + // Points to the next injection that has not yet been made into the Page + private int nextInjectionPosition = 0; + + private final UI ui; + + private Styles(UI ui) { + this.ui = ui; + } + + /** + * Injects a raw CSS string into the page. + * + * @param css + * The CSS to inject + */ + public void add(String css) { + if (css == null) { + throw new IllegalArgumentException( + "Cannot inject null CSS string"); + } + + stringInjections.put(injectionCounter++, css); + ui.markAsDirty(); + } + + /** + * Injects a CSS resource into the page + * + * @param resource + * The resource to inject. + */ + public void add(Resource resource) { + if (resource == null) { + throw new IllegalArgumentException( + "Cannot inject null resource"); + } + + resourceInjections.put(injectionCounter++, resource); + ui.markAsDirty(); + } + + private void paint(PaintTarget target) throws PaintException { + + // If full repaint repaint all injections + if (target.isFullRepaint()) { + nextInjectionPosition = 0; + } + + if (injectionCounter > nextInjectionPosition) { + + target.startTag("css-injections"); + + while (injectionCounter > nextInjectionPosition) { + + String stringInjection = stringInjections + .get(nextInjectionPosition); + if (stringInjection != null) { + target.startTag("css-string"); + target.addAttribute("id", nextInjectionPosition); + target.addText(stringInjection); + target.endTag("css-string"); + } + + Resource resourceInjection = resourceInjections + .get(nextInjectionPosition); + if (resourceInjection != null) { + target.startTag("css-resource"); + target.addAttribute("id", nextInjectionPosition); + target.addAttribute("url", resourceInjection); + target.endTag("css-resource"); + } + + nextInjectionPosition++; + } + + target.endTag("css-injections"); + } + } + } + private EventRouter eventRouter; private final UI uI; @@ -312,13 +412,18 @@ public class Page implements Serializable { private JavaScript javaScript; + private Styles styles; + /** * The current browser location. */ private URI location; - public Page(UI uI) { + private final PageState state; + + public Page(UI uI, PageState state) { this.uI = uI; + this.state = state; } private void addListener(Class<?> eventType, Object target, Method method) { @@ -504,20 +609,27 @@ public class Page implements Serializable { } /** - * Adds a new {@link BrowserWindowResizeListener} to this uI. The listener - * will be notified whenever the browser window within which this uI resides + * Adds a new {@link BrowserWindowResizeListener} to this UI. The listener + * will be notified whenever the browser window within which this UI resides * is resized. + * <p> + * In most cases, the UI should be in lazy resize mode when using browser + * window resize listeners. Otherwise, a large number of events can be + * received while a resize is being performed. Use + * {@link UI#setResizeLazy(boolean)}. + * </p> * * @param resizeListener * the listener to add * * @see BrowserWindowResizeListener#browserWindowResized(BrowserWindowResizeEvent) - * @see #setResizeLazy(boolean) + * @see UI#setResizeLazy(boolean) */ public void addBrowserWindowResizeListener( BrowserWindowResizeListener resizeListener) { addListener(BrowserWindowResizeEvent.class, resizeListener, - BROWSWER_RESIZE_METHOD); + BROWSER_RESIZE_METHOD); + getState(true).hasResizeListeners = true; } /** @@ -539,7 +651,9 @@ public class Page implements Serializable { public void removeBrowserWindowResizeListener( BrowserWindowResizeListener resizeListener) { removeListener(BrowserWindowResizeEvent.class, resizeListener, - BROWSWER_RESIZE_METHOD); + BROWSER_RESIZE_METHOD); + getState(true).hasResizeListeners = eventRouter + .hasListeners(BrowserWindowResizeEvent.class); } /** @@ -576,10 +690,23 @@ public class Page implements Serializable { javaScript = new JavaScript(); javaScript.extend(uI); } - return javaScript; } + /** + * Returns that stylesheet associated with this Page. The stylesheet + * contains additional styles injected at runtime into the HTML document. + * + * @since 7.1 + */ + public Styles getStyles() { + + if (styles == null) { + styles = new Styles(uI); + } + return styles; + } + public void paintContent(PaintTarget target) throws PaintException { if (!openList.isEmpty()) { for (final Iterator<OpenResource> i = openList.iterator(); i @@ -637,6 +764,9 @@ public class Page implements Serializable { location.toString()); } + if (styles != null) { + styles.paint(target); + } } /** @@ -915,4 +1045,36 @@ public class Page implements Serializable { uI.getRpcProxy(PageClientRpc.class).setTitle(title); } + /** + * Reloads the page in the browser. + */ + public void reload() { + uI.getRpcProxy(PageClientRpc.class).reload(); + } + + /** + * Returns the page state. + * <p> + * The page state is transmitted to UIConnector together with + * {@link UIState} rather than as an individual entity. + * </p> + * <p> + * The state should be considered an internal detail of Page. Classes + * outside of Page should not access it directly but only through public + * APIs provided by Page. + * </p> + * + * @since 7.1 + * @param markAsDirty + * true to mark the state as dirty + * @return PageState object that can be read in any case and modified if + * markAsDirty is true + */ + protected PageState getState(boolean markAsDirty) { + if (markAsDirty) { + uI.markAsDirty(); + } + return state; + } + } diff --git a/server/src/com/vaadin/server/PortletCommunicationManager.java b/server/src/com/vaadin/server/PortletCommunicationManager.java deleted file mode 100644 index cece75847c..0000000000 --- a/server/src/com/vaadin/server/PortletCommunicationManager.java +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package com.vaadin.server; - -import java.io.IOException; -import java.io.InputStream; - -import javax.portlet.MimeResponse; -import javax.portlet.PortletContext; -import javax.portlet.PortletRequest; -import javax.portlet.PortletResponse; -import javax.portlet.RenderRequest; -import javax.portlet.RenderResponse; -import javax.portlet.ResourceURL; - -import org.json.JSONException; -import org.json.JSONObject; - -import com.vaadin.shared.ApplicationConstants; -import com.vaadin.ui.UI; - -/** - * TODO document me! - * - * @author peholmst - * - * - * @deprecated As of 7.0. Will likely change or be removed in a future version - */ -@Deprecated -@SuppressWarnings("serial") -public class PortletCommunicationManager extends AbstractCommunicationManager { - - public PortletCommunicationManager(VaadinSession session) { - super(session); - } - - @Override - protected BootstrapHandler createBootstrapHandler() { - return new BootstrapHandler() { - @Override - public boolean handleRequest(VaadinSession session, - VaadinRequest request, VaadinResponse response) - throws IOException { - PortletRequest portletRequest = ((VaadinPortletRequest) request) - .getPortletRequest(); - if (portletRequest instanceof RenderRequest) { - return super.handleRequest(session, request, response); - } else { - return false; - } - } - - @Override - protected String getServiceUrl(BootstrapContext context) { - ResourceURL portletResourceUrl = getRenderResponse(context) - .createResourceURL(); - portletResourceUrl.setResourceID(VaadinPortlet.RESOURCE_URL_ID); - return portletResourceUrl.toString(); - } - - private RenderResponse getRenderResponse(BootstrapContext context) { - PortletResponse response = ((VaadinPortletResponse) context - .getResponse()).getPortletResponse(); - - RenderResponse renderResponse = (RenderResponse) response; - return renderResponse; - } - - @Override - protected void appendMainScriptTagContents( - BootstrapContext context, StringBuilder builder) - throws JSONException, IOException { - // fixed base theme to use - all portal pages with Vaadin - // applications will load this exactly once - String portalTheme = ((VaadinPortletRequest) context - .getRequest()) - .getPortalProperty(VaadinPortlet.PORTAL_PARAMETER_VAADIN_THEME); - if (portalTheme != null - && !portalTheme.equals(context.getThemeName())) { - String portalThemeUri = getThemeUri(context, portalTheme); - // XSS safe - originates from portal properties - builder.append("vaadin.loadTheme('" + portalThemeUri - + "');"); - } - - super.appendMainScriptTagContents(context, builder); - } - - @Override - protected String getMainDivStyle(BootstrapContext context) { - VaadinService vaadinService = context.getRequest().getService(); - return vaadinService.getDeploymentConfiguration() - .getApplicationOrSystemProperty( - VaadinPortlet.PORTLET_PARAMETER_STYLE, null); - } - - @Override - protected JSONObject getApplicationParameters( - BootstrapContext context) throws JSONException, - PaintException { - JSONObject parameters = super.getApplicationParameters(context); - VaadinPortletResponse response = (VaadinPortletResponse) context - .getResponse(); - MimeResponse portletResponse = (MimeResponse) response - .getPortletResponse(); - ResourceURL resourceURL = portletResponse.createResourceURL(); - resourceURL.setResourceID("v-browserDetails"); - parameters.put("browserDetailsUrl", resourceURL.toString()); - - // Always send path info as a query parameter - parameters.put( - ApplicationConstants.SERVICE_URL_PATH_AS_PARAMETER, - true); - - return parameters; - } - - }; - - } - - @Override - protected InputStream getThemeResourceAsStream(UI uI, String themeName, - String resource) { - VaadinPortletSession session = (VaadinPortletSession) uI.getSession(); - PortletContext portletContext = session.getPortletSession() - .getPortletContext(); - return portletContext.getResourceAsStream("/" - + VaadinPortlet.THEME_DIR_PATH + '/' + themeName + "/" - + resource); - } - -} diff --git a/server/src/com/vaadin/server/RequestHandler.java b/server/src/com/vaadin/server/RequestHandler.java index 24107b744b..873752c5f2 100644 --- a/server/src/com/vaadin/server/RequestHandler.java +++ b/server/src/com/vaadin/server/RequestHandler.java @@ -19,17 +19,26 @@ package com.vaadin.server; import java.io.IOException; import java.io.Serializable; +import com.vaadin.ui.UI; + /** - * Handler for producing a response to non-UIDL requests. Handlers can be added - * to service sessions using - * {@link VaadinSession#addRequestHandler(RequestHandler)} + * Handler for producing a response to HTTP requests. Handlers can be either + * added on a {@link VaadinService service} level, common for all users, or on a + * {@link VaadinSession session} level for only a single user. */ public interface RequestHandler extends Serializable { /** - * Handles a non-UIDL request. If a response is written, this method should - * return <code>true</code> to indicate that no more request handlers should - * be invoked for the request. + * Called when a request needs to be handled. If a response is written, this + * method should return <code>true</code> to indicate that no more request + * handlers should be invoked for the request. + * <p> + * Note that request handlers by default do not lock the session. If you are + * using VaadinSession or anything inside the VaadinSession you must ensure + * the session is locked. This can be done by extending + * {@link SynchronizedRequestHandler} or by using + * {@link VaadinSession#access(Runnable)} or {@link UI#access(Runnable)}. + * </p> * * @param session * The session for the request @@ -40,6 +49,7 @@ public interface RequestHandler extends Serializable { * @return true if a response has been written and no further request * handlers should be called, otherwise false * @throws IOException + * If an IO error occurred */ boolean handleRequest(VaadinSession session, VaadinRequest request, VaadinResponse response) throws IOException; diff --git a/server/src/com/vaadin/server/RequestTimer.java b/server/src/com/vaadin/server/RequestTimer.java deleted file mode 100644 index 2f91348ce5..0000000000 --- a/server/src/com/vaadin/server/RequestTimer.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package com.vaadin.server; - -import java.io.Serializable; - -/** - * Times the handling of requests and stores the information as an attribute in - * the request. The timing info is later passed on to the client in the UIDL and - * the client provides JavaScript API for accessing this data from e.g. - * TestBench. - * - * @author Jonatan Kronqvist / Vaadin Ltd - */ -public class RequestTimer implements Serializable { - private long requestStartTime = 0; - - /** - * Starts the timing of a request. This should be called before any - * processing of the request. - */ - public void start() { - requestStartTime = System.nanoTime(); - } - - /** - * Stops the timing of a request. This should be called when all processing - * of a request has finished. - * - * @param context - */ - public void stop(VaadinSession context) { - // Measure and store the total handling time. This data can be - // used in TestBench 3 tests. - long time = (System.nanoTime() - requestStartTime) / 1000000; - - // The timings must be stored in the context, since a new - // RequestTimer is created for every request. - context.setLastRequestDuration(time); - } -} diff --git a/server/src/com/vaadin/server/ServerRpcManager.java b/server/src/com/vaadin/server/ServerRpcManager.java index ec25ce83ca..a1682cb453 100644 --- a/server/src/com/vaadin/server/ServerRpcManager.java +++ b/server/src/com/vaadin/server/ServerRpcManager.java @@ -139,7 +139,7 @@ public class ServerRpcManager<T extends ServerRpc> implements Serializable { * * @return RPC interface type */ - protected Class<T> getRpcInterface() { + public Class<T> getRpcInterface() { return rpcInterface; } diff --git a/server/src/com/vaadin/server/ServletPortletHelper.java b/server/src/com/vaadin/server/ServletPortletHelper.java index ce9872f40e..c14467a10e 100644 --- a/server/src/com/vaadin/server/ServletPortletHelper.java +++ b/server/src/com/vaadin/server/ServletPortletHelper.java @@ -23,23 +23,14 @@ import com.vaadin.shared.ApplicationConstants; import com.vaadin.ui.Component; import com.vaadin.ui.UI; -/* - * Copyright 2000-2013 Vaadin Ltd. +/** + * Contains helper methods shared by {@link VaadinServlet} and + * {@link VaadinPortlet}. * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. + * @deprecated As of 7.1. Will be removed or refactored in the future. */ - -class ServletPortletHelper implements Serializable { +@Deprecated +public class ServletPortletHelper implements Serializable { public static final String UPLOAD_URL_PREFIX = "APP/UPLOAD/"; /** * The default SystemMessages (read-only). @@ -132,6 +123,10 @@ class ServletPortletHelper implements Serializable { return hasPathPrefix(request, ApplicationConstants.HEARTBEAT_PATH + '/'); } + public static boolean isPushRequest(VaadinRequest request) { + return hasPathPrefix(request, ApplicationConstants.PUSH_PATH + '/'); + } + public static void initDefaultUIProvider(VaadinSession session, VaadinService vaadinService) throws ServiceException { String uiProperty = vaadinService.getDeploymentConfiguration() @@ -200,7 +195,7 @@ class ServletPortletHelper implements Serializable { * <li>{@link Locale#getDefault()}</li> * </ol> */ - static Locale findLocale(Component component, VaadinSession session, + public static Locale findLocale(Component component, VaadinSession session, VaadinRequest request) { if (component == null) { component = UI.getCurrent(); @@ -234,5 +229,4 @@ class ServletPortletHelper implements Serializable { return Locale.getDefault(); } - } diff --git a/server/src/com/vaadin/server/SessionExpiredHandler.java b/server/src/com/vaadin/server/SessionExpiredHandler.java new file mode 100644 index 0000000000..6a7896f3d1 --- /dev/null +++ b/server/src/com/vaadin/server/SessionExpiredHandler.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server; + +import java.io.IOException; + +/** + * A specialized RequestHandler which is capable of sending session expiration + * messages to the user. + * + * @since 7.1 + * @author Vaadin Ltd + */ +public interface SessionExpiredHandler extends RequestHandler { + + /** + * Called when the a session expiration has occured and a notification needs + * to be sent to the user. If a response is written, this method should + * return <code>true</code> to indicate that no more + * {@link SessionExpiredHandler} handlers should be invoked for the request. + * + * @param request + * The request to handle + * @param response + * The response object to which a response can be written. + * @return true if a response has been written and no further request + * handlers should be called, otherwise false + * @throws IOException + * If an IO error occurred + * @since 7.1 + */ + boolean handleSessionExpired(VaadinRequest request, VaadinResponse response) + throws IOException; + +} diff --git a/server/src/com/vaadin/server/SynchronizedRequestHandler.java b/server/src/com/vaadin/server/SynchronizedRequestHandler.java new file mode 100644 index 0000000000..ac730dcecb --- /dev/null +++ b/server/src/com/vaadin/server/SynchronizedRequestHandler.java @@ -0,0 +1,65 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server; + +import java.io.IOException; + +/** + * RequestHandler which takes care of locking and unlocking of the VaadinSession + * automatically. The session is locked before + * {@link #synchronizedHandleRequest(VaadinSession, VaadinRequest, VaadinResponse)} + * is called and unlocked after it has completed. + * + * @author Vaadin Ltd + * @version @VERSION@ + * @since 7.1 + */ +public abstract class SynchronizedRequestHandler implements RequestHandler { + + @Override + public boolean handleRequest(VaadinSession session, VaadinRequest request, + VaadinResponse response) throws IOException { + session.lock(); + try { + return synchronizedHandleRequest(session, request, response); + } finally { + session.unlock(); + } + } + + /** + * Identical to + * {@link #handleRequest(VaadinSession, VaadinRequest, VaadinResponse)} + * except the {@link VaadinSession} is locked before this is called and + * unlocked after this has completed. + * + * @see #handleRequest(VaadinSession, VaadinRequest, VaadinResponse) + * @param session + * The session for the request + * @param request + * The request to handle + * @param response + * The response object to which a response can be written. + * @return true if a response has been written and no further request + * handlers should be called, otherwise false + * + * @throws IOException + * If an IO error occurred + */ + public abstract boolean synchronizedHandleRequest(VaadinSession session, + VaadinRequest request, VaadinResponse response) throws IOException; + +} diff --git a/server/src/com/vaadin/server/UIProvider.java b/server/src/com/vaadin/server/UIProvider.java index a91db6b88d..0305b907e6 100644 --- a/server/src/com/vaadin/server/UIProvider.java +++ b/server/src/com/vaadin/server/UIProvider.java @@ -20,9 +20,11 @@ import java.io.Serializable; import java.lang.annotation.Annotation; import com.vaadin.annotations.PreserveOnRefresh; +import com.vaadin.annotations.Push; import com.vaadin.annotations.Theme; import com.vaadin.annotations.Title; import com.vaadin.annotations.Widgetset; +import com.vaadin.shared.communication.PushMode; import com.vaadin.ui.UI; public abstract class UIProvider implements Serializable { @@ -149,4 +151,27 @@ public abstract class UIProvider implements Serializable { return titleAnnotation.value(); } } + + /** + * Finds the {@link PushMode} to use for a specific UI. If no specific push + * mode is required, <code>null</code> is returned. + * <p> + * The default implementation uses the @{@link Push} annotation if it's + * defined for the UI class. + * + * @param event + * the UI create event with information about the UI and the + * current request. + * @return the push mode to use, or <code>null</code> if the default push + * mode should be used + * + */ + public PushMode getPushMode(UICreateEvent event) { + Push push = getAnnotationFor(event.getUIClass(), Push.class); + if (push == null) { + return null; + } else { + return push.value(); + } + } } diff --git a/server/src/com/vaadin/server/UnsupportedBrowserHandler.java b/server/src/com/vaadin/server/UnsupportedBrowserHandler.java index 55d5a5c78f..5fc00408a9 100644 --- a/server/src/com/vaadin/server/UnsupportedBrowserHandler.java +++ b/server/src/com/vaadin/server/UnsupportedBrowserHandler.java @@ -24,18 +24,18 @@ import java.io.Writer; * * <p> * This handler is usually added to the application by - * {@link AbstractCommunicationManager}. + * {@link LegacyCommunicationManager}. * </p> */ @SuppressWarnings("serial") -public class UnsupportedBrowserHandler implements RequestHandler { +public class UnsupportedBrowserHandler extends SynchronizedRequestHandler { /** Cookie used to ignore browser checks */ public static final String FORCE_LOAD_COOKIE = "vaadinforceload=1"; @Override - public boolean handleRequest(VaadinSession session, VaadinRequest request, - VaadinResponse response) throws IOException { + public boolean synchronizedHandleRequest(VaadinSession session, + VaadinRequest request, VaadinResponse response) throws IOException { // Check if the browser is supported // If Chrome Frame is available we'll assume it's ok diff --git a/server/src/com/vaadin/server/VaadinPortlet.java b/server/src/com/vaadin/server/VaadinPortlet.java index ac4e904898..327ce78a6c 100644 --- a/server/src/com/vaadin/server/VaadinPortlet.java +++ b/server/src/com/vaadin/server/VaadinPortlet.java @@ -15,16 +15,10 @@ */ package com.vaadin.server; -import java.io.BufferedWriter; import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.net.MalformedURLException; -import java.security.GeneralSecurityException; import java.util.Enumeration; import java.util.Map; import java.util.Properties; @@ -46,12 +40,11 @@ import javax.portlet.ResourceRequest; import javax.portlet.ResourceResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; -import javax.servlet.http.HttpServletResponse; import com.liferay.portal.kernel.util.PortalClassLoaderUtil; import com.liferay.portal.kernel.util.PropsUtil; -import com.vaadin.server.AbstractCommunicationManager.Callback; -import com.vaadin.ui.UI; +import com.vaadin.server.communication.PortletDummyRequestHandler; +import com.vaadin.server.communication.PortletUIInitHandler; import com.vaadin.util.CurrentInstance; /** @@ -257,24 +250,6 @@ public class VaadinPortlet extends GenericPortlet implements Constants, } - public static class AbstractApplicationPortletWrapper implements Callback { - - private final VaadinPortlet portlet; - - public AbstractApplicationPortletWrapper(VaadinPortlet portlet) { - this.portlet = portlet; - } - - @Override - public void criticalNotification(VaadinRequest request, - VaadinResponse response, String cap, String msg, - String details, String outOfSyncURL) throws IOException { - portlet.criticalNotification((VaadinPortletRequest) request, - (VaadinPortletResponse) response, cap, msg, details, - outOfSyncURL); - } - } - /** * This portlet parameter is used to add styles to the main element. E.g * "height:500px" generates a style="height:500px" to the main element. @@ -332,11 +307,16 @@ public class VaadinPortlet extends GenericPortlet implements Constants, } DeploymentConfiguration deploymentConfiguration = createDeploymentConfiguration(initParameters); - vaadinService = createPortletService(deploymentConfiguration); + try { + vaadinService = createPortletService(deploymentConfiguration); + } catch (ServiceException e) { + throw new PortletException("Could not initialized VaadinPortlet", e); + } // Sets current service even though there are no request and response vaadinService.setCurrentInstances(null, null); portletInitialized(); + CurrentInstance.clearAll(); } @@ -350,15 +330,21 @@ public class VaadinPortlet extends GenericPortlet implements Constants, } protected VaadinPortletService createPortletService( - DeploymentConfiguration deploymentConfiguration) { - return new VaadinPortletService(this, deploymentConfiguration); + DeploymentConfiguration deploymentConfiguration) + throws ServiceException { + VaadinPortletService service = new VaadinPortletService(this, + deploymentConfiguration); + service.init(); + return service; } /** * @author Vaadin Ltd * - * @deprecated As of 7.0. Will likely change or be removed in a future - * version + * @deprecated As of 7.0. This is no longer used and only provided for + * backwards compatibility. Each {@link RequestHandler} can + * individually decide whether it wants to handle a request or + * not. */ @Deprecated protected enum RequestType { @@ -369,8 +355,10 @@ public class VaadinPortlet extends GenericPortlet implements Constants, * @param vaadinRequest * @return * - * @deprecated As of 7.0. Will likely change or be removed in a future - * version + * @deprecated As of 7.0. This is no longer used and only provided for + * backwards compatibility. Each {@link RequestHandler} can + * individually decide whether it wants to handle a request or + * not. */ @Deprecated protected RequestType getRequestType(VaadinPortletRequest vaadinRequest) { @@ -381,7 +369,7 @@ public class VaadinPortlet extends GenericPortlet implements Constants, ResourceRequest resourceRequest = (ResourceRequest) request; if (ServletPortletHelper.isUIDLRequest(vaadinRequest)) { return RequestType.UIDL; - } else if (isBrowserDetailsRequest(resourceRequest)) { + } else if (PortletUIInitHandler.isUIInitRequest(vaadinRequest)) { return RequestType.BROWSER_DETAILS; } else if (ServletPortletHelper.isFileUploadRequest(vaadinRequest)) { return RequestType.FILE_UPLOAD; @@ -392,12 +380,9 @@ public class VaadinPortlet extends GenericPortlet implements Constants, return RequestType.APP; } else if (ServletPortletHelper.isHeartbeatRequest(vaadinRequest)) { return RequestType.HEARTBEAT; - } else if (isDummyRequest(resourceRequest)) { + } else if (PortletDummyRequestHandler.isDummyRequest(vaadinRequest)) { return RequestType.DUMMY; } else { - // these are not served with ResourceRequests, but by a servlet - // on the portal at portlet root path (configured by default by - // Liferay at deployment time, similar on other portals) return RequestType.STATIC_FILE; } } else if (request instanceof ActionRequest) { @@ -408,16 +393,6 @@ public class VaadinPortlet extends GenericPortlet implements Constants, return RequestType.UNKNOWN; } - private boolean isBrowserDetailsRequest(ResourceRequest request) { - return request.getResourceID() != null - && request.getResourceID().equals("v-browserDetails"); - } - - private boolean isDummyRequest(ResourceRequest request) { - return request.getResourceID() != null - && request.getResourceID().equals("DUMMY"); - } - /** * @param request * @param response @@ -430,145 +405,14 @@ public class VaadinPortlet extends GenericPortlet implements Constants, @Deprecated protected void handleRequest(PortletRequest request, PortletResponse response) throws PortletException, IOException { - RequestTimer requestTimer = new RequestTimer(); - requestTimer.start(); CurrentInstance.clearAll(); setCurrent(this); - try { - AbstractApplicationPortletWrapper portletWrapper = new AbstractApplicationPortletWrapper( - this); - - VaadinPortletRequest vaadinRequest = createVaadinRequest(request); - - VaadinPortletResponse vaadinResponse = new VaadinPortletResponse( - response, getService()); - - getService().setCurrentInstances(vaadinRequest, vaadinResponse); - - RequestType requestType = getRequestType(vaadinRequest); - - if (requestType == RequestType.UNKNOWN) { - handleUnknownRequest(request, response); - } else if (requestType == RequestType.DUMMY) { - /* - * This dummy page is used by action responses to redirect to, - * in order to prevent the boot strap code from being rendered - * into strange places such as iframes. - */ - ((ResourceResponse) response).setContentType("text/html"); - final OutputStream out = ((ResourceResponse) response) - .getPortletOutputStream(); - final PrintWriter outWriter = new PrintWriter( - new BufferedWriter(new OutputStreamWriter(out, "UTF-8"))); - outWriter.print("<html><body>dummy page</body></html>"); - outWriter.close(); - } else { - VaadinPortletSession vaadinSession = null; - - try { - // TODO What about PARAM_UNLOADBURST & - // redirectToApplication?? - - vaadinSession = (VaadinPortletSession) getService() - .findVaadinSession(vaadinRequest); - if (vaadinSession == null) { - return; - } - - PortletCommunicationManager communicationManager = (PortletCommunicationManager) vaadinSession - .getCommunicationManager(); - - if (requestType == RequestType.PUBLISHED_FILE) { - communicationManager.servePublishedFile(vaadinRequest, - vaadinResponse); - return; - } else if (requestType == RequestType.HEARTBEAT) { - communicationManager.handleHeartbeatRequest( - vaadinRequest, vaadinResponse, vaadinSession); - return; - } - - /* Update browser information from request */ - vaadinSession.getBrowser().updateRequestDetails( - vaadinRequest); - - /* Notify listeners */ - - // Finds the right UI - UI uI = null; - if (requestType == RequestType.UIDL) { - uI = getService().findUI(vaadinRequest); - } - - // TODO Should this happen before or after the transaction - // starts? - if (request instanceof RenderRequest) { - vaadinSession.firePortletRenderRequest(uI, - (RenderRequest) request, - (RenderResponse) response); - } else if (request instanceof ActionRequest) { - vaadinSession.firePortletActionRequest(uI, - (ActionRequest) request, - (ActionResponse) response); - } else if (request instanceof EventRequest) { - vaadinSession.firePortletEventRequest(uI, - (EventRequest) request, - (EventResponse) response); - } else if (request instanceof ResourceRequest) { - vaadinSession.firePortletResourceRequest(uI, - (ResourceRequest) request, - (ResourceResponse) response); - } - - /* Handle the request */ - if (requestType == RequestType.FILE_UPLOAD) { - // UI is resolved in handleFileUpload by - // PortletCommunicationManager - communicationManager.handleFileUpload(vaadinSession, - vaadinRequest, vaadinResponse); - return; - } else if (requestType == RequestType.BROWSER_DETAILS) { - communicationManager.handleBrowserDetailsRequest( - vaadinRequest, vaadinResponse, vaadinSession); - return; - } else if (requestType == RequestType.UIDL) { - // Handles AJAX UIDL requests - communicationManager.handleUidlRequest(vaadinRequest, - vaadinResponse, portletWrapper, uI); - - // Ensure that the browser does not cache UIDL - // responses. - // iOS 6 Safari requires this (#9732) - response.setProperty("Cache-Control", "no-cache"); - return; - } else { - handleOtherRequest(vaadinRequest, vaadinResponse, - requestType, vaadinSession, - communicationManager); - } - } catch (final SessionExpiredException e) { - // TODO Figure out a better way to deal with - // SessionExpiredExceptions - getLogger().finest("A user session has expired"); - } catch (final GeneralSecurityException e) { - // TODO Figure out a better way to deal with - // GeneralSecurityExceptions - getLogger() - .fine("General security exception, the security key was probably incorrect."); - } catch (final Throwable e) { - handleServiceException(vaadinRequest, vaadinResponse, - vaadinSession, e); - } finally { - if (vaadinSession != null) { - getService().cleanupSession(vaadinSession); - requestTimer.stop(vaadinSession); - } - } - } - } finally { - CurrentInstance.clearAll(); + getService().handleRequest(createVaadinRequest(request), + createVaadinResponse(response)); + } catch (ServiceException e) { + throw new PortletException(e); } } @@ -592,50 +436,12 @@ public class VaadinPortlet extends GenericPortlet implements Constants, } - protected VaadinPortletService getService() { - return vaadinService; - } - - private void handleUnknownRequest(PortletRequest request, - PortletResponse response) { - getLogger().warning("Unknown request type"); + private VaadinPortletResponse createVaadinResponse(PortletResponse response) { + return new VaadinPortletResponse(response, getService()); } - /** - * Handle a portlet request that is not for static files, UIDL or upload. - * Also render requests are handled here. - * - * This method is called after starting the application and calling portlet - * and transaction listeners. - * - * @param request - * @param response - * @param requestType - * @param vaadinSession - * @param vaadinSession - * @param communicationManager - * @throws PortletException - * @throws IOException - * @throws MalformedURLException - */ - private void handleOtherRequest(VaadinPortletRequest request, - VaadinResponse response, RequestType requestType, - VaadinSession vaadinSession, - PortletCommunicationManager communicationManager) - throws PortletException, IOException, MalformedURLException { - if (requestType == RequestType.APP || requestType == RequestType.RENDER) { - if (!communicationManager.handleOtherRequest(request, response)) { - response.sendError(HttpServletResponse.SC_NOT_FOUND, - "Not found"); - } - } else if (requestType == RequestType.EVENT) { - // nothing to do, listeners do all the work - } else if (requestType == RequestType.ACTION) { - // nothing to do, listeners do all the work - } else { - throw new IllegalStateException( - "handleRequest() without anything to do - should never happen!"); - } + protected VaadinPortletService getService() { + return vaadinService; } @Override @@ -678,98 +484,6 @@ public class VaadinPortlet extends GenericPortlet implements Constants, handleRequest(request, response); } - private void handleServiceException(VaadinPortletRequest request, - VaadinPortletResponse response, VaadinSession vaadinSession, - Throwable e) throws IOException, PortletException { - // TODO Check that this error handler is working when running inside a - // portlet - - // if this was an UIDL request, response UIDL back to client - ErrorHandler errorHandler = ErrorEvent.findErrorHandler(vaadinSession); - if (getRequestType(request) == RequestType.UIDL) { - SystemMessages ci = getService().getSystemMessages( - ServletPortletHelper.findLocale(null, vaadinSession, - request), request); - criticalNotification(request, response, - ci.getInternalErrorCaption(), ci.getInternalErrorMessage(), - null, ci.getInternalErrorURL()); - if (errorHandler != null) { - errorHandler.error(new ErrorEvent(e)); - } - } else { - if (errorHandler != null) { - errorHandler.error(new ErrorEvent(e)); - } else { - // Re-throw other exceptions - throw new PortletException(e); - } - } - } - - /** - * Send notification to client's application. Used to notify client of - * critical errors and session expiration due to long inactivity. Server has - * no knowledge of what application client refers to. - * - * @param request - * the Portlet request instance. - * @param response - * the Portlet response to write to. - * @param caption - * for the notification - * @param message - * for the notification - * @param details - * a detail message to show in addition to the passed message. - * Currently shown directly but could be hidden behind a details - * drop down. - * @param url - * url to load after message, null for current page - * @throws IOException - * if the writing failed due to input/output error. - * - * @deprecated As of 7.0. Will likely change or be removed in a future - * version - */ - @Deprecated - void criticalNotification(VaadinPortletRequest request, - VaadinPortletResponse response, String caption, String message, - String details, String url) throws IOException { - - // clients JS app is still running, but server application either - // no longer exists or it might fail to perform reasonably. - // send a notification to client's application and link how - // to "restart" application. - - if (caption != null) { - caption = "\"" + caption + "\""; - } - if (details != null) { - if (message == null) { - message = details; - } else { - message += "<br/><br/>" + details; - } - } - if (message != null) { - message = "\"" + message + "\""; - } - if (url != null) { - url = "\"" + url + "\""; - } - - // Set the response type - response.setContentType("application/json; charset=UTF-8"); - final OutputStream out = response.getOutputStream(); - final PrintWriter outWriter = new PrintWriter(new BufferedWriter( - new OutputStreamWriter(out, "UTF-8"))); - outWriter.print("for(;;);[{\"changes\":[], \"meta\" : {" - + "\"appError\": {" + "\"caption\":" + caption + "," - + "\"message\" : " + message + "," + "\"url\" : " + url - + "}}, \"resources\": {}, \"locales\":[]}]"); - outWriter.close(); - } - private static final Logger getLogger() { return Logger.getLogger(VaadinPortlet.class.getName()); } diff --git a/server/src/com/vaadin/server/VaadinPortletService.java b/server/src/com/vaadin/server/VaadinPortletService.java index e59ea7fd5e..2eca07dd4a 100644 --- a/server/src/com/vaadin/server/VaadinPortletService.java +++ b/server/src/com/vaadin/server/VaadinPortletService.java @@ -17,21 +17,30 @@ package com.vaadin.server; import java.io.File; +import java.io.InputStream; import java.net.URL; +import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; +import javax.portlet.EventRequest; import javax.portlet.PortletContext; import javax.portlet.PortletRequest; +import javax.portlet.RenderRequest; import com.vaadin.server.VaadinPortlet.RequestType; +import com.vaadin.server.communication.PortletBootstrapHandler; +import com.vaadin.server.communication.PortletDummyRequestHandler; +import com.vaadin.server.communication.PortletListenerNotifier; +import com.vaadin.server.communication.PortletUIInitHandler; import com.vaadin.ui.UI; public class VaadinPortletService extends VaadinService { private final VaadinPortlet portlet; public VaadinPortletService(VaadinPortlet portlet, - DeploymentConfiguration deploymentConfiguration) { + DeploymentConfiguration deploymentConfiguration) + throws ServiceException { super(deploymentConfiguration); this.portlet = portlet; @@ -46,7 +55,25 @@ public class VaadinPortletService extends VaadinService { } } - protected VaadinPortlet getPortlet() { + @Override + protected List<RequestHandler> createRequestHandlers() + throws ServiceException { + List<RequestHandler> handlers = super.createRequestHandlers(); + + handlers.add(new PortletUIInitHandler()); + handlers.add(new PortletListenerNotifier()); + handlers.add(0, new PortletDummyRequestHandler()); + handlers.add(0, new PortletBootstrapHandler()); + + return handlers; + } + + /** + * Retrieves a reference to the portlet associated with this service. + * + * @return A reference to the VaadinPortlet this service is using + */ + public VaadinPortlet getPortlet() { return portlet; } @@ -155,13 +182,19 @@ public class VaadinPortletService extends VaadinService { @Override protected boolean requestCanCreateSession(VaadinRequest request) { - RequestType requestType = getRequestType(request); - if (requestType == RequestType.RENDER) { + if (!(request instanceof VaadinPortletRequest)) { + throw new IllegalArgumentException( + "Request is not a VaadinPortletRequest"); + } + + PortletRequest portletRequest = ((VaadinPortletRequest) request) + .getPortletRequest(); + if (portletRequest instanceof RenderRequest) { // In most cases the first request is a render request that // renders the HTML fragment. This should create a Vaadin // session unless there is already one. return true; - } else if (requestType == RequestType.EVENT) { + } else if (portletRequest instanceof EventRequest) { // A portlet can also be sent an event even though it has not // been rendered, e.g. portlet on one page sends an event to a // portlet on another page and then moves the user to that page. @@ -191,12 +224,6 @@ public class VaadinPortletService extends VaadinService { return type; } - @Override - protected AbstractCommunicationManager createCommunicationManager( - VaadinSession session) { - return new PortletCommunicationManager(session); - } - public static PortletRequest getCurrentPortletRequest() { VaadinRequest currentRequest = VaadinService.getCurrentRequest(); if (currentRequest instanceof VaadinPortletRequest) { @@ -230,6 +257,17 @@ public class VaadinPortletService extends VaadinService { } @Override + public InputStream getThemeResourceAsStream(UI uI, String themeName, + String resource) { + VaadinPortletSession session = (VaadinPortletSession) uI.getSession(); + PortletContext portletContext = session.getPortletSession() + .getPortletContext(); + return portletContext.getResourceAsStream("/" + + VaadinPortlet.THEME_DIR_PATH + '/' + themeName + "/" + + resource); + } + + @Override public String getMainDivId(VaadinSession session, VaadinRequest request, Class<? extends UI> uiClass) { PortletRequest portletRequest = ((VaadinPortletRequest) request) @@ -240,4 +278,20 @@ public class VaadinPortletService extends VaadinService { */ return "v-" + portletRequest.getWindowID(); } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.server.VaadinService#handleSessionExpired(com.vaadin.server + * .VaadinRequest, com.vaadin.server.VaadinResponse) + */ + @Override + protected void handleSessionExpired(VaadinRequest request, + VaadinResponse response) { + // TODO Figure out a better way to deal with + // SessionExpiredExceptions + getLogger().finest("A user session has expired"); + } + } diff --git a/server/src/com/vaadin/server/VaadinService.java b/server/src/com/vaadin/server/VaadinService.java index ada0fac107..af0c280c19 100644 --- a/server/src/com/vaadin/server/VaadinService.java +++ b/server/src/com/vaadin/server/VaadinService.java @@ -16,24 +16,43 @@ package com.vaadin.server; +import java.io.BufferedWriter; import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; import java.io.Serializable; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; +import java.util.Collections; import java.util.HashMap; +import java.util.List; import java.util.Locale; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import java.util.logging.Logger; import javax.portlet.PortletContext; import javax.servlet.ServletContext; -import javax.servlet.ServletException; +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONException; +import org.json.JSONObject; import com.vaadin.annotations.PreserveOnRefresh; import com.vaadin.event.EventRouter; +import com.vaadin.server.communication.FileUploadHandler; +import com.vaadin.server.communication.HeartbeatHandler; +import com.vaadin.server.communication.PublishedFileHandler; +import com.vaadin.server.communication.SessionRequestHandler; +import com.vaadin.server.communication.UidlRequestHandler; +import com.vaadin.shared.JsonConstants; import com.vaadin.shared.ui.ui.UIConstants; import com.vaadin.ui.UI; import com.vaadin.util.CurrentInstance; @@ -70,6 +89,8 @@ public abstract class VaadinService implements Serializable { @Deprecated public static final String URL_PARAMETER_CLOSE_APPLICATION = "closeApplication"; + private static final String REQUEST_START_TIME_ATTRIBUTE = "requestStartTime"; + private final DeploymentConfiguration deploymentConfiguration; private final EventRouter eventRouter = new EventRouter(); @@ -79,6 +100,15 @@ public abstract class VaadinService implements Serializable { private ClassLoader classLoader; + private Iterable<RequestHandler> requestHandlers; + + /** + * Keeps track of whether a warning about missing push support has already + * been logged. This is used to avoid spamming the log with the same message + * every time a new UI is bootstrapped. + */ + private boolean pushWarningEmitted = false; + /** * Creates a new vaadin service based on a deployment configuration * @@ -107,6 +137,45 @@ public abstract class VaadinService implements Serializable { } /** + * Initializes this service. The service should be initialized before it is + * used. + * + * @since 7.1 + * @throws ServiceException + * if a problem occurs when creating the service + */ + public void init() throws ServiceException { + List<RequestHandler> handlers = createRequestHandlers(); + Collections.reverse(handlers); + requestHandlers = Collections.unmodifiableCollection(handlers); + } + + /** + * Called during initialization to add the request handlers for the service. + * Note that the returned list will be reversed so the last handler will be + * called first. This enables overriding this method and using add on the + * returned list to add a custom request handler which overrides any + * predefined handler. + * + * @return The list of request handlers used by this service. + * @throws ServiceException + * if a problem occurs when creating the request handlers + */ + protected List<RequestHandler> createRequestHandlers() + throws ServiceException { + ArrayList<RequestHandler> handlers = new ArrayList<RequestHandler>(); + handlers.add(new SessionRequestHandler()); + handlers.add(new PublishedFileHandler()); + handlers.add(new HeartbeatHandler()); + handlers.add(new FileUploadHandler()); + handlers.add(new UidlRequestHandler()); + handlers.add(new UnsupportedBrowserHandler()); + handlers.add(new ConnectorResourceHandler()); + + return handlers; + } + + /** * Return the URL from where static files, e.g. the widgetset and the theme, * are served. In a standard configuration the VAADIN folder inside the * returned folder is what is used for widgetsets and themes. @@ -329,18 +398,40 @@ public abstract class VaadinService implements Serializable { SESSION_DESTROY_METHOD); } + /** + * Handles destruction of the given session. Internally ensures proper + * locking is done. + * + * @param vaadinSession + * The session to destroy + */ public void fireSessionDestroy(VaadinSession vaadinSession) { - for (UI ui : new ArrayList<UI>(vaadinSession.getUIs())) { - // close() called here for consistency so that it is always called - // before a UI is removed. UI.isClosing() is thus always true in - // UI.detach() and associated detach listeners. - if (!ui.isClosing()) { - ui.close(); + final VaadinSession session = vaadinSession; + session.access(new Runnable() { + @Override + public void run() { + ArrayList<UI> uis = new ArrayList<UI>(session.getUIs()); + for (final UI ui : uis) { + ui.access(new Runnable() { + @Override + public void run() { + /* + * close() called here for consistency so that it is + * always called before a UI is removed. + * UI.isClosing() is thus always true in UI.detach() + * and associated detach listeners. + */ + if (!ui.isClosing()) { + ui.close(); + } + session.removeUI(ui); + } + }); + } + eventRouter.fireEvent(new SessionDestroyEvent( + VaadinService.this, session)); } - vaadinSession.removeUI(ui); - } - - eventRouter.fireEvent(new SessionDestroyEvent(this, vaadinSession)); + }); } /** @@ -358,6 +449,10 @@ public abstract class VaadinService implements Serializable { /** * Attempts to find a Vaadin service session associated with this request. + * <p> + * Handles locking of the session internally to avoid creation of duplicate + * sessions by two threads simultaneously. + * </p> * * @param request * the request to get a vaadin service session for. @@ -381,9 +476,135 @@ public abstract class VaadinService implements Serializable { return vaadinSession; } + /** + * Associates the given lock with this service and the given wrapped + * session. This method should not be called more than once when the lock is + * initialized for the session. + * + * @see #getSessionLock(WrappedSession) + * @param wrappedSession + * The wrapped session the lock is associated with + * @param lock + * The lock object + */ + private void setSessionLock(WrappedSession wrappedSession, Lock lock) { + assert wrappedSession != null : "Can't set a lock for a null session"; + assert wrappedSession.getAttribute(getLockAttributeName()) == null : "Changing the lock for a session is not allowed"; + + wrappedSession.setAttribute(getLockAttributeName(), lock); + } + + /** + * Returns the name used to store the lock in the HTTP session. + * + * @return The attribute name for the lock + */ + private String getLockAttributeName() { + return getServiceName() + ".lock"; + } + + /** + * Gets the lock instance used to lock the VaadinSession associated with the + * given wrapped session. + * <p> + * This method uses the wrapped session instead of VaadinSession to be able + * to lock even before the VaadinSession has been initialized. + * </p> + * + * @param wrappedSession + * The wrapped session + * @return A lock instance used for locking access to the wrapped session + */ + protected Lock getSessionLock(WrappedSession wrappedSession) { + Object lock = wrappedSession.getAttribute(getLockAttributeName()); + + if (lock instanceof ReentrantLock) { + return (ReentrantLock) lock; + } + + if (lock == null) { + return null; + } + + throw new RuntimeException( + "Something else than a ReentrantLock was stored in the " + + getLockAttributeName() + " in the session"); + } + + /** + * Locks the given session for this service instance. Typically you want to + * call {@link VaadinSession#lock()} instead of this method. + * + * @param wrappedSession + * The session to lock + */ + protected void lockSession(WrappedSession wrappedSession) { + Lock lock = getSessionLock(wrappedSession); + if (lock == null) { + /* + * No lock found in the session attribute. Ensure only one lock is + * created and used by everybody by doing double checked locking. + * Assumes there is a memory barrier for the attribute (i.e. that + * the CPU flushes its caches and reads the value directly from main + * memory). + */ + synchronized (VaadinService.class) { + lock = getSessionLock(wrappedSession); + if (lock == null) { + lock = new ReentrantLock(); + setSessionLock(wrappedSession, lock); + } + } + } + lock.lock(); + } + + /** + * Releases the lock for the given session for this service instance. + * Typically you want to call {@link VaadinSession#unlock()} instead of this + * method. + * + * @param wrappedSession + * The session to unlock + */ + protected void unlockSession(WrappedSession wrappedSession) { + assert getSessionLock(wrappedSession) != null; + assert ((ReentrantLock) getSessionLock(wrappedSession)) + .isHeldByCurrentThread() : "Trying to unlock the session but it has not been locked by this thread"; + getSessionLock(wrappedSession).unlock(); + } + private VaadinSession findOrCreateVaadinSession(VaadinRequest request) throws SessionExpiredException, ServiceException { boolean requestCanCreateSession = requestCanCreateSession(request); + WrappedSession wrappedSession = getWrappedSession(request, + requestCanCreateSession); + + lockSession(wrappedSession); + try { + return doFindOrCreateVaadinSession(request, requestCanCreateSession); + } finally { + unlockSession(wrappedSession); + } + + } + + /** + * Finds or creates a Vaadin session. Assumes necessary synchronization has + * been done by the caller to ensure this is not called simultaneously by + * several threads. + * + * @param request + * @param requestCanCreateSession + * @return + * @throws SessionExpiredException + * @throws ServiceException + */ + private VaadinSession doFindOrCreateVaadinSession(VaadinRequest request, + boolean requestCanCreateSession) throws SessionExpiredException, + ServiceException { + assert ((ReentrantLock) getSessionLock(request.getWrappedSession())) + .isHeldByCurrentThread() : "Session has not been locked by this thread"; /* Find an existing session for this request. */ VaadinSession session = getExistingSession(request, @@ -395,10 +616,12 @@ public abstract class VaadinService implements Serializable { * not specifically requested to close or restart it. */ - final boolean restartApplication = (request - .getParameter(URL_PARAMETER_RESTART_APPLICATION) != null); - final boolean closeApplication = (request - .getParameter(URL_PARAMETER_CLOSE_APPLICATION) != null); + final boolean restartApplication = hasParameter(request, + URL_PARAMETER_RESTART_APPLICATION) + && !hasParameter(request, + BootstrapHandler.IGNORE_RESTART_PARAM); + final boolean closeApplication = hasParameter(request, + URL_PARAMETER_CLOSE_APPLICATION); if (restartApplication) { closeSession(session, request.getWrappedSession(false)); @@ -429,8 +652,26 @@ public abstract class VaadinService implements Serializable { } + private static boolean hasParameter(VaadinRequest request, + String parameterName) { + return request.getParameter(parameterName) != null; + } + + /** + * Creates and registers a new VaadinSession for this service. Assumes + * proper locking has been taken care of by the caller. + * + * + * @param request + * The request which triggered session creation. + * @return A new VaadinSession instance + * @throws ServiceException + */ private VaadinSession createAndRegisterSession(VaadinRequest request) throws ServiceException { + assert ((ReentrantLock) getSessionLock(request.getWrappedSession())) + .isHeldByCurrentThread() : "Session has not been locked by this thread"; + VaadinSession session = createVaadinSession(request); VaadinSession.setCurrent(session); @@ -441,7 +682,7 @@ public abstract class VaadinService implements Serializable { Locale locale = request.getLocale(); session.setLocale(locale); session.setConfiguration(getDeploymentConfiguration()); - session.setCommunicationManager(createCommunicationManager(session)); + session.setCommunicationManager(new LegacyCommunicationManager(session)); ServletPortletHelper.initDefaultUIProvider(session, this); onVaadinSessionStarted(request, session); @@ -468,23 +709,13 @@ public abstract class VaadinService implements Serializable { } /** - * Create a communication manager to use for the given service session. - * - * @param session - * the service session for which a new communication manager is - * needed - * @return a new communication manager - */ - protected abstract AbstractCommunicationManager createCommunicationManager( - VaadinSession session); - - /** - * Creates a new Vaadin service session. + * Creates a new Vaadin session for this service and request * * @param request - * @return - * @throws ServletException - * @throws MalformedURLException + * The request for which to create a VaadinSession + * @return A new VaadinSession + * @throws ServiceException + * */ protected VaadinSession createVaadinSession(VaadinRequest request) throws ServiceException { @@ -512,12 +743,8 @@ public abstract class VaadinService implements Serializable { protected VaadinSession getExistingSession(VaadinRequest request, boolean allowSessionCreation) throws SessionExpiredException { - // Ensures that the session is still valid - final WrappedSession session = request - .getWrappedSession(allowSessionCreation); - if (session == null) { - throw new SessionExpiredException(); - } + final WrappedSession session = getWrappedSession(request, + allowSessionCreation); VaadinSession vaadinSession = VaadinSession .getForSession(this, session); @@ -530,6 +757,28 @@ public abstract class VaadinService implements Serializable { } /** + * Retrieves the wrapped session for the request. + * + * @param request + * The request for which to retrieve a session + * @param requestCanCreateSession + * true to create a new session if one currently does not exist + * @return The retrieved (or created) wrapped session + * @throws SessionExpiredException + * If the request is not associated to a session and new session + * creation is not allowed + */ + private WrappedSession getWrappedSession(VaadinRequest request, + boolean requestCanCreateSession) throws SessionExpiredException { + final WrappedSession session = request + .getWrappedSession(requestCanCreateSession); + if (session == null) { + throw new SessionExpiredException(); + } + return session; + } + + /** * Checks whether it's valid to create a new service session as a result of * the given request. * @@ -582,12 +831,21 @@ public abstract class VaadinService implements Serializable { */ public void setCurrentInstances(VaadinRequest request, VaadinResponse response) { - CurrentInstance.setInheritable(VaadinService.class, this); + setCurrent(this); CurrentInstance.set(VaadinRequest.class, request); CurrentInstance.set(VaadinResponse.class, response); } /** + * Sets the given Vaadin service as the current service. + * + * @param service + */ + public static void setCurrent(VaadinService service) { + CurrentInstance.setInheritable(VaadinService.class, service); + } + + /** * Gets the currently processed Vaadin request. The current request is * automatically defined when the request is started. The current request * can not be used in e.g. background threads because of the way server @@ -640,6 +898,7 @@ public abstract class VaadinService implements Serializable { * */ public UI findUI(VaadinRequest request) { + // getForSession asserts that the lock is held VaadinSession session = VaadinSession.getForSession(this, request.getWrappedSession()); @@ -647,16 +906,10 @@ public abstract class VaadinService implements Serializable { String uiIdString = request.getParameter(UIConstants.UI_ID_PARAMETER); int uiId = Integer.parseInt(uiIdString); - // Get lock before accessing data in session - session.lock(); - try { - UI ui = session.getUIById(uiId); + UI ui = session.getUIById(uiId); - UI.setCurrent(ui); - return ui; - } finally { - session.unlock(); - } + UI.setCurrent(ui); + return ui; } /** @@ -725,8 +978,12 @@ public abstract class VaadinService implements Serializable { // Ensure VaadinServiceSession knows where it's stored if (value instanceof VaadinSession) { VaadinSession serviceSession = (VaadinSession) value; - serviceSession.storeInSession(serviceSession.getService(), - newSession); + VaadinService service = serviceSession.getService(); + // Use the same lock instance in the new session + service.setSessionLock(newSession, + serviceSession.getLockInstance()); + + serviceSession.storeInSession(service, newSession); serviceSession .setAttribute(REINITIALIZING_SESSION_MARKER, null); } @@ -735,6 +992,19 @@ public abstract class VaadinService implements Serializable { } /** + * TODO PUSH Document + * + * TODO Pass UI or VaadinSession? + * + * @param uI + * @param themeName + * @param resource + * @return + */ + public abstract InputStream getThemeResourceAsStream(UI uI, + String themeName, String resource); + + /** * Creates and returns a unique ID for the DIV where the UI is to be * rendered. * @@ -814,13 +1084,19 @@ public abstract class VaadinService implements Serializable { * * @param session */ - private void removeClosedUIs(VaadinSession session) { - for (UI ui : new ArrayList<UI>(session.getUIs())) { - if (ui.isClosing()) { - getLogger().log(Level.FINER, "Removing closed UI {0}", - ui.getUIId()); - session.removeUI(ui); - } + private void removeClosedUIs(final VaadinSession session) { + ArrayList<UI> uis = new ArrayList<UI>(session.getUIs()); + for (final UI ui : uis) { + ui.access(new Runnable() { + @Override + public void run() { + if (ui.isClosing()) { + getLogger().log(Level.FINER, "Removing closed UI {0}", + ui.getUIId()); + session.removeUI(ui); + } + } + }); } } @@ -938,4 +1214,387 @@ public abstract class VaadinService implements Serializable { private static final Logger getLogger() { return Logger.getLogger(VaadinService.class.getName()); } + + /** + * Called before the framework starts handling a request + * + * @param request + * The request + * @param response + * The response + */ + public void requestStart(VaadinRequest request, VaadinResponse response) { + setCurrentInstances(request, response); + request.setAttribute(REQUEST_START_TIME_ATTRIBUTE, System.nanoTime()); + } + + /** + * Called after the framework has handled a request and the response has + * been written. + * + * @param request + * The request object + * @param response + * The response object + * @param session + * The session which was used during the request or null if the + * request did not use a session + */ + public void requestEnd(VaadinRequest request, VaadinResponse response, + VaadinSession session) { + if (session != null) { + final VaadinSession finalSession = session; + + session.access(new Runnable() { + @Override + public void run() { + cleanupSession(finalSession); + } + }); + + final long duration = (System.nanoTime() - (Long) request + .getAttribute(REQUEST_START_TIME_ATTRIBUTE)) / 1000000; + session.access(new Runnable() { + @Override + public void run() { + finalSession.setLastRequestDuration(duration); + } + }); + } + CurrentInstance.clearAll(); + } + + /** + * Returns the request handlers that are registered with this service. The + * iteration order of the returned collection is the same as the order in + * which the request handlers will be invoked when a request is handled. + * + * @return a collection of request handlers in the order they are invoked + * + * @see #createRequestHandlers() + * + * @since 7.1 + */ + public Iterable<RequestHandler> getRequestHandlers() { + return requestHandlers; + } + + /** + * Handles the incoming request and writes the response into the response + * object. Uses {@link #getRequestHandlers()} for handling the request. + * <p> + * If a session expiration is detected during request handling then each + * {@link RequestHandler request handler} has an opportunity to handle the + * expiration event if it implements {@link SessionExpiredHandler}. If no + * request handler handles session expiration a default expiration message + * will be written. + * </p> + * + * @param request + * The incoming request + * @param response + * The outgoing response + * @throws ServiceException + * Any exception that occurs during response handling will be + * wrapped in a ServiceException + */ + public void handleRequest(VaadinRequest request, VaadinResponse response) + throws ServiceException { + requestStart(request, response); + + VaadinSession vaadinSession = null; + try { + // Find out the service session this request is related to + vaadinSession = findVaadinSession(request); + if (vaadinSession == null) { + return; + } + + for (RequestHandler handler : getRequestHandlers()) { + if (handler.handleRequest(vaadinSession, request, response)) { + return; + } + } + + // Request not handled by any RequestHandler + response.sendError(HttpServletResponse.SC_NOT_FOUND, + "Request was not handled by any registered handler."); + + } catch (final SessionExpiredException e) { + handleSessionExpired(request, response); + } catch (final Throwable e) { + handleExceptionDuringRequest(request, response, vaadinSession, e); + } finally { + requestEnd(request, response, vaadinSession); + } + } + + private void handleExceptionDuringRequest(VaadinRequest request, + VaadinResponse response, VaadinSession vaadinSession, Throwable t) + throws ServiceException { + if (vaadinSession != null) { + vaadinSession.lock(); + } + try { + ErrorHandler errorHandler = ErrorEvent + .findErrorHandler(vaadinSession); + + // if this was an UIDL request, send UIDL back to the client + if (ServletPortletHelper.isUIDLRequest(request)) { + SystemMessages ci = getSystemMessages( + ServletPortletHelper.findLocale(null, vaadinSession, + request), request); + try { + writeStringResponse( + response, + JsonConstants.JSON_CONTENT_TYPE, + createCriticalNotificationJSON( + ci.getInternalErrorCaption(), + ci.getInternalErrorMessage(), null, + ci.getInternalErrorURL())); + } catch (IOException e) { + // An exception occured while writing the response. Log + // it and continue handling only the original error. + getLogger() + .log(Level.WARNING, + "Failed to write critical notification response to the client", + e); + } + if (errorHandler != null) { + errorHandler.error(new ErrorEvent(t)); + } + } else { + if (errorHandler != null) { + errorHandler.error(new ErrorEvent(t)); + } + + // Re-throw other exceptions + throw new ServiceException(t); + } + } finally { + if (vaadinSession != null) { + vaadinSession.unlock(); + } + } + + } + + /** + * Writes the given string as a response using the given content type. + * + * @param response + * The response reference + * @param contentType + * The content type of the response + * @param reponseString + * The actual response + * @throws IOException + * If an error occured while writing the response + */ + public void writeStringResponse(VaadinResponse response, + String contentType, String reponseString) throws IOException { + + response.setContentType(contentType); + + final OutputStream out = response.getOutputStream(); + final PrintWriter outWriter = new PrintWriter(new BufferedWriter( + new OutputStreamWriter(out, "UTF-8"))); + outWriter.print(reponseString); + outWriter.close(); + } + + /** + * Called when the session has expired and the request handling is therefore + * aborted. + * + * @param request + * The request + * @param response + * The response + * @throws ServiceException + * Thrown if there was any problem handling the expiration of + * the session + */ + protected void handleSessionExpired(VaadinRequest request, + VaadinResponse response) throws ServiceException { + for (RequestHandler handler : getRequestHandlers()) { + if (handler instanceof SessionExpiredHandler) { + try { + if (((SessionExpiredHandler) handler).handleSessionExpired( + request, response)) { + return; + } + } catch (IOException e) { + throw new ServiceException( + "Handling of session expired failed", e); + } + } + } + + // No request handlers handled the request. Write a normal HTTP response + + try { + // If there is a URL, try to redirect there + SystemMessages systemMessages = getSystemMessages( + ServletPortletHelper.findLocale(null, null, request), + request); + String sessionExpiredURL = systemMessages.getSessionExpiredURL(); + if (sessionExpiredURL != null + && (response instanceof VaadinServletResponse)) { + ((VaadinServletResponse) response) + .sendRedirect(sessionExpiredURL); + } else { + /* + * Session expired as a result of a standard http request and we + * have nowhere to redirect. Reloading would likely cause an + * endless loop. This can at least happen if refreshing a + * resource when the session has expired. + */ + response.sendError(HttpServletResponse.SC_GONE, + "Session expired"); + } + } catch (IOException e) { + throw new ServiceException(e); + } + } + + /** + * Creates a JSON message which, when sent to client as-is, will cause a + * critical error to be shown with the given details. + * + * @param caption + * The caption of the error or null to omit + * @param message + * The error message or null to omit + * @param details + * Additional error details or null to omit + * @param url + * A url to redirect to. If no other details are given then the + * user will be immediately redirected to this URL. Otherwise the + * message will be shown and the browser will redirect to the + * given URL only after the user acknowledges the message. If + * null then the browser will refresh the current page. + * @return A JSON string to be sent to the client + */ + public static String createCriticalNotificationJSON(String caption, + String message, String details, String url) { + String returnString = ""; + try { + if (message == null) { + message = details; + } else if (details != null) { + message += "<br/><br/>" + details; + } + + JSONObject appError = new JSONObject(); + appError.put("caption", caption); + appError.put("message", message); + appError.put("url", url); + + JSONObject meta = new JSONObject(); + meta.put("appError", appError); + + JSONObject json = new JSONObject(); + json.put("changes", Collections.EMPTY_LIST); + json.put("resources", Collections.EMPTY_MAP); + json.put("locales", Collections.EMPTY_LIST); + json.put("meta", meta); + returnString = json.toString(); + } catch (JSONException e) { + getLogger().log(Level.WARNING, + "Error creating critical notification JSON message", e); + } + + return "for(;;);[" + returnString + "]"; + } + + /** + * @deprecated As of 7.0. Will likely change or be removed in a future + * version + */ + @Deprecated + public void criticalNotification(VaadinRequest request, + VaadinResponse response, String caption, String message, + String details, String url) throws IOException { + writeStringResponse(response, JsonConstants.JSON_CONTENT_TYPE, + createCriticalNotificationJSON(caption, message, details, url)); + } + + /** + * Enables push if push support is available and push has not yet been + * enabled. + * + * If push support is not available, a warning explaining the situation will + * be logged at least the first time this method is invoked. + * + * @return <code>true</code> if push can be used; <code>false</code> if push + * is not available. + */ + public boolean ensurePushAvailable() { + if (!pushWarningEmitted) { + pushWarningEmitted = true; + getLogger().log(Level.WARNING, Constants.PUSH_NOT_SUPPORTED_ERROR, + getClass().getSimpleName()); + } + // Not supported by default for now, sublcasses may override + return false; + } + + /** + * Checks that another {@link VaadinSession} instance is not locked. This is + * internally used by {@link VaadinSession#access(Runnable)} and + * {@link UI#access(Runnable)} to help avoid causing deadlocks. + * + * @since 7.1 + * @param session + * the session that is being locked + * @throws IllegalStateException + * if the current thread holds the lock for another session + */ + public static void verifyNoOtherSessionLocked(VaadinSession session) { + VaadinSession otherSession = VaadinSession.getCurrent(); + if (otherSession != null && otherSession != session + && otherSession.hasLock()) { + throw new IllegalStateException( + "Can't access session while another session is locked by the same thread. This restriction is intended to help avoid deadlocks."); + } + } + + /** + * Verifies that the given CSRF token (aka double submit cookie) is valid + * for the given session. This is used to protect against Cross Site Request + * Forgery attacks. + * <p> + * This protection is enabled by default, but it might need to be disabled + * to allow a certain type of testing. For these cases, the check can be + * disabled by setting the init parameter + * {@value Constants#SERVLET_PARAMETER_DISABLE_XSRF_PROTECTION} to + * <code>true</code>. + * + * @see DeploymentConfiguration#isXsrfProtectionEnabled() + * + * @since 7.1 + * + * @param session + * the vaadin session for which the check should be done + * @param requestToken + * the CSRF token provided in the request + * @return <code>true</code> if the token is valid or if the protection is + * disabled; <code>false</code> if protection is enabled and the + * token is invalid + */ + public static boolean isCsrfTokenValid(VaadinSession session, + String requestToken) { + + if (session.getService().getDeploymentConfiguration() + .isXsrfProtectionEnabled()) { + String sessionToken = session.getCsrfToken(); + + if (sessionToken == null || !sessionToken.equals(requestToken)) { + return false; + } + } + return true; + } + } diff --git a/server/src/com/vaadin/server/VaadinServlet.java b/server/src/com/vaadin/server/VaadinServlet.java index 35d5fd7cc1..de074941c1 100644 --- a/server/src/com/vaadin/server/VaadinServlet.java +++ b/server/src/com/vaadin/server/VaadinServlet.java @@ -24,7 +24,6 @@ import java.io.PrintWriter; import java.net.MalformedURLException; import java.net.URL; import java.net.URLConnection; -import java.security.GeneralSecurityException; import java.util.Arrays; import java.util.Collection; import java.util.Enumeration; @@ -35,43 +34,18 @@ import java.util.logging.Logger; import javax.servlet.ServletContext; import javax.servlet.ServletException; -import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.vaadin.sass.internal.ScssStylesheet; -import com.vaadin.server.AbstractCommunicationManager.Callback; -import com.vaadin.shared.ApplicationConstants; -import com.vaadin.ui.UI; +import com.vaadin.server.communication.ServletUIInitHandler; +import com.vaadin.shared.JsonConstants; import com.vaadin.util.CurrentInstance; @SuppressWarnings("serial") public class VaadinServlet extends HttpServlet implements Constants { - private static class AbstractApplicationServletWrapper implements Callback { - - private final VaadinServlet servlet; - - public AbstractApplicationServletWrapper(VaadinServlet servlet) { - this.servlet = servlet; - } - - @Override - public void criticalNotification(VaadinRequest request, - VaadinResponse response, String cap, String msg, - String details, String outOfSyncURL) throws IOException { - servlet.criticalNotification((VaadinServletRequest) request, - ((VaadinServletResponse) response), cap, msg, details, - outOfSyncURL); - } - } - - // TODO Move some (all?) of the constants to a separate interface (shared - // with portlet) - - private final String resourcePath = null; - private VaadinServletService servletService; /** @@ -110,7 +84,11 @@ public class VaadinServlet extends HttpServlet implements Constants { } DeploymentConfiguration deploymentConfiguration = createDeploymentConfiguration(initParameters); - servletService = createServletService(deploymentConfiguration); + try { + servletService = createServletService(deploymentConfiguration); + } catch (ServiceException e) { + throw new ServletException("Could not initialize VaadinServlet", e); + } // Sets current service even though there are no request and response servletService.setCurrentInstances(null, null); @@ -168,8 +146,12 @@ public class VaadinServlet extends HttpServlet implements Constants { } protected VaadinServletService createServletService( - DeploymentConfiguration deploymentConfiguration) { - return new VaadinServletService(this, deploymentConfiguration); + DeploymentConfiguration deploymentConfiguration) + throws ServiceException { + VaadinServletService service = new VaadinServletService(this, + deploymentConfiguration); + service.init(); + return service; } /** @@ -198,7 +180,23 @@ public class VaadinServlet extends HttpServlet implements Constants { } CurrentInstance.clearAll(); setCurrent(this); - service(createVaadinRequest(request), createVaadinResponse(response)); + + VaadinServletRequest vaadinRequest = createVaadinRequest(request); + VaadinServletResponse vaadinResponse = createVaadinResponse(response); + if (!ensureCookiesEnabled(vaadinRequest, vaadinResponse)) { + return; + } + + if (isStaticResourceRequest(request)) { + serveStaticResources(request, response); + return; + } + try { + getService().handleRequest(vaadinRequest, vaadinResponse); + } catch (ServiceException e) { + throw new ServletException(e); + } + } /** @@ -218,7 +216,7 @@ public class VaadinServlet extends HttpServlet implements Constants { */ protected boolean handleContextRootWithoutSlash(HttpServletRequest request, HttpServletResponse response) throws IOException { - if ("/".equals(request.getPathInfo()) + if ((request.getPathInfo() == null || "/".equals(request.getPathInfo())) && "".equals(request.getServletPath()) && !request.getRequestURI().endsWith("/")) { /* @@ -237,119 +235,6 @@ public class VaadinServlet extends HttpServlet implements Constants { } } - private void service(VaadinServletRequest request, - VaadinServletResponse response) throws ServletException, - IOException { - RequestTimer requestTimer = new RequestTimer(); - requestTimer.start(); - - getService().setCurrentInstances(request, response); - - AbstractApplicationServletWrapper servletWrapper = new AbstractApplicationServletWrapper( - this); - - RequestType requestType = getRequestType(request); - if (!ensureCookiesEnabled(requestType, request, response)) { - return; - } - - if (requestType == RequestType.STATIC_FILE) { - serveStaticResources(request, response); - return; - } - - VaadinSession vaadinSession = null; - - try { - // If a duplicate "close application" URL is received for an - // application that is not open, redirect to the application's main - // page. - // This is needed as e.g. Spring Security remembers the last - // URL from the application, which is the logout URL, and repeats - // it. - // We can tell apart a real onunload request from a repeated one - // based on the real one having content (at least the UIDL security - // key). - if (requestType == RequestType.UIDL - && request.getParameterMap().containsKey( - ApplicationConstants.PARAM_UNLOADBURST) - && request.getContentLength() < 1 - && getService().getExistingSession(request, false) == null) { - redirectToApplication(request, response); - return; - } - - // Find out the service session this request is related to - vaadinSession = getService().findVaadinSession(request); - if (vaadinSession == null) { - return; - } - - CommunicationManager communicationManager = (CommunicationManager) vaadinSession - .getCommunicationManager(); - - if (requestType == RequestType.PUBLISHED_FILE) { - communicationManager.servePublishedFile(request, response); - return; - } else if (requestType == RequestType.HEARTBEAT) { - communicationManager.handleHeartbeatRequest(request, response, - vaadinSession); - return; - } - - /* Update browser information from the request */ - vaadinSession.getBrowser().updateRequestDetails(request); - - /* Handle the request */ - if (requestType == RequestType.FILE_UPLOAD) { - // UI is resolved in communication manager - communicationManager.handleFileUpload(vaadinSession, request, - response); - return; - } else if (requestType == RequestType.UIDL) { - UI uI = getService().findUI(request); - if (uI == null) { - throw new ServletException(ERROR_NO_UI_FOUND); - } - // Handles AJAX UIDL requests - communicationManager.handleUidlRequest(request, response, - servletWrapper, uI); - - // Ensure that the browser does not cache UIDL responses. - // iOS 6 Safari requires this (#9732) - response.setHeader("Cache-Control", "no-cache"); - - return; - } else if (requestType == RequestType.BROWSER_DETAILS) { - // Browser details - not related to a specific UI - communicationManager.handleBrowserDetailsRequest(request, - response, vaadinSession); - return; - } - - if (communicationManager.handleOtherRequest(request, response)) { - return; - } - - // Request not handled by any RequestHandler -> 404 - response.sendError(HttpServletResponse.SC_NOT_FOUND); - - } catch (final SessionExpiredException e) { - // Session has expired, notify user - handleServiceSessionExpired(request, response); - } catch (final GeneralSecurityException e) { - handleServiceSecurityException(request, response); - } catch (final Throwable e) { - handleServiceException(request, response, vaadinSession, e); - } finally { - if (vaadinSession != null) { - getService().cleanupSession(vaadinSession); - requestTimer.stop(vaadinSession); - } - CurrentInstance.clearAll(); - } - } - private VaadinServletResponse createVaadinResponse( HttpServletResponse response) { return new VaadinServletResponse(response, getService()); @@ -391,10 +276,9 @@ public class VaadinServlet extends HttpServlet implements Constants { * @return false if cookies are disabled, true otherwise * @throws IOException */ - private boolean ensureCookiesEnabled(RequestType requestType, - VaadinServletRequest request, VaadinServletResponse response) - throws IOException { - if (requestType == RequestType.UIDL) { + private boolean ensureCookiesEnabled(VaadinServletRequest request, + VaadinServletResponse response) throws IOException { + if (ServletPortletHelper.isUIDLRequest(request)) { // In all other but the first UIDL request a cookie should be // returned by the browser. // This can be removed if cookieless mode (#3228) is supported @@ -403,10 +287,13 @@ public class VaadinServlet extends HttpServlet implements Constants { SystemMessages systemMessages = getService().getSystemMessages( ServletPortletHelper.findLocale(null, null, request), request); - criticalNotification(request, response, - systemMessages.getCookiesDisabledCaption(), - systemMessages.getCookiesDisabledMessage(), null, - systemMessages.getCookiesDisabledURL()); + getService().writeStringResponse( + response, + JsonConstants.JSON_CONTENT_TYPE, + VaadinService.createCriticalNotificationJSON( + systemMessages.getCookiesDisabledCaption(), + systemMessages.getCookiesDisabledMessage(), + null, systemMessages.getCookiesDisabledURL())); return false; } } @@ -437,39 +324,19 @@ public class VaadinServlet extends HttpServlet implements Constants { * @throws IOException * if the writing failed due to input/output error. * - * @deprecated As of 7.0. Will likely change or be removed in a future - * version + * @deprecated As of 7.0. This method is retained only for backwards + * compatibility and for {@link GAEVaadinServlet}. */ @Deprecated protected void criticalNotification(VaadinServletRequest request, - HttpServletResponse response, String caption, String message, + VaadinServletResponse response, String caption, String message, String details, String url) throws IOException { if (ServletPortletHelper.isUIDLRequest(request)) { - - if (caption != null) { - caption = "\"" + JsonPaintTarget.escapeJSON(caption) + "\""; - } - if (details != null) { - if (message == null) { - message = details; - } else { - message += "<br/><br/>" + details; - } - } - - if (message != null) { - message = "\"" + JsonPaintTarget.escapeJSON(message) + "\""; - } - if (url != null) { - url = "\"" + JsonPaintTarget.escapeJSON(url) + "\""; - } - - String output = "for(;;);[{\"changes\":[], \"meta\" : {" - + "\"appError\": {" + "\"caption\":" + caption + "," - + "\"message\" : " + message + "," + "\"url\" : " + url - + "}}, \"resources\": {}, \"locales\":[]}]"; - writeResponse(response, "application/json; charset=UTF-8", output); + String output = VaadinService.createCriticalNotificationJSON( + caption, message, details, url); + getService().writeStringResponse(response, + JsonConstants.JSON_CONTENT_TYPE, output); } else { // Create an HTML reponse with the error String output = ""; @@ -492,10 +359,9 @@ public class VaadinServlet extends HttpServlet implements Constants { if (url != null) { output += "</a>"; } - writeResponse(response, "text/html; charset=UTF-8", output); - + getService().writeStringResponse(response, + "text/html; charset=UTF-8", output); } - } /** @@ -511,7 +377,7 @@ public class VaadinServlet extends HttpServlet implements Constants { private void writeResponse(HttpServletResponse response, String contentType, String output) throws IOException { response.setContentType(contentType); - final ServletOutputStream out = response.getOutputStream(); + final OutputStream out = response.getOutputStream(); // Set the response type final PrintWriter outWriter = new PrintWriter(new BufferedWriter( new OutputStreamWriter(out, "UTF-8"))); @@ -555,33 +421,6 @@ public class VaadinServlet extends HttpServlet implements Constants { return resultPath; } - private void handleServiceException(VaadinServletRequest request, - VaadinServletResponse response, VaadinSession vaadinSession, - Throwable e) throws IOException, ServletException { - ErrorHandler errorHandler = ErrorEvent.findErrorHandler(vaadinSession); - - // if this was an UIDL request, response UIDL back to client - if (getRequestType(request) == RequestType.UIDL) { - SystemMessages ci = getService().getSystemMessages( - ServletPortletHelper.findLocale(null, vaadinSession, - request), request); - criticalNotification(request, response, - ci.getInternalErrorCaption(), ci.getInternalErrorMessage(), - null, ci.getInternalErrorURL()); - if (errorHandler != null) { - errorHandler.error(new ErrorEvent(e)); - } - } else { - if (errorHandler != null) { - errorHandler.error(new ErrorEvent(e)); - } - - // Re-throw other exceptions - throw new ServletException(e); - } - - } - /** * A helper method to strip away characters that might somehow be used for * XSS attacs. Leaves at least alphanumeric characters intact. Also removes @@ -626,74 +465,9 @@ public class VaadinServlet extends HttpServlet implements Constants { return DEFAULT_THEME_NAME; } - /** - * @param request - * @param response - * @throws IOException - * @throws ServletException - * - * @deprecated As of 7.0. Will likely change or be removed in a future - * version - */ - @Deprecated - void handleServiceSessionExpired(VaadinServletRequest request, - VaadinServletResponse response) throws IOException, - ServletException { - - if (isOnUnloadRequest(request)) { - /* - * Request was an unload request (e.g. window close event) and the - * client expects no response if it fails. - */ - return; - } - - try { - SystemMessages ci = getService().getSystemMessages( - ServletPortletHelper.findLocale(null, null, request), - request); - RequestType requestType = getRequestType(request); - if (requestType == RequestType.UIDL) { - /* - * Invalidate session (weird to have session if we're saying - * that it's expired, and worse: portal integration will fail - * since the session is not created by the portal. - * - * Session must be invalidated before criticalNotification as it - * commits the response. - */ - request.getSession().invalidate(); - - // send uidl redirect - criticalNotification(request, response, - ci.getSessionExpiredCaption(), - ci.getSessionExpiredMessage(), null, - ci.getSessionExpiredURL()); - - } else if (requestType == RequestType.HEARTBEAT) { - response.sendError(HttpServletResponse.SC_GONE, - "Session expired"); - } else { - // 'plain' http req - e.g. browser reload; - // just go ahead redirect the browser - response.sendRedirect(ci.getSessionExpiredURL()); - } - } catch (SystemMessageException ee) { - throw new ServletException(ee); - } - - } - private void handleServiceSecurityException(VaadinServletRequest request, VaadinServletResponse response) throws IOException, ServletException { - if (isOnUnloadRequest(request)) { - /* - * Request was an unload request (e.g. window close event) and the - * client expects no response if it fails. - */ - return; - } try { /* @@ -702,20 +476,17 @@ public class VaadinServlet extends HttpServlet implements Constants { */ SystemMessages ci = getService().getSystemMessages( request.getLocale(), request); - RequestType requestType = getRequestType(request); - if (requestType == RequestType.UIDL) { + if (ServletPortletHelper.isUIDLRequest(request)) { // send uidl redirect - criticalNotification(request, response, - ci.getCommunicationErrorCaption(), - ci.getCommunicationErrorMessage(), - INVALID_SECURITY_KEY_MSG, ci.getCommunicationErrorURL()); - /* - * Invalidate session. Portal integration will fail otherwise - * since the session is not created by the portal. - */ - request.getSession().invalidate(); - - } else if (requestType == RequestType.HEARTBEAT) { + getService().writeStringResponse( + response, + JsonConstants.JSON_CONTENT_TYPE, + VaadinService.createCriticalNotificationJSON( + ci.getCommunicationErrorCaption(), + ci.getCommunicationErrorMessage(), + INVALID_SECURITY_KEY_MSG, + ci.getCommunicationErrorURL())); + } else if (ServletPortletHelper.isHeartbeatRequest(request)) { response.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden"); } else { @@ -744,9 +515,8 @@ public class VaadinServlet extends HttpServlet implements Constants { private boolean serveStaticResources(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { - // FIXME What does 10 refer to? String pathInfo = request.getPathInfo(); - if (pathInfo == null || pathInfo.length() <= 10) { + if (pathInfo == null) { return false; } @@ -1118,10 +888,12 @@ public class VaadinServlet extends HttpServlet implements Constants { /** * * @author Vaadin Ltd - * @since 7.0.0 + * @since 7.0 * - * @deprecated As of 7.0. Will likely change or be removed in a future - * version + * @deprecated As of 7.0. This is no longer used and only provided for + * backwards compatibility. Each {@link RequestHandler} can + * individually decide whether it wants to handle a request or + * not. */ @Deprecated protected enum RequestType { @@ -1132,8 +904,10 @@ public class VaadinServlet extends HttpServlet implements Constants { * @param request * @return * - * @deprecated As of 7.0. Will likely change or be removed in a future - * version + * @deprecated As of 7.0. This is no longer used and only provided for + * backwards compatibility. Each {@link RequestHandler} can + * individually decide whether it wants to handle a request or + * not. */ @Deprecated protected RequestType getRequestType(VaadinServletRequest request) { @@ -1141,7 +915,7 @@ public class VaadinServlet extends HttpServlet implements Constants { return RequestType.FILE_UPLOAD; } else if (ServletPortletHelper.isPublishedFileRequest(request)) { return RequestType.PUBLISHED_FILE; - } else if (isBrowserDetailsRequest(request)) { + } else if (ServletUIInitHandler.isUIInitRequest(request)) { return RequestType.BROWSER_DETAILS; } else if (ServletPortletHelper.isUIDLRequest(request)) { return RequestType.UIDL; @@ -1156,14 +930,9 @@ public class VaadinServlet extends HttpServlet implements Constants { } - private static boolean isBrowserDetailsRequest(HttpServletRequest request) { - return "POST".equals(request.getMethod()) - && request.getParameter("v-browserDetails") != null; - } - - private boolean isStaticResourceRequest(HttpServletRequest request) { + protected boolean isStaticResourceRequest(HttpServletRequest request) { String pathInfo = request.getPathInfo(); - if (pathInfo == null || pathInfo.length() <= 10) { + if (pathInfo == null) { return false; } @@ -1178,10 +947,6 @@ public class VaadinServlet extends HttpServlet implements Constants { return false; } - private boolean isOnUnloadRequest(HttpServletRequest request) { - return request.getParameter(ApplicationConstants.PARAM_UNLOADBURST) != null; - } - /** * Remove any heading or trailing "what" from the "string". * @@ -1301,4 +1066,5 @@ public class VaadinServlet extends HttpServlet implements Constants { private static final Logger getLogger() { return Logger.getLogger(VaadinServlet.class.getName()); } + } diff --git a/server/src/com/vaadin/server/VaadinServletService.java b/server/src/com/vaadin/server/VaadinServletService.java index 71f47ea217..3b39f17849 100644 --- a/server/src/com/vaadin/server/VaadinServletService.java +++ b/server/src/com/vaadin/server/VaadinServletService.java @@ -17,19 +17,38 @@ package com.vaadin.server; import java.io.File; +import java.io.InputStream; import java.net.MalformedURLException; import java.net.URL; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; -import com.vaadin.server.VaadinServlet.RequestType; +import org.atmosphere.util.Version; + +import com.vaadin.server.communication.PushRequestHandler; +import com.vaadin.server.communication.ServletBootstrapHandler; +import com.vaadin.server.communication.ServletUIInitHandler; import com.vaadin.ui.UI; public class VaadinServletService extends VaadinService { private final VaadinServlet servlet; + private final static boolean atmosphereAvailable = checkAtmosphereSupport(); + + /** + * Keeps track of whether a warning about missing push support has already + * been logged. This is used to avoid spamming the log with the same message + * every time a new UI is bootstrapped. + */ + private boolean pushWarningLogged = false; + public VaadinServletService(VaadinServlet servlet, - DeploymentConfiguration deploymentConfiguration) { + DeploymentConfiguration deploymentConfiguration) + throws ServiceException { super(deploymentConfiguration); this.servlet = servlet; @@ -44,7 +63,40 @@ public class VaadinServletService extends VaadinService { } } - protected VaadinServlet getServlet() { + private static boolean checkAtmosphereSupport() { + try { + String rawVersion = Version.getRawVersion(); + if (!Constants.REQUIRED_ATMOSPHERE_VERSION.equals(rawVersion)) { + getLogger().log( + Level.WARNING, + Constants.INVALID_ATMOSPHERE_VERSION_WARNING, + new Object[] { Constants.REQUIRED_ATMOSPHERE_VERSION, + rawVersion }); + } + return true; + } catch (NoClassDefFoundError e) { + return false; + } + } + + @Override + protected List<RequestHandler> createRequestHandlers() + throws ServiceException { + List<RequestHandler> handlers = super.createRequestHandlers(); + handlers.add(0, new ServletBootstrapHandler()); + handlers.add(new ServletUIInitHandler()); + if (atmosphereAvailable) { + handlers.add(new PushRequestHandler(this)); + } + return handlers; + } + + /** + * Retrieves a reference to the servlet associated with this service. + * + * @return A reference to the VaadinServlet this service is using + */ + public VaadinServlet getServlet() { return servlet; } @@ -127,12 +179,11 @@ public class VaadinServletService extends VaadinService { @Override protected boolean requestCanCreateSession(VaadinRequest request) { - RequestType requestType = getRequestType(request); - if (requestType == RequestType.BROWSER_DETAILS) { + if (ServletUIInitHandler.isUIInitRequest(request)) { // This is the first request if you are embedding by writing the // embedding code yourself return true; - } else if (requestType == RequestType.OTHER) { + } else if (isOtherRequest(request)) { /* * I.e URIs that are not RPC calls or static (theme) files. */ @@ -142,25 +193,16 @@ public class VaadinServletService extends VaadinService { return false; } - /** - * Gets the request type for the request. - * - * @param request - * the request to get a request type for - * @return the request type - * - * @deprecated As of 7.0. Will likely change or be removed in a future - * version - */ - @Deprecated - protected RequestType getRequestType(VaadinRequest request) { - RequestType type = (RequestType) request.getAttribute(RequestType.class - .getName()); - if (type == null) { - type = getServlet().getRequestType((VaadinServletRequest) request); - request.setAttribute(RequestType.class.getName(), type); - } - return type; + private boolean isOtherRequest(VaadinRequest request) { + // TODO This should be refactored in some way. It should not be + // necessary to check all these types. + return (!ServletPortletHelper.isAppRequest(request) + && !ServletUIInitHandler.isUIInitRequest(request) + && !ServletPortletHelper.isFileUploadRequest(request) + && !ServletPortletHelper.isHeartbeatRequest(request) + && !ServletPortletHelper.isPublishedFileRequest(request) + && !ServletPortletHelper.isUIDLRequest(request) && !ServletPortletHelper + .isPushRequest(request)); } @Override @@ -169,12 +211,6 @@ public class VaadinServletService extends VaadinService { return getServlet().getApplicationUrl((VaadinServletRequest) request); } - @Override - protected AbstractCommunicationManager createCommunicationManager( - VaadinSession session) { - return new CommunicationManager(session); - } - public static HttpServletRequest getCurrentServletRequest() { VaadinRequest currentRequest = VaadinService.getCurrentRequest(); if (currentRequest instanceof VaadinServletRequest) { @@ -194,6 +230,18 @@ public class VaadinServletService extends VaadinService { } @Override + public InputStream getThemeResourceAsStream(UI uI, String themeName, + String resource) { + VaadinServletService service = (VaadinServletService) uI.getSession() + .getService(); + ServletContext servletContext = service.getServlet() + .getServletContext(); + return servletContext.getResourceAsStream("/" + + VaadinServlet.THEME_DIR_PATH + '/' + themeName + "/" + + resource); + } + + @Override public String getMainDivId(VaadinSession session, VaadinRequest request, Class<? extends UI> uiClass) { String appId = null; @@ -220,4 +268,22 @@ public class VaadinServletService extends VaadinService { appId = appId + "-" + hashCode; return appId; } + + private static final Logger getLogger() { + return Logger.getLogger(VaadinServletService.class.getName()); + } + + @Override + public boolean ensurePushAvailable() { + if (atmosphereAvailable) { + return true; + } else { + if (!pushWarningLogged) { + pushWarningLogged = true; + getLogger().log(Level.WARNING, + Constants.ATMOSPHERE_MISSING_ERROR); + } + return false; + } + } } diff --git a/server/src/com/vaadin/server/VaadinSession.java b/server/src/com/vaadin/server/VaadinSession.java index d3619ebabf..317ea6cf7b 100644 --- a/server/src/com/vaadin/server/VaadinSession.java +++ b/server/src/com/vaadin/server/VaadinSession.java @@ -25,6 +25,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; +import java.util.UUID; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Logger; @@ -39,6 +40,7 @@ import com.vaadin.data.util.converter.Converter; import com.vaadin.data.util.converter.ConverterFactory; import com.vaadin.data.util.converter.DefaultConverterFactory; import com.vaadin.event.EventRouter; +import com.vaadin.shared.communication.PushMode; import com.vaadin.ui.AbstractField; import com.vaadin.ui.Table; import com.vaadin.ui.UI; @@ -73,8 +75,6 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { .findMethod(BootstrapListener.class, "modifyBootstrapPage", BootstrapPageResponse.class); - private final Lock lock = new ReentrantLock(); - /** * Configuration for the session. */ @@ -110,7 +110,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { protected WebBrowser browser = new WebBrowser(); - private AbstractCommunicationManager communicationManager; + private LegacyCommunicationManager communicationManager; private long cumulativeRequestDuration = 0; @@ -128,6 +128,8 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { private transient VaadinService service; + private transient Lock lock; + /** * Create a new service session tied to a Vaadin service * @@ -162,6 +164,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { + "This might happen if a session is deserialized but never used before it expires."); } else if (VaadinService.getCurrentRequest() != null && getCurrent() == this) { + assert hasLock(); // Ignore if the session is being moved to a different backing // session if (getAttribute(VaadinService.REINITIALIZING_SESSION_MARKER) == Boolean.TRUE) { @@ -192,6 +195,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { */ @Deprecated public WebBrowser getBrowser() { + assert hasLock(); return browser; } @@ -200,6 +204,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * milliseconds. */ public long getCumulativeRequestDuration() { + assert hasLock(); return cumulativeRequestDuration; } @@ -211,6 +216,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * The time spent in the last request, in milliseconds. */ public void setLastRequestDuration(long time) { + assert hasLock(); lastRequestDuration = time; cumulativeRequestDuration += time; } @@ -220,6 +226,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * milliseconds. */ public long getLastRequestDuration() { + assert hasLock(); return lastRequestDuration; } @@ -232,6 +239,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * */ public void setLastRequestTimestamp(long timestamp) { + assert hasLock(); lastRequestTimestamp = timestamp; } @@ -242,6 +250,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * the epoch. */ public long getLastRequestTimestamp() { + assert hasLock(); return lastRequestTimestamp; } @@ -252,6 +261,11 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * @return the wrapped session for this context */ public WrappedSession getSession() { + /* + * This is used to fetch the underlying session and there is no need for + * having a lock when doing this. On the contrary this is sometimes done + * to be able to lock the session. + */ return session; } @@ -262,65 +276,97 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * version */ @Deprecated - public AbstractCommunicationManager getCommunicationManager() { + public LegacyCommunicationManager getCommunicationManager() { + assert hasLock(); return communicationManager; } /** + * Loads the VaadinSession for the given service and WrappedSession from the + * HTTP session. + * * @param service - * TODO + * The service the VaadinSession is associated with * @param underlyingSession - * @return - * - * @deprecated As of 7.0. Will likely change or be removed in a future - * version + * The wrapped HTTP session for the user + * @return A VaadinSession instance for the service, session combination or + * null if none was found. + * @deprecated As of 7.0. Should be moved to a separate session storage + * class some day. */ @Deprecated public static VaadinSession getForSession(VaadinService service, WrappedSession underlyingSession) { - Object attribute = underlyingSession.getAttribute(VaadinSession.class - .getName() + "." + service.getServiceName()); - if (attribute instanceof VaadinSession) { - VaadinSession vaadinSession = (VaadinSession) attribute; - vaadinSession.session = underlyingSession; - vaadinSession.service = service; - return vaadinSession; + assert hasLock(service, underlyingSession); + + VaadinSession vaadinSession = (VaadinSession) underlyingSession + .getAttribute(getSessionAttributeName(service)); + if (vaadinSession == null) { + return null; } - return null; + vaadinSession.session = underlyingSession; + vaadinSession.service = service; + vaadinSession.refreshLock(); + return vaadinSession; } /** + * Removes this VaadinSession from the HTTP session. * * @param service - * TODO - * @deprecated As of 7.0. Will likely change or be removed in a future - * version + * The service this session is associated with + * @deprecated As of 7.0. Should be moved to a separate session storage + * class some day. */ @Deprecated public void removeFromSession(VaadinService service) { - assert (getForSession(service, session) == this); - session.setAttribute( - VaadinSession.class.getName() + "." + service.getServiceName(), - null); + assert hasLock(); + session.removeAttribute(getSessionAttributeName(service)); } /** - * @param session + * Retrieves the name of the attribute used for storing a VaadinSession for + * the given service. * - * @deprecated As of 7.0. Will likely change or be removed in a future - * version + * @param service + * The service associated with the sessio + * @return The attribute name used for storing the session + */ + private static String getSessionAttributeName(VaadinService service) { + return VaadinSession.class.getName() + "." + service.getServiceName(); + } + + /** + * Stores this VaadinSession in the HTTP session. + * + * @param service + * The service this session is associated with + * @param session + * The HTTP session this VaadinSession should be stored in + * @deprecated As of 7.0. Should be moved to a separate session storage + * class some day. */ @Deprecated public void storeInSession(VaadinService service, WrappedSession session) { - session.setAttribute( - VaadinSession.class.getName() + "." + service.getServiceName(), - this); + assert hasLock(service, session); + session.setAttribute(getSessionAttributeName(service), this); this.session = session; + refreshLock(); + } + + /** + * Updates the transient session lock from VaadinService. + */ + private void refreshLock() { + assert lock == null || lock == service.getSessionLock(session) : "Cannot change the lock from one instance to another"; + assert hasLock(service, session); + lock = service.getSessionLock(session); } public void setCommunicationManager( - AbstractCommunicationManager communicationManager) { + LegacyCommunicationManager communicationManager) { + assert hasLock(); if (communicationManager == null) { throw new IllegalArgumentException("Can not set to null"); } @@ -329,6 +375,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { } public void setConfiguration(DeploymentConfiguration configuration) { + assert hasLock(); if (configuration == null) { throw new IllegalArgumentException("Can not set to null"); } @@ -342,6 +389,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * @return the deployment configuration */ public DeploymentConfiguration getConfiguration() { + assert hasLock(); return configuration; } @@ -354,6 +402,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * @return the locale of this session. */ public Locale getLocale() { + assert hasLock(); if (locale != null) { return locale; } @@ -371,6 +420,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * */ public void setLocale(Locale locale) { + assert hasLock(); this.locale = locale; } @@ -380,6 +430,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * @return the current error handler */ public ErrorHandler getErrorHandler() { + assert hasLock(); return errorHandler; } @@ -389,6 +440,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * @param errorHandler */ public void setErrorHandler(ErrorHandler errorHandler) { + assert hasLock(); this.errorHandler = errorHandler; } @@ -401,6 +453,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * @return The converter factory used in the session */ public ConverterFactory getConverterFactory() { + assert hasLock(); return converterFactory; } @@ -426,6 +479,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * The converter factory used in the session */ public void setConverterFactory(ConverterFactory converterFactory) { + assert hasLock(); this.converterFactory = converterFactory; } @@ -446,6 +500,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * @since 7.0 */ public void addRequestHandler(RequestHandler handler) { + assert hasLock(); requestHandlers.addFirst(handler); } @@ -458,6 +513,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * @since 7.0 */ public void removeRequestHandler(RequestHandler handler) { + assert hasLock(); requestHandlers.remove(handler); } @@ -475,6 +531,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * @since 7.0 */ public Collection<RequestHandler> getRequestHandlers() { + assert hasLock(); return Collections.unmodifiableCollection(requestHandlers); } @@ -528,11 +585,14 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * @since 7.0 */ public Collection<UI> getUIs() { + assert hasLock(); return Collections.unmodifiableCollection(uIs.values()); } private int connectorIdSequence = 0; + private final String csrfToken = UUID.randomUUID().toString(); + /** * Generate an id for the given Connector. Connectors must not call this * method more than once, the first time they need an id. @@ -546,6 +606,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { */ @Deprecated public String createConnectorId(ClientConnector connector) { + assert hasLock(); return String.valueOf(connectorIdSequence++); } @@ -560,10 +621,32 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * @return The UI with the given id or null if not found */ public UI getUIById(int uiId) { + assert hasLock(); return uIs.get(uiId); } /** + * Checks if the current thread has exclusive access to this VaadinSession + * + * @return true if the thread has exclusive access, false otherwise + */ + public boolean hasLock() { + ReentrantLock l = ((ReentrantLock) getLockInstance()); + return l.isHeldByCurrentThread(); + } + + /** + * Checks if the current thread has exclusive access to the given + * WrappedSession. + * + * @return true if this thread has exclusive access, false otherwise + */ + private static boolean hasLock(VaadinService service, WrappedSession session) { + ReentrantLock l = (ReentrantLock) service.getSessionLock(session); + return l.isHeldByCurrentThread(); + } + + /** * Adds a listener that will be invoked when the bootstrap HTML is about to * be generated. This can be used to modify the contents of the HTML that * loads the Vaadin application in the browser and the HTTP headers that are @@ -576,6 +659,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * the bootstrap listener to add */ public void addBootstrapListener(BootstrapListener listener) { + assert hasLock(); eventRouter.addListener(BootstrapFragmentResponse.class, listener, BOOTSTRAP_FRAGMENT_METHOD); eventRouter.addListener(BootstrapPageResponse.class, listener, @@ -591,6 +675,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * the bootstrap listener to remove */ public void removeBootstrapListener(BootstrapListener listener) { + assert hasLock(); eventRouter.removeListener(BootstrapFragmentResponse.class, listener, BOOTSTRAP_FRAGMENT_METHOD); eventRouter.removeListener(BootstrapPageResponse.class, listener, @@ -611,6 +696,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { */ @Deprecated public void modifyBootstrapResponse(BootstrapResponse response) { + assert hasLock(); eventRouter.fireEvent(response); } @@ -622,6 +708,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * the UI to remove */ public void removeUI(UI ui) { + assert hasLock(); int id = ui.getUIId(); ui.setSession(null); uIs.remove(id); @@ -646,6 +733,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * @since 7.0.0 */ public GlobalResourceHandler getGlobalResourceHandler(boolean createOnDemand) { + assert hasLock(); if (globalResourceHandler == null && createOnDemand) { globalResourceHandler = new GlobalResourceHandler(); addRequestHandler(globalResourceHandler); @@ -677,9 +765,24 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Locks this session to protect its data from concurrent access. Accessing * the UI state from outside the normal request handling should always lock - * the session and unlock it when done. To ensure that the lock is always - * released, you should typically wrap the code in a <code>try</code> block - * and unlock the session in <code>finally</code>: + * the session and unlock it when done. The preferred way to ensure locking + * is done correctly is to wrap your code using {@link UI#access(Runnable)} + * (or {@link VaadinSession#access(Runnable)} if you are only touching the + * session and not any UI), e.g.: + * + * <pre> + * myUI.access(new Runnable() { + * @Override + * public void run() { + * // Here it is safe to update the UI. + * // UI.getCurrent can also be used + * myUI.getContent().setCaption("Changed safely"); + * } + * }); + * </pre> + * + * If you for whatever reason want to do locking manually, you should do it + * like: * * <pre> * session.lock(); @@ -689,7 +792,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * session.unlock(); * } * </pre> - * <p> + * * This method will block until the lock can be retrieved. * <p> * {@link #getLockInstance()} can be used if more control over the locking @@ -697,6 +800,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * * @see #unlock() * @see #getLockInstance() + * @see #hasLock() */ public void lock() { getLockInstance().lock(); @@ -705,11 +809,29 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { /** * Unlocks this session. This method should always be used in a finally * block after {@link #lock()} to ensure that the lock is always released. + * <p> + * If {@link #getPushMode() the push mode} is {@link PushMode#AUTOMATIC + * automatic}, pushes the changes in all UIs in this session to their + * respective clients. * - * @see #unlock() + * @see #lock() + * @see UI#push() */ public void unlock() { - getLockInstance().unlock(); + assert hasLock(); + try { + if (((ReentrantLock) getLockInstance()).getHoldCount() == 1) { + // Only push if the reentrant lock will actually be released by + // this unlock() invocation. + for (UI ui : getUIs()) { + if (ui.getPushMode() == PushMode.AUTOMATIC) { + ui.push(); + } + } + } + } finally { + getLockInstance().unlock(); + } } /** @@ -728,6 +850,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * remove a previous association. */ public void setAttribute(String name, Object value) { + assert hasLock(); if (name == null) { throw new IllegalArgumentException("name can not be null"); } @@ -759,6 +882,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * remove a previous association. */ public <T> void setAttribute(Class<T> type, T value) { + assert hasLock(); if (type == null) { throw new IllegalArgumentException("type can not be null"); } @@ -783,6 +907,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * it has been set to null. */ public Object getAttribute(String name) { + assert hasLock(); if (name == null) { throw new IllegalArgumentException("name can not be null"); } @@ -808,6 +933,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * it has been set to null. */ public <T> T getAttribute(Class<T> type) { + assert hasLock(); if (type == null) { throw new IllegalArgumentException("type can not be null"); } @@ -825,6 +951,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * @return a unique UI id */ public int getNextUIid() { + assert hasLock(); return nextUIId++; } @@ -838,6 +965,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * @return the mapping between window names and UI ids for this session. */ public Map<String, Integer> getPreserveOnRefreshUIs() { + assert hasLock(); return retainOnRefreshUIs; } @@ -848,6 +976,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * the initialized UI to add. */ public void addUI(UI ui) { + assert hasLock(); if (ui.getUIId() == -1) { throw new IllegalArgumentException( "Can not add an UI that has not been initialized."); @@ -867,6 +996,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * the UI provider that should be added */ public void addUIProvider(UIProvider uiProvider) { + assert hasLock(); uiProviders.addFirst(uiProvider); } @@ -877,6 +1007,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * the UI provider that should be removed */ public void removeUIProvider(UIProvider uiProvider) { + assert hasLock(); uiProviders.remove(uiProvider); } @@ -886,6 +1017,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * @return an unmodifiable list of UI providers */ public List<UIProvider> getUIProviders() { + assert hasLock(); return Collections.unmodifiableList(uiProviders); } @@ -910,6 +1042,7 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * */ public void close() { + assert hasLock(); closing = true; } @@ -918,13 +1051,84 @@ public class VaadinSession implements HttpSessionBindingListener, Serializable { * * @see #close() * - * @return + * @return true if this session is marked to be closed, false otherwise */ public boolean isClosing() { + assert hasLock(); return closing; } private static final Logger getLogger() { return Logger.getLogger(VaadinSession.class.getName()); } + + /** + * Provides exclusive access to this session from outside a request handling + * thread. + * <p> + * The given runnable is executed while holding the session lock to ensure + * exclusive access to this session. The session and related thread locals + * are set properly before executing the runnable. + * </p> + * <p> + * RPC handlers for components inside this session do not need this method + * as the session is automatically locked by the framework during request + * handling. + * </p> + * <p> + * Note that calling this method while another session is locked by the + * current thread will cause an exception. This is to prevent deadlock + * situations when two threads have locked one session each and are both + * waiting for the lock for the other session. + * </p> + * + * @param runnable + * the runnable which accesses the session + * + * @throws IllegalStateException + * if the current thread holds the lock for another session + * + * + * @see #lock() + * @see #getCurrent() + * @see UI#access(Runnable) + */ + public void access(Runnable runnable) { + VaadinService.verifyNoOtherSessionLocked(this); + + Map<Class<?>, CurrentInstance> old = null; + lock(); + try { + old = CurrentInstance.setThreadLocals(this); + runnable.run(); + } finally { + unlock(); + if (old != null) { + CurrentInstance.restoreThreadLocals(old); + } + } + + } + + /** + * @deprecated As of 7.1.0.beta1, use {@link #access(Runnable)} instead. + * This method will be removed before the final 7.1.0 release. + */ + @Deprecated + public void runSafely(Runnable runnable) { + access(runnable); + } + + /** + * Gets the CSRF token (aka double submit cookie) that is used to protect + * against Cross Site Request Forgery attacks. + * + * @since 7.1 + * @return the csrf token string + */ + public String getCsrfToken() { + assert hasLock(); + return csrfToken; + } + } diff --git a/server/src/com/vaadin/server/WebBrowser.java b/server/src/com/vaadin/server/WebBrowser.java index 4122f053ae..8038bbc207 100644 --- a/server/src/com/vaadin/server/WebBrowser.java +++ b/server/src/com/vaadin/server/WebBrowser.java @@ -319,6 +319,17 @@ public class WebBrowser implements Serializable { * entirely accurate due to varying network latencies, but should provide a * close-enough value for most cases. Also note that the returned Date * object uses servers default time zone, not the clients. + * <p> + * To get the actual date and time shown in the end users computer, you can + * do something like: + * + * <pre> + * WebBrowser browser = ...; + * SimpleTimeZone timeZone = new SimpleTimeZone(browser.getTimezoneOffset(), "Fake client time zone"); + * DateFormat format = DateFormat.getDateTimeInstance(); + * format.setTimeZone(timeZone); + * myLabel.setValue(format.format(browser.getCurrentDate())); + * </pre> * * @return the current date and time of the browser. * @see #isDSTInEffect() @@ -413,7 +424,7 @@ public class WebBrowser implements Serializable { * @param request * the Vaadin request to read the information from */ - void updateRequestDetails(VaadinRequest request) { + public void updateRequestDetails(VaadinRequest request) { locale = request.getLocale(); address = request.getRemoteAddr(); secureConnection = request.isSecure(); diff --git a/server/src/com/vaadin/server/AbstractStreamingEvent.java b/server/src/com/vaadin/server/communication/AbstractStreamingEvent.java index b7bf4e042f..b97a60fd56 100644 --- a/server/src/com/vaadin/server/AbstractStreamingEvent.java +++ b/server/src/com/vaadin/server/communication/AbstractStreamingEvent.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.server; +package com.vaadin.server.communication; import com.vaadin.server.StreamVariable.StreamingEvent; diff --git a/server/src/com/vaadin/server/communication/AtmospherePushConnection.java b/server/src/com/vaadin/server/communication/AtmospherePushConnection.java new file mode 100644 index 0000000000..0bba65ff1d --- /dev/null +++ b/server/src/com/vaadin/server/communication/AtmospherePushConnection.java @@ -0,0 +1,247 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; +import java.io.Reader; +import java.io.Serializable; +import java.io.StringReader; +import java.io.StringWriter; +import java.io.Writer; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.atmosphere.cpr.AtmosphereResource; +import org.atmosphere.cpr.AtmosphereResource.TRANSPORT; +import org.json.JSONException; + +import com.vaadin.shared.ApplicationConstants; +import com.vaadin.ui.UI; + +/** + * {@link PushConnection} implementation using the Atmosphere push support that + * is by default included in Vaadin. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class AtmospherePushConnection implements Serializable, PushConnection { + + /** + * Represents a message that can arrive as multiple fragments. + */ + protected static class FragmentedMessage { + private final StringBuilder message = new StringBuilder(); + private final int messageLength; + + public FragmentedMessage(Reader reader) throws IOException { + // Messages are prefixed by the total message length plus '|' + String length = ""; + int c; + while ((c = reader.read()) != -1 + && c != ApplicationConstants.WEBSOCKET_MESSAGE_DELIMITER) { + length += (char) c; + } + try { + messageLength = Integer.parseInt(length); + } catch (NumberFormatException e) { + throw new IOException("Invalid message length " + length, e); + } + } + + /** + * Appends all the data from the given Reader to this message and + * returns whether the message was completed. + * + * @param reader + * The Reader from which to read. + * @return true if this message is complete, false otherwise. + * @throws IOException + */ + public boolean append(Reader reader) throws IOException { + char[] buffer = new char[ApplicationConstants.WEBSOCKET_BUFFER_SIZE]; + int read; + while ((read = reader.read(buffer)) != -1) { + message.append(buffer, 0, read); + assert message.length() <= messageLength : "Received message " + + message.length() + "chars, expected " + messageLength; + } + return message.length() == messageLength; + } + + public Reader getReader() { + return new StringReader(message.toString()); + } + } + + private UI ui; + private transient AtmosphereResource resource; + private transient Future<String> outgoingMessage; + private transient FragmentedMessage incomingMessage; + + public AtmospherePushConnection(UI ui) { + this.ui = ui; + } + + @Override + public void push() { + assert isConnected(); + try { + push(true); + } catch (IOException e) { + // TODO Error handling + throw new RuntimeException("Push failed", e); + } + } + + /** + * Pushes pending state changes and client RPC calls to the client. + * + * @param async + * True if this push asynchronously originates from the server, + * false if it is a response to a client request. + * @throws IOException + */ + protected void push(boolean async) throws IOException { + Writer writer = new StringWriter(); + try { + new UidlWriter().write(getUI(), writer, false, false, async); + } catch (JSONException e) { + throw new IOException("Error writing UIDL", e); + } + sendMessage("for(;;);[{" + writer.toString() + "}]"); + } + + /** + * Sends the given message to the current client. + * + * @param message + * The message to send + */ + void sendMessage(String message) { + // "Broadcast" the changes to the single client only + outgoingMessage = getResource().getBroadcaster().broadcast(message, + getResource()); + } + + /** + * Reads and buffers a (possibly partial) message. If a complete message was + * received, or if the call resulted in the completion of a partially + * received message, returns a {@link Reader} yielding the complete message. + * Otherwise, returns null. + * + * @param reader + * A Reader from which to read the (partial) message + * @return A Reader yielding a complete message or null if the message is + * not yet complete. + * @throws IOException + */ + protected Reader receiveMessage(Reader reader) throws IOException { + + if (resource.transport() != TRANSPORT.WEBSOCKET) { + return reader; + } + + if (incomingMessage == null) { + // No existing partially received message + incomingMessage = new FragmentedMessage(reader); + } + + if (incomingMessage.append(reader)) { + // Message is complete + Reader completeReader = incomingMessage.getReader(); + incomingMessage = null; + return completeReader; + } else { + // Only received a partial message + return null; + } + } + + /** + * Associates this connection with the given AtmosphereResource. If there is + * a push pending, commits it. + * + * @param resource + * The AtmosphereResource representing the push channel. + * @throws IOException + */ + protected void connect(AtmosphereResource resource) throws IOException { + this.resource = resource; + } + + /** + * Returns whether this connection is currently open. + */ + @Override + public boolean isConnected() { + return resource != null + && resource.getBroadcaster().getAtmosphereResources() + .contains(resource); + } + + /** + * @return the UI associated with this connection. + */ + protected UI getUI() { + return ui; + } + + /** + * @return The AtmosphereResource associated with this connection or null if + * connection not open. + */ + protected AtmosphereResource getResource() { + return resource; + } + + @Override + public void disconnect() { + if (outgoingMessage != null) { + // Wait for the last message to be sent before closing the + // connection (assumes that futures are completed in order) + try { + outgoingMessage.get(1000, TimeUnit.MILLISECONDS); + } catch (TimeoutException e) { + getLogger() + .log(Level.INFO, + "Timeout waiting for messages to be sent to client before disconnect"); + } catch (Exception e) { + getLogger() + .log(Level.INFO, + "Error waiting for messages to be sent to client before disconnect"); + } + outgoingMessage = null; + } + + resource.resume(); + assert !resource.getBroadcaster().getAtmosphereResources() + .contains(resource); + resource = null; + } + + /** + * @since + * @return + */ + private static Logger getLogger() { + return Logger.getLogger(AtmospherePushConnection.class.getName()); + } +} diff --git a/server/src/com/vaadin/server/communication/ClientRpcWriter.java b/server/src/com/vaadin/server/communication/ClientRpcWriter.java new file mode 100644 index 0000000000..285adac7a5 --- /dev/null +++ b/server/src/com/vaadin/server/communication/ClientRpcWriter.java @@ -0,0 +1,141 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; +import java.io.Serializable; +import java.io.Writer; +import java.lang.reflect.Type; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +import org.json.JSONArray; +import org.json.JSONException; + +import com.vaadin.server.ClientConnector; +import com.vaadin.server.ClientMethodInvocation; +import com.vaadin.server.EncodeResult; +import com.vaadin.server.JsonCodec; +import com.vaadin.server.PaintException; +import com.vaadin.shared.communication.ClientRpc; +import com.vaadin.ui.UI; + +/** + * Serializes {@link ClientRpc client RPC} invocations to JSON. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class ClientRpcWriter implements Serializable { + + /** + * Writes a JSON object containing all pending client RPC invocations in the + * given UI. + * + * @param ui + * The {@link UI} whose RPC calls to write. + * @param writer + * The {@link Writer} used to write the JSON. + * @throws IOException + * If the serialization fails. + */ + public void write(UI ui, Writer writer) throws IOException { + + Collection<ClientMethodInvocation> pendingInvocations = collectPendingRpcCalls(ui + .getConnectorTracker().getDirtyVisibleConnectors()); + + JSONArray rpcCalls = new JSONArray(); + for (ClientMethodInvocation invocation : pendingInvocations) { + // add invocation to rpcCalls + try { + JSONArray invocationJson = new JSONArray(); + invocationJson.put(invocation.getConnector().getConnectorId()); + invocationJson.put(invocation.getInterfaceName()); + invocationJson.put(invocation.getMethodName()); + JSONArray paramJson = new JSONArray(); + for (int i = 0; i < invocation.getParameterTypes().length; ++i) { + Type parameterType = invocation.getParameterTypes()[i]; + Object referenceParameter = null; + // TODO Use default values for RPC parameter types + // if (!JsonCodec.isInternalType(parameterType)) { + // try { + // referenceParameter = parameterType.newInstance(); + // } catch (Exception e) { + // logger.log(Level.WARNING, + // "Error creating reference object for parameter of type " + // + parameterType.getName()); + // } + // } + EncodeResult encodeResult = JsonCodec.encode( + invocation.getParameters()[i], referenceParameter, + parameterType, ui.getConnectorTracker()); + paramJson.put(encodeResult.getEncodedValue()); + } + invocationJson.put(paramJson); + rpcCalls.put(invocationJson); + } catch (JSONException e) { + throw new PaintException( + "Failed to serialize RPC method call parameters for connector " + + invocation.getConnector().getConnectorId() + + " method " + invocation.getInterfaceName() + + "." + invocation.getMethodName() + ": " + + e.getMessage(), e); + } + } + writer.write(rpcCalls.toString()); + } + + /** + * Collects all pending RPC calls from listed {@link ClientConnector}s and + * clears their RPC queues. + * + * @param rpcPendingQueue + * list of {@link ClientConnector} of interest + * @return ordered list of pending RPC calls + */ + private Collection<ClientMethodInvocation> collectPendingRpcCalls( + Collection<ClientConnector> rpcPendingQueue) { + List<ClientMethodInvocation> pendingInvocations = new ArrayList<ClientMethodInvocation>(); + for (ClientConnector connector : rpcPendingQueue) { + List<ClientMethodInvocation> paintablePendingRpc = connector + .retrievePendingRpcCalls(); + if (null != paintablePendingRpc && !paintablePendingRpc.isEmpty()) { + List<ClientMethodInvocation> oldPendingRpc = pendingInvocations; + int totalCalls = pendingInvocations.size() + + paintablePendingRpc.size(); + pendingInvocations = new ArrayList<ClientMethodInvocation>( + totalCalls); + + // merge two ordered comparable lists + for (int destIndex = 0, oldIndex = 0, paintableIndex = 0; destIndex < totalCalls; destIndex++) { + if (paintableIndex >= paintablePendingRpc.size() + || (oldIndex < oldPendingRpc.size() && ((Comparable<ClientMethodInvocation>) oldPendingRpc + .get(oldIndex)) + .compareTo(paintablePendingRpc + .get(paintableIndex)) <= 0)) { + pendingInvocations.add(oldPendingRpc.get(oldIndex++)); + } else { + pendingInvocations.add(paintablePendingRpc + .get(paintableIndex++)); + } + } + } + } + return pendingInvocations; + } +} diff --git a/server/src/com/vaadin/server/communication/ConnectorHierarchyWriter.java b/server/src/com/vaadin/server/communication/ConnectorHierarchyWriter.java new file mode 100644 index 0000000000..467bddbdce --- /dev/null +++ b/server/src/com/vaadin/server/communication/ConnectorHierarchyWriter.java @@ -0,0 +1,81 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; +import java.io.Serializable; +import java.io.Writer; +import java.util.Collection; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import com.vaadin.server.AbstractClientConnector; +import com.vaadin.server.ClientConnector; +import com.vaadin.server.LegacyCommunicationManager; +import com.vaadin.server.PaintException; +import com.vaadin.ui.UI; + +/** + * Serializes a connector hierarchy to JSON. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class ConnectorHierarchyWriter implements Serializable { + + /** + * Writes a JSON object containing the connector hierarchy (parent-child + * mappings) of the dirty connectors in the given UI. + * + * @param ui + * The {@link UI} whose hierarchy to write. + * @param writer + * The {@link Writer} used to write the JSON. + * @throws IOException + * If the serialization fails. + */ + public void write(UI ui, Writer writer) throws IOException { + + Collection<ClientConnector> dirtyVisibleConnectors = ui + .getConnectorTracker().getDirtyVisibleConnectors(); + + JSONObject hierarchyInfo = new JSONObject(); + for (ClientConnector connector : dirtyVisibleConnectors) { + String connectorId = connector.getConnectorId(); + JSONArray children = new JSONArray(); + + for (ClientConnector child : AbstractClientConnector + .getAllChildrenIterable(connector)) { + if (LegacyCommunicationManager + .isConnectorVisibleToClient(child)) { + children.put(child.getConnectorId()); + } + } + try { + hierarchyInfo.put(connectorId, children); + } catch (JSONException e) { + throw new PaintException( + "Failed to send hierarchy information about " + + connectorId + " to the client: " + + e.getMessage(), e); + } + } + writer.write(hierarchyInfo.toString()); + } +} diff --git a/server/src/com/vaadin/server/communication/ConnectorTypeWriter.java b/server/src/com/vaadin/server/communication/ConnectorTypeWriter.java new file mode 100644 index 0000000000..eaa1c83ff2 --- /dev/null +++ b/server/src/com/vaadin/server/communication/ConnectorTypeWriter.java @@ -0,0 +1,73 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; +import java.io.Serializable; +import java.io.Writer; +import java.util.Collection; + +import org.json.JSONException; +import org.json.JSONObject; + +import com.vaadin.server.ClientConnector; +import com.vaadin.server.PaintException; +import com.vaadin.server.PaintTarget; +import com.vaadin.ui.UI; + +/** + * Serializes connector type mappings to JSON. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class ConnectorTypeWriter implements Serializable { + + /** + * Writes a JSON object containing connector-ID-to-type-ID mappings for each + * dirty Connector in the given UI. + * + * @param ui + * The {@link UI} containing dirty connectors + * @param writer + * The {@link Writer} used to write the JSON. + * @param target + * The paint target containing the connector type IDs. + * @throws IOException + * If the serialization fails. + */ + public void write(UI ui, Writer writer, PaintTarget target) + throws IOException { + + Collection<ClientConnector> dirtyVisibleConnectors = ui + .getConnectorTracker().getDirtyVisibleConnectors(); + + JSONObject connectorTypes = new JSONObject(); + for (ClientConnector connector : dirtyVisibleConnectors) { + String connectorType = target.getTag(connector); + try { + connectorTypes.put(connector.getConnectorId(), connectorType); + } catch (JSONException e) { + throw new PaintException( + "Failed to send connector type for connector " + + connector.getConnectorId() + ": " + + e.getMessage(), e); + } + } + writer.write(connectorTypes.toString()); + } +} diff --git a/server/src/com/vaadin/server/communication/FileUploadHandler.java b/server/src/com/vaadin/server/communication/FileUploadHandler.java new file mode 100644 index 0000000000..e875a4e861 --- /dev/null +++ b/server/src/com/vaadin/server/communication/FileUploadHandler.java @@ -0,0 +1,645 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.BufferedWriter; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +import com.vaadin.server.ClientConnector; +import com.vaadin.server.NoInputStreamException; +import com.vaadin.server.NoOutputStreamException; +import com.vaadin.server.RequestHandler; +import com.vaadin.server.ServletPortletHelper; +import com.vaadin.server.StreamVariable; +import com.vaadin.server.StreamVariable.StreamingEndEvent; +import com.vaadin.server.StreamVariable.StreamingErrorEvent; +import com.vaadin.server.UploadException; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinResponse; +import com.vaadin.server.VaadinSession; +import com.vaadin.ui.Component; +import com.vaadin.ui.UI; + +/** + * Handles a file upload request submitted via an Upload component. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class FileUploadHandler implements RequestHandler { + + /** + * Stream that extracts content from another stream until the boundary + * string is encountered. + * + * Public only for unit tests, should be considered private for all other + * purposes. + */ + public static class SimpleMultiPartInputStream extends InputStream { + + /** + * Counter of how many characters have been matched to boundary string + * from the stream + */ + int matchedCount = -1; + + /** + * Used as pointer when returning bytes after partly matched boundary + * string. + */ + int curBoundaryIndex = 0; + /** + * The byte found after a "promising start for boundary" + */ + private int bufferedByte = -1; + private boolean atTheEnd = false; + + private final char[] boundary; + + private final InputStream realInputStream; + + public SimpleMultiPartInputStream(InputStream realInputStream, + String boundaryString) { + boundary = (CRLF + DASHDASH + boundaryString).toCharArray(); + this.realInputStream = realInputStream; + } + + @Override + public int read() throws IOException { + if (atTheEnd) { + // End boundary reached, nothing more to read + return -1; + } else if (bufferedByte >= 0) { + /* Purge partially matched boundary if there was such */ + return getBuffered(); + } else if (matchedCount != -1) { + /* + * Special case where last "failed" matching ended with first + * character from boundary string + */ + return matchForBoundary(); + } else { + int fromActualStream = realInputStream.read(); + if (fromActualStream == -1) { + // unexpected end of stream + throw new IOException( + "The multipart stream ended unexpectedly"); + } + if (boundary[0] == fromActualStream) { + /* + * If matches the first character in boundary string, start + * checking if the boundary is fetched. + */ + return matchForBoundary(); + } + return fromActualStream; + } + } + + /** + * Reads the input to expect a boundary string. Expects that the first + * character has already been matched. + * + * @return -1 if the boundary was matched, else returns the first byte + * from boundary + * @throws IOException + */ + private int matchForBoundary() throws IOException { + matchedCount = 0; + /* + * Going to "buffered mode". Read until full boundary match or a + * different character. + */ + while (true) { + matchedCount++; + if (matchedCount == boundary.length) { + /* + * The whole boundary matched so we have reached the end of + * file + */ + atTheEnd = true; + return -1; + } + int fromActualStream = realInputStream.read(); + if (fromActualStream != boundary[matchedCount]) { + /* + * Did not find full boundary, cache the mismatching byte + * and start returning the partially matched boundary. + */ + bufferedByte = fromActualStream; + return getBuffered(); + } + } + } + + /** + * Returns the partly matched boundary string and the byte following + * that. + * + * @return + * @throws IOException + */ + private int getBuffered() throws IOException { + int b; + if (matchedCount == 0) { + // The boundary has been returned, return the buffered byte. + b = bufferedByte; + bufferedByte = -1; + matchedCount = -1; + } else { + b = boundary[curBoundaryIndex++]; + if (curBoundaryIndex == matchedCount) { + // The full boundary has been returned, remaining is the + // char that did not match the boundary. + + curBoundaryIndex = 0; + if (bufferedByte != boundary[0]) { + /* + * next call for getBuffered will return the + * bufferedByte that came after the partial boundary + * match + */ + matchedCount = 0; + } else { + /* + * Special case where buffered byte again matches the + * boundaryString. This could be the start of the real + * end boundary. + */ + matchedCount = 0; + bufferedByte = -1; + } + } + } + if (b == -1) { + throw new IOException("The multipart stream ended unexpectedly"); + } + return b; + } + } + + private static class UploadInterruptedException extends Exception { + public UploadInterruptedException() { + super("Upload interrupted by other thread"); + } + } + + private static final int LF = "\n".getBytes()[0]; + + private static final String CRLF = "\r\n"; + + private static final String UTF8 = "UTF-8"; + + private static final String DASHDASH = "--"; + + /* Same as in apache commons file upload library that was previously used. */ + private static final int MAX_UPLOAD_BUFFER_SIZE = 4 * 1024; + + @Override + public boolean handleRequest(VaadinSession session, VaadinRequest request, + VaadinResponse response) throws IOException { + if (!ServletPortletHelper.isFileUploadRequest(request)) { + return false; + } + + /* + * URI pattern: APP/UPLOAD/[UIID]/[PID]/[NAME]/[SECKEY] See + * #createReceiverUrl + */ + + String pathInfo = request.getPathInfo(); + // strip away part until the data we are interested starts + int startOfData = pathInfo + .indexOf(ServletPortletHelper.UPLOAD_URL_PREFIX) + + ServletPortletHelper.UPLOAD_URL_PREFIX.length(); + String uppUri = pathInfo.substring(startOfData); + String[] parts = uppUri.split("/", 4); // 0= UIid, 1 = cid, 2= name, 3 + // = sec key + String uiId = parts[0]; + String connectorId = parts[1]; + String variableName = parts[2]; + + // These are retrieved while session is locked + ClientConnector source; + StreamVariable streamVariable; + + session.lock(); + try { + UI uI = session.getUIById(Integer.parseInt(uiId)); + UI.setCurrent(uI); + + streamVariable = uI.getConnectorTracker().getStreamVariable( + connectorId, variableName); + String secKey = uI.getConnectorTracker().getSeckey(streamVariable); + if (!secKey.equals(parts[3])) { + // TODO Should rethink error handling + return true; + } + + source = session.getCommunicationManager().getConnector(uI, + connectorId); + } finally { + session.unlock(); + } + + String contentType = request.getContentType(); + if (contentType.contains("boundary")) { + // Multipart requests contain boundary string + doHandleSimpleMultipartFileUpload(session, request, response, + streamVariable, variableName, source, + contentType.split("boundary=")[1]); + } else { + // if boundary string does not exist, the posted file is from + // XHR2.post(File) + doHandleXhrFilePost(session, request, response, streamVariable, + variableName, source, request.getContentLength()); + } + return true; + } + + private static String readLine(InputStream stream) throws IOException { + ByteArrayOutputStream bout = new ByteArrayOutputStream(); + int readByte = stream.read(); + while (readByte != LF) { + bout.write(readByte); + readByte = stream.read(); + } + byte[] bytes = bout.toByteArray(); + return new String(bytes, 0, bytes.length - 1, UTF8); + } + + /** + * Method used to stream content from a multipart request (either from + * servlet or portlet request) to given StreamVariable. + * <p> + * This method takes care of locking the session as needed and does not + * assume the caller has locked the session. This allows the session to be + * locked only when needed and not when handling the upload data. + * </p> + * + * @param session + * The session containing the stream variable + * @param request + * The upload request + * @param response + * The upload response + * @param streamVariable + * The destination stream variable + * @param variableName + * The name of the destination stream variable + * @param owner + * The owner of the stream variable + * @param boundary + * The mime boundary used in the upload request + * @throws IOException + * If there is a problem reading the request or writing the + * response + */ + protected void doHandleSimpleMultipartFileUpload(VaadinSession session, + VaadinRequest request, VaadinResponse response, + StreamVariable streamVariable, String variableName, + ClientConnector owner, String boundary) throws IOException { + // multipart parsing, supports only one file for request, but that is + // fine for our current terminal + + final InputStream inputStream = request.getInputStream(); + + int contentLength = request.getContentLength(); + + boolean atStart = false; + boolean firstFileFieldFound = false; + + String rawfilename = "unknown"; + String rawMimeType = "application/octet-stream"; + + /* + * Read the stream until the actual file starts (empty line). Read + * filename and content type from multipart headers. + */ + while (!atStart) { + String readLine = readLine(inputStream); + contentLength -= (readLine.getBytes(UTF8).length + CRLF.length()); + if (readLine.startsWith("Content-Disposition:") + && readLine.indexOf("filename=") > 0) { + rawfilename = readLine.replaceAll(".*filename=", ""); + char quote = rawfilename.charAt(0); + rawfilename = rawfilename.substring(1); + rawfilename = rawfilename.substring(0, + rawfilename.indexOf(quote)); + firstFileFieldFound = true; + } else if (firstFileFieldFound && readLine.equals("")) { + atStart = true; + } else if (readLine.startsWith("Content-Type")) { + rawMimeType = readLine.split(": ")[1]; + } + } + + contentLength -= (boundary.length() + CRLF.length() + 2 + * DASHDASH.length() + CRLF.length()); + + /* + * Reads bytes from the underlying stream. Compares the read bytes to + * the boundary string and returns -1 if met. + * + * The matching happens so that if the read byte equals to the first + * char of boundary string, the stream goes to "buffering mode". In + * buffering mode bytes are read until the character does not match the + * corresponding from boundary string or the full boundary string is + * found. + * + * Note, if this is someday needed elsewhere, don't shoot yourself to + * foot and split to a top level helper class. + */ + InputStream simpleMultiPartReader = new SimpleMultiPartInputStream( + inputStream, boundary); + + /* + * Should report only the filename even if the browser sends the path + */ + final String filename = removePath(rawfilename); + final String mimeType = rawMimeType; + + try { + handleFileUploadValidationAndData(session, simpleMultiPartReader, + streamVariable, filename, mimeType, contentLength, owner, + variableName); + } catch (UploadException e) { + session.getCommunicationManager().handleConnectorRelatedException( + owner, e); + } + sendUploadResponse(request, response); + + } + + private void handleFileUploadValidationAndData(VaadinSession session, + InputStream inputStream, StreamVariable streamVariable, + String filename, String mimeType, int contentLength, + ClientConnector connector, String variableName) + throws UploadException { + session.lock(); + try { + if (connector == null) { + throw new UploadException( + "File upload ignored because the connector for the stream variable was not found"); + } + if (!connector.isConnectorEnabled()) { + throw new UploadException("Warning: file upload ignored for " + + connector.getConnectorId() + + " because the component was disabled"); + } + if ((connector instanceof Component) + && ((Component) connector).isReadOnly()) { + // Only checked for legacy reasons + throw new UploadException( + "File upload ignored because the component is read-only"); + } + } finally { + session.unlock(); + } + try { + boolean forgetVariable = streamToReceiver(session, inputStream, + streamVariable, filename, mimeType, contentLength); + if (forgetVariable) { + cleanStreamVariable(session, connector, variableName); + } + } catch (Exception e) { + session.lock(); + try { + session.getCommunicationManager() + .handleConnectorRelatedException(connector, e); + } finally { + session.unlock(); + } + } + } + + /** + * Used to stream plain file post (aka XHR2.post(File)) + * <p> + * This method takes care of locking the session as needed and does not + * assume the caller has locked the session. This allows the session to be + * locked only when needed and not when handling the upload data. + * </p> + * + * @param session + * The session containing the stream variable + * @param request + * The upload request + * @param response + * The upload response + * @param streamVariable + * The destination stream variable + * @param variableName + * The name of the destination stream variable + * @param owner + * The owner of the stream variable + * @param contentLength + * The length of the request content + * @throws IOException + * If there is a problem reading the request or writing the + * response + */ + protected void doHandleXhrFilePost(VaadinSession session, + VaadinRequest request, VaadinResponse response, + StreamVariable streamVariable, String variableName, + ClientConnector owner, int contentLength) throws IOException { + + // These are unknown in filexhr ATM, maybe add to Accept header that + // is accessible in portlets + final String filename = "unknown"; + final String mimeType = filename; + final InputStream stream = request.getInputStream(); + + try { + handleFileUploadValidationAndData(session, stream, streamVariable, + filename, mimeType, contentLength, owner, variableName); + } catch (UploadException e) { + session.getCommunicationManager().handleConnectorRelatedException( + owner, e); + } + sendUploadResponse(request, response); + } + + /** + * @param in + * @param streamVariable + * @param filename + * @param type + * @param contentLength + * @return true if the streamvariable has informed that the terminal can + * forget this variable + * @throws UploadException + */ + protected final boolean streamToReceiver(VaadinSession session, + final InputStream in, StreamVariable streamVariable, + String filename, String type, int contentLength) + throws UploadException { + if (streamVariable == null) { + throw new IllegalStateException( + "StreamVariable for the post not found"); + } + + OutputStream out = null; + int totalBytes = 0; + StreamingStartEventImpl startedEvent = new StreamingStartEventImpl( + filename, type, contentLength); + try { + boolean listenProgress; + session.lock(); + try { + streamVariable.streamingStarted(startedEvent); + out = streamVariable.getOutputStream(); + listenProgress = streamVariable.listenProgress(); + } finally { + session.unlock(); + } + + // Gets the output target stream + if (out == null) { + throw new NoOutputStreamException(); + } + + if (null == in) { + // No file, for instance non-existent filename in html upload + throw new NoInputStreamException(); + } + + final byte buffer[] = new byte[MAX_UPLOAD_BUFFER_SIZE]; + int bytesReadToBuffer = 0; + while ((bytesReadToBuffer = in.read(buffer)) > 0) { + out.write(buffer, 0, bytesReadToBuffer); + totalBytes += bytesReadToBuffer; + if (listenProgress) { + // update progress if listener set and contentLength + // received + session.lock(); + try { + StreamingProgressEventImpl progressEvent = new StreamingProgressEventImpl( + filename, type, contentLength, totalBytes); + streamVariable.onProgress(progressEvent); + } finally { + session.unlock(); + } + } + if (streamVariable.isInterrupted()) { + throw new UploadInterruptedException(); + } + } + + // upload successful + out.close(); + StreamingEndEvent event = new StreamingEndEventImpl(filename, type, + totalBytes); + session.lock(); + try { + streamVariable.streamingFinished(event); + } finally { + session.unlock(); + } + + } catch (UploadInterruptedException e) { + // Download interrupted by application code + tryToCloseStream(out); + StreamingErrorEvent event = new StreamingErrorEventImpl(filename, + type, contentLength, totalBytes, e); + session.lock(); + try { + streamVariable.streamingFailed(event); + } finally { + session.unlock(); + } + // Note, we are not throwing interrupted exception forward as it is + // not a terminal level error like all other exception. + } catch (final Exception e) { + tryToCloseStream(out); + session.lock(); + try { + StreamingErrorEvent event = new StreamingErrorEventImpl( + filename, type, contentLength, totalBytes, e); + streamVariable.streamingFailed(event); + // throw exception for terminal to be handled (to be passed to + // terminalErrorHandler) + throw new UploadException(e); + } finally { + session.unlock(); + } + } + return startedEvent.isDisposed(); + } + + static void tryToCloseStream(OutputStream out) { + try { + // try to close output stream (e.g. file handle) + if (out != null) { + out.close(); + } + } catch (IOException e1) { + // NOP + } + } + + /** + * Removes any possible path information from the filename and returns the + * filename. Separators / and \\ are used. + * + * @param name + * @return + */ + private static String removePath(String filename) { + if (filename != null) { + filename = filename.replaceAll("^.*[/\\\\]", ""); + } + + return filename; + } + + /** + * TODO document + * + * @param request + * @param response + * @throws IOException + */ + protected void sendUploadResponse(VaadinRequest request, + VaadinResponse response) throws IOException { + response.setContentType("text/html"); + final OutputStream out = response.getOutputStream(); + final PrintWriter outWriter = new PrintWriter(new BufferedWriter( + new OutputStreamWriter(out, "UTF-8"))); + outWriter.print("<html><body>download handled</body></html>"); + outWriter.flush(); + out.close(); + } + + private void cleanStreamVariable(VaadinSession session, + final ClientConnector owner, final String variableName) { + session.access(new Runnable() { + @Override + public void run() { + owner.getUI() + .getConnectorTracker() + .cleanStreamVariable(owner.getConnectorId(), + variableName); + } + }); + } +} diff --git a/server/src/com/vaadin/server/communication/HeartbeatHandler.java b/server/src/com/vaadin/server/communication/HeartbeatHandler.java new file mode 100644 index 0000000000..16c21224ab --- /dev/null +++ b/server/src/com/vaadin/server/communication/HeartbeatHandler.java @@ -0,0 +1,90 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; + +import javax.servlet.http.HttpServletResponse; + +import com.vaadin.server.ServletPortletHelper; +import com.vaadin.server.SessionExpiredHandler; +import com.vaadin.server.SynchronizedRequestHandler; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinResponse; +import com.vaadin.server.VaadinSession; +import com.vaadin.shared.ui.ui.UIConstants; +import com.vaadin.ui.UI; + +/** + * Handles heartbeat requests. Heartbeat requests are periodically sent by the + * client-side to inform the server that the UI sending the heartbeat is still + * alive (the browser window is open, the connection is up) even when there are + * no UIDL requests for a prolonged period of time. UIs that do not receive + * either heartbeat or UIDL requests are eventually removed from the session and + * garbage collected. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class HeartbeatHandler extends SynchronizedRequestHandler implements + SessionExpiredHandler { + + /** + * Handles a heartbeat request for the given session. Reads the GET + * parameter named {@link UIConstants#UI_ID_PARAMETER} to identify the UI. + * If the UI is found in the session, sets it + * {@link UI#getLastHeartbeatTimestamp() heartbeat timestamp} to the current + * time. Otherwise, writes a HTTP Not Found error to the response. + */ + @Override + public boolean synchronizedHandleRequest(VaadinSession session, + VaadinRequest request, VaadinResponse response) throws IOException { + if (!ServletPortletHelper.isHeartbeatRequest(request)) { + return false; + } + + UI ui = session.getService().findUI(request); + if (ui != null) { + ui.setLastHeartbeatTimestamp(System.currentTimeMillis()); + // Ensure that the browser does not cache heartbeat responses. + // iOS 6 Safari requires this (#10370) + response.setHeader("Cache-Control", "no-cache"); + } else { + response.sendError(HttpServletResponse.SC_NOT_FOUND, "UI not found"); + } + + return true; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.server.SessionExpiredHandler#handleSessionExpired(com.vaadin + * .server.VaadinRequest, com.vaadin.server.VaadinResponse) + */ + @Override + public boolean handleSessionExpired(VaadinRequest request, + VaadinResponse response) throws IOException { + if (!ServletPortletHelper.isHeartbeatRequest(request)) { + return false; + } + + response.sendError(HttpServletResponse.SC_GONE, "Session expired"); + return true; + } +} diff --git a/server/src/com/vaadin/server/communication/LegacyUidlWriter.java b/server/src/com/vaadin/server/communication/LegacyUidlWriter.java new file mode 100644 index 0000000000..ad99a2d8b5 --- /dev/null +++ b/server/src/com/vaadin/server/communication/LegacyUidlWriter.java @@ -0,0 +1,118 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; +import java.io.Serializable; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.logging.Logger; + +import com.vaadin.server.ClientConnector; +import com.vaadin.server.LegacyPaint; +import com.vaadin.server.PaintTarget; +import com.vaadin.ui.Component; +import com.vaadin.ui.LegacyComponent; +import com.vaadin.ui.UI; + +/** + * Serializes legacy UIDL changes to JSON. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class LegacyUidlWriter implements Serializable { + + /** + * Writes a JSON array containing the changes of all dirty + * {@link LegacyComponent}s in the given UI. + * + * @param ui + * The {@link UI} whose legacy changes to write + * @param writer + * The {@link Writer} to write the JSON with + * @param target + * The {@link PaintTarget} to use + * @throws IOException + * If the serialization fails. + */ + public void write(UI ui, Writer writer, PaintTarget target) + throws IOException { + + Collection<ClientConnector> dirtyVisibleConnectors = ui + .getConnectorTracker().getDirtyVisibleConnectors(); + + List<Component> legacyComponents = new ArrayList<Component>(); + for (ClientConnector connector : dirtyVisibleConnectors) { + // All Components that want to use paintContent must implement + // LegacyComponent + if (connector instanceof LegacyComponent) { + legacyComponents.add((Component) connector); + } + } + sortByHierarchy(legacyComponents); + + writer.write("["); + for (Component c : legacyComponents) { + getLogger().fine( + "Painting LegacyComponent " + c.getClass().getName() + "@" + + Integer.toHexString(c.hashCode())); + target.startTag("change"); + final String pid = c.getConnectorId(); + target.addAttribute("pid", pid); + LegacyPaint.paint(c, target); + target.endTag("change"); + } + writer.write("]"); + } + + private void sortByHierarchy(List<Component> paintables) { + // Vaadin 6 requires parents to be painted before children as component + // containers rely on that their updateFromUIDL method has been called + // before children start calling e.g. updateCaption + Collections.sort(paintables, new Comparator<Component>() { + @Override + public int compare(Component c1, Component c2) { + int depth1 = 0; + while (c1.getParent() != null) { + depth1++; + c1 = c1.getParent(); + } + int depth2 = 0; + while (c2.getParent() != null) { + depth2++; + c2 = c2.getParent(); + } + if (depth1 < depth2) { + return -1; + } + if (depth1 > depth2) { + return 1; + } + return 0; + } + }); + } + + private static final Logger getLogger() { + return Logger.getLogger(LegacyUidlWriter.class.getName()); + } +} diff --git a/server/src/com/vaadin/server/communication/LocaleWriter.java b/server/src/com/vaadin/server/communication/LocaleWriter.java new file mode 100644 index 0000000000..c05649da19 --- /dev/null +++ b/server/src/com/vaadin/server/communication/LocaleWriter.java @@ -0,0 +1,204 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; +import java.io.Serializable; +import java.io.Writer; +import java.text.DateFormat; +import java.text.DateFormatSymbols; +import java.text.SimpleDateFormat; +import java.util.Calendar; +import java.util.GregorianCalendar; +import java.util.List; +import java.util.Locale; +import java.util.logging.Logger; + +/** + * Serializes locale information to JSON. + * + * @author Vaadin Ltd + * @since 7.1 + * @deprecated See <a href="http://dev.vaadin.com/ticket/11378">ticket + * #11378</a>. + */ +@Deprecated +public class LocaleWriter implements Serializable { + + /** + * Writes a JSON object containing localized strings of the given locales. + * + * @param locales + * The list of {@link Locale}s to write. + * @param writer + * The {@link Writer} used to write the JSON. + * @throws IOException + * If the serialization fails. + * + */ + public void write(List<String> locales, Writer writer) throws IOException { + + // Send locale informations to client + writer.write("["); + // TODO locales are currently sent on each request; this will be fixed + // by implementing #11378. + for (int pendingLocalesIndex = 0; pendingLocalesIndex < locales.size(); pendingLocalesIndex++) { + + final Locale l = generateLocale(locales.get(pendingLocalesIndex)); + // Locale name + writer.write("{\"name\":\"" + l.toString() + "\","); + + /* + * Month names (both short and full) + */ + final DateFormatSymbols dfs = new DateFormatSymbols(l); + final String[] short_months = dfs.getShortMonths(); + final String[] months = dfs.getMonths(); + writer.write("\"smn\":[\"" + + // ShortMonthNames + short_months[0] + "\",\"" + short_months[1] + "\",\"" + + short_months[2] + "\",\"" + short_months[3] + "\",\"" + + short_months[4] + "\",\"" + short_months[5] + "\",\"" + + short_months[6] + "\",\"" + short_months[7] + "\",\"" + + short_months[8] + "\",\"" + short_months[9] + "\",\"" + + short_months[10] + "\",\"" + short_months[11] + "\"" + + "],"); + writer.write("\"mn\":[\"" + + // MonthNames + months[0] + "\",\"" + months[1] + "\",\"" + months[2] + + "\",\"" + months[3] + "\",\"" + months[4] + "\",\"" + + months[5] + "\",\"" + months[6] + "\",\"" + months[7] + + "\",\"" + months[8] + "\",\"" + months[9] + "\",\"" + + months[10] + "\",\"" + months[11] + "\"" + "],"); + + /* + * Weekday names (both short and full) + */ + final String[] short_days = dfs.getShortWeekdays(); + final String[] days = dfs.getWeekdays(); + writer.write("\"sdn\":[\"" + + // ShortDayNames + short_days[1] + "\",\"" + short_days[2] + "\",\"" + + short_days[3] + "\",\"" + short_days[4] + "\",\"" + + short_days[5] + "\",\"" + short_days[6] + "\",\"" + + short_days[7] + "\"" + "],"); + writer.write("\"dn\":[\"" + + // DayNames + days[1] + "\",\"" + days[2] + "\",\"" + days[3] + "\",\"" + + days[4] + "\",\"" + days[5] + "\",\"" + days[6] + "\",\"" + + days[7] + "\"" + "],"); + + /* + * First day of week (0 = sunday, 1 = monday) + */ + final Calendar cal = new GregorianCalendar(l); + writer.write("\"fdow\":" + (cal.getFirstDayOfWeek() - 1) + ","); + + /* + * Date formatting (MM/DD/YYYY etc.) + */ + + DateFormat dateFormat = DateFormat.getDateTimeInstance( + DateFormat.SHORT, DateFormat.SHORT, l); + if (!(dateFormat instanceof SimpleDateFormat)) { + getLogger().warning( + "Unable to get default date pattern for locale " + + l.toString()); + dateFormat = new SimpleDateFormat(); + } + final String df = ((SimpleDateFormat) dateFormat).toPattern(); + + int timeStart = df.indexOf("H"); + if (timeStart < 0) { + timeStart = df.indexOf("h"); + } + final int ampm_first = df.indexOf("a"); + // E.g. in Korean locale AM/PM is before h:mm + // TODO should take that into consideration on client-side as well, + // now always h:mm a + if (ampm_first > 0 && ampm_first < timeStart) { + timeStart = ampm_first; + } + // Hebrew locale has time before the date + final boolean timeFirst = timeStart == 0; + String dateformat; + if (timeFirst) { + int dateStart = df.indexOf(' '); + if (ampm_first > dateStart) { + dateStart = df.indexOf(' ', ampm_first); + } + dateformat = df.substring(dateStart + 1); + } else { + dateformat = df.substring(0, timeStart - 1); + } + + writer.write("\"df\":\"" + dateformat.trim() + "\","); + + /* + * Time formatting (24 or 12 hour clock and AM/PM suffixes) + */ + final String timeformat = df.substring(timeStart, df.length()); + /* + * Doesn't return second or milliseconds. + * + * We use timeformat to determine 12/24-hour clock + */ + final boolean twelve_hour_clock = timeformat.indexOf("a") > -1; + // TODO there are other possibilities as well, like 'h' in french + // (ignore them, too complicated) + final String hour_min_delimiter = timeformat.indexOf(".") > -1 ? "." + : ":"; + // outWriter.print("\"tf\":\"" + timeformat + "\","); + writer.write("\"thc\":" + twelve_hour_clock + ","); + writer.write("\"hmd\":\"" + hour_min_delimiter + "\""); + if (twelve_hour_clock) { + final String[] ampm = dfs.getAmPmStrings(); + writer.write(",\"ampm\":[\"" + ampm[0] + "\",\"" + ampm[1] + + "\"]"); + } + writer.write("}"); + if (pendingLocalesIndex < locales.size() - 1) { + writer.write(","); + } + } + writer.write("]"); // Close locales + } + + /** + * Constructs a {@link Locale} instance to be sent to the client based on a + * short locale description string. + * + * @see #requireLocale(String) + * + * @param value + * @return + */ + private Locale generateLocale(String value) { + final String[] temp = value.split("_"); + if (temp.length == 1) { + return new Locale(temp[0]); + } else if (temp.length == 2) { + return new Locale(temp[0], temp[1]); + } else { + return new Locale(temp[0], temp[1], temp[2]); + } + } + + private static final Logger getLogger() { + return Logger.getLogger(LocaleWriter.class.getName()); + } +} diff --git a/server/src/com/vaadin/server/communication/MetadataWriter.java b/server/src/com/vaadin/server/communication/MetadataWriter.java new file mode 100644 index 0000000000..1a3f0e946a --- /dev/null +++ b/server/src/com/vaadin/server/communication/MetadataWriter.java @@ -0,0 +1,148 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Serializable; +import java.io.Writer; +import java.util.List; + +import com.vaadin.server.ClientConnector; +import com.vaadin.server.ComponentSizeValidator; +import com.vaadin.server.ComponentSizeValidator.InvalidLayout; +import com.vaadin.server.SystemMessages; +import com.vaadin.ui.UI; +import com.vaadin.ui.Window; + +/** + * Serializes miscellaneous metadata to JSON. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class MetadataWriter implements Serializable { + + private int timeoutInterval = -1; + + /** + * Writes a JSON object containing metadata related to the given UI. + * + * @param ui + * The UI whose metadata to write. + * @param writer + * The writer used. + * @param repaintAll + * Whether the client should repaint everything. + * @param analyzeLayouts + * Whether detected layout problems should be reported in client + * and server console. + * @param async + * True if this message is sent by the server asynchronously, + * false if it is a response to a client message. + * @param hilightedConnector + * The connector that should be highlighted on the client or null + * if none. + * @param messages + * a {@link SystemMessages} containing client-side error + * messages. + * @throws IOException + * If the serialization fails. + * + */ + public void write(UI ui, Writer writer, boolean repaintAll, + boolean analyzeLayouts, boolean async, + ClientConnector hilightedConnector, SystemMessages messages) + throws IOException { + + List<InvalidLayout> invalidComponentRelativeSizes = null; + + if (analyzeLayouts) { + invalidComponentRelativeSizes = ComponentSizeValidator + .validateComponentRelativeSizes(ui.getContent(), null, null); + + // Also check any existing subwindows + if (ui.getWindows() != null) { + for (Window subWindow : ui.getWindows()) { + invalidComponentRelativeSizes = ComponentSizeValidator + .validateComponentRelativeSizes( + subWindow.getContent(), + invalidComponentRelativeSizes, null); + } + } + } + + writer.write("{"); + + boolean metaOpen = false; + if (repaintAll) { + metaOpen = true; + writer.write("\"repaintAll\":true"); + if (analyzeLayouts) { + writer.write(", \"invalidLayouts\":"); + writer.write("["); + if (invalidComponentRelativeSizes != null) { + boolean first = true; + for (InvalidLayout invalidLayout : invalidComponentRelativeSizes) { + if (!first) { + writer.write(","); + } else { + first = false; + } + invalidLayout.reportErrors(new PrintWriter(writer), + System.err); + } + } + writer.write("]"); + } + if (hilightedConnector != null) { + writer.write(", \"hl\":\""); + writer.write(hilightedConnector.getConnectorId()); + writer.write("\""); + } + } + + if (async) { + if (metaOpen) { + writer.write(", "); + } + writer.write("\"async\":true"); + } + + // meta instruction for client to enable auto-forward to + // sessionExpiredURL after timer expires. + if (messages != null && messages.getSessionExpiredMessage() == null + && messages.getSessionExpiredCaption() == null + && messages.isSessionExpiredNotificationEnabled()) { + int newTimeoutInterval = ui.getSession().getSession() + .getMaxInactiveInterval(); + if (repaintAll || (timeoutInterval != newTimeoutInterval)) { + String escapedURL = messages.getSessionExpiredURL() == null ? "" + : messages.getSessionExpiredURL().replace("/", "\\/"); + if (metaOpen) { + writer.write(","); + } + writer.write("\"timedRedirect\":{\"interval\":" + + (newTimeoutInterval + 15) + ",\"url\":\"" + + escapedURL + "\"}"); + metaOpen = true; + } + timeoutInterval = newTimeoutInterval; + } + writer.write("}"); + } +} diff --git a/server/src/com/vaadin/server/communication/PortletBootstrapHandler.java b/server/src/com/vaadin/server/communication/PortletBootstrapHandler.java new file mode 100644 index 0000000000..2458951ada --- /dev/null +++ b/server/src/com/vaadin/server/communication/PortletBootstrapHandler.java @@ -0,0 +1,113 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; + +import javax.portlet.MimeResponse; +import javax.portlet.PortletRequest; +import javax.portlet.PortletResponse; +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; +import javax.portlet.ResourceURL; + +import org.json.JSONException; +import org.json.JSONObject; + +import com.vaadin.server.BootstrapHandler; +import com.vaadin.server.PaintException; +import com.vaadin.server.VaadinPortlet; +import com.vaadin.server.VaadinPortletRequest; +import com.vaadin.server.VaadinPortletResponse; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinResponse; +import com.vaadin.server.VaadinService; +import com.vaadin.server.VaadinSession; +import com.vaadin.shared.ApplicationConstants; + +public class PortletBootstrapHandler extends BootstrapHandler { + @Override + public boolean handleRequest(VaadinSession session, VaadinRequest request, + VaadinResponse response) throws IOException { + PortletRequest portletRequest = ((VaadinPortletRequest) request) + .getPortletRequest(); + if (portletRequest instanceof RenderRequest) { + return super.handleRequest(session, request, response); + } else { + return false; + } + } + + @Override + protected String getServiceUrl(BootstrapContext context) { + ResourceURL portletResourceUrl = getRenderResponse(context) + .createResourceURL(); + portletResourceUrl.setResourceID(VaadinPortlet.RESOURCE_URL_ID); + return portletResourceUrl.toString(); + } + + private RenderResponse getRenderResponse(BootstrapContext context) { + PortletResponse response = ((VaadinPortletResponse) context + .getResponse()).getPortletResponse(); + + RenderResponse renderResponse = (RenderResponse) response; + return renderResponse; + } + + @Override + protected void appendMainScriptTagContents(BootstrapContext context, + StringBuilder builder) throws JSONException, IOException { + // fixed base theme to use - all portal pages with Vaadin + // applications will load this exactly once + String portalTheme = ((VaadinPortletRequest) context.getRequest()) + .getPortalProperty(VaadinPortlet.PORTAL_PARAMETER_VAADIN_THEME); + if (portalTheme != null && !portalTheme.equals(context.getThemeName())) { + String portalThemeUri = getThemeUri(context, portalTheme); + // XSS safe - originates from portal properties + builder.append("vaadin.loadTheme('" + portalThemeUri + "');"); + } + + super.appendMainScriptTagContents(context, builder); + } + + @Override + protected String getMainDivStyle(BootstrapContext context) { + VaadinService vaadinService = context.getRequest().getService(); + return vaadinService.getDeploymentConfiguration() + .getApplicationOrSystemProperty( + VaadinPortlet.PORTLET_PARAMETER_STYLE, null); + } + + @Override + protected JSONObject getApplicationParameters(BootstrapContext context) + throws JSONException, PaintException { + JSONObject parameters = super.getApplicationParameters(context); + VaadinPortletResponse response = (VaadinPortletResponse) context + .getResponse(); + MimeResponse portletResponse = (MimeResponse) response + .getPortletResponse(); + ResourceURL resourceURL = portletResponse.createResourceURL(); + resourceURL.setResourceID("v-browserDetails"); + parameters.put("browserDetailsUrl", resourceURL.toString()); + + // Always send path info as a query parameter + parameters + .put(ApplicationConstants.SERVICE_URL_PATH_AS_PARAMETER, true); + + return parameters; + } +}
\ No newline at end of file diff --git a/server/src/com/vaadin/server/communication/PortletDummyRequestHandler.java b/server/src/com/vaadin/server/communication/PortletDummyRequestHandler.java new file mode 100644 index 0000000000..8383cf607b --- /dev/null +++ b/server/src/com/vaadin/server/communication/PortletDummyRequestHandler.java @@ -0,0 +1,82 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.communication; + +import java.io.BufferedWriter; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; + +import javax.portlet.PortletResponse; +import javax.portlet.ResourceRequest; +import javax.portlet.ResourceResponse; + +import com.vaadin.server.RequestHandler; +import com.vaadin.server.VaadinPortletResponse; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinResponse; +import com.vaadin.server.VaadinSession; + +/** + * Request handler which provides a dummy HTML response to any resource request + * with the resource id DUMMY. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class PortletDummyRequestHandler implements RequestHandler { + + @Override + public boolean handleRequest(VaadinSession session, VaadinRequest request, + VaadinResponse response) throws IOException { + if (!isDummyRequest(request)) { + return false; + } + + /* + * This dummy page is used by action responses to redirect to, in order + * to prevent the boot strap code from being rendered into strange + * places such as iframes. + */ + PortletResponse portletResponse = ((VaadinPortletResponse) response) + .getPortletResponse(); + if (portletResponse instanceof ResourceResponse) { + ((ResourceResponse) portletResponse).setContentType("text/html"); + } + + final OutputStream out = ((ResourceResponse) response) + .getPortletOutputStream(); + final PrintWriter outWriter = new PrintWriter(new BufferedWriter( + new OutputStreamWriter(out, "UTF-8"))); + outWriter.print("<html><body>dummy page</body></html>"); + outWriter.close(); + + return true; + } + + public static boolean isDummyRequest(VaadinRequest request) { + ResourceRequest resourceRequest = PortletUIInitHandler + .getResourceRequest(request); + if (resourceRequest == null) { + return false; + } + + return resourceRequest.getResourceID() != null + && resourceRequest.getResourceID().equals("DUMMY"); + } + +} diff --git a/server/src/com/vaadin/server/communication/PortletListenerNotifier.java b/server/src/com/vaadin/server/communication/PortletListenerNotifier.java new file mode 100644 index 0000000000..5c03a6f4dc --- /dev/null +++ b/server/src/com/vaadin/server/communication/PortletListenerNotifier.java @@ -0,0 +1,89 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; + +import javax.portlet.ActionRequest; +import javax.portlet.ActionResponse; +import javax.portlet.EventRequest; +import javax.portlet.EventResponse; +import javax.portlet.PortletRequest; +import javax.portlet.PortletResponse; +import javax.portlet.RenderRequest; +import javax.portlet.RenderResponse; +import javax.portlet.ResourceRequest; +import javax.portlet.ResourceResponse; + +import com.vaadin.server.ServletPortletHelper; +import com.vaadin.server.SynchronizedRequestHandler; +import com.vaadin.server.VaadinPortletRequest; +import com.vaadin.server.VaadinPortletResponse; +import com.vaadin.server.VaadinPortletSession; +import com.vaadin.server.VaadinPortletSession.PortletListener; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinResponse; +import com.vaadin.server.VaadinSession; +import com.vaadin.ui.UI; + +/** + * Notifies {@link PortletListener}s of a received portlet request. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class PortletListenerNotifier extends SynchronizedRequestHandler { + + /** + * Fires portlet request events to any {@link PortletListener}s registered + * to the given session using + * {@link VaadinPortletSession#addPortletListener(PortletListener)}. The + * PortletListener method corresponding to the request type is invoked. + */ + @Override + public boolean synchronizedHandleRequest(VaadinSession session, + VaadinRequest request, VaadinResponse response) throws IOException { + + VaadinPortletSession sess = (VaadinPortletSession) session; + PortletRequest req = ((VaadinPortletRequest) request) + .getPortletRequest(); + PortletResponse resp = ((VaadinPortletResponse) response) + .getPortletResponse(); + + // Finds the right UI + UI uI = null; + if (ServletPortletHelper.isUIDLRequest(request)) { + uI = session.getService().findUI(request); + } + + if (request instanceof RenderRequest) { + sess.firePortletRenderRequest(uI, (RenderRequest) req, + (RenderResponse) resp); + } else if (request instanceof ActionRequest) { + sess.firePortletActionRequest(uI, (ActionRequest) req, + (ActionResponse) resp); + } else if (request instanceof EventRequest) { + sess.firePortletEventRequest(uI, (EventRequest) req, + (EventResponse) resp); + } else if (request instanceof ResourceRequest) { + sess.firePortletResourceRequest(uI, (ResourceRequest) req, + (ResourceResponse) resp); + } + + return false; + } +} diff --git a/server/src/com/vaadin/server/communication/PortletUIInitHandler.java b/server/src/com/vaadin/server/communication/PortletUIInitHandler.java new file mode 100644 index 0000000000..d5d1e6b98d --- /dev/null +++ b/server/src/com/vaadin/server/communication/PortletUIInitHandler.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.communication; + +import javax.portlet.PortletRequest; +import javax.portlet.ResourceRequest; + +import com.vaadin.server.VaadinPortletRequest; +import com.vaadin.server.VaadinRequest; + +public class PortletUIInitHandler extends UIInitHandler { + + @Override + protected boolean isInitRequest(VaadinRequest request) { + return isUIInitRequest(request); + } + + public static boolean isUIInitRequest(VaadinRequest request) { + ResourceRequest resourceRequest = getResourceRequest(request); + if (resourceRequest == null) { + return false; + } + + return UIInitHandler.BROWSER_DETAILS_PARAMETER.equals(resourceRequest + .getResourceID()); + } + + /** + * Returns the {@link ResourceRequest} for the given request or null if none + * could be found. + * + * @param request + * The original request, must be a {@link VaadinPortletRequest} + * @return The resource request from the request parameter or null + */ + static ResourceRequest getResourceRequest(VaadinRequest request) { + if (!(request instanceof VaadinPortletRequest)) { + throw new IllegalArgumentException( + "Request must a VaadinPortletRequest"); + } + PortletRequest portletRequest = ((VaadinPortletRequest) request) + .getPortletRequest(); + if (!(portletRequest instanceof ResourceRequest)) { + return null; + } + + return (ResourceRequest) portletRequest; + + } +} diff --git a/server/src/com/vaadin/server/communication/PublishedFileHandler.java b/server/src/com/vaadin/server/communication/PublishedFileHandler.java new file mode 100644 index 0000000000..8fe0f7085f --- /dev/null +++ b/server/src/com/vaadin/server/communication/PublishedFileHandler.java @@ -0,0 +1,151 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.logging.Logger; + +import javax.servlet.http.HttpServletResponse; + +import com.vaadin.annotations.JavaScript; +import com.vaadin.annotations.StyleSheet; +import com.vaadin.server.Constants; +import com.vaadin.server.LegacyCommunicationManager; +import com.vaadin.server.RequestHandler; +import com.vaadin.server.ServletPortletHelper; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinResponse; +import com.vaadin.server.VaadinSession; +import com.vaadin.shared.ApplicationConstants; + +/** + * Serves a connector resource from the classpath if the resource has previously + * been registered by calling + * {@link LegacyCommunicationManager#registerDependency(String, Class)}. Sending + * arbitrary files from the classpath is prevented by only accepting resource + * names that have explicitly been registered. Resources can currently only be + * registered by including a {@link JavaScript} or {@link StyleSheet} annotation + * on a Connector class. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class PublishedFileHandler implements RequestHandler { + + /** + * Writes the connector resource identified by the request URI to the + * response. If a published resource corresponding to the URI path is not + * found, writes a HTTP Not Found error to the response. + */ + @Override + public boolean handleRequest(VaadinSession session, VaadinRequest request, + VaadinResponse response) throws IOException { + if (!ServletPortletHelper.isPublishedFileRequest(request)) { + return false; + } + + String pathInfo = request.getPathInfo(); + // + 2 to also remove beginning and ending slashes + String fileName = pathInfo + .substring(ApplicationConstants.PUBLISHED_FILE_PATH.length() + 2); + + final String mimetype = response.getService().getMimeType(fileName); + + // Security check: avoid accidentally serving from the UI of the + // classpath instead of relative to the context class + if (fileName.startsWith("/")) { + getLogger().warning( + "Published file request starting with / rejected: " + + fileName); + response.sendError(HttpServletResponse.SC_NOT_FOUND, fileName); + return true; + } + + // Check that the resource name has been registered + session.lock(); + Class<?> context; + try { + context = session.getCommunicationManager().getDependencies() + .get(fileName); + } finally { + session.unlock(); + } + + // Security check: don't serve resource if the name hasn't been + // registered in the map + if (context == null) { + getLogger().warning( + "Rejecting published file request for file that has not been published: " + + fileName); + response.sendError(HttpServletResponse.SC_NOT_FOUND, fileName); + return true; + } + + // Resolve file relative to the location of the context class + InputStream in = context.getResourceAsStream(fileName); + if (in == null) { + getLogger().warning( + fileName + " published by " + context.getName() + + " not found. Verify that the file " + + context.getPackage().getName().replace('.', '/') + + '/' + fileName + + " is available on the classpath."); + response.sendError(HttpServletResponse.SC_NOT_FOUND, fileName); + return true; + } + + // TODO Check and set cache headers + + OutputStream out = null; + try { + if (mimetype != null) { + response.setContentType(mimetype); + } + + out = response.getOutputStream(); + + final byte[] buffer = new byte[Constants.DEFAULT_BUFFER_SIZE]; + + int bytesRead = 0; + while ((bytesRead = in.read(buffer)) > 0) { + out.write(buffer, 0, bytesRead); + } + out.flush(); + } finally { + try { + in.close(); + } catch (Exception e) { + // Do nothing + } + if (out != null) { + try { + out.close(); + } catch (Exception e) { + // Do nothing + } + } + } + + return true; + } + + private static final Logger getLogger() { + return Logger.getLogger(PublishedFileHandler.class.getName()); + } +} diff --git a/server/src/com/vaadin/server/communication/PushConnection.java b/server/src/com/vaadin/server/communication/PushConnection.java new file mode 100644 index 0000000000..4e043f565f --- /dev/null +++ b/server/src/com/vaadin/server/communication/PushConnection.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import com.vaadin.ui.UI; + +/** + * Represents a bidirectional ("push") connection between a single UI and its + * client-side. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public interface PushConnection { + + /** + * Pushes pending state changes and client RPC calls to the client. It is + * NOT safe to invoke this method if not holding the session lock. + * <p> + * This is internal API; please use {@link UI#push()} instead. + */ + public void push(); + + /** + * Disconnects the connection. + */ + public void disconnect(); + + /** + * Returns whether this connection is currently open. + */ + public boolean isConnected(); + +}
\ No newline at end of file diff --git a/server/src/com/vaadin/server/communication/PushHandler.java b/server/src/com/vaadin/server/communication/PushHandler.java new file mode 100644 index 0000000000..e740db410d --- /dev/null +++ b/server/src/com/vaadin/server/communication/PushHandler.java @@ -0,0 +1,380 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; +import java.io.Reader; +import java.io.Writer; +import java.util.Arrays; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.atmosphere.cpr.AtmosphereHandler; +import org.atmosphere.cpr.AtmosphereRequest; +import org.atmosphere.cpr.AtmosphereResource; +import org.atmosphere.cpr.AtmosphereResource.TRANSPORT; +import org.atmosphere.cpr.AtmosphereResourceEvent; +import org.json.JSONException; + +import com.vaadin.server.LegacyCommunicationManager.InvalidUIDLSecurityKeyException; +import com.vaadin.server.ServiceException; +import com.vaadin.server.ServletPortletHelper; +import com.vaadin.server.SessionExpiredException; +import com.vaadin.server.SystemMessages; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinService; +import com.vaadin.server.VaadinServletRequest; +import com.vaadin.server.VaadinServletService; +import com.vaadin.server.VaadinSession; +import com.vaadin.server.WebBrowser; +import com.vaadin.shared.ApplicationConstants; +import com.vaadin.shared.communication.PushMode; +import com.vaadin.ui.UI; + +/** + * Establishes bidirectional ("push") communication channels + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class PushHandler implements AtmosphereHandler { + + /** + * Callback interface used internally to process an event with the + * corresponding UI properly locked. + */ + private interface PushEventCallback { + public void run(AtmosphereResource resource, UI ui) throws IOException; + } + + /** + * Callback used when we receive a request to establish a push channel for a + * UI. Associate the AtmosphereResource with the UI and leave the connection + * open by calling resource.suspend(). If there is a pending push, send it + * now. + */ + private static PushEventCallback establishCallback = new PushEventCallback() { + @Override + public void run(AtmosphereResource resource, UI ui) throws IOException { + getLogger().log(Level.FINER, + "New push connection with transport {0}", + resource.transport()); + resource.getResponse().setContentType("text/plain; charset=UTF-8"); + + VaadinSession session = ui.getSession(); + if (resource.transport() == TRANSPORT.STREAMING) { + // IE8 requires a longer padding to work properly if the + // initial message is small (#11573). Chrome does not work + // without the original padding... + WebBrowser browser = session.getBrowser(); + if (browser.isIE() && browser.getBrowserMajorVersion() == 8) { + resource.padding(LONG_PADDING); + } + } + + String requestToken = resource.getRequest().getParameter( + ApplicationConstants.CSRF_TOKEN_PARAMETER); + if (!VaadinService.isCsrfTokenValid(session, requestToken)) { + getLogger() + .log(Level.WARNING, + "Invalid CSRF token in new connection received from {0}", + resource.getRequest().getRemoteHost()); + // Refresh on client side, create connection just for + // sending a message + sendRefreshAndDisconnect(resource); + return; + } + + resource.suspend(); + + AtmospherePushConnection connection = new AtmospherePushConnection( + ui); + connection.connect(resource); + + ui.setPushConnection(connection); + } + }; + + /** + * Callback used when we receive a UIDL request through Atmosphere. If the + * push channel is bidirectional (websockets), the request was sent via the + * same channel. Otherwise, the client used a separate AJAX request. Handle + * the request and send changed UI state via the push channel (we do not + * respond to the request directly.) + */ + private static PushEventCallback receiveCallback = new PushEventCallback() { + @Override + public void run(AtmosphereResource resource, UI ui) throws IOException { + AtmosphereRequest req = resource.getRequest(); + + AtmospherePushConnection connection = getConnectionForUI(ui); + + assert connection != null : "Got push from the client " + + "even though the connection does not seem to be " + + "valid. This might happen if a HttpSession is " + + "serialized and deserialized while the push " + + "connection is kept open or if the UI has a " + + "connection of unexpected type."; + + Reader reader = connection.receiveMessage(req.getReader()); + if (reader == null) { + // The whole message was not yet received + return; + } + + // Should be set up by caller + VaadinRequest vaadinRequest = VaadinService.getCurrentRequest(); + assert vaadinRequest != null; + + try { + new ServerRpcHandler().handleRpc(ui, reader, vaadinRequest); + connection.push(false); + } catch (JSONException e) { + getLogger().log(Level.SEVERE, "Error writing JSON to response", + e); + // Refresh on client side + sendRefreshAndDisconnect(resource); + } catch (InvalidUIDLSecurityKeyException e) { + getLogger().log(Level.WARNING, + "Invalid security key received from {0}", + resource.getRequest().getRemoteHost()); + // Refresh on client side + sendRefreshAndDisconnect(resource); + } + } + }; + + /** + * Callback used when a connection is closed by the client. + */ + PushEventCallback disconnectCallback = new PushEventCallback() { + @Override + public void run(AtmosphereResource resource, UI ui) throws IOException { + PushMode pushMode = ui.getPushMode(); + AtmospherePushConnection pushConnection = getConnectionForUI(ui); + + String id = resource.uuid(); + + if (pushConnection == null) { + getLogger() + .log(Level.WARNING, + "Could not find push connection to close: {0} with transport {1}", + new Object[] { id, resource.transport() }); + } else { + if (!pushMode.isEnabled()) { + /* + * The client is expected to close the connection after push + * mode has been set to disabled, just clean up some stuff + * and be done with it + */ + getLogger().log(Level.FINEST, + "Connection closed for resource {0}", id); + } else { + /* + * Unexpected cancel, e.g. if the user closes the browser + * tab. + */ + getLogger() + .log(Level.FINE, + "Connection unexpectedly closed for resource {0} with transport {1}", + new Object[] { id, resource.transport() }); + } + ui.setPushConnection(null); + } + } + }; + + private static final String LONG_PADDING; + + static { + char[] array = new char[4096]; + Arrays.fill(array, '-'); + LONG_PADDING = String.copyValueOf(array); + + } + private VaadinServletService service; + + public PushHandler(VaadinServletService service) { + this.service = service; + } + + /** + * Find the UI for the atmosphere resource, lock it and invoke the callback. + * + * @param resource + * the atmosphere resource for the current request + * @param callback + * the push callback to call when a UI is found and locked + */ + private void callWithUi(final AtmosphereResource resource, + final PushEventCallback callback) { + AtmosphereRequest req = resource.getRequest(); + VaadinServletRequest vaadinRequest = new VaadinServletRequest(req, + service); + VaadinSession session = null; + + service.requestStart(vaadinRequest, null); + try { + try { + session = service.findVaadinSession(vaadinRequest); + } catch (ServiceException e) { + getLogger().log(Level.SEVERE, + "Could not get session. This should never happen", e); + } catch (SessionExpiredException e) { + SystemMessages msg = service.getSystemMessages( + ServletPortletHelper.findLocale(null, null, + vaadinRequest), vaadinRequest); + try { + resource.getResponse() + .getWriter() + .write(VaadinService + .createCriticalNotificationJSON( + msg.getSessionExpiredCaption(), + msg.getSessionExpiredMessage(), + null, msg.getSessionExpiredURL())); + } catch (IOException e1) { + getLogger() + .log(Level.WARNING, + "Failed to notify client about unavailable session", + e); + } + return; + } + + session.lock(); + try { + VaadinSession.setCurrent(session); + // Sets UI.currentInstance + final UI ui = service.findUI(vaadinRequest); + if (ui == null) { + // This a request through an already open push connection to + // a UI which no longer exists. + resource.getResponse() + .getWriter() + .write(UidlRequestHandler.getUINotFoundErrorJSON( + service, vaadinRequest)); + // End the connection + resource.resume(); + return; + } + + callback.run(resource, ui); + } catch (IOException e) { + getLogger().log(Level.INFO, + "An error occured while writing a push response", e); + } finally { + session.unlock(); + } + } finally { + service.requestEnd(vaadinRequest, null, session); + } + } + + @Override + public void onRequest(AtmosphereResource resource) { + AtmosphereRequest req = resource.getRequest(); + + if (req.getMethod().equalsIgnoreCase("GET")) { + callWithUi(resource, establishCallback); + } else if (req.getMethod().equalsIgnoreCase("POST")) { + callWithUi(resource, receiveCallback); + } + } + + private static AtmospherePushConnection getConnectionForUI(UI ui) { + PushConnection pushConnection = ui.getPushConnection(); + if (pushConnection instanceof AtmospherePushConnection) { + AtmospherePushConnection apc = (AtmospherePushConnection) pushConnection; + if (apc.isConnected()) { + return apc; + } + } + + return null; + } + + @Override + public void onStateChange(AtmosphereResourceEvent event) throws IOException { + AtmosphereResource resource = event.getResource(); + + String id = resource.uuid(); + if (event.isCancelled()) { + callWithUi(resource, disconnectCallback); + } else if (event.isResuming()) { + // A connection that was suspended earlier was resumed (committed to + // the client.) Should only happen if the transport is JSONP or + // long-polling. + getLogger().log(Level.FINER, "Resuming request for resource {0}", + id); + } else { + // A message was broadcast to this resource and should be sent to + // the client. We don't do any actual broadcasting, in the sense of + // sending to multiple recipients; any UIDL message is specific to a + // single client. + getLogger().log(Level.FINER, "Writing message to resource {0}", id); + + Writer writer = resource.getResponse().getWriter(); + writer.write(event.getMessage().toString()); + + switch (resource.transport()) { + case SSE: + case WEBSOCKET: + break; + case STREAMING: + writer.flush(); + break; + case JSONP: + case LONG_POLLING: + resource.resume(); + break; + default: + getLogger().log(Level.SEVERE, "Unknown transport {0}", + resource.transport()); + } + } + } + + @Override + public void destroy() { + } + + /** + * Sends a refresh message to the given atmosphere resource. Uses an + * AtmosphereResource instead of an AtmospherePushConnection even though it + * might be possible to look up the AtmospherePushConnection from the UI to + * ensure border cases work correctly, especially when there temporarily are + * two push connections which try to use the same UI. Using the + * AtmosphereResource directly guarantees the message goes to the correct + * recipient. + * + * @param resource + * The atmosphere resource to send refresh to + * + */ + private static void sendRefreshAndDisconnect(AtmosphereResource resource) + throws IOException { + AtmospherePushConnection connection = new AtmospherePushConnection(null); + connection.connect(resource); + connection.sendMessage(VaadinService.createCriticalNotificationJSON( + null, null, null, null)); + connection.disconnect(); + } + + private static final Logger getLogger() { + return Logger.getLogger(PushHandler.class.getName()); + } +} diff --git a/server/src/com/vaadin/server/communication/PushRequestHandler.java b/server/src/com/vaadin/server/communication/PushRequestHandler.java new file mode 100644 index 0000000000..8360e08af9 --- /dev/null +++ b/server/src/com/vaadin/server/communication/PushRequestHandler.java @@ -0,0 +1,134 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; + +import javax.servlet.ServletException; + +import org.atmosphere.client.TrackMessageSizeInterceptor; +import org.atmosphere.cpr.ApplicationConfig; +import org.atmosphere.cpr.AtmosphereFramework; +import org.atmosphere.cpr.AtmosphereRequest; +import org.atmosphere.cpr.AtmosphereResponse; + +import com.vaadin.server.RequestHandler; +import com.vaadin.server.ServiceException; +import com.vaadin.server.ServletPortletHelper; +import com.vaadin.server.SessionExpiredHandler; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinResponse; +import com.vaadin.server.VaadinServletRequest; +import com.vaadin.server.VaadinServletResponse; +import com.vaadin.server.VaadinServletService; +import com.vaadin.server.VaadinSession; +import com.vaadin.shared.ApplicationConstants; + +/** + * Handles requests to open a push (bidirectional) communication channel between + * the client and the server. After the initial request, communication through + * the push channel is managed by {@link PushHandler}. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class PushRequestHandler implements RequestHandler, + SessionExpiredHandler { + + private AtmosphereFramework atmosphere; + private PushHandler pushHandler; + + public PushRequestHandler(VaadinServletService service) + throws ServiceException { + + atmosphere = new AtmosphereFramework(); + + pushHandler = new PushHandler(service); + atmosphere.addAtmosphereHandler("/*", pushHandler); + atmosphere.addInitParameter(ApplicationConfig.PROPERTY_SESSION_SUPPORT, + "true"); + + final String bufferSize = String + .valueOf(ApplicationConstants.WEBSOCKET_BUFFER_SIZE); + atmosphere.addInitParameter(ApplicationConfig.WEBSOCKET_BUFFER_SIZE, + bufferSize); + atmosphere.addInitParameter(ApplicationConfig.WEBSOCKET_MAXTEXTSIZE, + bufferSize); + atmosphere.addInitParameter(ApplicationConfig.WEBSOCKET_MAXBINARYSIZE, + bufferSize); + + // Disable Atmosphere's message about commercial support + atmosphere.addInitParameter("org.atmosphere.cpr.showSupportMessage", + "false"); + + // Required to ensure the client-side knows at which points to split the + // message stream into individual messages when using certain transports + atmosphere.interceptor(new TrackMessageSizeInterceptor()); + + try { + atmosphere.init(service.getServlet().getServletConfig()); + } catch (ServletException e) { + throw new ServiceException("Could not read atmosphere settings", e); + } + } + + @Override + public boolean handleRequest(VaadinSession session, VaadinRequest request, + VaadinResponse response) throws IOException { + + if (!ServletPortletHelper.isPushRequest(request)) { + return false; + } + + if (request instanceof VaadinServletRequest) { + try { + atmosphere.doCometSupport(AtmosphereRequest + .wrap((VaadinServletRequest) request), + AtmosphereResponse + .wrap((VaadinServletResponse) response)); + } catch (ServletException e) { + // TODO PUSH decide how to handle + throw new RuntimeException(e); + } + } else { + throw new IllegalArgumentException( + "Portlets not currently supported"); + } + + return true; + } + + public void destroy() { + atmosphere.destroy(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.server.SessionExpiredHandler#handleSessionExpired(com.vaadin + * .server.VaadinRequest, com.vaadin.server.VaadinResponse) + */ + @Override + public boolean handleSessionExpired(VaadinRequest request, + VaadinResponse response) throws IOException { + // Websockets request must be handled by accepting the websocket + // connection and then sending session expired so we let + // PushRequestHandler handle it + return handleRequest(null, request, response); + } +} diff --git a/server/src/com/vaadin/server/communication/ResourceWriter.java b/server/src/com/vaadin/server/communication/ResourceWriter.java new file mode 100644 index 0000000000..080027943f --- /dev/null +++ b/server/src/com/vaadin/server/communication/ResourceWriter.java @@ -0,0 +1,113 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Serializable; +import java.io.Writer; +import java.util.Iterator; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.vaadin.server.JsonPaintTarget; +import com.vaadin.server.LegacyCommunicationManager; +import com.vaadin.ui.CustomLayout; +import com.vaadin.ui.UI; + +/** + * Serializes resources to JSON. Currently only used for {@link CustomLayout} + * templates. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class ResourceWriter implements Serializable { + + /** + * Writes a JSON object containing registered resources. + * + * @param ui + * The {@link UI} whose resources to write. + * @param writer + * The {@link Writer} to use. + * @param target + * The {@link JsonPaintTarget} containing the resources. + * @throws IOException + */ + public void write(UI ui, Writer writer, JsonPaintTarget target) + throws IOException { + + // TODO PUSH Refactor so that this is not needed + LegacyCommunicationManager manager = ui.getSession() + .getCommunicationManager(); + + // Precache custom layouts + + // TODO We should only precache the layouts that are not + // cached already (plagiate from usedPaintableTypes) + + writer.write("{"); + int resourceIndex = 0; + for (final Iterator<Object> i = target.getUsedResources().iterator(); i + .hasNext();) { + final String resource = (String) i.next(); + InputStream is = null; + try { + is = ui.getSession() + .getService() + .getThemeResourceAsStream(ui, manager.getTheme(ui), + resource); + } catch (final Exception e) { + // FIXME: Handle exception + getLogger().log(Level.FINER, + "Failed to get theme resource stream.", e); + } + if (is != null) { + + writer.write((resourceIndex++ > 0 ? ", " : "") + "\"" + + resource + "\" : "); + final StringBuffer layout = new StringBuffer(); + + try { + final InputStreamReader r = new InputStreamReader(is, + "UTF-8"); + final char[] buffer = new char[20000]; + int charsRead = 0; + while ((charsRead = r.read(buffer)) > 0) { + layout.append(buffer, 0, charsRead); + } + r.close(); + } catch (final java.io.IOException e) { + // FIXME: Handle exception + getLogger().log(Level.INFO, "Resource transfer failed", e); + } + writer.write("\"" + + JsonPaintTarget.escapeJSON(layout.toString()) + "\""); + } else { + // FIXME: Handle exception + getLogger().severe("CustomLayout not found: " + resource); + } + } + writer.write("}"); + } + + private static final Logger getLogger() { + return Logger.getLogger(ResourceWriter.class.getName()); + } +} diff --git a/server/src/com/vaadin/server/communication/ServerRpcHandler.java b/server/src/com/vaadin/server/communication/ServerRpcHandler.java new file mode 100644 index 0000000000..3cc85909ee --- /dev/null +++ b/server/src/com/vaadin/server/communication/ServerRpcHandler.java @@ -0,0 +1,469 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; +import java.io.Reader; +import java.io.Serializable; +import java.lang.reflect.Type; +import java.text.CharacterIterator; +import java.text.StringCharacterIterator; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.json.JSONArray; +import org.json.JSONException; + +import com.vaadin.server.ClientConnector; +import com.vaadin.server.JsonCodec; +import com.vaadin.server.LegacyCommunicationManager; +import com.vaadin.server.LegacyCommunicationManager.InvalidUIDLSecurityKeyException; +import com.vaadin.server.ServerRpcManager; +import com.vaadin.server.ServerRpcManager.RpcInvocationException; +import com.vaadin.server.ServerRpcMethodInvocation; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinService; +import com.vaadin.server.VariableOwner; +import com.vaadin.shared.ApplicationConstants; +import com.vaadin.shared.Connector; +import com.vaadin.shared.communication.LegacyChangeVariablesInvocation; +import com.vaadin.shared.communication.MethodInvocation; +import com.vaadin.shared.communication.ServerRpc; +import com.vaadin.shared.communication.UidlValue; +import com.vaadin.ui.Component; +import com.vaadin.ui.ConnectorTracker; +import com.vaadin.ui.UI; + +/** + * Handles a client-to-server message containing serialized {@link ServerRpc + * server RPC} invocations. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class ServerRpcHandler implements Serializable { + + /* Variable records indexes */ + public static final char VAR_BURST_SEPARATOR = '\u001d'; + + public static final char VAR_ESCAPE_CHARACTER = '\u001b'; + + private static final int MAX_BUFFER_SIZE = 64 * 1024; + + /** + * Reads JSON containing zero or more serialized RPC calls (including legacy + * variable changes) and executes the calls. + * + * @param ui + * The {@link UI} receiving the calls. Cannot be null. + * @param reader + * The {@link Reader} used to read the JSON. + * @param request + * @throws IOException + * If reading the message fails. + * @throws InvalidUIDLSecurityKeyException + * If the received security key does not match the one stored in + * the session. + * @throws JSONException + * If deserializing the JSON fails. + */ + public void handleRpc(UI ui, Reader reader, VaadinRequest request) + throws IOException, InvalidUIDLSecurityKeyException, JSONException { + ui.getSession().setLastRequestTimestamp(System.currentTimeMillis()); + + String changes = getMessage(reader); + + final String[] bursts = changes.split(String + .valueOf(VAR_BURST_SEPARATOR)); + + if (bursts.length > 2) { + throw new RuntimeException( + "Multiple variable bursts not supported in Vaadin 7"); + } else if (bursts.length <= 1) { + // The client sometimes sends empty messages, this is probably a bug + return; + } + + // Security: double cookie submission pattern unless disabled by + // property + if (!VaadinService.isCsrfTokenValid(ui.getSession(), bursts[0])) { + throw new InvalidUIDLSecurityKeyException(""); + } + handleBurst(ui, unescapeBurst(bursts[1])); + } + + /** + * Processes a message burst received from the client. + * + * A burst can contain any number of RPC calls, including legacy variable + * change calls that are processed separately. + * + * Consecutive changes to the value of the same variable are combined and + * changeVariables() is only called once for them. This preserves the Vaadin + * 6 semantics for components and add-ons that do not use Vaadin 7 RPC + * directly. + * + * @param source + * @param uI + * the UI receiving the burst + * @param burst + * the content of the burst as a String to be parsed + */ + private void handleBurst(UI uI, String burst) { + // TODO PUSH Refactor so that this is not needed + LegacyCommunicationManager manager = uI.getSession() + .getCommunicationManager(); + + try { + Set<Connector> enabledConnectors = new HashSet<Connector>(); + + List<MethodInvocation> invocations = parseInvocations( + uI.getConnectorTracker(), burst); + for (MethodInvocation invocation : invocations) { + final ClientConnector connector = manager.getConnector(uI, + invocation.getConnectorId()); + + if (connector != null && connector.isConnectorEnabled()) { + enabledConnectors.add(connector); + } + } + + for (int i = 0; i < invocations.size(); i++) { + MethodInvocation invocation = invocations.get(i); + + final ClientConnector connector = manager.getConnector(uI, + invocation.getConnectorId()); + if (connector == null) { + getLogger() + .log(Level.WARNING, + "Received RPC call for unknown connector with id {0} (tried to invoke {1}.{2})", + new Object[] { invocation.getConnectorId(), + invocation.getInterfaceName(), + invocation.getMethodName() }); + continue; + } + + if (!enabledConnectors.contains(connector)) { + + if (invocation instanceof LegacyChangeVariablesInvocation) { + LegacyChangeVariablesInvocation legacyInvocation = (LegacyChangeVariablesInvocation) invocation; + // TODO convert window close to a separate RPC call and + // handle above - not a variable change + + // Handle special case where window-close is called + // after the window has been removed from the + // application or the application has closed + Map<String, Object> changes = legacyInvocation + .getVariableChanges(); + if (changes.size() == 1 && changes.containsKey("close") + && Boolean.TRUE.equals(changes.get("close"))) { + // Silently ignore this + continue; + } + } + + // Connector is disabled, log a warning and move to the next + String msg = "Ignoring RPC call for disabled connector " + + connector.getClass().getName(); + if (connector instanceof Component) { + String caption = ((Component) connector).getCaption(); + if (caption != null) { + msg += ", caption=" + caption; + } + } + getLogger().warning(msg); + continue; + } + // DragAndDropService has null UI + if (connector.getUI() != null && connector.getUI().isClosing()) { + String msg = "Ignoring RPC call for connector " + + connector.getClass().getName(); + if (connector instanceof Component) { + String caption = ((Component) connector).getCaption(); + if (caption != null) { + msg += ", caption=" + caption; + } + } + msg += " in closed UI"; + getLogger().warning(msg); + continue; + + } + + if (invocation instanceof ServerRpcMethodInvocation) { + try { + ServerRpcManager.applyInvocation(connector, + (ServerRpcMethodInvocation) invocation); + } catch (RpcInvocationException e) { + manager.handleConnectorRelatedException(connector, e); + } + } else { + + // All code below is for legacy variable changes + LegacyChangeVariablesInvocation legacyInvocation = (LegacyChangeVariablesInvocation) invocation; + Map<String, Object> changes = legacyInvocation + .getVariableChanges(); + try { + if (connector instanceof VariableOwner) { + // The source parameter is never used anywhere + changeVariables(null, (VariableOwner) connector, + changes); + } else { + throw new IllegalStateException( + "Received legacy variable change for " + + connector.getClass().getName() + + " (" + + connector.getConnectorId() + + ") which is not a VariableOwner. The client-side connector sent these legacy varaibles: " + + changes.keySet()); + } + } catch (Exception e) { + manager.handleConnectorRelatedException(connector, e); + } + } + } + } catch (JSONException e) { + getLogger().warning( + "Unable to parse RPC call from the client: " + + e.getMessage()); + throw new RuntimeException(e); + } + } + + /** + * Parse a message burst from the client into a list of MethodInvocation + * instances. + * + * @param connectorTracker + * The ConnectorTracker used to lookup connectors + * @param burst + * message string (JSON) + * @return list of MethodInvocation to perform + * @throws JSONException + */ + private List<MethodInvocation> parseInvocations( + ConnectorTracker connectorTracker, String burst) + throws JSONException { + JSONArray invocationsJson = new JSONArray(burst); + + ArrayList<MethodInvocation> invocations = new ArrayList<MethodInvocation>(); + + MethodInvocation previousInvocation = null; + // parse JSON to MethodInvocations + for (int i = 0; i < invocationsJson.length(); ++i) { + + JSONArray invocationJson = invocationsJson.getJSONArray(i); + + MethodInvocation invocation = parseInvocation(invocationJson, + previousInvocation, connectorTracker); + if (invocation != null) { + // Can be null if the invocation was a legacy invocation and it + // was merged with the previous one or if the invocation was + // rejected because of an error. + invocations.add(invocation); + previousInvocation = invocation; + } + } + return invocations; + } + + private MethodInvocation parseInvocation(JSONArray invocationJson, + MethodInvocation previousInvocation, + ConnectorTracker connectorTracker) throws JSONException { + String connectorId = invocationJson.getString(0); + String interfaceName = invocationJson.getString(1); + String methodName = invocationJson.getString(2); + + if (connectorTracker.getConnector(connectorId) == null + && !connectorId + .equals(ApplicationConstants.DRAG_AND_DROP_CONNECTOR_ID)) { + getLogger() + .log(Level.WARNING, + "RPC call to " + + interfaceName + + "." + + methodName + + " received for connector " + + connectorId + + " but no such connector could be found. Resynchronizing client."); + // This is likely an out of sync issue (client tries to update a + // connector which is not present). Force resync. + connectorTracker.markAllConnectorsDirty(); + return null; + } + + JSONArray parametersJson = invocationJson.getJSONArray(3); + + if (LegacyChangeVariablesInvocation.isLegacyVariableChange( + interfaceName, methodName)) { + if (!(previousInvocation instanceof LegacyChangeVariablesInvocation)) { + previousInvocation = null; + } + + return parseLegacyChangeVariablesInvocation(connectorId, + interfaceName, methodName, + (LegacyChangeVariablesInvocation) previousInvocation, + parametersJson, connectorTracker); + } else { + return parseServerRpcInvocation(connectorId, interfaceName, + methodName, parametersJson, connectorTracker); + } + + } + + private LegacyChangeVariablesInvocation parseLegacyChangeVariablesInvocation( + String connectorId, String interfaceName, String methodName, + LegacyChangeVariablesInvocation previousInvocation, + JSONArray parametersJson, ConnectorTracker connectorTracker) + throws JSONException { + if (parametersJson.length() != 2) { + throw new JSONException( + "Invalid parameters in legacy change variables call. Expected 2, was " + + parametersJson.length()); + } + String variableName = parametersJson.getString(0); + UidlValue uidlValue = (UidlValue) JsonCodec.decodeInternalType( + UidlValue.class, true, parametersJson.get(1), connectorTracker); + + Object value = uidlValue.getValue(); + + if (previousInvocation != null + && previousInvocation.getConnectorId().equals(connectorId)) { + previousInvocation.setVariableChange(variableName, value); + return null; + } else { + return new LegacyChangeVariablesInvocation(connectorId, + variableName, value); + } + } + + private ServerRpcMethodInvocation parseServerRpcInvocation( + String connectorId, String interfaceName, String methodName, + JSONArray parametersJson, ConnectorTracker connectorTracker) + throws JSONException { + ClientConnector connector = connectorTracker.getConnector(connectorId); + + ServerRpcManager<?> rpcManager = connector.getRpcManager(interfaceName); + if (rpcManager == null) { + /* + * Security: Don't even decode the json parameters if no RpcManager + * corresponding to the received method invocation has been + * registered. + */ + getLogger().warning( + "Ignoring RPC call to " + interfaceName + "." + methodName + + " in connector " + connector.getClass().getName() + + "(" + connectorId + + ") as no RPC implementation is regsitered"); + return null; + } + + // Use interface from RpcManager instead of loading the class based on + // the string name to avoid problems with OSGi + Class<? extends ServerRpc> rpcInterface = rpcManager.getRpcInterface(); + + ServerRpcMethodInvocation invocation = new ServerRpcMethodInvocation( + connectorId, rpcInterface, methodName, parametersJson.length()); + + Object[] parameters = new Object[parametersJson.length()]; + Type[] declaredRpcMethodParameterTypes = invocation.getMethod() + .getGenericParameterTypes(); + + for (int j = 0; j < parametersJson.length(); ++j) { + Object parameterValue = parametersJson.get(j); + Type parameterType = declaredRpcMethodParameterTypes[j]; + parameters[j] = JsonCodec.decodeInternalOrCustomType(parameterType, + parameterValue, connectorTracker); + } + invocation.setParameters(parameters); + return invocation; + } + + protected void changeVariables(Object source, VariableOwner owner, + Map<String, Object> m) { + owner.changeVariables(source, m); + } + + /** + * Unescape encoded burst separator characters in a burst received from the + * client. This protects from separator injection attacks. + * + * @param encodedValue + * to decode + * @return decoded value + */ + protected String unescapeBurst(String encodedValue) { + final StringBuilder result = new StringBuilder(); + final StringCharacterIterator iterator = new StringCharacterIterator( + encodedValue); + char character = iterator.current(); + while (character != CharacterIterator.DONE) { + if (VAR_ESCAPE_CHARACTER == character) { + character = iterator.next(); + switch (character) { + case VAR_ESCAPE_CHARACTER + 0x30: + // escaped escape character + result.append(VAR_ESCAPE_CHARACTER); + break; + case VAR_BURST_SEPARATOR + 0x30: + // +0x30 makes these letters for easier reading + result.append((char) (character - 0x30)); + break; + case CharacterIterator.DONE: + // error + throw new RuntimeException( + "Communication error: Unexpected end of message"); + default: + // other escaped character - probably a client-server + // version mismatch + throw new RuntimeException( + "Invalid escaped character from the client - check that the widgetset and server versions match"); + } + } else { + // not a special character - add it to the result as is + result.append(character); + } + character = iterator.next(); + } + return result.toString(); + } + + protected String getMessage(Reader reader) throws IOException { + + StringBuilder sb = new StringBuilder(MAX_BUFFER_SIZE); + char[] buffer = new char[MAX_BUFFER_SIZE]; + + while (true) { + int read = reader.read(buffer); + if (read == -1) { + break; + } + sb.append(buffer, 0, read); + } + + return sb.toString(); + } + + private static final Logger getLogger() { + return Logger.getLogger(ServerRpcHandler.class.getName()); + } +} diff --git a/server/src/com/vaadin/server/communication/ServletBootstrapHandler.java b/server/src/com/vaadin/server/communication/ServletBootstrapHandler.java new file mode 100644 index 0000000000..4b6517c30f --- /dev/null +++ b/server/src/com/vaadin/server/communication/ServletBootstrapHandler.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import com.vaadin.server.BootstrapHandler; +import com.vaadin.server.VaadinServlet; +import com.vaadin.server.VaadinServletService; + +public class ServletBootstrapHandler extends BootstrapHandler { + @Override + protected String getServiceUrl(BootstrapContext context) { + String pathInfo = context.getRequest().getPathInfo(); + if (pathInfo == null) { + return null; + } else { + /* + * Make a relative URL to the servlet by adding one ../ for each + * path segment in pathInfo (i.e. the part of the requested path + * that comes after the servlet mapping) + */ + return VaadinServletService.getCancelingRelativePath(pathInfo); + } + } + + @Override + public String getThemeName(BootstrapContext context) { + String themeName = context.getRequest().getParameter( + VaadinServlet.URL_PARAMETER_THEME); + if (themeName == null) { + themeName = super.getThemeName(context); + } + return themeName; + } +}
\ No newline at end of file diff --git a/client/src/com/vaadin/client/SynchronousXHR.java b/server/src/com/vaadin/server/communication/ServletUIInitHandler.java index a19c9bad16..6286c161f2 100644 --- a/client/src/com/vaadin/client/SynchronousXHR.java +++ b/server/src/com/vaadin/server/communication/ServletUIInitHandler.java @@ -13,24 +13,21 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.client; +package com.vaadin.server.communication; -import com.google.gwt.xhr.client.XMLHttpRequest; +import com.vaadin.server.VaadinRequest; -public class SynchronousXHR extends XMLHttpRequest { +public class ServletUIInitHandler extends UIInitHandler { - protected SynchronousXHR() { + @Override + protected boolean isInitRequest(VaadinRequest request) { + return isUIInitRequest(request); } - public native final void synchronousPost(String uri, String requestData) - /*-{ - try { - this.open("POST", uri, false); - this.setRequestHeader("Content-Type", "text/plain;charset=utf-8"); - this.send(requestData); - } catch (e) { - // No errors are managed as this is synchronous forceful send that can just fail - } - }-*/; + public static boolean isUIInitRequest(VaadinRequest request) { + return "POST".equals(request.getMethod()) + && request + .getParameter(UIInitHandler.BROWSER_DETAILS_PARAMETER) != null; + } } diff --git a/server/src/com/vaadin/server/communication/SessionRequestHandler.java b/server/src/com/vaadin/server/communication/SessionRequestHandler.java new file mode 100644 index 0000000000..244cb0121d --- /dev/null +++ b/server/src/com/vaadin/server/communication/SessionRequestHandler.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.communication; + +import java.io.IOException; +import java.util.ArrayList; + +import com.vaadin.server.RequestHandler; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinResponse; +import com.vaadin.server.VaadinSession; + +/** + * Handles a request by passing it to each registered {@link RequestHandler} in + * the session in turn until one produces a response. This method is used for + * requests that have not been handled by any specific functionality in the + * servlet/portlet. + * <p> + * The request handlers are invoked in the reverse order in which they were + * added to the session until a response has been produced. This means that the + * most recently added handler is used first and the first request handler that + * was added to the session is invoked towards the end unless any previous + * handler has already produced a response. + * </p> + * <p> + * The session is not locked during execution of the request handlers. The + * request handler can itself decide if it needs to lock the session or not. + * </p> + * + * @see VaadinSession#addRequestHandler(RequestHandler) + * @see RequestHandler + * + * @since 7.1 + */ +public class SessionRequestHandler implements RequestHandler { + + @Override + public boolean handleRequest(VaadinSession session, VaadinRequest request, + VaadinResponse response) throws IOException { + // Use a copy to avoid ConcurrentModificationException + session.lock(); + ArrayList<RequestHandler> requestHandlers; + try { + requestHandlers = new ArrayList<RequestHandler>( + session.getRequestHandlers()); + } finally { + session.unlock(); + } + for (RequestHandler handler : requestHandlers) { + if (handler.handleRequest(session, request, response)) { + return true; + } + } + // If not handled + return false; + } +} diff --git a/server/src/com/vaadin/server/communication/SharedStateWriter.java b/server/src/com/vaadin/server/communication/SharedStateWriter.java new file mode 100644 index 0000000000..fdf834387f --- /dev/null +++ b/server/src/com/vaadin/server/communication/SharedStateWriter.java @@ -0,0 +1,75 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; +import java.io.Serializable; +import java.io.Writer; +import java.util.Collection; + +import org.json.JSONException; +import org.json.JSONObject; + +import com.vaadin.server.ClientConnector; +import com.vaadin.server.PaintException; +import com.vaadin.shared.communication.SharedState; +import com.vaadin.ui.UI; + +/** + * Serializes {@link SharedState shared state} changes to JSON. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class SharedStateWriter implements Serializable { + + /** + * Writes a JSON object containing the pending state changes of the dirty + * connectors of the given UI. + * + * @param ui + * The UI whose state changes should be written. + * @param writer + * The writer to use. + * @throws IOException + * If the serialization fails. + */ + public void write(UI ui, Writer writer) throws IOException { + + Collection<ClientConnector> dirtyVisibleConnectors = ui + .getConnectorTracker().getDirtyVisibleConnectors(); + + JSONObject sharedStates = new JSONObject(); + for (ClientConnector connector : dirtyVisibleConnectors) { + // encode and send shared state + try { + JSONObject stateJson = connector.encodeState(); + + if (stateJson != null && stateJson.length() != 0) { + sharedStates.put(connector.getConnectorId(), stateJson); + } + } catch (JSONException e) { + throw new PaintException( + "Failed to serialize shared state for connector " + + connector.getClass().getName() + " (" + + connector.getConnectorId() + "): " + + e.getMessage(), e); + } + } + writer.write(sharedStates.toString()); + } +} diff --git a/server/src/com/vaadin/server/StreamingEndEventImpl.java b/server/src/com/vaadin/server/communication/StreamingEndEventImpl.java index 756cadee6b..f8cfb160be 100644 --- a/server/src/com/vaadin/server/StreamingEndEventImpl.java +++ b/server/src/com/vaadin/server/communication/StreamingEndEventImpl.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.server; +package com.vaadin.server.communication; import com.vaadin.server.StreamVariable.StreamingEndEvent; diff --git a/server/src/com/vaadin/server/StreamingErrorEventImpl.java b/server/src/com/vaadin/server/communication/StreamingErrorEventImpl.java index 53e25399cd..9d9a19e4fe 100644 --- a/server/src/com/vaadin/server/StreamingErrorEventImpl.java +++ b/server/src/com/vaadin/server/communication/StreamingErrorEventImpl.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.server; +package com.vaadin.server.communication; import com.vaadin.server.StreamVariable.StreamingErrorEvent; diff --git a/server/src/com/vaadin/server/StreamingProgressEventImpl.java b/server/src/com/vaadin/server/communication/StreamingProgressEventImpl.java index 610cd30c13..69f3bfb29c 100644 --- a/server/src/com/vaadin/server/StreamingProgressEventImpl.java +++ b/server/src/com/vaadin/server/communication/StreamingProgressEventImpl.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.server; +package com.vaadin.server.communication; import com.vaadin.server.StreamVariable.StreamingProgressEvent; diff --git a/server/src/com/vaadin/server/StreamingStartEventImpl.java b/server/src/com/vaadin/server/communication/StreamingStartEventImpl.java index 3cd41bbb6d..bd16f08801 100644 --- a/server/src/com/vaadin/server/StreamingStartEventImpl.java +++ b/server/src/com/vaadin/server/communication/StreamingStartEventImpl.java @@ -13,7 +13,7 @@ * License for the specific language governing permissions and limitations under * the License. */ -package com.vaadin.server; +package com.vaadin.server.communication; import com.vaadin.server.StreamVariable.StreamingStartEvent; diff --git a/server/src/com/vaadin/server/communication/UIInitHandler.java b/server/src/com/vaadin/server/communication/UIInitHandler.java new file mode 100644 index 0000000000..e4b5360b49 --- /dev/null +++ b/server/src/com/vaadin/server/communication/UIInitHandler.java @@ -0,0 +1,304 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.StringWriter; +import java.util.List; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.json.JSONException; +import org.json.JSONObject; + +import com.vaadin.annotations.PreserveOnRefresh; +import com.vaadin.server.LegacyApplicationUIProvider; +import com.vaadin.server.SynchronizedRequestHandler; +import com.vaadin.server.UIClassSelectionEvent; +import com.vaadin.server.UICreateEvent; +import com.vaadin.server.UIProvider; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinResponse; +import com.vaadin.server.VaadinService; +import com.vaadin.server.VaadinSession; +import com.vaadin.shared.ApplicationConstants; +import com.vaadin.shared.communication.PushMode; +import com.vaadin.shared.ui.ui.UIConstants; +import com.vaadin.ui.UI; + +/** + * Handles an initial request from the client to initialize a {@link UI}. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public abstract class UIInitHandler extends SynchronizedRequestHandler { + + public static final String BROWSER_DETAILS_PARAMETER = "v-browserDetails"; + + protected abstract boolean isInitRequest(VaadinRequest request); + + @Override + public boolean synchronizedHandleRequest(VaadinSession session, + VaadinRequest request, VaadinResponse response) throws IOException { + if (!isInitRequest(request)) { + return false; + } + + StringWriter stringWriter = new StringWriter(); + + try { + assert UI.getCurrent() == null; + + // Set browser information from the request + session.getBrowser().updateRequestDetails(request); + + UI uI = getBrowserDetailsUI(request, session); + + session.getCommunicationManager().repaintAll(uI); + + JSONObject params = new JSONObject(); + params.put(UIConstants.UI_ID_PARAMETER, uI.getUIId()); + String initialUIDL = getInitialUidl(request, uI); + params.put("uidl", initialUIDL); + + stringWriter.write(params.toString()); + } catch (JSONException e) { + throw new IOException("Error producing initial UIDL", e); + } finally { + stringWriter.close(); + } + + return commitJsonResponse(request, response, stringWriter.toString()); + } + + /** + * Commit the JSON response. We can't write immediately to the output stream + * as we want to write only a critical notification if something goes wrong + * during the response handling. + * + * @param request + * The request that resulted in this response + * @param response + * The response to write to + * @param json + * The JSON to write + * @return true if the JSON was written successfully, false otherwise + * @throws IOException + * If there was an exception while writing to the output + */ + static boolean commitJsonResponse(VaadinRequest request, + VaadinResponse response, String json) throws IOException { + // The response was produced without errors so write it to the client + response.setContentType("application/json; charset=UTF-8"); + + // Ensure that the browser does not cache UIDL responses. + // iOS 6 Safari requires this (#9732) + response.setHeader("Cache-Control", "no-cache"); + + // NOTE! GateIn requires, for some weird reason, getOutputStream + // to be used instead of getWriter() (it seems to interpret + // application/json as a binary content type) + OutputStreamWriter outputWriter = new OutputStreamWriter( + response.getOutputStream(), "UTF-8"); + try { + outputWriter.write(json); + // NOTE GateIn requires the buffers to be flushed to work + outputWriter.flush(); + } finally { + outputWriter.close(); + } + + return true; + } + + private UI getBrowserDetailsUI(VaadinRequest request, VaadinSession session) { + VaadinService vaadinService = request.getService(); + + List<UIProvider> uiProviders = session.getUIProviders(); + + UIClassSelectionEvent classSelectionEvent = new UIClassSelectionEvent( + request); + + UIProvider provider = null; + Class<? extends UI> uiClass = null; + for (UIProvider p : uiProviders) { + // Check for existing LegacyWindow + if (p instanceof LegacyApplicationUIProvider) { + LegacyApplicationUIProvider legacyProvider = (LegacyApplicationUIProvider) p; + + UI existingUi = legacyProvider + .getExistingUI(classSelectionEvent); + if (existingUi != null) { + reinitUI(existingUi, request); + return existingUi; + } + } + + uiClass = p.getUIClass(classSelectionEvent); + if (uiClass != null) { + provider = p; + break; + } + } + + if (provider == null || uiClass == null) { + return null; + } + + // Check for an existing UI based on window.name + + // Special parameter sent by vaadinBootstrap.js + String windowName = request.getParameter("v-wn"); + + Map<String, Integer> retainOnRefreshUIs = session + .getPreserveOnRefreshUIs(); + if (windowName != null && !retainOnRefreshUIs.isEmpty()) { + // Check for a known UI + + Integer retainedUIId = retainOnRefreshUIs.get(windowName); + + if (retainedUIId != null) { + UI retainedUI = session.getUIById(retainedUIId.intValue()); + if (uiClass.isInstance(retainedUI)) { + reinitUI(retainedUI, request); + return retainedUI; + } else { + getLogger().info( + "Not using retained UI in " + windowName + + " because retained UI was of type " + + retainedUI.getClass() + " but " + uiClass + + " is expected for the request."); + } + } + } + + // No existing UI found - go on by creating and initializing one + + Integer uiId = Integer.valueOf(session.getNextUIid()); + + // Explicit Class.cast to detect if the UIProvider does something + // unexpected + UICreateEvent event = new UICreateEvent(request, uiClass, uiId); + UI ui = uiClass.cast(provider.createInstance(event)); + + // Initialize some fields for a newly created UI + if (ui.getSession() != session) { + // Session already set for LegacyWindow + ui.setSession(session); + } + + PushMode pushMode = provider.getPushMode(event); + if (pushMode == null) { + pushMode = session.getService().getDeploymentConfiguration() + .getPushMode(); + } + ui.setPushMode(pushMode); + + // Set thread local here so it is available in init + UI.setCurrent(ui); + + ui.doInit(request, uiId.intValue()); + + session.addUI(ui); + + // Remember if it should be remembered + if (vaadinService.preserveUIOnRefresh(provider, event)) { + // Remember this UI + if (windowName == null) { + getLogger().warning( + "There is no window.name available for UI " + uiClass + + " that should be preserved."); + } else { + session.getPreserveOnRefreshUIs().put(windowName, uiId); + } + } + + return ui; + } + + /** + * Updates a UI that has already been initialized but is now loaded again, + * e.g. because of {@link PreserveOnRefresh}. + * + * @param ui + * @param request + */ + private void reinitUI(UI ui, VaadinRequest request) { + UI.setCurrent(ui); + + // Fire fragment change if the fragment has changed + String location = request.getParameter("v-loc"); + if (location != null) { + ui.getPage().updateLocation(location); + } + } + + /** + * Generates the initial UIDL message that can e.g. be included in a html + * page to avoid a separate round trip just for getting the UIDL. + * + * @param request + * the request that caused the initialization + * @param uI + * the UI for which the UIDL should be generated + * @return a string with the initial UIDL message + * @throws JSONException + * if an exception occurs while encoding output + * @throws IOException + */ + protected String getInitialUidl(VaadinRequest request, UI uI) + throws JSONException, IOException { + StringWriter writer = new StringWriter(); + try { + writer.write("{"); + + VaadinSession session = uI.getSession(); + if (session.getConfiguration().isXsrfProtectionEnabled()) { + writer.write(getSecurityKeyUIDL(session)); + } + new UidlWriter().write(uI, writer, true, false, false); + writer.write("}"); + + String initialUIDL = writer.toString(); + getLogger().log(Level.FINE, "Initial UIDL:" + initialUIDL); + return initialUIDL; + } finally { + writer.close(); + } + } + + /** + * Gets the security key (and generates one if needed) as UIDL. + * + * @param session + * the vaadin session to which the security key belongs + * @return the security key UIDL or "" if the feature is turned off + */ + private static String getSecurityKeyUIDL(VaadinSession session) { + String seckey = session.getCsrfToken(); + + return "\"" + ApplicationConstants.UIDL_SECURITY_TOKEN_ID + "\":\"" + + seckey + "\","; + } + + private static final Logger getLogger() { + return Logger.getLogger(UIInitHandler.class.getName()); + } +} diff --git a/server/src/com/vaadin/server/communication/UidlRequestHandler.java b/server/src/com/vaadin/server/communication/UidlRequestHandler.java new file mode 100644 index 0000000000..73ff92f8bd --- /dev/null +++ b/server/src/com/vaadin/server/communication/UidlRequestHandler.java @@ -0,0 +1,306 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.LinkedList; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.json.JSONException; + +import com.vaadin.server.ClientConnector; +import com.vaadin.server.Constants; +import com.vaadin.server.LegacyCommunicationManager.InvalidUIDLSecurityKeyException; +import com.vaadin.server.ServletPortletHelper; +import com.vaadin.server.SessionExpiredHandler; +import com.vaadin.server.SynchronizedRequestHandler; +import com.vaadin.server.SystemMessages; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinResponse; +import com.vaadin.server.VaadinService; +import com.vaadin.server.VaadinSession; +import com.vaadin.shared.ApplicationConstants; +import com.vaadin.shared.JsonConstants; +import com.vaadin.shared.Version; +import com.vaadin.ui.Component; +import com.vaadin.ui.UI; + +/** + * Processes a UIDL request from the client. + * + * Uses {@link ServerRpcHandler} to execute client-to-server RPC invocations and + * {@link UidlWriter} to write state changes and client RPC calls back to the + * client. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class UidlRequestHandler extends SynchronizedRequestHandler implements + SessionExpiredHandler { + + public static final String UIDL_PATH = "UIDL/"; + + private ServerRpcHandler rpcHandler = new ServerRpcHandler(); + + public UidlRequestHandler() { + } + + @Override + public boolean synchronizedHandleRequest(VaadinSession session, + VaadinRequest request, VaadinResponse response) throws IOException { + if (!ServletPortletHelper.isUIDLRequest(request)) { + return false; + } + UI uI = session.getService().findUI(request); + if (uI == null) { + // This should not happen but it will if the UI has been closed. We + // really don't want to see it in the server logs though + response.getWriter().write( + getUINotFoundErrorJSON(session.getService(), request)); + return true; + } + + checkWidgetsetVersion(request); + String requestThemeName = request.getParameter("theme"); + ClientConnector highlightedConnector; + // repaint requested or session has timed out and new one is created + boolean repaintAll; + + // TODO PUSH repaintAll, analyzeLayouts, highlightConnector should be + // part of the message payload to make the functionality transport + // agnostic + + repaintAll = (request + .getParameter(ApplicationConstants.URL_PARAMETER_REPAINT_ALL) != null); + + boolean analyzeLayouts = false; + if (repaintAll) { + // analyzing can be done only with repaintAll + analyzeLayouts = (request + .getParameter(ApplicationConstants.PARAM_ANALYZE_LAYOUTS) != null); + + String pid = request + .getParameter(ApplicationConstants.PARAM_HIGHLIGHT_CONNECTOR); + if (pid != null) { + highlightedConnector = uI.getConnectorTracker().getConnector( + pid); + highlightConnector(highlightedConnector); + } + } + + StringWriter stringWriter = new StringWriter(); + + try { + rpcHandler.handleRpc(uI, request.getReader(), request); + + if (repaintAll) { + session.getCommunicationManager().repaintAll(uI); + } + + writeUidl(request, response, uI, stringWriter, repaintAll, + analyzeLayouts); + } catch (JSONException e) { + getLogger().log(Level.SEVERE, "Error writing JSON to response", e); + // Refresh on client side + response.getWriter().write( + VaadinService.createCriticalNotificationJSON(null, null, + null, null)); + return true; + } catch (InvalidUIDLSecurityKeyException e) { + getLogger().log(Level.WARNING, + "Invalid security key received from {0}", + request.getRemoteHost()); + // Refresh on client side + response.getWriter().write( + VaadinService.createCriticalNotificationJSON(null, null, + null, null)); + return true; + } finally { + stringWriter.close(); + requestThemeName = null; + } + + return UIInitHandler.commitJsonResponse(request, response, + stringWriter.toString()); + } + + /** + * Checks that the version reported by the client (widgetset) matches that + * of the server. + * + * @param request + */ + private void checkWidgetsetVersion(VaadinRequest request) { + String widgetsetVersion = request.getParameter("v-wsver"); + if (widgetsetVersion == null) { + // Only check when the widgetset version is reported. It is reported + // in the first UIDL request (not the initial request as it is a + // plain GET /) + return; + } + + if (!Version.getFullVersion().equals(widgetsetVersion)) { + getLogger().warning( + String.format(Constants.WIDGETSET_MISMATCH_INFO, + Version.getFullVersion(), widgetsetVersion)); + } + } + + private void writeUidl(VaadinRequest request, VaadinResponse response, + UI ui, Writer writer, boolean repaintAll, boolean analyzeLayouts) + throws IOException, JSONException { + openJsonMessage(writer, response); + + new UidlWriter().write(ui, writer, repaintAll, analyzeLayouts, false); + + closeJsonMessage(writer); + } + + protected void closeJsonMessage(Writer outWriter) throws IOException { + outWriter.write("}]"); + } + + /** + * Writes the opening of JSON message to be sent to client. + * + * @param outWriter + * @param response + * @throws IOException + */ + protected void openJsonMessage(Writer outWriter, VaadinResponse response) + throws IOException { + // some dirt to prevent cross site scripting + outWriter.write("for(;;);[{"); + } + + // TODO Does this belong here? + protected void highlightConnector(ClientConnector highlightedConnector) { + StringBuilder sb = new StringBuilder(); + sb.append("*** Debug details of a connector: *** \n"); + sb.append("Type: "); + sb.append(highlightedConnector.getClass().getName()); + sb.append("\nId:"); + sb.append(highlightedConnector.getConnectorId()); + if (highlightedConnector instanceof Component) { + Component component = (Component) highlightedConnector; + if (component.getCaption() != null) { + sb.append("\nCaption:"); + sb.append(component.getCaption()); + } + } + printHighlightedConnectorHierarchy(sb, highlightedConnector); + getLogger().info(sb.toString()); + } + + // TODO Does this belong here? + protected void printHighlightedConnectorHierarchy(StringBuilder sb, + ClientConnector connector) { + LinkedList<ClientConnector> h = new LinkedList<ClientConnector>(); + h.add(connector); + ClientConnector parent = connector.getParent(); + while (parent != null) { + h.addFirst(parent); + parent = parent.getParent(); + } + + sb.append("\nConnector hierarchy:\n"); + VaadinSession session2 = connector.getUI().getSession(); + sb.append(session2.getClass().getName()); + sb.append("("); + sb.append(session2.getClass().getSimpleName()); + sb.append(".java"); + sb.append(":1)"); + int l = 1; + for (ClientConnector connector2 : h) { + sb.append("\n"); + for (int i = 0; i < l; i++) { + sb.append(" "); + } + l++; + Class<? extends ClientConnector> connectorClass = connector2 + .getClass(); + Class<?> topClass = connectorClass; + while (topClass.getEnclosingClass() != null) { + topClass = topClass.getEnclosingClass(); + } + sb.append(connectorClass.getName()); + sb.append("("); + sb.append(topClass.getSimpleName()); + sb.append(".java:1)"); + } + } + + private static final Logger getLogger() { + return Logger.getLogger(UidlRequestHandler.class.getName()); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.server.SessionExpiredHandler#handleSessionExpired(com.vaadin + * .server.VaadinRequest, com.vaadin.server.VaadinResponse) + */ + @Override + public boolean handleSessionExpired(VaadinRequest request, + VaadinResponse response) throws IOException { + if (!ServletPortletHelper.isUIDLRequest(request)) { + return false; + } + VaadinService service = request.getService(); + SystemMessages systemMessages = service.getSystemMessages( + ServletPortletHelper.findLocale(null, null, request), request); + + service.writeStringResponse(response, JsonConstants.JSON_CONTENT_TYPE, + VaadinService.createCriticalNotificationJSON( + systemMessages.getSessionExpiredCaption(), + systemMessages.getSessionExpiredMessage(), null, + systemMessages.getSessionExpiredURL())); + + return true; + } + + /** + * Returns the JSON which should be returned to the client when a request + * for a non-existent UI arrives. + * + * @param service + * The VaadinService + * @param vaadinRequest + * The request which triggered this, or null if not available + * @since 7.1 + * @return A JSON string + */ + static String getUINotFoundErrorJSON(VaadinService service, + VaadinRequest vaadinRequest) { + SystemMessages ci = service.getSystemMessages( + vaadinRequest.getLocale(), vaadinRequest); + // Session Expired is not really the correct message as the + // session exists but the requested UI does not. + // Using Communication Error for now. + String json = VaadinService.createCriticalNotificationJSON( + ci.getCommunicationErrorCaption(), + ci.getCommunicationErrorMessage(), null, + ci.getCommunicationErrorURL()); + + return json; + } + +} diff --git a/server/src/com/vaadin/server/communication/UidlWriter.java b/server/src/com/vaadin/server/communication/UidlWriter.java new file mode 100644 index 0000000000..fbe2fb86d5 --- /dev/null +++ b/server/src/com/vaadin/server/communication/UidlWriter.java @@ -0,0 +1,317 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.server.communication; + +import java.io.IOException; +import java.io.Serializable; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.json.JSONArray; +import org.json.JSONException; + +import com.vaadin.annotations.JavaScript; +import com.vaadin.annotations.StyleSheet; +import com.vaadin.server.ClientConnector; +import com.vaadin.server.JsonPaintTarget; +import com.vaadin.server.LegacyCommunicationManager; +import com.vaadin.server.LegacyCommunicationManager.ClientCache; +import com.vaadin.server.SystemMessages; +import com.vaadin.server.VaadinSession; +import com.vaadin.ui.ConnectorTracker; +import com.vaadin.ui.UI; + +/** + * Serializes pending server-side changes to UI state to JSON. This includes + * shared state, client RPC invocations, connector hierarchy changes, connector + * type information among others. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class UidlWriter implements Serializable { + + /** + * Writes a JSON object containing all pending changes to the given UI. + * + * @param ui + * The {@link UI} whose changes to write + * @param writer + * The writer to use + * @param repaintAll + * Whether the client should re-render the whole UI. + * @param analyzeLayouts + * Whether detected layout problems should be logged. + * @param async + * True if this message is sent by the server asynchronously, + * false if it is a response to a client message. + * + * @throws IOException + * If the writing fails. + * @throws JSONException + * If the JSON serialization fails. + */ + public void write(UI ui, Writer writer, boolean repaintAll, + boolean analyzeLayouts, boolean async) throws IOException, + JSONException { + ArrayList<ClientConnector> dirtyVisibleConnectors = ui + .getConnectorTracker().getDirtyVisibleConnectors(); + VaadinSession session = ui.getSession(); + LegacyCommunicationManager manager = session.getCommunicationManager(); + // Paints components + ConnectorTracker uiConnectorTracker = ui.getConnectorTracker(); + getLogger().log(Level.FINE, "* Creating response to client"); + + getLogger().log( + Level.FINE, + "Found " + dirtyVisibleConnectors.size() + + " dirty connectors to paint"); + for (ClientConnector connector : dirtyVisibleConnectors) { + boolean initialized = uiConnectorTracker + .isClientSideInitialized(connector); + connector.beforeClientResponse(!initialized); + } + + uiConnectorTracker.setWritingResponse(true); + try { + writer.write("\"changes\" : "); + + JsonPaintTarget paintTarget = new JsonPaintTarget(manager, writer, + !repaintAll); + + new LegacyUidlWriter().write(ui, writer, paintTarget); + + paintTarget.close(); + writer.write(", "); // close changes + + // send shared state to client + + // for now, send the complete state of all modified and new + // components + + // Ideally, all this would be sent before "changes", but that causes + // complications with legacy components that create sub-components + // in their paint phase. Nevertheless, this will be processed on the + // client after component creation but before legacy UIDL + // processing. + + writer.write("\"state\":"); + new SharedStateWriter().write(ui, writer); + writer.write(", "); // close states + + // TODO This should be optimized. The type only needs to be + // sent once for each connector id + on refresh. Use the same cache + // as + // widget mapping + + writer.write("\"types\":"); + new ConnectorTypeWriter().write(ui, writer, paintTarget); + writer.write(", "); // close states + + // Send update hierarchy information to the client. + + // This could be optimized aswell to send only info if hierarchy has + // actually changed. Much like with the shared state. Note though + // that an empty hierarchy is information aswell (e.g. change from 1 + // child to 0 children) + + writer.write("\"hierarchy\":"); + new ConnectorHierarchyWriter().write(ui, writer); + writer.write(", "); // close hierarchy + + // send server to client RPC calls for components in the UI, in call + // order + + // collect RPC calls from components in the UI in the order in + // which they were performed, remove the calls from components + + writer.write("\"rpc\" : "); + new ClientRpcWriter().write(ui, writer); + writer.write(", "); // close rpc + + uiConnectorTracker.markAllConnectorsClean(); + + writer.write("\"meta\" : "); + + SystemMessages messages = ui.getSession().getService() + .getSystemMessages(ui.getLocale(), null); + // TODO hilightedConnector + new MetadataWriter().write(ui, writer, repaintAll, analyzeLayouts, + async, null, messages); + writer.write(", "); + + writer.write("\"resources\" : "); + new ResourceWriter().write(ui, writer, paintTarget); + + Collection<Class<? extends ClientConnector>> usedClientConnectors = paintTarget + .getUsedClientConnectors(); + boolean typeMappingsOpen = false; + ClientCache clientCache = manager.getClientCache(ui); + + List<Class<? extends ClientConnector>> newConnectorTypes = new ArrayList<Class<? extends ClientConnector>>(); + + for (Class<? extends ClientConnector> class1 : usedClientConnectors) { + if (clientCache.cache(class1)) { + // client does not know the mapping key for this type, send + // mapping to client + newConnectorTypes.add(class1); + + if (!typeMappingsOpen) { + typeMappingsOpen = true; + writer.write(", \"typeMappings\" : { "); + } else { + writer.write(" , "); + } + String canonicalName = class1.getCanonicalName(); + writer.write("\""); + writer.write(canonicalName); + writer.write("\" : "); + writer.write(manager.getTagForType(class1)); + } + } + if (typeMappingsOpen) { + writer.write(" }"); + } + + // TODO PUSH Refactor to TypeInheritanceWriter or something + boolean typeInheritanceMapOpen = false; + if (typeMappingsOpen) { + // send the whole type inheritance map if any new mappings + for (Class<? extends ClientConnector> class1 : usedClientConnectors) { + if (!ClientConnector.class.isAssignableFrom(class1 + .getSuperclass())) { + continue; + } + if (!typeInheritanceMapOpen) { + typeInheritanceMapOpen = true; + writer.write(", \"typeInheritanceMap\" : { "); + } else { + writer.write(" , "); + } + writer.write("\""); + writer.write(manager.getTagForType(class1)); + writer.write("\" : "); + writer.write(manager + .getTagForType((Class<? extends ClientConnector>) class1 + .getSuperclass())); + } + if (typeInheritanceMapOpen) { + writer.write(" }"); + } + } + + // TODO Refactor to DependencyWriter or something + /* + * Ensure super classes come before sub classes to get script + * dependency order right. Sub class @JavaScript might assume that + * + * @JavaScript defined by super class is already loaded. + */ + Collections.sort(newConnectorTypes, new Comparator<Class<?>>() { + @Override + public int compare(Class<?> o1, Class<?> o2) { + // TODO optimize using Class.isAssignableFrom? + return hierarchyDepth(o1) - hierarchyDepth(o2); + } + + private int hierarchyDepth(Class<?> type) { + if (type == Object.class) { + return 0; + } else { + return hierarchyDepth(type.getSuperclass()) + 1; + } + } + }); + + List<String> scriptDependencies = new ArrayList<String>(); + List<String> styleDependencies = new ArrayList<String>(); + + for (Class<? extends ClientConnector> class1 : newConnectorTypes) { + JavaScript jsAnnotation = class1 + .getAnnotation(JavaScript.class); + if (jsAnnotation != null) { + for (String uri : jsAnnotation.value()) { + scriptDependencies.add(manager.registerDependency(uri, + class1)); + } + } + + StyleSheet styleAnnotation = class1 + .getAnnotation(StyleSheet.class); + if (styleAnnotation != null) { + for (String uri : styleAnnotation.value()) { + styleDependencies.add(manager.registerDependency(uri, + class1)); + } + } + } + + // Include script dependencies in output if there are any + if (!scriptDependencies.isEmpty()) { + writer.write(", \"scriptDependencies\": " + + new JSONArray(scriptDependencies).toString()); + } + + // Include style dependencies in output if there are any + if (!styleDependencies.isEmpty()) { + writer.write(", \"styleDependencies\": " + + new JSONArray(styleDependencies).toString()); + } + + // add any pending locale definitions requested by the client + writer.write(", \"locales\": "); + manager.printLocaleDeclarations(writer); + + if (manager.getDragAndDropService() != null) { + manager.getDragAndDropService().printJSONResponse(writer); + } + + for (ClientConnector connector : dirtyVisibleConnectors) { + uiConnectorTracker.markClientSideInitialized(connector); + } + + assert (uiConnectorTracker.getDirtyConnectors().isEmpty()) : "Connectors have been marked as dirty during the end of the paint phase. This is most certainly not intended."; + + writePerformanceData(ui, writer); + } finally { + uiConnectorTracker.setWritingResponse(false); + uiConnectorTracker.cleanConnectorMap(); + } + } + + /** + * Adds the performance timing data (used by TestBench 3) to the UIDL + * response. + * + * @throws IOException + */ + private void writePerformanceData(UI ui, Writer writer) throws IOException { + writer.write(String.format(", \"timings\":[%d, %d]", ui.getSession() + .getCumulativeRequestDuration(), ui.getSession() + .getLastRequestDuration())); + } + + private static final Logger getLogger() { + return Logger.getLogger(UidlWriter.class.getName()); + } +} diff --git a/server/src/com/vaadin/server/themeutils/SASSAddonImportFileCreator.java b/server/src/com/vaadin/server/themeutils/SASSAddonImportFileCreator.java new file mode 100644 index 0000000000..f199c347eb --- /dev/null +++ b/server/src/com/vaadin/server/themeutils/SASSAddonImportFileCreator.java @@ -0,0 +1,200 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server.themeutils; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +import com.vaadin.server.widgetsetutils.ClassPathExplorer; +import com.vaadin.server.widgetsetutils.ClassPathExplorer.LocationInfo; + +/** + * Helper class for managing the addon imports and creating an a SCSS file for + * importing all your addon themes. The helper method searches the classpath for + * Vaadin addons and uses the 'Vaadin-Themes' metadata to create the imports. + * + * <p> + * The addons.scss is always overwritten when this tool is invoked. + * </p> + * + * @since 7.1 + */ +public class SASSAddonImportFileCreator { + + private static final String ADDON_IMPORTS_FILE = "addons.scss"; + + private static final String ADDON_IMPORTS_FILE_TEXT = "This file is automatically managed and " + + "will be overwritten from time to time."; + + /** + * + * @param args + * Theme directory where the addons.scss file should be created + */ + public static void main(String[] args) throws IOException { + if (args.length == 0) { + printUsage(); + } else { + String themeDirectory = args[0]; + updateTheme(themeDirectory); + } + } + + /** + * Updates a themes addons.scss with the addon themes found on the classpath + * + * @param themeDirectory + * The target theme directory + */ + public static void updateTheme(String themeDirectory) throws IOException { + + File addonImports = new File(themeDirectory, ADDON_IMPORTS_FILE); + + if (!addonImports.exists()) { + + // Ensure directory exists + addonImports.getParentFile().mkdirs(); + + // Ensure file exists + addonImports.createNewFile(); + } + + LocationInfo info = ClassPathExplorer + .getAvailableWidgetSetsAndStylesheets(); + + try { + PrintStream printStream = new PrintStream(new FileOutputStream( + addonImports)); + + printStream.println("/* " + ADDON_IMPORTS_FILE_TEXT + " */"); + + printStream.println("/* Do not manually edit this file. */"); + + printStream.println(); + + Map<String, URL> addonThemes = info.getAddonStyles(); + + // Sort addon styles so that CSS imports are first and SCSS import + // last + List<String> paths = new ArrayList<String>(addonThemes.keySet()); + Collections.sort(paths, new Comparator<String>() { + + @Override + public int compare(String path1, String path2) { + if (path1.toLowerCase().endsWith(".css") + && path2.toLowerCase().endsWith(".scss")) { + return -1; + } + if (path1.toLowerCase().endsWith(".scss") + && path2.toLowerCase().endsWith(".css")) { + return 1; + } + return 0; + } + }); + + List<String> mixins = new ArrayList<String>(); + for (String path : paths) { + mixins.addAll(addImport(printStream, path, + addonThemes.get(path))); + printStream.println(); + } + + createAddonsMixin(printStream, mixins); + + } catch (FileNotFoundException e) { + // Should not happen since file is checked before this + e.printStackTrace(); + } + } + + private static List<String> addImport(PrintStream stream, String file, + URL location) { + + // Add import comment + printImportComment(stream, location); + + List<String> foundMixins = new ArrayList<String>(); + + if (file.endsWith(".css")) { + stream.print("@import url(\"../../../" + file + "\");\n"); + } else { + // Assume SASS + stream.print("@import \"../../../" + file + "\";\n"); + + // Convention is to name the mixin after the stylesheet. Strip + // .scss from filename + String mixin = file.substring(file.lastIndexOf("/") + 1, + file.length() - ".scss".length()); + + foundMixins.add(mixin); + } + + stream.println(); + + return foundMixins; + } + + private static void printImportComment(PrintStream stream, URL location) { + + // file:/absolute/path/to/addon.jar!/ + String path = location.getPath(); + + try { + // Try to parse path for better readability + path = path.substring(path.lastIndexOf(":") + 1, + path.lastIndexOf("!")); + + // Extract jar archive filename + path = path.substring(path.lastIndexOf("/") + 1); + + } catch (Exception e) { + // Parsing failed but no worries, we then use whatever + // location.getPath() returns + } + + stream.println("/* Provided by " + path + " */"); + } + + private static void createAddonsMixin(PrintStream stream, + List<String> mixins) { + + stream.println("/* Import and include this mixin into your project theme to include the addon themes */"); + stream.println("@mixin addons {"); + for (String addon : mixins) { + stream.println("\t@include " + addon + ";"); + } + stream.println("}"); + stream.println(); + } + + private static void printUsage() { + String className = SASSAddonImportFileCreator.class.getSimpleName(); + PrintStream o = System.out; + o.println(className + " usage:"); + o.println(); + o.println("./" + className + " [Path to target theme folder]"); + } +} diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/ClassPathExplorer.java b/server/src/com/vaadin/server/widgetsetutils/ClassPathExplorer.java index 34024f1616..cc04e50b3c 100644 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/ClassPathExplorer.java +++ b/server/src/com/vaadin/server/widgetsetutils/ClassPathExplorer.java @@ -72,6 +72,32 @@ public class ClassPathExplorer { }; /** + * Contains information about widgetsets and themes found on the classpath + * + * @since 7.1 + */ + public static class LocationInfo { + + private final Map<String, URL> widgetsets; + + private final Map<String, URL> addonStyles; + + public LocationInfo(Map<String, URL> widgetsets, Map<String, URL> themes) { + this.widgetsets = widgetsets; + addonStyles = themes; + } + + public Map<String, URL> getWidgetsets() { + return widgetsets; + } + + public Map<String, URL> getAddonStyles() { + return addonStyles; + } + + } + + /** * Raw class path entries as given in the java class path string. Only * entries that could include widgets/widgetsets are listed (primarily * directories, Vaadin JARs and add-on JARs). @@ -95,13 +121,26 @@ public class ClassPathExplorer { * Finds the names and locations of widgetsets available on the class path. * * @return map from widgetset classname to widgetset location URL + * @deprecated Use {@link #getAvailableWidgetSetsAndStylesheets()} instead */ + @Deprecated public static Map<String, URL> getAvailableWidgetSets() { + return getAvailableWidgetSetsAndStylesheets().getWidgetsets(); + } + + /** + * Finds the names and locations of widgetsets and themes available on the + * class path. + * + * @return + */ + public static LocationInfo getAvailableWidgetSetsAndStylesheets() { long start = System.currentTimeMillis(); Map<String, URL> widgetsets = new HashMap<String, URL>(); + Map<String, URL> themes = new HashMap<String, URL>(); Set<String> keySet = classpathLocations.keySet(); for (String location : keySet) { - searchForWidgetSets(location, widgetsets); + searchForWidgetSetsAndAddonStyles(location, widgetsets, themes); } long end = System.currentTimeMillis(); @@ -114,14 +153,25 @@ public class ClassPathExplorer { sb.append(widgetsets.get(ws)); sb.append("\n"); } + + sb.append("Addon styles found from classpath:\n"); + for (String theme : themes.keySet()) { + sb.append("\t"); + sb.append(theme); + sb.append(" in "); + sb.append(themes.get(theme)); + sb.append("\n"); + } + final Logger logger = getLogger(); logger.info(sb.toString()); logger.info("Search took " + (end - start) + "ms"); - return widgetsets; + return new LocationInfo(widgetsets, themes); } /** - * Finds all GWT modules / Vaadin widgetsets in a valid location. + * Finds all GWT modules / Vaadin widgetsets and Addon styles in a valid + * location. * * If the location is a directory, all GWT modules (files with the * ".gwt.xml" extension) are added to widgetsets. @@ -136,8 +186,9 @@ public class ClassPathExplorer { * separators) to a URL (see {@link #classpathLocations}) - new * entries are added to this map */ - private static void searchForWidgetSets(String locationString, - Map<String, URL> widgetsets) { + private static void searchForWidgetSetsAndAddonStyles( + String locationString, Map<String, URL> widgetsets, + Map<String, URL> addonStyles) { URL location = classpathLocations.get(locationString); File directory = new File(location.getFile()); @@ -197,18 +248,32 @@ public class ClassPathExplorer { // No manifest so this is not a Vaadin Add-on return; } + + // Check for widgetset attribute String value = manifest.getMainAttributes().getValue( "Vaadin-Widgetsets"); if (value != null) { String[] widgetsetNames = value.split(","); for (int i = 0; i < widgetsetNames.length; i++) { - String widgetsetname = widgetsetNames[i].trim() - .intern(); + String widgetsetname = widgetsetNames[i].trim(); if (!widgetsetname.equals("")) { widgetsets.put(widgetsetname, location); } } } + + // Check for theme attribute + value = manifest.getMainAttributes().getValue( + "Vaadin-Stylesheets"); + if (value != null) { + String[] stylesheets = value.split(","); + for (int i = 0; i < stylesheets.length; i++) { + String stylesheet = stylesheets[i].trim(); + if (!stylesheet.equals("")) { + addonStyles.put(stylesheet, location); + } + } + } } } catch (IOException e) { getLogger().log(Level.WARNING, "Error parsing jar file", e); @@ -323,6 +388,9 @@ public class ClassPathExplorer { if (mainAttributes.getValue("Vaadin-Widgetsets") != null) { return true; } + if (mainAttributes.getValue("Vaadin-Stylesheets") != null) { + return true; + } } } catch (MalformedURLException e) { getLogger().log(Level.FINEST, "Failed to inspect JAR file", @@ -457,14 +525,10 @@ public class ClassPathExplorer { * Test method for helper tool */ public static void main(String[] args) { - getLogger().info("Searching available widgetsets..."); + getLogger().info( + "Searching for available widgetsets and stylesheets..."); - Map<String, URL> availableWidgetSets = ClassPathExplorer - .getAvailableWidgetSets(); - for (String string : availableWidgetSets.keySet()) { - - getLogger().info(string + " in " + availableWidgetSets.get(string)); - } + ClassPathExplorer.getAvailableWidgetSetsAndStylesheets(); } private static final Logger getLogger() { diff --git a/client-compiler/src/com/vaadin/server/widgetsetutils/WidgetSetBuilder.java b/server/src/com/vaadin/server/widgetsetutils/WidgetSetBuilder.java index 3a0e59df71..3a0e59df71 100644 --- a/client-compiler/src/com/vaadin/server/widgetsetutils/WidgetSetBuilder.java +++ b/server/src/com/vaadin/server/widgetsetutils/WidgetSetBuilder.java diff --git a/server/src/com/vaadin/ui/AbstractColorPicker.java b/server/src/com/vaadin/ui/AbstractColorPicker.java index d7037e366d..c3bdd49155 100644 --- a/server/src/com/vaadin/ui/AbstractColorPicker.java +++ b/server/src/com/vaadin/ui/AbstractColorPicker.java @@ -405,6 +405,7 @@ public abstract class AbstractColorPicker extends AbstractComponent implements window.setImmediate(true); window.addCloseListener(this); window.addColorChangeListener(new ColorChangeListener() { + @Override public void colorChanged(ColorChangeEvent event) { AbstractColorPicker.this.colorChanged(event); } diff --git a/server/src/com/vaadin/ui/AbstractField.java b/server/src/com/vaadin/ui/AbstractField.java index 422e0a1796..6648f69ff9 100644 --- a/server/src/com/vaadin/ui/AbstractField.java +++ b/server/src/com/vaadin/ui/AbstractField.java @@ -25,13 +25,13 @@ import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Locale; -import java.util.logging.Logger; import com.vaadin.data.Buffered; import com.vaadin.data.Property; import com.vaadin.data.Validatable; import com.vaadin.data.Validator; import com.vaadin.data.Validator.InvalidValueException; +import com.vaadin.data.util.LegacyPropertyHelper; import com.vaadin.data.util.converter.Converter; import com.vaadin.data.util.converter.Converter.ConversionException; import com.vaadin.data.util.converter.ConverterUtil; @@ -42,6 +42,7 @@ import com.vaadin.server.AbstractErrorMessage; import com.vaadin.server.CompositeErrorMessage; import com.vaadin.server.ErrorMessage; import com.vaadin.shared.AbstractFieldState; +import com.vaadin.shared.util.SharedUtil; /** * <p> @@ -74,9 +75,6 @@ public abstract class AbstractField<T> extends AbstractComponent implements /* Private members */ - private static final Logger logger = Logger.getLogger(AbstractField.class - .getName()); - /** * Value of the abstract field. */ @@ -370,13 +368,24 @@ public abstract class AbstractField<T> extends AbstractComponent implements return buffered; } - /* Property interface implementation */ - /** - * Returns the (field) value converted to a String using toString(). + * Returns a string representation of this object. The returned string + * representation depends on if the legacy Property toString mode is enabled + * or disabled. + * <p> + * If legacy Property toString mode is enabled, returns the value of this + * <code>Field</code> converted to a String. + * </p> + * <p> + * If legacy Property toString mode is disabled, the string representation + * has no special meaning + * </p> + * + * @see LegacyPropertyHelper#isLegacyToStringEnabled() * - * @see java.lang.Object#toString() - * @deprecated As of 7.0, use {@link #getValue()} to get the value of the + * @return A string representation of the value value stored in the Property + * or a string representation of the Property object. + * @deprecated As of 7.0. Use {@link #getValue()} to get the value of the * field, {@link #getConvertedValue()} to get the field value * converted to the data model type or * {@link #getPropertyDataSource()} .getValue() to get the value @@ -385,17 +394,15 @@ public abstract class AbstractField<T> extends AbstractComponent implements @Deprecated @Override public String toString() { - logger.warning("You are using AbstractField.toString() to get the value for a " - + getClass().getSimpleName() - + ". This will not be supported starting from Vaadin 7.1 " - + "(your debugger might call toString() and cause this message to appear)."); - final Object value = getFieldValue(); - if (value == null) { - return null; + if (!LegacyPropertyHelper.isLegacyToStringEnabled()) { + return super.toString(); + } else { + return LegacyPropertyHelper.legacyPropertyToString(this); } - return value.toString(); } + /* Property interface implementation */ + /** * Gets the current value of the field. * @@ -451,7 +458,7 @@ public abstract class AbstractField<T> extends AbstractComponent implements throws Property.ReadOnlyException, Converter.ConversionException, InvalidValueException { - if (!equals(newFieldValue, getInternalValue())) { + if (!SharedUtil.equals(newFieldValue, getInternalValue())) { // Read only fields can not be changed if (isReadOnly()) { @@ -459,7 +466,8 @@ public abstract class AbstractField<T> extends AbstractComponent implements } try { T doubleConvertedFieldValue = convertFromModel(convertToModel(newFieldValue)); - if (!equals(newFieldValue, doubleConvertedFieldValue)) { + if (!SharedUtil + .equals(newFieldValue, doubleConvertedFieldValue)) { newFieldValue = doubleConvertedFieldValue; repaintIsNotNeeded = false; } @@ -536,11 +544,9 @@ public abstract class AbstractField<T> extends AbstractComponent implements } } + @Deprecated static boolean equals(Object value1, Object value2) { - if (value1 == null) { - return value2 == null; - } - return value1.equals(value2); + return SharedUtil.equals(value1, value2); } /* External data source */ @@ -1228,8 +1234,8 @@ public abstract class AbstractField<T> extends AbstractComponent implements public void valueChange(Property.ValueChangeEvent event) { if (!isBuffered()) { if (committingValueToDataSource) { - boolean propertyNotifiesOfTheBufferedValue = equals(event - .getProperty().getValue(), getInternalValue()); + boolean propertyNotifiesOfTheBufferedValue = SharedUtil.equals( + event.getProperty().getValue(), getInternalValue()); if (!propertyNotifiesOfTheBufferedValue) { /* * Property (or chained property like PropertyFormatter) now @@ -1345,15 +1351,33 @@ public abstract class AbstractField<T> extends AbstractComponent implements } private void localeMightHaveChanged() { - if (!equals(valueLocale, getLocale()) && dataSource != null - && !isModified()) { - // When we have a data source and the internal value is directly - // read from that we want to update the value - T newInternalValue = convertFromModel(getPropertyDataSource() - .getValue()); - if (!equals(newInternalValue, getInternalValue())) { - setInternalValue(newInternalValue); - fireValueChange(false); + if (!SharedUtil.equals(valueLocale, getLocale())) { + // The locale HAS actually changed + + if (dataSource != null && !isModified()) { + // When we have a data source and the internal value is directly + // read from that we want to update the value + T newInternalValue = convertFromModel(getPropertyDataSource() + .getValue()); + if (!SharedUtil.equals(newInternalValue, getInternalValue())) { + setInternalValue(newInternalValue); + fireValueChange(false); + } + } else if (dataSource == null && converter != null) { + /* + * No data source but a converter has been set. The same issues + * as above but we cannot use propertyDataSource. Convert the + * current value back to a model value using the old locale and + * then convert back using the new locale. If this does not + * match the field value we need to set the converted value + * again. + */ + Object convertedValue = convertToModel(getInternalValue(), + valueLocale); + T newinternalValue = convertFromModel(convertedValue); + if (!SharedUtil.equals(getInternalValue(), newinternalValue)) { + setConvertedValue(convertedValue); + } } } } @@ -1600,7 +1624,7 @@ public abstract class AbstractField<T> extends AbstractComponent implements setModified(false); // If the new value differs from the previous one - if (!equals(newFieldValue, getInternalValue())) { + if (!SharedUtil.equals(newFieldValue, getInternalValue())) { setInternalValue(newFieldValue); fireValueChange(false); } else if (wasModified) { diff --git a/server/src/com/vaadin/ui/AbstractMedia.java b/server/src/com/vaadin/ui/AbstractMedia.java index 41677467bb..97947b568d 100644 --- a/server/src/com/vaadin/ui/AbstractMedia.java +++ b/server/src/com/vaadin/ui/AbstractMedia.java @@ -25,6 +25,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import com.vaadin.server.ConnectorResource; +import com.vaadin.server.DownloadStream; import com.vaadin.server.Resource; import com.vaadin.server.ResourceReference; import com.vaadin.server.VaadinRequest; @@ -83,7 +84,14 @@ public abstract class AbstractMedia extends AbstractComponent { public boolean handleConnectorRequest(VaadinRequest request, VaadinResponse response, String path) throws IOException { Matcher matcher = Pattern.compile("(\\d+)(/.*)?").matcher(path); - if (matcher.matches()) { + if (!matcher.matches()) { + return super.handleConnectorRequest(request, response, path); + } + + DownloadStream stream; + + getSession().lock(); + try { List<URLReference> sources = getState().sources; int sourceIndex = Integer.parseInt(matcher.group(1)); @@ -98,11 +106,13 @@ public abstract class AbstractMedia extends AbstractComponent { URLReference reference = sources.get(sourceIndex); ConnectorResource resource = (ConnectorResource) ResourceReference .getResource(reference); - resource.getStream().writeResponse(request, response); - return true; - } else { - return super.handleConnectorRequest(request, response, path); + stream = resource.getStream(); + } finally { + getSession().unlock(); } + + stream.writeResponse(request, response); + return true; } private Logger getLogger() { diff --git a/server/src/com/vaadin/ui/AbstractOrderedLayout.java b/server/src/com/vaadin/ui/AbstractOrderedLayout.java index 8c2f86926d..c9eb756daa 100644 --- a/server/src/com/vaadin/ui/AbstractOrderedLayout.java +++ b/server/src/com/vaadin/ui/AbstractOrderedLayout.java @@ -53,6 +53,8 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements */ protected LinkedList<Component> components = new LinkedList<Component>(); + private Alignment defaultComponentAlignment = Alignment.TOP_LEFT; + /* Child component alignments */ /** @@ -147,7 +149,9 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements } private void componentAdded(Component c) { - getState().childData.put(c, new ChildComponentData()); + ChildComponentData ccd = new ChildComponentData(); + ccd.alignmentBitmask = getDefaultComponentAlignment().getBitMask(); + getState().childData.put(c, ccd); } /** @@ -417,4 +421,27 @@ public abstract class AbstractOrderedLayout extends AbstractLayout implements public void setMargin(MarginInfo marginInfo) { getState().marginsBitmask = marginInfo.getBitMask(); } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.Layout.AlignmentHandler#getDefaultComponentAlignment() + */ + @Override + public Alignment getDefaultComponentAlignment() { + return defaultComponentAlignment; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.ui.Layout.AlignmentHandler#setDefaultComponentAlignment(com + * .vaadin.ui.Alignment) + */ + @Override + public void setDefaultComponentAlignment(Alignment defaultAlignment) { + defaultComponentAlignment = defaultAlignment; + } + } diff --git a/server/src/com/vaadin/ui/Button.java b/server/src/com/vaadin/ui/Button.java index fcfc55aadc..1bcf802f12 100644 --- a/server/src/com/vaadin/ui/Button.java +++ b/server/src/com/vaadin/ui/Button.java @@ -32,6 +32,7 @@ import com.vaadin.event.ShortcutAction; import com.vaadin.event.ShortcutAction.KeyCode; import com.vaadin.event.ShortcutAction.ModifierKey; import com.vaadin.event.ShortcutListener; +import com.vaadin.server.Resource; import com.vaadin.shared.MouseEventDetails; import com.vaadin.shared.ui.button.ButtonServerRpc; import com.vaadin.shared.ui.button.ButtonState; @@ -581,6 +582,35 @@ public class Button extends AbstractComponent implements } /** + * Sets the component's icon and alt text. + * + * An alt text is shown when an image could not be loaded, and read by + * assisitve devices. + * + * @param icon + * the icon to be shown with the component's caption. + * @param iconAltText + * String to use as alt text + */ + public void setIcon(Resource icon, String iconAltText) { + super.setIcon(icon); + getState().iconAltText = iconAltText == null ? "" : iconAltText; + } + + /** + * Returns the icon's alt text. + * + * @return String with the alt text + */ + public String getIconAlternateText() { + return getState().iconAltText; + } + + public void setIconAlternateText(String iconAltText) { + getState().iconAltText = iconAltText; + } + + /** * Set whether the caption text is rendered as HTML or not. You might need * to retheme button to allow higher content than the original text style. * diff --git a/server/src/com/vaadin/ui/Calendar.java b/server/src/com/vaadin/ui/Calendar.java new file mode 100644 index 0000000000..38fa355dd8 --- /dev/null +++ b/server/src/com/vaadin/ui/Calendar.java @@ -0,0 +1,1845 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui; + +import java.lang.reflect.Method; +import java.text.DateFormat; +import java.text.DateFormatSymbols; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.EventListener; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.TimeZone; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.vaadin.data.Container; +import com.vaadin.data.util.BeanItemContainer; +import com.vaadin.event.Action; +import com.vaadin.event.Action.Handler; +import com.vaadin.event.dd.DropHandler; +import com.vaadin.event.dd.DropTarget; +import com.vaadin.event.dd.TargetDetails; +import com.vaadin.server.KeyMapper; +import com.vaadin.shared.ui.calendar.CalendarEventId; +import com.vaadin.shared.ui.calendar.CalendarServerRpc; +import com.vaadin.shared.ui.calendar.CalendarState; +import com.vaadin.shared.ui.calendar.DateConstants; +import com.vaadin.ui.components.calendar.CalendarComponentEvent; +import com.vaadin.ui.components.calendar.CalendarComponentEvents; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.BackwardEvent; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.BackwardHandler; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickEvent; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickHandler; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventClick; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventClickHandler; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventMoveHandler; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResize; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResizeHandler; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.ForwardEvent; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.ForwardHandler; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.MoveEvent; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.RangeSelectEvent; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.RangeSelectHandler; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClick; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClickHandler; +import com.vaadin.ui.components.calendar.CalendarDateRange; +import com.vaadin.ui.components.calendar.CalendarTargetDetails; +import com.vaadin.ui.components.calendar.ContainerEventProvider; +import com.vaadin.ui.components.calendar.event.BasicEventProvider; +import com.vaadin.ui.components.calendar.event.CalendarEditableEventProvider; +import com.vaadin.ui.components.calendar.event.CalendarEvent; +import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeEvent; +import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeListener; +import com.vaadin.ui.components.calendar.event.CalendarEventProvider; +import com.vaadin.ui.components.calendar.handler.BasicBackwardHandler; +import com.vaadin.ui.components.calendar.handler.BasicDateClickHandler; +import com.vaadin.ui.components.calendar.handler.BasicEventMoveHandler; +import com.vaadin.ui.components.calendar.handler.BasicEventResizeHandler; +import com.vaadin.ui.components.calendar.handler.BasicForwardHandler; +import com.vaadin.ui.components.calendar.handler.BasicWeekClickHandler; + +/** + * <p> + * Vaadin Calendar is for visualizing events in a calendar. Calendar events can + * be visualized in the variable length view depending on the start and end + * dates. + * </p> + * + * <li>You can set the viewable date range with the {@link #setStartDate(Date)} + * and {@link #setEndDate(Date)} methods. Calendar has a default date range of + * one week</li> + * + * <li>Calendar has two kind of views: monthly and weekly view</li> + * + * <li>If date range is seven days or shorter, the weekly view is used.</li> + * + * <li>Calendar queries its events by using a + * {@link com.vaadin.addon.calendar.event.CalendarEventProvider + * CalendarEventProvider}. By default, a + * {@link com.vaadin.addon.calendar.event.BasicEventProvider BasicEventProvider} + * is used.</li> + * + * @since 7.1 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class Calendar extends AbstractComponent implements + CalendarComponentEvents.NavigationNotifier, + CalendarComponentEvents.EventMoveNotifier, + CalendarComponentEvents.RangeSelectNotifier, + CalendarComponentEvents.EventResizeNotifier, + CalendarEventProvider.EventSetChangeListener, DropTarget, + CalendarEditableEventProvider, Action.Container { + + /** + * Calendar can use either 12 hours clock or 24 hours clock. + */ + public enum TimeFormat { + + Format12H(), Format24H(); + } + + /** Defines currently active format for time. 12H/24H. */ + protected TimeFormat currentTimeFormat; + + /** Internal calendar data source. */ + protected java.util.Calendar currentCalendar = java.util.Calendar + .getInstance(); + + /** Defines the component's active time zone. */ + protected TimeZone timezone; + + /** Defines the calendar's date range starting point. */ + protected Date startDate = null; + + /** Defines the calendar's date range ending point. */ + protected Date endDate = null; + + /** Event provider. */ + private CalendarEventProvider calendarEventProvider; + + /** + * Internal buffer for the events that are retrieved from the event + * provider. + */ + protected List<CalendarEvent> events; + + /** Date format that will be used in the UIDL for dates. */ + protected DateFormat df_date = new SimpleDateFormat("yyyy-MM-dd"); + + /** Time format that will be used in the UIDL for time. */ + protected DateFormat df_time = new SimpleDateFormat("HH:mm:ss"); + + /** Date format that will be used in the UIDL for both date and time. */ + protected DateFormat df_date_time = new SimpleDateFormat( + DateConstants.CLIENT_DATE_FORMAT + "-" + + DateConstants.CLIENT_TIME_FORMAT); + + /** + * Week view's scroll position. Client sends updates to this value so that + * scroll position wont reset all the time. + */ + private int scrollTop = 0; + + /** Caption format for the weekly view */ + private String weeklyCaptionFormat = null; + + /** Map from event ids to event handlers */ + private final Map<String, EventListener> handlers; + + /** + * Drop Handler for Vaadin DD. By default null. + */ + private DropHandler dropHandler; + + /** + * First day to show for a week + */ + private int firstDay = 1; + + /** + * Last day to show for a week + */ + private int lastDay = 7; + + /** + * First hour to show for a day + */ + private int firstHour = 0; + + /** + * Last hour to show for a day + */ + private int lastHour = 23; + + /** + * List of action handlers. + */ + private LinkedList<Action.Handler> actionHandlers = null; + + /** + * Action mapper. + */ + private KeyMapper<Action> actionMapper = null; + + /** + * + */ + private CalendarServerRpcImpl rpc = new CalendarServerRpcImpl(); + + /** + * Returns the logger for the calendar + */ + protected Logger getLogger() { + return Logger.getLogger(Calendar.class.getName()); + } + + /** + * Construct a Vaadin Calendar with a BasicEventProvider and no caption. + * Default date range is one week. + */ + public Calendar() { + this(null, new BasicEventProvider()); + } + + /** + * Construct a Vaadin Calendar with a BasicEventProvider and the provided + * caption. Default date range is one week. + * + * @param caption + */ + public Calendar(String caption) { + this(caption, new BasicEventProvider()); + } + + /** + * <p> + * Construct a Vaadin Calendar with event provider. Event provider is + * obligatory, because calendar component will query active events through + * it. + * </p> + * + * <p> + * By default, Vaadin Calendar will show dates from the start of the current + * week to the end of the current week. Use {@link #setStartDate(Date)} and + * {@link #setEndDate(Date)} to change this. + * </p> + * + * @param eventProvider + * Event provider, cannot be null. + */ + public Calendar(CalendarEventProvider eventProvider) { + this(null, eventProvider); + } + + /** + * <p> + * Construct a Vaadin Calendar with event provider and a caption. Event + * provider is obligatory, because calendar component will query active + * events through it. + * </p> + * + * <p> + * By default, Vaadin Calendar will show dates from the start of the current + * week to the end of the current week. Use {@link #setStartDate(Date)} and + * {@link #setEndDate(Date)} to change this. + * </p> + * + * @param eventProvider + * Event provider, cannot be null. + */ + // this is the constructor every other constructor calls + public Calendar(String caption, CalendarEventProvider eventProvider) { + registerRpc(rpc); + setCaption(caption); + handlers = new HashMap<String, EventListener>(); + setDefaultHandlers(); + currentCalendar.setTime(new Date()); + setEventProvider(eventProvider); + getState().firstDayOfWeek = firstDay; + getState().lastVisibleDayOfWeek = lastDay; + getState().firstHourOfDay = firstHour; + getState().lastHourOfDay = lastHour; + setTimeFormat(null); + + } + + @Override + public CalendarState getState() { + return (CalendarState) super.getState(); + } + + @Override + public void beforeClientResponse(boolean initial) { + super.beforeClientResponse(initial); + + initCalendarWithLocale(); + + getState().format24H = TimeFormat.Format24H == getTimeFormat(); + setupDaysAndActions(); + setupCalendarEvents(); + rpc.scroll(scrollTop); + } + + /** + * Set all the wanted default handlers here. This is always called after + * constructing this object. All other events have default handlers except + * range and event click. + */ + protected void setDefaultHandlers() { + setHandler(new BasicBackwardHandler()); + setHandler(new BasicForwardHandler()); + setHandler(new BasicWeekClickHandler()); + setHandler(new BasicDateClickHandler()); + setHandler(new BasicEventMoveHandler()); + setHandler(new BasicEventResizeHandler()); + } + + /** + * Gets the calendar's start date. + * + * @return First visible date. + */ + public Date getStartDate() { + if (startDate == null) { + currentCalendar.set(java.util.Calendar.DAY_OF_WEEK, + currentCalendar.getFirstDayOfWeek()); + return currentCalendar.getTime(); + } + return startDate; + } + + /** + * Sets start date for the calendar. This and {@link #setEndDate(Date)} + * control the range of dates visible on the component. The default range is + * one week. + * + * @param date + * First visible date to show. + */ + public void setStartDate(Date date) { + if (!date.equals(startDate)) { + startDate = date; + markAsDirty(); + } + } + + /** + * Gets the calendar's end date. + * + * @return Last visible date. + */ + public Date getEndDate() { + if (endDate == null) { + currentCalendar.set(java.util.Calendar.DAY_OF_WEEK, + currentCalendar.getFirstDayOfWeek() + 6); + return currentCalendar.getTime(); + } + return endDate; + } + + /** + * Sets end date for the calendar. Starting from startDate, only six weeks + * will be shown if duration to endDate is longer than six weeks. + * + * This and {@link #setStartDate(Date)} control the range of dates visible + * on the component. The default range is one week. + * + * @param date + * Last visible date to show. + */ + public void setEndDate(Date date) { + if (startDate != null && startDate.after(date)) { + startDate = (Date) date.clone(); + markAsDirty(); + } else if (!date.equals(endDate)) { + endDate = date; + markAsDirty(); + } + } + + /** + * Sets the locale to be used in the Calendar component. + * + * @see com.vaadin.ui.AbstractComponent#setLocale(java.util.Locale) + */ + @Override + public void setLocale(Locale newLocale) { + super.setLocale(newLocale); + initCalendarWithLocale(); + } + + /** + * Initialize the java calendar instance with the current locale and + * timezone. + */ + private void initCalendarWithLocale() { + if (timezone != null) { + currentCalendar = java.util.Calendar.getInstance(timezone, + getLocale()); + + } else { + currentCalendar = java.util.Calendar.getInstance(getLocale()); + } + } + + private void setupCalendarEvents() { + int durationInDays = (int) (((endDate.getTime()) - startDate.getTime()) / DateConstants.DAYINMILLIS); + durationInDays++; + if (durationInDays > 60) { + throw new RuntimeException("Daterange is too big (max 60) = " + + durationInDays); + } + + Date firstDateToShow = expandStartDate(startDate, durationInDays > 7); + Date lastDateToShow = expandEndDate(endDate, durationInDays > 7); + + currentCalendar.setTime(firstDateToShow); + events = getEventProvider().getEvents(firstDateToShow, lastDateToShow); + + List<CalendarState.Event> calendarStateEvents = new ArrayList<CalendarState.Event>(); + if (events != null) { + for (int i = 0; i < events.size(); i++) { + CalendarEvent e = events.get(i); + CalendarState.Event event = new CalendarState.Event(); + event.index = i; + event.caption = e.getCaption() == null ? "" : e.getCaption(); + event.dateFrom = df_date.format(e.getStart()); + event.dateTo = df_date.format(e.getEnd()); + event.timeFrom = df_time.format(e.getStart()); + event.timeTo = df_time.format(e.getEnd()); + event.description = e.getDescription() == null ? "" : e + .getDescription(); + event.styleName = e.getStyleName() == null ? "" : e + .getStyleName(); + event.allDay = e.isAllDay(); + calendarStateEvents.add(event); + } + } + getState().events = calendarStateEvents; + } + + private void setupDaysAndActions() { + // Make sure we have a up-to-date locale + initCalendarWithLocale(); + + CalendarState state = getState(); + + state.firstDayOfWeek = currentCalendar.getFirstDayOfWeek(); + + // If only one is null, throw exception + // If both are null, set defaults + if (startDate == null ^ endDate == null) { + String message = "Schedule cannot be painted without a proper date range.\n"; + if (startDate == null) { + throw new IllegalStateException(message + + "You must set a start date using setStartDate(Date)."); + + } else { + throw new IllegalStateException(message + + "You must set an end date using setEndDate(Date)."); + } + + } else if (startDate == null && endDate == null) { + // set defaults + startDate = getStartDate(); + endDate = getEndDate(); + } + + int durationInDays = (int) (((endDate.getTime()) - startDate.getTime()) / DateConstants.DAYINMILLIS); + durationInDays++; + if (durationInDays > 60) { + throw new RuntimeException("Daterange is too big (max 60) = " + + durationInDays); + } + + state.dayNames = getDayNamesShort(); + state.monthNames = getMonthNamesShort(); + + // Use same timezone in all dates this component handles. + // Show "now"-marker in browser within given timezone. + Date now = new Date(); + currentCalendar.setTime(now); + now = currentCalendar.getTime(); + + // Reset time zones for custom date formats + df_date.setTimeZone(currentCalendar.getTimeZone()); + df_time.setTimeZone(currentCalendar.getTimeZone()); + + state.now = (df_date.format(now) + " " + df_time.format(now)); + + Date firstDateToShow = expandStartDate(startDate, durationInDays > 7); + Date lastDateToShow = expandEndDate(endDate, durationInDays > 7); + + currentCalendar.setTime(firstDateToShow); + + DateFormat weeklyCaptionFormatter = getWeeklyCaptionFormatter(); + weeklyCaptionFormatter.setTimeZone(currentCalendar.getTimeZone()); + + Map<CalendarDateRange, Set<Action>> actionMap = new HashMap<CalendarDateRange, Set<Action>>(); + + List<CalendarState.Day> days = new ArrayList<CalendarState.Day>(); + + // Send all dates to client from server. This + // approach was taken because gwt doesn't + // support date localization properly. + while (currentCalendar.getTime().compareTo(lastDateToShow) < 1) { + final Date date = currentCalendar.getTime(); + final CalendarState.Day day = new CalendarState.Day(); + day.date = df_date.format(date); + day.localizedDateFormat = weeklyCaptionFormatter.format(date); + day.dayOfWeek = getDowByLocale(currentCalendar); + day.week = currentCalendar.get(java.util.Calendar.WEEK_OF_YEAR); + + days.add(day); + + // Get actions for a specific date + if (actionHandlers != null) { + for (Action.Handler actionHandler : actionHandlers) { + + // Create calendar which omits time + GregorianCalendar cal = new GregorianCalendar( + getTimeZone(), getLocale()); + cal.clear(); + cal.set(currentCalendar.get(java.util.Calendar.YEAR), + currentCalendar.get(java.util.Calendar.MONTH), + currentCalendar.get(java.util.Calendar.DATE)); + + // Get day start and end times + Date start = cal.getTime(); + cal.add(java.util.Calendar.DATE, 1); + Date end = cal.getTime(); + + boolean monthView = (durationInDays > 7); + + /** + * If in day or week view add actions for each half-an-hour. + * If in month view add actions for each day + */ + if (monthView) { + setActionsForDay(actionMap, start, end, actionHandler); + } else { + setActionsForEachHalfHour(actionMap, start, end, + actionHandler); + } + + } + } + + currentCalendar.add(java.util.Calendar.DATE, 1); + } + state.days = days; + state.actions = createActionsList(actionMap); + } + + private void setActionsForEachHalfHour( + Map<CalendarDateRange, Set<Action>> actionMap, Date start, + Date end, Action.Handler actionHandler) { + GregorianCalendar cal = new GregorianCalendar(getTimeZone(), + getLocale()); + cal.setTime(start); + while (cal.getTime().before(end)) { + Date s = cal.getTime(); + cal.add(java.util.Calendar.MINUTE, 30); + Date e = cal.getTime(); + CalendarDateRange range = new CalendarDateRange(s, e, getTimeZone()); + Action[] actions = actionHandler.getActions(range, this); + if (actions != null) { + Set<Action> actionSet = new HashSet<Action>( + Arrays.asList(actions)); + actionMap.put(range, actionSet); + } + } + } + + private void setActionsForDay( + Map<CalendarDateRange, Set<Action>> actionMap, Date start, + Date end, Action.Handler actionHandler) { + CalendarDateRange range = new CalendarDateRange(start, end, + getTimeZone()); + Action[] actions = actionHandler.getActions(range, this); + if (actions != null) { + Set<Action> actionSet = new HashSet<Action>(Arrays.asList(actions)); + actionMap.put(range, actionSet); + } + } + + private List<CalendarState.Action> createActionsList( + Map<CalendarDateRange, Set<Action>> actionMap) { + if (actionMap.isEmpty()) { + return null; + } + + List<CalendarState.Action> calendarActions = new ArrayList<CalendarState.Action>(); + + SimpleDateFormat formatter = new SimpleDateFormat( + DateConstants.ACTION_DATE_FORMAT_PATTERN); + formatter.setTimeZone(getTimeZone()); + + for (Entry<CalendarDateRange, Set<Action>> entry : actionMap.entrySet()) { + CalendarDateRange range = entry.getKey(); + Set<Action> actions = entry.getValue(); + for (Action action : actions) { + String key = actionMapper.key(action); + CalendarState.Action calendarAction = new CalendarState.Action(); + calendarAction.actionKey = key; + calendarAction.caption = action.getCaption(); + setResource(key, action.getIcon()); + calendarAction.iconKey = key; + calendarAction.startDate = formatter.format(range.getStart()); + calendarAction.endDate = formatter.format(range.getEnd()); + calendarActions.add(calendarAction); + } + } + + return calendarActions; + } + + /** + * Gets currently active time format. Value is either TimeFormat.Format12H + * or TimeFormat.Format24H. + * + * @return TimeFormat Format for the time. + */ + public TimeFormat getTimeFormat() { + if (currentTimeFormat == null) { + SimpleDateFormat f = (SimpleDateFormat) SimpleDateFormat + .getTimeInstance(SimpleDateFormat.SHORT, getLocale()); + String p = f.toPattern(); + if (p.indexOf("HH") != -1 || p.indexOf("H") != -1) { + return TimeFormat.Format24H; + } + return TimeFormat.Format12H; + } + return currentTimeFormat; + } + + /** + * Example: <code>setTimeFormat(TimeFormat.Format12H);</code></br> Set to + * null, if you want the format being defined by the locale. + * + * @param format + * Set 12h or 24h format. Default is defined by the locale. + */ + public void setTimeFormat(TimeFormat format) { + currentTimeFormat = format; + markAsDirty(); + } + + /** + * Returns a time zone that is currently used by this component. + * + * @return Component's Time zone + */ + public TimeZone getTimeZone() { + if (timezone == null) { + return currentCalendar.getTimeZone(); + } + return timezone; + } + + /** + * Set time zone that this component will use. Null value sets the default + * time zone. + * + * @param zone + * Time zone to use + */ + public void setTimeZone(TimeZone zone) { + timezone = zone; + if (!currentCalendar.getTimeZone().equals(zone)) { + if (zone == null) { + zone = TimeZone.getDefault(); + } + currentCalendar.setTimeZone(zone); + df_date_time.setTimeZone(zone); + markAsDirty(); + } + } + + /** + * Get the internally used Calendar instance. This is the currently used + * instance of {@link java.util.Calendar} but is bound to change during the + * lifetime of the component. + * + * @return the currently used java calendar + */ + public java.util.Calendar getInternalCalendar() { + return currentCalendar; + } + + /** + * <p> + * This method restricts the weekdays that are shown. This affects both the + * monthly and the weekly view. The general contract is that <b>firstDay < + * lastDay</b>. + * </p> + * + * <p> + * Note that this only affects the rendering process. Events are still + * requested by the dates set by {@link #setStartDate(Date)} and + * {@link #setEndDate(Date)}. + * </p> + * + * @param firstDay + * the first day of the week to show, between 1 and 7 + */ + public void setFirstVisibleDayOfWeek(int firstDay) { + if (this.firstDay != firstDay && firstDay >= 1 && firstDay <= 7 + && getLastVisibleDayOfWeek() >= firstDay) { + this.firstDay = firstDay; + getState().firstVisibleDayOfWeek = firstDay; + } + } + + /** + * Get the first visible day of the week. Returns the weekdays as integers + * represented by {@link java.util.Calendar#DAY_OF_WEEK} + * + * @return An integer representing the week day according to + * {@link java.util.Calendar#DAY_OF_WEEK} + */ + public int getFirstVisibleDayOfWeek() { + return firstDay; + } + + /** + * <p> + * This method restricts the weekdays that are shown. This affects both the + * monthly and the weekly view. The general contract is that <b>firstDay < + * lastDay</b>. + * </p> + * + * <p> + * Note that this only affects the rendering process. Events are still + * requested by the dates set by {@link #setStartDate(Date)} and + * {@link #setEndDate(Date)}. + * </p> + * + * @param lastDay + * the first day of the week to show, between 1 and 7 + */ + public void setLastVisibleDayOfWeek(int lastDay) { + if (this.lastDay != lastDay && lastDay >= 1 && lastDay <= 7 + && getFirstVisibleDayOfWeek() <= lastDay) { + this.lastDay = lastDay; + getState().lastVisibleDayOfWeek = lastDay; + } + } + + /** + * Get the last visible day of the week. Returns the weekdays as integers + * represented by {@link java.util.Calendar#DAY_OF_WEEK} + * + * @return An integer representing the week day according to + * {@link java.util.Calendar#DAY_OF_WEEK} + */ + public int getLastVisibleDayOfWeek() { + return lastDay; + } + + /** + * <p> + * This method restricts the hours that are shown per day. This affects the + * weekly view. The general contract is that <b>firstHour < lastHour</b>. + * </p> + * + * <p> + * Note that this only affects the rendering process. Events are still + * requested by the dates set by {@link #setStartDate(Date)} and + * {@link #setEndDate(Date)}. + * </p> + * + * @param firstHour + * the first hour of the day to show, between 0 and 23 + */ + public void setFirstVisibleHourOfDay(int firstHour) { + if (this.firstHour != firstHour && firstHour >= 0 && firstHour <= 23 + && firstHour <= getLastVisibleHourOfDay()) { + this.firstHour = firstHour; + getState().firstHourOfDay = firstHour; + } + } + + /** + * Returns the first visible hour in the week view. Returns the hour using a + * 24h time format + * + */ + public int getFirstVisibleHourOfDay() { + return firstHour; + } + + /** + * <p> + * This method restricts the hours that are shown per day. This affects the + * weekly view. The general contract is that <b>firstHour < lastHour</b>. + * </p> + * + * <p> + * Note that this only affects the rendering process. Events are still + * requested by the dates set by {@link #setStartDate(Date)} and + * {@link #setEndDate(Date)}. + * </p> + * + * @param lastHour + * the first hour of the day to show, between 0 and 23 + */ + public void setLastVisibleHourOfDay(int lastHour) { + if (this.lastHour != lastHour && lastHour >= 0 && lastHour <= 23 + && lastHour >= getFirstVisibleHourOfDay()) { + this.lastHour = lastHour; + getState().lastHourOfDay = lastHour; + } + } + + /** + * Returns the last visible hour in the week view. Returns the hour using a + * 24h time format + * + */ + public int getLastVisibleHourOfDay() { + return lastHour; + } + + /** + * Gets the date caption format for the weekly view. + * + * @return The pattern used in caption of dates in weekly view. + */ + public String getWeeklyCaptionFormat() { + return weeklyCaptionFormat; + } + + /** + * Sets custom date format for the weekly view. This is the caption of the + * date. Format could be like "mmm MM/dd". + * + * @param dateFormatPattern + * The date caption pattern. + */ + public void setWeeklyCaptionFormat(String dateFormatPattern) { + if ((weeklyCaptionFormat == null && dateFormatPattern != null) + || (weeklyCaptionFormat != null && !weeklyCaptionFormat + .equals(dateFormatPattern))) { + weeklyCaptionFormat = dateFormatPattern; + markAsDirty(); + } + } + + private DateFormat getWeeklyCaptionFormatter() { + if (weeklyCaptionFormat != null) { + return new SimpleDateFormat(weeklyCaptionFormat, getLocale()); + } else { + return SimpleDateFormat.getDateInstance(SimpleDateFormat.SHORT, + getLocale()); + } + } + + /** + * Get the day of week by the given calendar and its locale + * + * @param calendar + * The calendar to use + * @return + */ + private static int getDowByLocale(java.util.Calendar calendar) { + int fow = calendar.get(java.util.Calendar.DAY_OF_WEEK); + + // monday first + if (calendar.getFirstDayOfWeek() == java.util.Calendar.MONDAY) { + fow = (fow == java.util.Calendar.SUNDAY) ? 7 : fow - 1; + } + + return fow; + } + + /** + * Is the user allowed to trigger events which alters the events + * + * @return true if the client is allowed to send changes to server + * @see #isEventClickAllowed() + */ + protected boolean isClientChangeAllowed() { + return !isReadOnly() && isEnabled(); + } + + /** + * Is the user allowed to trigger click events + * + * @return true if the client is allowed to click events + * @see #isClientChangeAllowed() + */ + protected boolean isEventClickAllowed() { + return isEnabled(); + } + + /** + * Fires an event when the user selecing moving forward/backward in the + * calendar. + * + * @param forward + * True if the calendar moved forward else backward is assumed. + */ + protected void fireNavigationEvent(boolean forward) { + if (forward) { + fireEvent(new ForwardEvent(this)); + } else { + fireEvent(new BackwardEvent(this)); + } + } + + /** + * Fires an event move event to all server side move listerners + * + * @param index + * The index of the event in the events list + * @param newFromDatetime + * The changed from date time + */ + protected void fireEventMove(int index, Date newFromDatetime) { + MoveEvent event = new MoveEvent(this, events.get(index), + newFromDatetime); + + if (calendarEventProvider instanceof EventMoveHandler) { + // Notify event provider if it is an event move handler + ((EventMoveHandler) calendarEventProvider).eventMove(event); + } + + // Notify event move handler attached by using the + // setHandler(EventMoveHandler) method + fireEvent(event); + } + + /** + * Fires event when a week was clicked in the calendar. + * + * @param week + * The week that was clicked + * @param year + * The year of the week + */ + protected void fireWeekClick(int week, int year) { + fireEvent(new WeekClick(this, week, year)); + } + + /** + * Fires event when a date was clicked in the calendar. Uses an existing + * event from the event cache. + * + * @param index + * The index of the event in the event cache. + */ + protected void fireEventClick(Integer index) { + fireEvent(new EventClick(this, events.get(index))); + } + + /** + * Fires event when a date was clicked in the calendar. Creates a new event + * for the date and passes it to the listener. + * + * @param date + * The date and time that was clicked + */ + protected void fireDateClick(Date date) { + fireEvent(new DateClickEvent(this, date)); + } + + /** + * Fires an event range selected event. The event is fired when a user + * highlights an area in the calendar. The highlighted areas start and end + * dates are returned as arguments. + * + * @param from + * The start date and time of the highlighted area + * @param to + * The end date and time of the highlighted area + * @param monthlyMode + * Is the calendar in monthly mode + */ + protected void fireRangeSelect(Date from, Date to, boolean monthlyMode) { + fireEvent(new RangeSelectEvent(this, from, to, monthlyMode)); + } + + /** + * Fires an event resize event. The event is fired when a user resizes the + * event in the calendar causing the time range of the event to increase or + * decrease. The new start and end times are returned as arguments to this + * method. + * + * @param index + * The index of the event in the event cache + * @param startTime + * The new start date and time of the event + * @param endTime + * The new end date and time of the event + */ + protected void fireEventResize(int index, Date startTime, Date endTime) { + EventResize event = new EventResize(this, events.get(index), startTime, + endTime); + + if (calendarEventProvider instanceof EventResizeHandler) { + // Notify event provider if it is an event resize handler + ((EventResizeHandler) calendarEventProvider).eventResize(event); + } + + // Notify event resize handler attached by using the + // setHandler(EventMoveHandler) method + fireEvent(event); + } + + /** + * Localized display names for week days starting from sunday. Returned + * array's length is always 7. + * + * @return Array of localized weekday names. + */ + protected String[] getDayNamesShort() { + DateFormatSymbols s = new DateFormatSymbols(getLocale()); + return Arrays.copyOfRange(s.getWeekdays(), 1, 8); + } + + /** + * Localized display names for months starting from January. Returned + * array's length is always 12. + * + * @return Array of localized month names. + */ + protected String[] getMonthNamesShort() { + DateFormatSymbols s = new DateFormatSymbols(getLocale()); + return Arrays.copyOf(s.getShortMonths(), 12); + } + + /** + * Gets a date that is first day in the week that target given date belongs + * to. + * + * @param date + * Target date + * @return Date that is first date in same week that given date is. + */ + protected Date getFirstDateForWeek(Date date) { + int firstDayOfWeek = currentCalendar.getFirstDayOfWeek(); + currentCalendar.setTime(date); + while (firstDayOfWeek != currentCalendar + .get(java.util.Calendar.DAY_OF_WEEK)) { + currentCalendar.add(java.util.Calendar.DATE, -1); + } + return currentCalendar.getTime(); + } + + /** + * Gets a date that is last day in the week that target given date belongs + * to. + * + * @param date + * Target date + * @return Date that is last date in same week that given date is. + */ + protected Date getLastDateForWeek(Date date) { + currentCalendar.setTime(date); + currentCalendar.add(java.util.Calendar.DATE, 1); + int firstDayOfWeek = currentCalendar.getFirstDayOfWeek(); + // Roll to weeks last day using firstdayofweek. Roll until FDofW is + // found and then roll back one day. + while (firstDayOfWeek != currentCalendar + .get(java.util.Calendar.DAY_OF_WEEK)) { + currentCalendar.add(java.util.Calendar.DATE, 1); + } + currentCalendar.add(java.util.Calendar.DATE, -1); + return currentCalendar.getTime(); + } + + /** + * Calculates the end time of the day using the given calendar and date + * + * @param date + * @param calendar + * the calendar instance to be used in the calculation. The given + * instance is unchanged in this operation. + * @return the given date, with time set to the end of the day + */ + private static Date getEndOfDay(java.util.Calendar calendar, Date date) { + java.util.Calendar calendarClone = (java.util.Calendar) calendar + .clone(); + + calendarClone.setTime(date); + calendarClone.set(java.util.Calendar.MILLISECOND, + calendarClone.getActualMaximum(java.util.Calendar.MILLISECOND)); + calendarClone.set(java.util.Calendar.SECOND, + calendarClone.getActualMaximum(java.util.Calendar.SECOND)); + calendarClone.set(java.util.Calendar.MINUTE, + calendarClone.getActualMaximum(java.util.Calendar.MINUTE)); + calendarClone.set(java.util.Calendar.HOUR, + calendarClone.getActualMaximum(java.util.Calendar.HOUR)); + calendarClone.set(java.util.Calendar.HOUR_OF_DAY, + calendarClone.getActualMaximum(java.util.Calendar.HOUR_OF_DAY)); + + return calendarClone.getTime(); + } + + /** + * Calculates the end time of the day using the given calendar and date + * + * @param date + * @param calendar + * the calendar instance to be used in the calculation. The given + * instance is unchanged in this operation. + * @return the given date, with time set to the end of the day + */ + private static Date getStartOfDay(java.util.Calendar calendar, Date date) { + java.util.Calendar calendarClone = (java.util.Calendar) calendar + .clone(); + + calendarClone.setTime(date); + calendarClone.set(java.util.Calendar.MILLISECOND, 0); + calendarClone.set(java.util.Calendar.SECOND, 0); + calendarClone.set(java.util.Calendar.MINUTE, 0); + calendarClone.set(java.util.Calendar.HOUR, 0); + calendarClone.set(java.util.Calendar.HOUR_OF_DAY, 0); + + return calendarClone.getTime(); + } + + /** + * Finds the first day of the week and returns a day representing the start + * of that day + * + * @param start + * The actual date + * @param expandToFullWeek + * Should the returned date be moved to the start of the week + * @return If expandToFullWeek is set then it returns the first day of the + * week, else it returns a clone of the actual date with the time + * set to the start of the day + */ + protected Date expandStartDate(Date start, boolean expandToFullWeek) { + // If the duration is more than week, use monthly view and get startweek + // and endweek. Example if views daterange is from tuesday to next weeks + // wednesday->expand to monday to nextweeks sunday. If firstdayofweek = + // monday + if (expandToFullWeek) { + start = getFirstDateForWeek(start); + + } else { + start = (Date) start.clone(); + } + + // Always expand to the start of the first day to the end of the last + // day + start = getStartOfDay(currentCalendar, start); + + return start; + } + + /** + * Finds the last day of the week and returns a day representing the end of + * that day + * + * @param end + * The actual date + * @param expandToFullWeek + * Should the returned date be moved to the end of the week + * @return If expandToFullWeek is set then it returns the last day of the + * week, else it returns a clone of the actual date with the time + * set to the end of the day + */ + protected Date expandEndDate(Date end, boolean expandToFullWeek) { + // If the duration is more than week, use monthly view and get startweek + // and endweek. Example if views daterange is from tuesday to next weeks + // wednesday->expand to monday to nextweeks sunday. If firstdayofweek = + // monday + if (expandToFullWeek) { + end = getLastDateForWeek(end); + + } else { + end = (Date) end.clone(); + } + + // Always expand to the start of the first day to the end of the last + // day + end = getEndOfDay(currentCalendar, end); + + return end; + } + + /** + * Set the {@link com.vaadin.addon.calendar.event.CalendarEventProvider + * CalendarEventProvider} to be used with this calendar. The EventProvider + * is used to query for events to show, and must be non-null. By default a + * {@link com.vaadin.addon.calendar.event.BasicEventProvider + * BasicEventProvider} is used. + * + * @param calendarEventProvider + * the calendarEventProvider to set. Cannot be null. + */ + public void setEventProvider(CalendarEventProvider calendarEventProvider) { + if (calendarEventProvider == null) { + throw new IllegalArgumentException( + "Calendar event provider cannot be null"); + } + + // remove old listener + if (getEventProvider() instanceof EventSetChangeNotifier) { + ((EventSetChangeNotifier) getEventProvider()) + .removeEventSetChangeListener(this); + } + + this.calendarEventProvider = calendarEventProvider; + + // add new listener + if (calendarEventProvider instanceof EventSetChangeNotifier) { + ((EventSetChangeNotifier) calendarEventProvider) + .addEventSetChangeListener(this); + } + } + + /** + * @return the {@link com.vaadin.addon.calendar.event.CalendarEventProvider + * CalendarEventProvider} currently used + */ + public CalendarEventProvider getEventProvider() { + return calendarEventProvider; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarEvents.EventChangeListener#eventChange + * (com.vaadin.addon.calendar.ui.CalendarEvents.EventChange) + */ + @Override + public void eventSetChange(EventSetChangeEvent changeEvent) { + // sanity check + if (calendarEventProvider == changeEvent.getProvider()) { + markAsDirty(); + } + } + + /** + * Set the handler for the given type information. Mirrors + * {@link #addListener(String, Class, Object, Method) addListener} from + * AbstractComponent + * + * @param eventId + * A unique id for the event. Usually one of + * {@link CalendarEventId} + * @param eventType + * The class of the event, most likely a subclass of + * {@link CalendarComponentEvent} + * @param listener + * A listener that listens to the given event + * @param listenerMethod + * The method on the lister to call when the event is triggered + */ + protected void setHandler(String eventId, Class<?> eventType, + EventListener listener, Method listenerMethod) { + if (handlers.get(eventId) != null) { + removeListener(eventId, eventType, handlers.get(eventId)); + handlers.remove(eventId); + } + + if (listener != null) { + addListener(eventId, eventType, listener, listenerMethod); + handlers.put(eventId, listener); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.ForwardHandler) + */ + @Override + public void setHandler(ForwardHandler listener) { + setHandler(ForwardEvent.EVENT_ID, ForwardEvent.class, listener, + ForwardHandler.forwardMethod); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.BackwardHandler) + */ + @Override + public void setHandler(BackwardHandler listener) { + setHandler(BackwardEvent.EVENT_ID, BackwardEvent.class, listener, + BackwardHandler.backwardMethod); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.DateClickHandler) + */ + @Override + public void setHandler(DateClickHandler listener) { + setHandler(DateClickEvent.EVENT_ID, DateClickEvent.class, listener, + DateClickHandler.dateClickMethod); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventClickHandler) + */ + @Override + public void setHandler(EventClickHandler listener) { + setHandler(EventClick.EVENT_ID, EventClick.class, listener, + EventClickHandler.eventClickMethod); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.NavigationNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.WeekClickHandler) + */ + @Override + public void setHandler(WeekClickHandler listener) { + setHandler(WeekClick.EVENT_ID, WeekClick.class, listener, + WeekClickHandler.weekClickMethod); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeHandler + * ) + */ + @Override + public void setHandler(EventResizeHandler listener) { + setHandler(EventResize.EVENT_ID, EventResize.class, listener, + EventResizeHandler.eventResizeMethod); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.RangeSelectNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.RangeSelectHandler + * ) + */ + @Override + public void setHandler(RangeSelectHandler listener) { + setHandler(RangeSelectEvent.EVENT_ID, RangeSelectEvent.class, listener, + RangeSelectHandler.rangeSelectMethod); + + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveHandler) + */ + @Override + public void setHandler(EventMoveHandler listener) { + setHandler(MoveEvent.EVENT_ID, MoveEvent.class, listener, + EventMoveHandler.eventMoveMethod); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.CalendarEventNotifier + * #getHandler(java.lang.String) + */ + @Override + public EventListener getHandler(String eventId) { + return handlers.get(eventId); + } + + /** + * Get the currently active drop handler + */ + @Override + public DropHandler getDropHandler() { + return dropHandler; + } + + /** + * Set the drop handler for the calendar See {@link DropHandler} for + * implementation details. + * + * @param dropHandler + * The drop handler to set + */ + public void setDropHandler(DropHandler dropHandler) { + this.dropHandler = dropHandler; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.event.dd.DropTarget#translateDropTargetDetails(java.util.Map) + */ + @Override + public TargetDetails translateDropTargetDetails( + Map<String, Object> clientVariables) { + Map<String, Object> serverVariables = new HashMap<String, Object>(1); + + if (clientVariables.containsKey("dropSlotIndex")) { + int slotIndex = (Integer) clientVariables.get("dropSlotIndex"); + int dayIndex = (Integer) clientVariables.get("dropDayIndex"); + + currentCalendar.setTime(getStartOfDay(currentCalendar, startDate)); + currentCalendar.add(java.util.Calendar.DATE, dayIndex); + + // change this if slot length is modified + currentCalendar.add(java.util.Calendar.MINUTE, slotIndex * 30); + + serverVariables.put("dropTime", currentCalendar.getTime()); + + } else { + int dayIndex = (Integer) clientVariables.get("dropDayIndex"); + currentCalendar.setTime(expandStartDate(startDate, true)); + currentCalendar.add(java.util.Calendar.DATE, dayIndex); + serverVariables.put("dropDay", currentCalendar.getTime()); + } + + CalendarTargetDetails td = new CalendarTargetDetails(serverVariables, + this); + td.setHasDropTime(clientVariables.containsKey("dropSlotIndex")); + + return td; + } + + /** + * Sets a container as a data source for the events in the calendar. + * Equivalent for doing + * <code>Calendar.setEventProvider(new ContainerEventProvider(container))</code> + * + * Use this method if you are adding a container which uses the default + * property ids like {@link BeanItemContainer} for instance. If you are + * using custom properties instead use + * {@link Calendar#setContainerDataSource(com.vaadin.data.Container.Indexed, Object, Object, Object, Object, Object)} + * + * Please note that the container must be sorted by date! + * + * @param container + * The container to use as a datasource + */ + public void setContainerDataSource(Container.Indexed container) { + ContainerEventProvider provider = new ContainerEventProvider(container); + provider.addEventSetChangeListener(new CalendarEventProvider.EventSetChangeListener() { + @Override + public void eventSetChange(EventSetChangeEvent changeEvent) { + // Repaint if events change + markAsDirty(); + } + }); + provider.addEventChangeListener(new EventChangeListener() { + @Override + public void eventChange(EventChangeEvent changeEvent) { + // Repaint if event changes + markAsDirty(); + } + }); + setEventProvider(provider); + } + + /** + * Sets a container as a data source for the events in the calendar. + * Equivalent for doing + * <code>Calendar.setEventProvider(new ContainerEventProvider(container))</code> + * + * Please note that the container must be sorted by date! + * + * @param container + * The container to use as a data source + * @param captionProperty + * The property that has the caption, null if no caption property + * is present + * @param descriptionProperty + * The property that has the description, null if no description + * property is present + * @param startDateProperty + * The property that has the starting date + * @param endDateProperty + * The property that has the ending date + * @param styleNameProperty + * The property that has the stylename, null if no stylname + * property is present + */ + public void setContainerDataSource(Container.Indexed container, + Object captionProperty, Object descriptionProperty, + Object startDateProperty, Object endDateProperty, + Object styleNameProperty) { + ContainerEventProvider provider = new ContainerEventProvider(container); + provider.setCaptionProperty(captionProperty); + provider.setDescriptionProperty(descriptionProperty); + provider.setStartDateProperty(startDateProperty); + provider.setEndDateProperty(endDateProperty); + provider.setStyleNameProperty(styleNameProperty); + provider.addEventSetChangeListener(new CalendarEventProvider.EventSetChangeListener() { + @Override + public void eventSetChange(EventSetChangeEvent changeEvent) { + // Repaint if events change + markAsDirty(); + } + }); + provider.addEventChangeListener(new EventChangeListener() { + @Override + public void eventChange(EventChangeEvent changeEvent) { + // Repaint if event changes + markAsDirty(); + } + }); + setEventProvider(provider); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventProvider#getEvents(java. + * util.Date, java.util.Date) + */ + @Override + public List<CalendarEvent> getEvents(Date startDate, Date endDate) { + return getEventProvider().getEvents(startDate, endDate); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#addEvent + * (com.vaadin.addon.calendar.event.CalendarEvent) + */ + @Override + public void addEvent(CalendarEvent event) { + if (getEventProvider() instanceof CalendarEditableEventProvider) { + CalendarEditableEventProvider provider = (CalendarEditableEventProvider) getEventProvider(); + provider.addEvent(event); + markAsDirty(); + } else { + throw new UnsupportedOperationException( + "Event provider does not support adding events"); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#removeEvent + * (com.vaadin.addon.calendar.event.CalendarEvent) + */ + @Override + public void removeEvent(CalendarEvent event) { + if (getEventProvider() instanceof CalendarEditableEventProvider) { + CalendarEditableEventProvider provider = (CalendarEditableEventProvider) getEventProvider(); + provider.removeEvent(event); + markAsDirty(); + } else { + throw new UnsupportedOperationException( + "Event provider does not support removing events"); + } + } + + /** + * Adds an action handler to the calender that handles event produced by the + * context menu. + * + * <p> + * The {@link Handler#getActions(Object, Object)} parameters depend on what + * view the Calendar is in: + * <ul> + * <li>If the Calendar is in <i>Day or Week View</i> then the target + * parameter will be a {@link CalendarDateRange} with a range of + * half-an-hour. The {@link Handler#getActions(Object, Object)} method will + * be called once per half-hour slot.</li> + * <li>If the Calendar is in <i>Month View</i> then the target parameter + * will be a {@link CalendarDateRange} with a range of one day. The + * {@link Handler#getActions(Object, Object)} will be called once for each + * day. + * </ul> + * The Dates passed into the {@link CalendarDateRange} are in the same + * timezone as the calendar is. + * </p> + * + * <p> + * The {@link Handler#handleAction(Action, Object, Object)} parameters + * depend on what the context menu is called upon: + * <ul> + * <li>If the context menu is called upon an event then the target parameter + * is the event, i.e. instanceof {@link CalendarEvent}</li> + * <li>If the context menu is called upon an empty slot then the target is a + * {@link Date} representing that slot + * </ul> + * </p> + */ + @Override + public void addActionHandler(Handler actionHandler) { + if (actionHandler != null) { + if (actionHandlers == null) { + actionHandlers = new LinkedList<Action.Handler>(); + actionMapper = new KeyMapper<Action>(); + } + if (!actionHandlers.contains(actionHandler)) { + actionHandlers.add(actionHandler); + markAsDirty(); + } + } + } + + /** + * Is the calendar in a mode where all days of the month is shown + * + * @return Returns true if calendar is in monthly mode and false if it is in + * weekly mode + */ + public boolean isMonthlyMode() { + CalendarState state = (CalendarState) getState(false); + if (state.days != null) { + return state.days.size() > 7; + } else { + // Default mode + return true; + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.event.Action.Container#removeActionHandler(com.vaadin.event + * .Action.Handler) + */ + @Override + public void removeActionHandler(Handler actionHandler) { + if (actionHandlers != null && actionHandlers.contains(actionHandler)) { + actionHandlers.remove(actionHandler); + if (actionHandlers.isEmpty()) { + actionHandlers = null; + actionMapper = null; + } + markAsDirty(); + } + } + + private class CalendarServerRpcImpl implements CalendarServerRpc { + + @Override + public void eventMove(int eventIndex, String newDate) { + if (!isClientChangeAllowed()) { + return; + } + if (newDate != null) { + try { + Date d = df_date_time.parse(newDate); + if (eventIndex >= 0 && eventIndex < events.size() + && events.get(eventIndex) != null) { + fireEventMove(eventIndex, d); + } + } catch (ParseException e) { + getLogger().log(Level.WARNING, e.getMessage()); + } + } + } + + @Override + public void rangeSelect(String range) { + if (!isClientChangeAllowed()) { + return; + } + + if (range != null && range.length() > 14 && range.contains("TO")) { + String[] dates = range.split("TO"); + try { + Date d1 = df_date.parse(dates[0]); + Date d2 = df_date.parse(dates[1]); + + fireRangeSelect(d1, d2, true); + + } catch (ParseException e) { + // NOP + } + } else if (range != null && range.length() > 12 + && range.contains(":")) { + String[] dates = range.split(":"); + if (dates.length == 3) { + try { + Date d = df_date.parse(dates[0]); + currentCalendar.setTime(d); + int startMinutes = Integer.parseInt(dates[1]); + int endMinutes = Integer.parseInt(dates[2]); + currentCalendar.add(java.util.Calendar.MINUTE, + startMinutes); + Date start = currentCalendar.getTime(); + currentCalendar.add(java.util.Calendar.MINUTE, + endMinutes - startMinutes); + Date end = currentCalendar.getTime(); + fireRangeSelect(start, end, false); + } catch (ParseException e) { + // NOP + } catch (NumberFormatException e) { + // NOP + } + } + } + } + + @Override + public void forward() { + fireEvent(new ForwardEvent(Calendar.this)); + } + + @Override + public void backward() { + fireEvent(new BackwardEvent(Calendar.this)); + } + + @Override + public void dateClick(String date) { + if (!isClientChangeAllowed()) { + return; + } + if (date != null && date.length() > 6) { + try { + Date d = df_date.parse(date); + fireDateClick(d); + } catch (ParseException e) { + } + } + } + + @Override + public void weekClick(String event) { + if (!isClientChangeAllowed()) { + return; + } + if (event.length() > 0 && event.contains("w")) { + String[] splitted = event.split("w"); + if (splitted.length == 2) { + try { + int yr = 1900 + Integer.parseInt(splitted[0]); + int week = Integer.parseInt(splitted[1]); + fireWeekClick(week, yr); + } catch (NumberFormatException e) { + // NOP + } + } + } + } + + @Override + public void eventClick(int eventIndex) { + if (!isEventClickAllowed()) { + return; + } + if (eventIndex >= 0 && eventIndex < events.size() + && events.get(eventIndex) != null) { + fireEventClick(eventIndex); + } + } + + @Override + public void eventResize(int eventIndex, String newStartDate, + String newEndDate) { + if (!isClientChangeAllowed()) { + return; + } + if (newStartDate != null && !"".equals(newStartDate) + && newEndDate != null && !"".equals(newEndDate)) { + try { + Date newStartTime = df_date_time.parse(newStartDate); + Date newEndTime = df_date_time.parse(newEndDate); + + fireEventResize(eventIndex, newStartTime, newEndTime); + } catch (ParseException e) { + // NOOP + } + } + } + + @Override + public void scroll(int scrollPosition) { + scrollTop = scrollPosition; + markAsDirty(); + } + + @Override + public void actionOnEmptyCell(String actionKey, String startDate, + String endDate) { + Action action = actionMapper.get(actionKey); + SimpleDateFormat formatter = new SimpleDateFormat( + DateConstants.ACTION_DATE_FORMAT_PATTERN); + formatter.setTimeZone(getTimeZone()); + try { + Date start = formatter.parse(startDate); + for (Action.Handler ah : actionHandlers) { + ah.handleAction(action, this, start); + } + + } catch (ParseException e) { + getLogger().log(Level.WARNING, + "Could not parse action date string"); + } + + } + + @Override + public void actionOnEvent(String actionKey, String startDate, + String endDate, int eventIndex) { + Action action = actionMapper.get(actionKey); + SimpleDateFormat formatter = new SimpleDateFormat( + DateConstants.ACTION_DATE_FORMAT_PATTERN); + formatter.setTimeZone(getTimeZone()); + for (Action.Handler ah : actionHandlers) { + ah.handleAction(action, this, events.get(eventIndex)); + } + } + } +}
\ No newline at end of file diff --git a/server/src/com/vaadin/ui/ConnectorTracker.java b/server/src/com/vaadin/ui/ConnectorTracker.java index a229003224..85cdcdf65c 100644 --- a/server/src/com/vaadin/ui/ConnectorTracker.java +++ b/server/src/com/vaadin/ui/ConnectorTracker.java @@ -17,6 +17,7 @@ package com.vaadin.ui; import java.io.IOException; import java.io.Serializable; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; @@ -31,9 +32,9 @@ import org.json.JSONException; import org.json.JSONObject; import com.vaadin.server.AbstractClientConnector; -import com.vaadin.server.AbstractCommunicationManager; import com.vaadin.server.ClientConnector; import com.vaadin.server.GlobalResourceHandler; +import com.vaadin.server.LegacyCommunicationManager; import com.vaadin.server.StreamVariable; /** @@ -295,7 +296,7 @@ public class ConnectorTracker implements Serializable { uninitializedConnectors.remove(connector); diffStates.remove(connector); iterator.remove(); - } else if (!AbstractCommunicationManager + } else if (!LegacyCommunicationManager .isConnectorVisibleToClient(connector) && !uninitializedConnectors.contains(connector)) { uninitializedConnectors.add(connector); @@ -463,6 +464,31 @@ public class ConnectorTracker implements Serializable { return dirtyConnectors; } + /** + * Checks if there a dirty connectors. + * + * @return true if there are dirty connectors, false otherwise + */ + public boolean hasDirtyConnectors() { + return !getDirtyConnectors().isEmpty(); + } + + /** + * Returns a collection of those {@link #getDirtyConnectors() dirty + * connectors} that are actually visible to the client. + * + * @return A list of dirty and visible connectors. + */ + public ArrayList<ClientConnector> getDirtyVisibleConnectors() { + ArrayList<ClientConnector> dirtyConnectors = new ArrayList<ClientConnector>(); + for (ClientConnector c : getDirtyConnectors()) { + if (LegacyCommunicationManager.isConnectorVisibleToClient(c)) { + dirtyConnectors.add(c); + } + } + return dirtyConnectors; + } + public JSONObject getDiffState(ClientConnector connector) { assert getConnector(connector.getConnectorId()) == connector; return diffStates.get(connector); diff --git a/server/src/com/vaadin/ui/DateField.java b/server/src/com/vaadin/ui/DateField.java index 1a8955801b..5017fac993 100644 --- a/server/src/com/vaadin/ui/DateField.java +++ b/server/src/com/vaadin/ui/DateField.java @@ -19,10 +19,8 @@ package com.vaadin.ui; import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.Collection; -import java.util.Collections; import java.util.Date; import java.util.HashMap; -import java.util.List; import java.util.Locale; import java.util.Map; import java.util.TimeZone; @@ -31,6 +29,7 @@ import com.vaadin.data.Property; import com.vaadin.data.Validator; import com.vaadin.data.Validator.InvalidValueException; import com.vaadin.data.util.converter.Converter; +import com.vaadin.data.validator.DateRangeValidator; import com.vaadin.event.FieldEvents; import com.vaadin.event.FieldEvents.BlurEvent; import com.vaadin.event.FieldEvents.BlurListener; @@ -40,6 +39,7 @@ import com.vaadin.server.PaintException; import com.vaadin.server.PaintTarget; import com.vaadin.shared.ui.datefield.DateFieldConstants; import com.vaadin.shared.ui.datefield.Resolution; +import com.vaadin.shared.ui.datefield.TextualDateFieldState; /** * <p> @@ -148,6 +148,10 @@ public class DateField extends AbstractField<Date> implements private TimeZone timeZone = null; private static Map<Resolution, String> variableNameForResolution = new HashMap<Resolution, String>(); + + private String dateOutOfRangeMessage = "Date is out of allowed range"; + + private DateRangeValidator currentRangeValidator; { variableNameForResolution.put(Resolution.SECOND, "sec"); variableNameForResolution.put(Resolution.MINUTE, "min"); @@ -279,6 +283,174 @@ public class DateField extends AbstractField<Date> implements return super.shouldHideErrors() && uiHasValidDateString; } + @Override + protected TextualDateFieldState getState() { + return (TextualDateFieldState) super.getState(); + } + + @Override + protected TextualDateFieldState getState(boolean markAsDirty) { + return (TextualDateFieldState) super.getState(markAsDirty); + } + + /** + * Sets the start range for this component. If the value is set before this + * date (taking the resolution into account), the component will not + * validate. If <code>startDate</code> is set to <code>null</code>, any + * value before <code>endDate</code> will be accepted by the range + * + * @param startDate + * - the allowed range's start date + */ + public void setRangeStart(Date startDate) { + if (startDate != null && getState().rangeEnd != null + && startDate.after(getState().rangeEnd)) { + throw new IllegalStateException( + "startDate cannot be later than endDate"); + } + getState().rangeStart = startDate; + // rangeStart = startDate; + // This has to be done to correct for the resolution + // updateRangeState(); + updateRangeValidator(); + } + + /** + * Sets the current error message if the range validation fails. + * + * @param dateOutOfRangeMessage + * - Localizable message which is shown when value (the date) is + * set outside allowed range + */ + public void setDateOutOfRangeMessage(String dateOutOfRangeMessage) { + this.dateOutOfRangeMessage = dateOutOfRangeMessage; + updateRangeValidator(); + } + + /** + * Gets the end range for a certain resolution. The range is inclusive, so + * if rangeEnd is set to zero milliseconds past year n and resolution is set + * to YEAR, any date in year n will be accepted. Resolutions lower than DAY + * will be interpreted on a DAY level. That is, everything below DATE is + * cleared + * + * @param forResolution + * - the range conforms to the resolution + * @return + */ + private Date getRangeEnd(Resolution forResolution) { + // We need to set the correct resolution for the dates, + // otherwise the range validator will complain + + Date rangeEnd = getState(false).rangeEnd; + if (rangeEnd == null) { + return null; + } + + Calendar endCal = Calendar.getInstance(); + endCal.setTime(rangeEnd); + + if (forResolution == Resolution.YEAR) { + // Adding one year (minresolution) and clearing the rest. + endCal.set(endCal.get(Calendar.YEAR) + 1, 0, 1, 0, 0, 0); + } else if (forResolution == Resolution.MONTH) { + // Adding one month (minresolution) and clearing the rest. + endCal.set(endCal.get(Calendar.YEAR), + endCal.get(Calendar.MONTH) + 1, 1, 0, 0, 0); + } else { + endCal.set(endCal.get(Calendar.YEAR), endCal.get(Calendar.MONTH), + endCal.get(Calendar.DATE) + 1, 0, 0, 0); + } + // removing one millisecond will now get the endDate to return to + // current resolution's set time span (year or month) + endCal.set(Calendar.MILLISECOND, -1); + return endCal.getTime(); + } + + /** + * Gets the start range for a certain resolution. The range is inclusive, so + * if <code>rangeStart</code> is set to one millisecond before year n and + * resolution is set to YEAR, any date in year n - 1 will be accepted. + * Lowest supported resolution is DAY. + * + * @param forResolution + * - the range conforms to the resolution + * @return + */ + private Date getRangeStart(Resolution forResolution) { + if (getState(false).rangeStart == null) { + return null; + } + Calendar startCal = Calendar.getInstance(); + startCal.setTime(getState(false).rangeStart); + + if (forResolution == Resolution.YEAR) { + startCal.set(startCal.get(Calendar.YEAR), 0, 1, 0, 0, 0); + } else if (forResolution == Resolution.MONTH) { + startCal.set(startCal.get(Calendar.YEAR), + startCal.get(Calendar.MONTH), 1, 0, 0, 0); + } else { + startCal.set(startCal.get(Calendar.YEAR), + startCal.get(Calendar.MONTH), startCal.get(Calendar.DATE), + 0, 0, 0); + } + + startCal.set(Calendar.MILLISECOND, 0); + return startCal.getTime(); + } + + private void updateRangeValidator() { + if (currentRangeValidator != null) { + removeValidator(currentRangeValidator); + } + + currentRangeValidator = new DateRangeValidator(dateOutOfRangeMessage, + getRangeStart(resolution), getRangeEnd(resolution), null); + + addValidator(currentRangeValidator); + + } + + /** + * Sets the end range for this component. If the value is set after this + * date (taking the resolution into account), the component will not + * validate. If <code>endDate</code> is set to <code>null</code>, any value + * after <code>startDate</code> will be accepted by the range. + * + * @param endDate + * - the allowed range's end date (inclusive, based on the + * current resolution) + */ + public void setRangeEnd(Date endDate) { + if (endDate != null && getState().rangeStart != null + && getState().rangeStart.after(endDate)) { + throw new IllegalStateException( + "endDate cannot be earlier than startDate"); + } + // rangeEnd = endDate; + getState().rangeEnd = endDate; + updateRangeValidator(); + } + + /** + * Returns the precise rangeStart used. + * + * @param startDate + * + */ + public Date getRangeStart() { + return getState(false).rangeStart; + } + + /** + * Returns the precise rangeEnd used. + * + * @param startDate + */ + public Date getRangeEnd() { + return getState(false).rangeEnd; + } + /* * Invoked when a variable of the component changes. Don't add a JavaDoc * comment here, we use the default documentation from implemented @@ -574,6 +746,7 @@ public class DateField extends AbstractField<Date> implements */ public void setResolution(Resolution resolution) { this.resolution = resolution; + updateRangeValidator(); markAsDirty(); } diff --git a/server/src/com/vaadin/ui/GridLayout.java b/server/src/com/vaadin/ui/GridLayout.java index e60d9c676a..53a25c1c83 100644 --- a/server/src/com/vaadin/ui/GridLayout.java +++ b/server/src/com/vaadin/ui/GridLayout.java @@ -92,6 +92,7 @@ public class GridLayout extends AbstractLayout implements private Map<Integer, Float> columnExpandRatio = new HashMap<Integer, Float>(); private Map<Integer, Float> rowExpandRatio = new HashMap<Integer, Float>(); + private Alignment defaultComponentAlignment = Alignment.TOP_LEFT; /** * Constructor for a grid of given size (number of columns and rows). @@ -573,6 +574,7 @@ public class GridLayout extends AbstractLayout implements int row2) { this.component = component; childData = new ChildComponentData(); + childData.alignment = getDefaultComponentAlignment().getBitMask(); childData.column1 = column1; childData.row1 = row1; childData.column2 = column2; @@ -1226,4 +1228,26 @@ public class GridLayout extends AbstractLayout implements return new MarginInfo(getState().marginsBitmask); } + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.Layout.AlignmentHandler#getDefaultComponentAlignment() + */ + @Override + public Alignment getDefaultComponentAlignment() { + return defaultComponentAlignment; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.ui.Layout.AlignmentHandler#setDefaultComponentAlignment(com + * .vaadin.ui.Alignment) + */ + @Override + public void setDefaultComponentAlignment(Alignment defaultAlignment) { + defaultComponentAlignment = defaultAlignment; + } + } diff --git a/server/src/com/vaadin/ui/Label.java b/server/src/com/vaadin/ui/Label.java index f49a1403cf..d037652a09 100644 --- a/server/src/com/vaadin/ui/Label.java +++ b/server/src/com/vaadin/ui/Label.java @@ -21,10 +21,13 @@ import java.util.Locale; import java.util.logging.Logger; import com.vaadin.data.Property; +import com.vaadin.data.util.AbstractProperty; +import com.vaadin.data.util.LegacyPropertyHelper; import com.vaadin.data.util.converter.Converter; import com.vaadin.data.util.converter.ConverterUtil; import com.vaadin.shared.ui.label.ContentMode; import com.vaadin.shared.ui.label.LabelState; +import com.vaadin.shared.util.SharedUtil; /** * Label component for showing non-editable short texts. @@ -203,23 +206,6 @@ public class Label extends AbstractComponent implements Property<String>, } /** - * Returns the value displayed by this label. - * - * @see java.lang.Object#toString() - * @deprecated As of 7.0, use {@link #getValue()} to get the value of the - * label or {@link #getPropertyDataSource()} .getValue() to get - * the value of the data source. - */ - @Deprecated - @Override - public String toString() { - logger.warning("You are using Label.toString() to get the value for a " - + getClass().getSimpleName() - + ". This will not be supported starting from Vaadin 7.1 (your debugger might call toString() and cause this message to appear)."); - return getValue(); - } - - /** * Gets the type of the Property. * * @see com.vaadin.data.Property#getType() @@ -419,7 +405,7 @@ public class Label extends AbstractComponent implements Property<String>, private void updateValueFromDataSource() { // Update the internal value from the data source String newConvertedValue = getDataSourceValue(); - if (!AbstractField.equals(newConvertedValue, getState().text)) { + if (!SharedUtil.equals(newConvertedValue, getState().text)) { getState().text = newConvertedValue; fireValueChange(); } @@ -541,4 +527,35 @@ public class Label extends AbstractComponent implements Property<String>, markAsDirty(); } + /** + * Returns a string representation of this object. The returned string + * representation depends on if the legacy Property toString mode is enabled + * or disabled. + * <p> + * If legacy Property toString mode is enabled, returns the value displayed + * by this label. + * </p> + * <p> + * If legacy Property toString mode is disabled, the string representation + * has no special meaning + * </p> + * + * @see AbstractProperty#isLegacyToStringEnabled() + * + * @return The value displayed by this label or a string representation of + * this Label object. + * + * @deprecated As of 7.0, use {@link #getValue()} to get the value of the + * label or {@link #getPropertyDataSource()}.getValue() to get + * the value of the data source. + */ + @Deprecated + @Override + public String toString() { + if (!LegacyPropertyHelper.isLegacyToStringEnabled()) { + return super.toString(); + } else { + return LegacyPropertyHelper.legacyPropertyToString(this); + } + } } diff --git a/server/src/com/vaadin/ui/Layout.java b/server/src/com/vaadin/ui/Layout.java index cd6ffc42d2..dc16b186f2 100644 --- a/server/src/com/vaadin/ui/Layout.java +++ b/server/src/com/vaadin/ui/Layout.java @@ -61,6 +61,23 @@ public interface Layout extends ComponentContainer, Serializable { */ public Alignment getComponentAlignment(Component childComponent); + /** + * Sets the alignment used for new components added to this layout. The + * default is {@link Alignment#TOP_LEFT}. + * + * @param defaultComponentAlignment + * The new default alignment + */ + public void setDefaultComponentAlignment( + Alignment defaultComponentAlignment); + + /** + * Returns the alignment used for new components added to this layout + * + * @return The default alignment + */ + public Alignment getDefaultComponentAlignment(); + } /** diff --git a/server/src/com/vaadin/ui/LoadingIndicatorConfiguration.java b/server/src/com/vaadin/ui/LoadingIndicatorConfiguration.java new file mode 100644 index 0000000000..57ccdc1b64 --- /dev/null +++ b/server/src/com/vaadin/ui/LoadingIndicatorConfiguration.java @@ -0,0 +1,160 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui; + +import java.io.Serializable; + +import com.vaadin.shared.ui.ui.UIState.LoadingIndicatorConfigurationState; + +/** + * Provides method for configuring the loading indicator. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public interface LoadingIndicatorConfiguration extends Serializable { + /** + * Sets the delay before the loading indicator is shown. The default is + * 300ms. + * + * @param firstDelay + * The first delay (in ms) + */ + public void setFirstDelay(int firstDelay); + + /** + * Returns the delay before the loading indicator is shown. + * + * @return The first delay (in ms) + */ + public int getFirstDelay(); + + /** + * Sets the delay before the loading indicator goes into the "second" state. + * The delay is calculated from the time when the loading indicator was + * triggered. The default is 1500ms. + * + * @param secondDelay + * The delay before going into the "second" state (in ms) + */ + public void setSecondDelay(int secondDelay); + + /** + * Returns the delay before the loading indicator goes into the "second" + * state. The delay is calculated from the time when the loading indicator + * was triggered. + * + * @return The delay before going into the "second" state (in ms) + */ + public int getSecondDelay(); + + /** + * Sets the delay before the loading indicator goes into the "third" state. + * The delay is calculated from the time when the loading indicator was + * triggered. The default is 5000ms. + * + * @param thirdDelay + * The delay before going into the "third" state (in ms) + */ + public void setThirdDelay(int thirdDelay); + + /** + * Returns the delay before the loading indicator goes into the "third" + * state. The delay is calculated from the time when the loading indicator + * was triggered. + * + * @return The delay before going into the "third" state (in ms) + */ + public int getThirdDelay(); +} + +class LoadingIndicatorConfigurationImpl implements + LoadingIndicatorConfiguration { + private UI ui; + + public LoadingIndicatorConfigurationImpl(UI ui) { + this.ui = ui; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.LoadingIndicator#setFirstDelay(int) + */ + @Override + public void setFirstDelay(int firstDelay) { + getState().firstDelay = firstDelay; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.LoadingIndicator#getFirstDelay() + */ + @Override + public int getFirstDelay() { + return getState(false).firstDelay; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.LoadingIndicator#setSecondDelay(int) + */ + @Override + public void setSecondDelay(int secondDelay) { + getState().secondDelay = secondDelay; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.LoadingIndicator#getSecondDelay() + */ + @Override + public int getSecondDelay() { + return getState(false).secondDelay; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.LoadingIndicator#setThirdDelay(int) + */ + @Override + public void setThirdDelay(int thirdDelay) { + getState().thirdDelay = thirdDelay; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.LoadingIndicator#getThirdDelay() + */ + @Override + public int getThirdDelay() { + return getState(false).thirdDelay; + } + + private LoadingIndicatorConfigurationState getState() { + return ui.getState().loadingIndicatorConfiguration; + } + + private LoadingIndicatorConfigurationState getState(boolean markAsDirty) { + return ui.getState(markAsDirty).loadingIndicatorConfiguration; + } + +} diff --git a/server/src/com/vaadin/ui/LoginForm.java b/server/src/com/vaadin/ui/LoginForm.java index 11ae1b379b..d06882927e 100644 --- a/server/src/com/vaadin/ui/LoginForm.java +++ b/server/src/com/vaadin/ui/LoginForm.java @@ -61,24 +61,30 @@ public class LoginForm extends CustomComponent { private Embedded iframe = new Embedded(); @Override - public boolean handleConnectorRequest(VaadinRequest request, - VaadinResponse response, String path) throws IOException { - String method = VaadinServletService.getCurrentServletRequest() - .getMethod(); + public boolean handleConnectorRequest(final VaadinRequest request, + final VaadinResponse response, String path) throws IOException { if (!path.equals("login")) { return super.handleConnectorRequest(request, response, path); } - String responseString = null; - if (method.equalsIgnoreCase("post")) { - responseString = handleLogin(request); - } else { - responseString = getLoginHTML(); - } + final StringBuilder responseBuilder = new StringBuilder(); + + getUI().access(new Runnable() { + @Override + public void run() { + String method = VaadinServletService.getCurrentServletRequest() + .getMethod(); + if (method.equalsIgnoreCase("post")) { + responseBuilder.append(handleLogin(request)); + } else { + responseBuilder.append(getLoginHTML()); + } + } + }); - if (responseString != null) { + if (responseBuilder.length() > 0) { response.setContentType("text/html; charset=utf-8"); response.setCacheTime(-1); - response.getWriter().write(responseString); + response.getWriter().write(responseBuilder.toString()); return true; } else { return false; diff --git a/server/src/com/vaadin/ui/PopupDateField.java b/server/src/com/vaadin/ui/PopupDateField.java index ae33493c89..f0bb0d74fe 100644 --- a/server/src/com/vaadin/ui/PopupDateField.java +++ b/server/src/com/vaadin/ui/PopupDateField.java @@ -118,4 +118,24 @@ public class PopupDateField extends DateField { getState().textFieldEnabled = state; } + /** + * Set a description that explains the usage of the Widget for users of + * assistive devices. + * + * @param description + * String with the description + */ + public void setAssistiveText(String description) { + getState().descriptionForAssistiveDevices = description; + } + + /** + * Get the description that explains the usage of the Widget for users of + * assistive devices. + * + * @return String with the description + */ + public String getAssistiveText() { + return getState().descriptionForAssistiveDevices; + } } diff --git a/server/src/com/vaadin/ui/TooltipConfiguration.java b/server/src/com/vaadin/ui/TooltipConfiguration.java new file mode 100644 index 0000000000..f9120aa18d --- /dev/null +++ b/server/src/com/vaadin/ui/TooltipConfiguration.java @@ -0,0 +1,240 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui; + +import java.io.Serializable; + +import com.vaadin.shared.ui.ui.UIState.TooltipConfigurationState; + +/** + * Provides method for configuring the tooltip. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public interface TooltipConfiguration extends Serializable { + /** + * Returns the time (in ms) the tooltip should be displayed after an event + * that will cause it to be closed (e.g. mouse click outside the component, + * key down). + * + * @return The close timeout + */ + public int getCloseTimeout(); + + /** + * Sets the time (in ms) the tooltip should be displayed after an event that + * will cause it to be closed (e.g. mouse click outside the component, key + * down). + * + * @param closeTimeout + * The close timeout + */ + public void setCloseTimeout(int closeTimeout); + + /** + * Returns the time (in ms) during which {@link #getQuickOpenDelay()} should + * be used instead of {@link #getOpenDelay()}. The quick open delay is used + * when the tooltip has very recently been shown, is currently hidden but + * about to be shown again. + * + * @return The quick open timeout + */ + public int getQuickOpenTimeout(); + + /** + * Sets the time (in ms) that determines when {@link #getQuickOpenDelay()} + * should be used instead of {@link #getOpenDelay()}. The quick open delay + * is used when the tooltip has very recently been shown, is currently + * hidden but about to be shown again. + * + * @param quickOpenTimeout + * The quick open timeout + */ + public void setQuickOpenTimeout(int quickOpenTimeout); + + /** + * Returns the time (in ms) that should elapse before a tooltip will be + * shown, in the situation when a tooltip has very recently been shown + * (within {@link #getQuickOpenDelay()} ms). + * + * @return The quick open delay + */ + public int getQuickOpenDelay(); + + /** + * Sets the time (in ms) that should elapse before a tooltip will be shown, + * in the situation when a tooltip has very recently been shown (within + * {@link #getQuickOpenDelay()} ms). + * + * @param quickOpenDelay + * The quick open delay + */ + public void setQuickOpenDelay(int quickOpenDelay); + + /** + * Returns the time (in ms) that should elapse after an event triggering + * tooltip showing has occurred (e.g. mouse over) before the tooltip is + * shown. If a tooltip has recently been shown, then + * {@link #getQuickOpenDelay()} is used instead of this. + * + * @return The open delay + */ + public int getOpenDelay(); + + /** + * Sets the time (in ms) that should elapse after an event triggering + * tooltip showing has occurred (e.g. mouse over) before the tooltip is + * shown. If a tooltip has recently been shown, then + * {@link #getQuickOpenDelay()} is used instead of this. + * + * @param openDelay + * The open delay + */ + public void setOpenDelay(int openDelay); + + /** + * Returns the maximum width of the tooltip popup. + * + * @return The maximum width the tooltip popup + */ + public int getMaxWidth(); + + /** + * Sets the maximum width of the tooltip popup. + * + * @param maxWidth + * The maximum width the tooltip popup + */ + public void setMaxWidth(int maxWidth); +} + +class TooltipConfigurationImpl implements TooltipConfiguration { + private UI ui; + + public TooltipConfigurationImpl(UI ui) { + this.ui = ui; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.UI.Tooltip#getCloseTimeout() + */ + @Override + public int getCloseTimeout() { + return getState(false).closeTimeout; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.Tooltip#setCloseTimeout(int) + */ + @Override + public void setCloseTimeout(int closeTimeout) { + getState().closeTimeout = closeTimeout; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.Tooltip#getQuickOpenTimeout() + */ + @Override + public int getQuickOpenTimeout() { + return getState(false).quickOpenTimeout; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.Tooltip#setQuickOpenTimeout(int) + */ + @Override + public void setQuickOpenTimeout(int quickOpenTimeout) { + getState().quickOpenTimeout = quickOpenTimeout; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.Tooltip#getQuickOpenDelay() + */ + @Override + public int getQuickOpenDelay() { + return getState(false).quickOpenDelay; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.Tooltip#setQuickOpenDelay(int) + */ + @Override + public void setQuickOpenDelay(int quickOpenDelay) { + getState().quickOpenDelay = quickOpenDelay; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.Tooltip#getOpenDelay() + */ + @Override + public int getOpenDelay() { + return getState(false).openDelay; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.Tooltip#setOpenDelay(int) + */ + @Override + public void setOpenDelay(int openDelay) { + getState().openDelay = openDelay; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.Tooltip#getMaxWidth() + */ + @Override + public int getMaxWidth() { + return getState(false).maxWidth; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.Tooltip#setMaxWidth(int) + */ + @Override + public void setMaxWidth(int maxWidth) { + getState().maxWidth = maxWidth; + } + + private TooltipConfigurationState getState() { + return ui.getState().tooltipConfiguration; + } + + private TooltipConfigurationState getState(boolean markAsDirty) { + return ui.getState(markAsDirty).tooltipConfiguration; + } + +} diff --git a/server/src/com/vaadin/ui/Tree.java b/server/src/com/vaadin/ui/Tree.java index a6dbea51ba..15175b5a8b 100644 --- a/server/src/com/vaadin/ui/Tree.java +++ b/server/src/com/vaadin/ui/Tree.java @@ -72,6 +72,13 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, /* Private members */ + private static final String NULL_ALT_EXCEPTION_MESSAGE = "Parameter 'altText' needs to be non null"; + + /** + * Item icons alt texts. + */ + private final HashMap<Object, String> itemIconAlts = new HashMap<Object, String>(); + /** * Set of expanded nodes. */ @@ -163,6 +170,70 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, super(caption, dataSource); } + @Override + public void setItemIcon(Object itemId, Resource icon) { + setItemIcon(itemId, icon, ""); + } + + /** + * Sets the icon for an item. + * + * @param itemId + * the id of the item to be assigned an icon. + * @param icon + * the icon to use or null. + * + * @param altText + * the alternative text for the icon + */ + public void setItemIcon(Object itemId, Resource icon, String altText) { + if (itemId != null) { + super.setItemIcon(itemId, icon); + + if (icon == null) { + itemIconAlts.remove(itemId); + } else if (altText == null) { + throw new IllegalArgumentException(NULL_ALT_EXCEPTION_MESSAGE); + } else { + itemIconAlts.put(itemId, altText); + } + markAsDirty(); + } + } + + /** + * Set the alternate text for an item. + * + * Used when the item has an icon. + * + * @param itemId + * the id of the item to be assigned an icon. + * @param altText + * the alternative text for the icon + */ + public void setItemIconAlternateText(Object itemId, String altText) { + if (itemId != null) { + if (altText == null) { + throw new IllegalArgumentException(NULL_ALT_EXCEPTION_MESSAGE); + } else { + itemIconAlts.put(itemId, altText); + } + } + } + + /** + * Return the alternate text of an icon in a tree item. + * + * @param itemId + * Object with the ID of the item + * @return String with the alternate text of the icon, or null when no icon + * was set + */ + public String getItemIconAlternateText(Object itemId) { + String storedAlt = itemIconAlts.get(itemId); + return storedAlt == null ? "" : storedAlt; + } + /* Expanding and collapsing */ /** @@ -638,6 +709,8 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, if (icon != null) { target.addAttribute(TreeConstants.ATTRIBUTE_NODE_ICON, getItemIcon(itemId)); + target.addAttribute(TreeConstants.ATTRIBUTE_NODE_ICON_ALT, + getItemIconAlternateText(itemId)); } final String key = itemIdMapper.key(itemId); target.addAttribute("key", key); @@ -861,6 +934,37 @@ public class Tree extends AbstractSelect implements Container.Hierarchical, } + @Override + public void containerItemSetChange( + com.vaadin.data.Container.ItemSetChangeEvent event) { + super.containerItemSetChange(event); + if (getContainerDataSource() instanceof Filterable) { + boolean hasFilters = !((Filterable) getContainerDataSource()) + .getContainerFilters().isEmpty(); + if (!hasFilters) { + /* + * If Container is not filtered then the itemsetchange is caused + * by either adding or removing items to the container. To + * prevent a memory leak we should cleanup the expanded list + * from items which was removed. + * + * However, there will still be a leak if the container is + * filtered to show only a subset of the items in the tree and + * later unfiltered items are removed from the container. In + * that case references to the unfiltered item ids will remain + * in the expanded list until the Tree instance is removed and + * the list is destroyed, or the container data source is + * replaced/updated. To force the removal of the removed items + * the application developer needs to a) remove the container + * filters temporarly or b) re-apply the container datasource + * using setContainerDataSource(getContainerDataSource()) + */ + cleanupExpandedItems(); + } + } + + } + /* Expand event and listener */ /** diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java index 796d1f08ea..e077b003b8 100644 --- a/server/src/com/vaadin/ui/UI.java +++ b/server/src/com/vaadin/ui/UI.java @@ -37,9 +37,12 @@ import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinService; import com.vaadin.server.VaadinServlet; import com.vaadin.server.VaadinSession; +import com.vaadin.server.communication.PushConnection; import com.vaadin.shared.EventId; import com.vaadin.shared.MouseEventDetails; +import com.vaadin.shared.communication.PushMode; import com.vaadin.shared.ui.ui.ScrollClientRpc; +import com.vaadin.shared.ui.ui.UIClientRpc; import com.vaadin.shared.ui.ui.UIConstants; import com.vaadin.shared.ui.ui.UIServerRpc; import com.vaadin.shared.ui.ui.UIState; @@ -114,7 +117,10 @@ public abstract class UI extends AbstractSingleComponentContainer implements /** Identifies the click event */ private ConnectorTracker connectorTracker = new ConnectorTracker(this); - private Page page = new Page(this); + private Page page = new Page(this, getState(false).pageState); + + private LoadingIndicatorConfiguration loadingIndicatorConfiguration = new LoadingIndicatorConfigurationImpl( + this); /** * Scroll Y position. @@ -144,6 +150,14 @@ public abstract class UI extends AbstractSingleComponentContainer implements UI.this.scrollTop = scrollTop; UI.this.scrollLeft = scrollLeft; } + + @Override + public void poll() { + /* + * No-op. This is only called to cause a server visit to check for + * changes. + */ + } }; /** @@ -155,6 +169,9 @@ public abstract class UI extends AbstractSingleComponentContainer implements private boolean closing = false; + private TooltipConfiguration tooltipConfiguration = new TooltipConfigurationImpl( + this); + /** * Creates a new empty UI without a caption. The content of the UI must be * set by calling {@link #setContent(Component)} before using the UI. @@ -347,6 +364,12 @@ public abstract class UI extends AbstractSingleComponentContainer implements } else { if (session == null) { detach(); + if (pushConnection != null && pushConnection.isConnected()) { + // Close the push connection when UI is detached. Otherwise + // the push connection and possibly VaadinSession will live + // on. + pushConnection.disconnect(); + } } this.session = session; } @@ -466,6 +489,10 @@ public abstract class UI extends AbstractSingleComponentContainer implements private Navigator navigator; + private PushConnection pushConnection = null; + + private boolean hasPendingPush = false; + /** * This method is used by Component.Focusable objects to request focus to * themselves. Focus renders must be handled at window level (instead of @@ -659,10 +686,16 @@ public abstract class UI extends AbstractSingleComponentContainer implements /** * Should resize operations be lazy, i.e. should there be a delay before - * layout sizes are recalculated. Speeds up resize operations in slow UIs - * with the penalty of slightly decreased usability. + * layout sizes are recalculated and resize events are sent to the server. + * Speeds up resize operations in slow UIs with the penalty of slightly + * decreased usability. * <p> * Default value: <code>false</code> + * </p> + * <p> + * When there are active window resize listeners, lazy resize mode should be + * used to avoid a large number of events during resize. + * </p> * * @param resizeLazy * true to use a delay before recalculating sizes, false to @@ -986,6 +1019,15 @@ public abstract class UI extends AbstractSingleComponentContainer implements */ public void close() { closing = true; + + boolean sessionExpired = (session == null || session.isClosing()); + getRpcProxy(UIClientRpc.class).uiClosed(sessionExpired); + if (getPushConnection() != null && getPushConnection().isConnected()) { + // Push the Rpc to the client. The connection will be closed when + // the UI is detached and cleaned up. + getPushConnection().push(); + } + } /** @@ -1054,4 +1096,265 @@ public abstract class UI extends AbstractSingleComponentContainer implements public int getTabIndex() { return getState(false).tabIndex; } + + /** + * Provides exclusive access to this UI from outside a request handling + * thread. + * <p> + * The given runnable is executed while holding the session lock to ensure + * exclusive access to this UI and its session. The UI and related thread + * locals are set properly before executing the runnable. + * </p> + * <p> + * RPC handlers for components inside this UI do not need this method as the + * session is automatically locked by the framework during request handling. + * </p> + * <p> + * Note that calling this method while another session is locked by the + * current thread will cause an exception. This is to prevent deadlock + * situations when two threads have locked one session each and are both + * waiting for the lock for the other session. + * </p> + * + * @param runnable + * the runnable which accesses the UI + * @throws UIDetachedException + * if the UI is not attached to a session (and locking can + * therefore not be done) + * @throws IllegalStateException + * if the current thread holds the lock for another session + * + * @see #getCurrent() + * @see VaadinSession#access(Runnable) + * @see VaadinSession#lock() + */ + public void access(Runnable runnable) throws UIDetachedException { + Map<Class<?>, CurrentInstance> old = null; + + VaadinSession session = getSession(); + + if (session == null) { + throw new UIDetachedException(); + } + + VaadinService.verifyNoOtherSessionLocked(session); + + session.lock(); + try { + if (getSession() == null) { + // UI was detached after fetching the session but before we + // acquired the lock. + throw new UIDetachedException(); + } + old = CurrentInstance.setThreadLocals(this); + runnable.run(); + } finally { + session.unlock(); + if (old != null) { + CurrentInstance.restoreThreadLocals(old); + } + } + + } + + /** + * @deprecated As of 7.1.0.beta1, use {@link #access(Runnable)} instead. + * This method will be removed before the final 7.1.0 release. + */ + @Deprecated + public void runSafely(Runnable runnable) throws UIDetachedException { + access(runnable); + } + + /** + * Retrieves the object used for configuring tooltips. + * + * @return The instance used for tooltip configuration + */ + public TooltipConfiguration getTooltipConfiguration() { + return tooltipConfiguration; + } + + /** + * Retrieves the object used for configuring the loading indicator. + * + * @return The instance used for configuring the loading indicator + */ + public LoadingIndicatorConfiguration getLoadingIndicatorConfiguration() { + return loadingIndicatorConfiguration; + } + + /** + * Pushes the pending changes and client RPC invocations of this UI to the + * client-side. + * <p> + * As with all UI methods, it is not safe to call push() without holding the + * {@link VaadinSession#lock() session lock}. + * + * @throws IllegalStateException + * if push is disabled. + * @throws UIDetachedException + * if this UI is not attached to a session. + * + * @see #getPushMode() + * + * @since 7.1 + */ + public void push() { + VaadinSession session = getSession(); + if (session != null) { + assert session.hasLock(); + if (!getConnectorTracker().hasDirtyConnectors()) { + // Do not push if there is nothing to push + return; + } + + if (!getPushMode().isEnabled()) { + throw new IllegalStateException("Push not enabled"); + } + + if (pushConnection == null) { + hasPendingPush = true; + } else { + pushConnection.push(); + } + } else { + throw new UIDetachedException("Trying to push a detached UI"); + } + } + + /** + * Returns the internal push connection object used by this UI. This method + * should only be called by the framework. + */ + public PushConnection getPushConnection() { + return pushConnection; + } + + /** + * Sets the internal push connection object used by this UI. This method + * should only be called by the framework. + */ + public void setPushConnection(PushConnection pushConnection) { + // If pushMode is disabled then there should never be a pushConnection + assert (getPushMode().isEnabled() || pushConnection == null); + + if (pushConnection == this.pushConnection) { + return; + } + + if (this.pushConnection != null) { + this.pushConnection.disconnect(); + } + + this.pushConnection = pushConnection; + if (pushConnection != null && hasPendingPush) { + hasPendingPush = false; + pushConnection.push(); + } + } + + /** + * Sets the interval with which the UI should poll the server to see if + * there are any changes. Polling is disabled by default. + * <p> + * Note that it is possible to enable push and polling at the same time but + * it should not be done to avoid excessive server traffic. + * </p> + * <p> + * Add-on developers should note that this method is only meant for the + * application developer. An add-on should not set the poll interval + * directly, rather instruct the user to set it. + * </p> + * + * @param intervalInMillis + * The interval (in ms) with which the UI should poll the server + * or -1 to disable polling + */ + public void setPollInterval(int intervalInMillis) { + getState().pollInterval = intervalInMillis; + } + + /** + * Returns the interval with which the UI polls the server. + * + * @return The interval (in ms) with which the UI polls the server or -1 if + * polling is disabled + */ + public int getPollInterval() { + return getState(false).pollInterval; + } + + /** + * Returns the mode of bidirectional ("push") communication that is used in + * this UI. + * + * @return The push mode. + */ + public PushMode getPushMode() { + return getState(false).pushMode; + } + + /** + * Sets the mode of bidirectional ("push") communication that should be used + * in this UI. + * <p> + * Add-on developers should note that this method is only meant for the + * application developer. An add-on should not set the push mode directly, + * rather instruct the user to set it. + * </p> + * + * @param pushMode + * The push mode to use. + * + * @throws IllegalArgumentException + * if the argument is null. + * @throws IllegalStateException + * if push support is not available. + */ + public void setPushMode(PushMode pushMode) { + if (pushMode == null) { + throw new IllegalArgumentException("Push mode cannot be null"); + } + + if (pushMode.isEnabled()) { + VaadinSession session = getSession(); + if (session != null && !session.getService().ensurePushAvailable()) { + throw new IllegalStateException( + "Push is not available. See previous log messages for more information."); + } + } + + /* + * Client-side will open a new connection or disconnect the old + * connection, so there's nothing more to do on the server at this + * point. + */ + getState().pushMode = pushMode; + } + + /** + * Get the label that is added to the container element, where tooltip, + * notification and dialogs are added to. + * + * @return the label of the container + */ + public String getOverlayContainerLabel() { + return getState().overlayContainerLabel; + } + + /** + * Sets the label that is added to the container element, where tooltip, + * notifications and dialogs are added to. + * <p> + * This is helpful for users of assistive devices, as this element is + * reachable for them. + * </p> + * + * @param overlayContainerLabel + * label to use for the container + */ + public void setOverlayContainerLabel(String overlayContainerLabel) { + getState().overlayContainerLabel = overlayContainerLabel; + } } diff --git a/server/src/com/vaadin/ui/UIDetachedException.java b/server/src/com/vaadin/ui/UIDetachedException.java new file mode 100644 index 0000000000..07207b0bf3 --- /dev/null +++ b/server/src/com/vaadin/ui/UIDetachedException.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui; + +/** + * Exception thrown if the UI has been detached when it should not be. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class UIDetachedException extends RuntimeException { + + public UIDetachedException() { + super(); + } + + public UIDetachedException(String message, Throwable cause) { + super(message, cause); + } + + public UIDetachedException(String message) { + super(message); + } + + public UIDetachedException(Throwable cause) { + super(cause); + } + +} diff --git a/server/src/com/vaadin/ui/Window.java b/server/src/com/vaadin/ui/Window.java index d8b33e6b25..9f64c9118e 100644 --- a/server/src/com/vaadin/ui/Window.java +++ b/server/src/com/vaadin/ui/Window.java @@ -35,8 +35,10 @@ import com.vaadin.server.ClientConnector; import com.vaadin.server.PaintException; import com.vaadin.server.PaintTarget; import com.vaadin.shared.MouseEventDetails; +import com.vaadin.shared.ui.window.WindowMode; import com.vaadin.shared.ui.window.WindowServerRpc; import com.vaadin.shared.ui.window.WindowState; +import com.vaadin.util.ReflectTools; /** * A component that represents a floating popup window that can be added to a @@ -71,6 +73,11 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, public void click(MouseEventDetails mouseDetails) { fireEvent(new ClickEvent(Window.this, mouseDetails)); } + + @Override + public void windowModeChanged(WindowMode newState) { + setWindowMode(newState); + } }; /** @@ -234,10 +241,10 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, /** * Gets the distance of Window left border in pixels from left border of the - * containing (main window). + * containing (main window) when the window is in {@link WindowMode#NORMAL}. * * @return the Distance of Window left border in pixels from left border of - * the containing (main window). or -1 if unspecified. + * the containing (main window).or -1 if unspecified * @since 4.0.0 */ public int getPositionX() { @@ -246,7 +253,8 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, /** * Sets the distance of Window left border in pixels from left border of the - * containing (main window). + * containing (main window). Has effect only if in {@link WindowMode#NORMAL} + * mode. * * @param positionX * the Distance of Window left border in pixels from left border @@ -260,10 +268,11 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, /** * Gets the distance of Window top border in pixels from top border of the - * containing (main window). + * containing (main window) when the window is in {@link WindowMode#NORMAL} + * state, or when next set to that state. * * @return Distance of Window top border in pixels from top border of the - * containing (main window). or -1 if unspecified . + * containing (main window). or -1 if unspecified * * @since 4.0.0 */ @@ -273,7 +282,8 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, /** * Sets the distance of Window top border in pixels from top border of the - * containing (main window). + * containing (main window). Has effect only if in {@link WindowMode#NORMAL} + * mode. * * @param positionY * the Distance of Window top border in pixels from top border of @@ -402,6 +412,101 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, } /** + * Event which is fired when the mode of the Window changes. + * + * @author Vaadin Ltd + * @since 7.1 + * + */ + public static class WindowModeChangeEvent extends Component.Event { + + private final WindowMode windowMode; + + /** + * + * @param source + */ + public WindowModeChangeEvent(Component source, WindowMode windowMode) { + super(source); + this.windowMode = windowMode; + } + + /** + * Gets the Window. + * + * @return the window + */ + public Window getWindow() { + return (Window) getSource(); + } + + /** + * Gets the new window mode. + * + * @return the new mode + */ + public WindowMode getWindowMode() { + return windowMode; + } + } + + /** + * An interface used for listening to Window maximize / restore events. Add + * the WindowModeChangeListener to a window and + * {@link WindowModeChangeListener#windowModeChanged(WindowModeChangeEvent)} + * will be called whenever the window is maximized ( + * {@link WindowMode#MAXIMIZED}) or restored ({@link WindowMode#NORMAL} ). + */ + public interface WindowModeChangeListener extends Serializable { + + public static final Method windowModeChangeMethod = ReflectTools + .findMethod(WindowModeChangeListener.class, + "windowModeChanged", WindowModeChangeEvent.class); + + /** + * Called when the user maximizes / restores a window. Use + * {@link WindowModeChangeEvent#getWindow()} to get a reference to the + * {@link Window} that was maximized / restored. Use + * {@link WindowModeChangeEvent#getWindowMode()} to get a reference to + * the new state. + * + * @param event + */ + public void windowModeChanged(WindowModeChangeEvent event); + } + + /** + * Adds a WindowModeChangeListener to the window. + * + * The WindowModeChangeEvent is fired when the user changed the display + * state by clicking the maximize/restore button or by double clicking on + * the window header. The event is also fired if the state is changed using + * {@link #setWindowMode(WindowMode)}. + * + * @param listener + * the WindowModeChangeListener to add. + */ + public void addWindowModeChangeListener(WindowModeChangeListener listener) { + addListener(WindowModeChangeEvent.class, listener, + WindowModeChangeListener.windowModeChangeMethod); + } + + /** + * Removes the WindowModeChangeListener from the window. + * + * @param listener + * the WindowModeChangeListener to remove. + */ + public void removeWindowModeChangeListener(WindowModeChangeListener listener) { + removeListener(WindowModeChangeEvent.class, listener, + WindowModeChangeListener.windowModeChangeMethod); + } + + protected void fireWindowWindowModeChange() { + fireEvent(new Window.WindowModeChangeEvent(this, getState().windowMode)); + } + + /** * Method for the resize event. */ private static final Method WINDOW_RESIZE_METHOD; @@ -670,6 +775,30 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, getState().draggable = draggable; } + /** + * Gets the current mode of the window. + * + * @see WindowMode + * @return the mode of the window. + */ + public WindowMode getWindowMode() { + return getState(false).windowMode; + } + + /** + * Sets the mode for the window + * + * @see WindowMode + * @param windowMode + * The new mode + */ + public void setWindowMode(WindowMode windowMode) { + if (windowMode != getWindowMode()) { + getState().windowMode = windowMode; + fireWindowWindowModeChange(); + } + } + /* * Actions */ @@ -873,4 +1002,9 @@ public class Window extends Panel implements FocusNotifier, BlurNotifier, protected WindowState getState() { return (WindowState) super.getState(); } + + @Override + protected WindowState getState(boolean markAsDirty) { + return (WindowState) super.getState(markAsDirty); + } } diff --git a/server/src/com/vaadin/ui/components/calendar/CalendarComponentEvent.java b/server/src/com/vaadin/ui/components/calendar/CalendarComponentEvent.java new file mode 100644 index 0000000000..1f012157b5 --- /dev/null +++ b/server/src/com/vaadin/ui/components/calendar/CalendarComponentEvent.java @@ -0,0 +1,51 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.components.calendar; + +import com.vaadin.ui.Calendar; +import com.vaadin.ui.Component; + +/** + * All Calendar events extends this class. + * + * @since 7.1 + * @author Vaadin Ltd. + * + */ +@SuppressWarnings("serial") +public class CalendarComponentEvent extends Component.Event { + + /** + * Set the source of the event + * + * @param source + * The source calendar + * + */ + public CalendarComponentEvent(Calendar source) { + super(source); + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.ui.Component.Event#getComponent() + */ + @Override + public Calendar getComponent() { + return (Calendar) super.getComponent(); + } +} diff --git a/server/src/com/vaadin/ui/components/calendar/CalendarComponentEvents.java b/server/src/com/vaadin/ui/components/calendar/CalendarComponentEvents.java new file mode 100644 index 0000000000..1904d69898 --- /dev/null +++ b/server/src/com/vaadin/ui/components/calendar/CalendarComponentEvents.java @@ -0,0 +1,603 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.components.calendar; + +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.Date; +import java.util.EventListener; + +import com.vaadin.shared.ui.calendar.CalendarEventId; +import com.vaadin.ui.Calendar; +import com.vaadin.ui.components.calendar.event.CalendarEvent; +import com.vaadin.util.ReflectTools; + +/** + * Interface for all Vaadin Calendar events. + * + * @since 7.1.0 + * @author Vaadin Ltd. + */ +public interface CalendarComponentEvents extends Serializable { + + /** + * Notifier interface for notifying listener of calendar events + */ + public interface CalendarEventNotifier extends Serializable { + /** + * Get the assigned event handler for the given eventId. + * + * @param eventId + * @return the assigned eventHandler, or null if no handler is assigned + */ + public EventListener getHandler(String eventId); + } + + /** + * Notifier interface for event drag & drops. + */ + public interface EventMoveNotifier extends CalendarEventNotifier { + + /** + * Set the EventMoveHandler. + * + * @param listener + * EventMoveHandler to be added + */ + public void setHandler(EventMoveHandler listener); + + } + + /** + * MoveEvent is sent when existing event is dragged to a new position. + */ + @SuppressWarnings("serial") + public class MoveEvent extends CalendarComponentEvent { + + public static final String EVENT_ID = CalendarEventId.EVENTMOVE; + + /** Index for the moved Schedule.Event. */ + private CalendarEvent calendarEvent; + + /** New starting date for the moved Calendar.Event. */ + private Date newStart; + + /** + * MoveEvent needs the target event and new start date. + * + * @param source + * Calendar component. + * @param calendarEvent + * Target event. + * @param newStart + * Target event's new start date. + */ + public MoveEvent(Calendar source, CalendarEvent calendarEvent, + Date newStart) { + super(source); + + this.calendarEvent = calendarEvent; + this.newStart = newStart; + } + + /** + * Get target event. + * + * @return Target event. + */ + public CalendarEvent getCalendarEvent() { + return calendarEvent; + } + + /** + * Get new start date. + * + * @return New start date. + */ + public Date getNewStart() { + return newStart; + } + } + + /** + * Handler interface for when events are being dragged on the calendar + * + */ + public interface EventMoveHandler extends EventListener, Serializable { + + /** Trigger method for the MoveEvent. */ + public static final Method eventMoveMethod = ReflectTools.findMethod( + EventMoveHandler.class, "eventMove", MoveEvent.class); + + /** + * This method will be called when event has been moved to a new + * position. + * + * @param event + * MoveEvent containing specific information of the new + * position and target event. + */ + public void eventMove(MoveEvent event); + } + + /** + * Handler interface for day or time cell drag-marking with mouse. + */ + public interface RangeSelectNotifier extends Serializable, + CalendarEventNotifier { + + /** + * Set the RangeSelectHandler that listens for drag-marking. + * + * @param listener + * RangeSelectHandler to be added. + */ + public void setHandler(RangeSelectHandler listener); + } + + /** + * RangeSelectEvent is sent when day or time cells are drag-marked with + * mouse. + */ + @SuppressWarnings("serial") + public class RangeSelectEvent extends CalendarComponentEvent { + + public static final String EVENT_ID = CalendarEventId.RANGESELECT; + + /** Calendar event's start date. */ + private Date start; + + /** Calendar event's end date. */ + private Date end; + + /** + * Defines the event's view mode. + */ + private boolean monthlyMode; + + /** + * RangeSelectEvent needs a start and end date. + * + * @param source + * Calendar component. + * @param start + * Start date. + * @param end + * End date. + * @param monthlyMode + * Calendar view mode. + */ + public RangeSelectEvent(Calendar source, Date start, Date end, + boolean monthlyMode) { + super(source); + this.start = start; + this.end = end; + this.monthlyMode = monthlyMode; + } + + /** + * Get start date. + * + * @return Start date. + */ + public Date getStart() { + return start; + } + + /** + * Get end date. + * + * @return End date. + */ + public Date getEnd() { + return end; + } + + /** + * Gets the event's view mode. Calendar can be be either in monthly or + * weekly mode, depending on the active date range. + * + * @deprecated User {@link Calendar#isMonthlyMode()} instead + * + * @return Returns true when monthly view is active. + */ + @Deprecated + public boolean isMonthlyMode() { + return monthlyMode; + } + } + + /** RangeSelectHandler handles RangeSelectEvent. */ + public interface RangeSelectHandler extends EventListener, Serializable { + + /** Trigger method for the RangeSelectEvent. */ + public static final Method rangeSelectMethod = ReflectTools + .findMethod(RangeSelectHandler.class, "rangeSelect", + RangeSelectEvent.class); + + /** + * This method will be called when day or time cells are drag-marked + * with mouse. + * + * @param event + * RangeSelectEvent that contains range start and end date. + */ + public void rangeSelect(RangeSelectEvent event); + } + + /** Notifier interface for navigation listening. */ + public interface NavigationNotifier extends Serializable { + /** + * Add a forward navigation listener. + * + * @param handler + * ForwardHandler to be added. + */ + public void setHandler(ForwardHandler handler); + + /** + * Add a backward navigation listener. + * + * @param handler + * BackwardHandler to be added. + */ + public void setHandler(BackwardHandler handler); + + /** + * Add a date click listener. + * + * @param handler + * DateClickHandler to be added. + */ + public void setHandler(DateClickHandler handler); + + /** + * Add a event click listener. + * + * @param handler + * EventClickHandler to be added. + */ + public void setHandler(EventClickHandler handler); + + /** + * Add a week click listener. + * + * @param handler + * WeekClickHandler to be added. + */ + public void setHandler(WeekClickHandler handler); + } + + /** + * ForwardEvent is sent when forward navigation button is clicked. + */ + @SuppressWarnings("serial") + public class ForwardEvent extends CalendarComponentEvent { + + public static final String EVENT_ID = CalendarEventId.FORWARD; + + /** + * ForwardEvent needs only the source component. + * + * @param source + * Calendar component. + */ + public ForwardEvent(Calendar source) { + super(source); + } + } + + /** ForwardHandler handles ForwardEvent. */ + public interface ForwardHandler extends EventListener, Serializable { + + /** Trigger method for the ForwardEvent. */ + public static final Method forwardMethod = ReflectTools.findMethod( + ForwardHandler.class, "forward", ForwardEvent.class); + + /** + * This method will be called when date range is moved forward. + * + * @param event + * ForwardEvent + */ + public void forward(ForwardEvent event); + } + + /** + * BackwardEvent is sent when backward navigation button is clicked. + */ + @SuppressWarnings("serial") + public class BackwardEvent extends CalendarComponentEvent { + + public static final String EVENT_ID = CalendarEventId.BACKWARD; + + /** + * BackwardEvent needs only the source source component. + * + * @param source + * Calendar component. + */ + public BackwardEvent(Calendar source) { + super(source); + } + } + + /** BackwardHandler handles BackwardEvent. */ + public interface BackwardHandler extends EventListener, Serializable { + + /** Trigger method for the BackwardEvent. */ + public static final Method backwardMethod = ReflectTools.findMethod( + BackwardHandler.class, "backward", BackwardEvent.class); + + /** + * This method will be called when date range is moved backwards. + * + * @param event + * BackwardEvent + */ + public void backward(BackwardEvent event); + } + + /** + * DateClickEvent is sent when a date is clicked. + */ + @SuppressWarnings("serial") + public class DateClickEvent extends CalendarComponentEvent { + + public static final String EVENT_ID = CalendarEventId.DATECLICK; + + /** Date that was clicked. */ + private Date date; + + /** DateClickEvent needs the target date that was clicked. */ + public DateClickEvent(Calendar source, Date date) { + super(source); + this.date = date; + } + + /** + * Get clicked date. + * + * @return Clicked date. + */ + public Date getDate() { + return date; + } + } + + /** DateClickHandler handles DateClickEvent. */ + public interface DateClickHandler extends EventListener, Serializable { + + /** Trigger method for the DateClickEvent. */ + public static final Method dateClickMethod = ReflectTools.findMethod( + DateClickHandler.class, "dateClick", DateClickEvent.class); + + /** + * This method will be called when a date is clicked. + * + * @param event + * DateClickEvent containing the target date. + */ + public void dateClick(DateClickEvent event); + } + + /** + * EventClick is sent when an event is clicked. + */ + @SuppressWarnings("serial") + public class EventClick extends CalendarComponentEvent { + + public static final String EVENT_ID = CalendarEventId.EVENTCLICK; + + /** Clicked source event. */ + private CalendarEvent calendarEvent; + + /** Target source event is needed for the EventClick. */ + public EventClick(Calendar source, CalendarEvent calendarEvent) { + super(source); + this.calendarEvent = calendarEvent; + } + + /** + * Get the clicked event. + * + * @return Clicked event. + */ + public CalendarEvent getCalendarEvent() { + return calendarEvent; + } + } + + /** EventClickHandler handles EventClick. */ + public interface EventClickHandler extends EventListener, Serializable { + + /** Trigger method for the EventClick. */ + public static final Method eventClickMethod = ReflectTools.findMethod( + EventClickHandler.class, "eventClick", EventClick.class); + + /** + * This method will be called when an event is clicked. + * + * @param event + * EventClick containing the target event. + */ + public void eventClick(EventClick event); + } + + /** + * WeekClick is sent when week is clicked. + */ + @SuppressWarnings("serial") + public class WeekClick extends CalendarComponentEvent { + + public static final String EVENT_ID = CalendarEventId.WEEKCLICK; + + /** Target week. */ + private int week; + + /** Target year. */ + private int year; + + /** + * WeekClick needs a target year and week. + * + * @param source + * Target source. + * @param week + * Target week. + * @param year + * Target year. + */ + public WeekClick(Calendar source, int week, int year) { + super(source); + this.week = week; + this.year = year; + } + + /** + * Get week as a integer. See {@link java.util.Calendar} for the allowed + * values. + * + * @return Week as a integer. + */ + public int getWeek() { + return week; + } + + /** + * Get year as a integer. See {@link java.util.Calendar} for the allowed + * values. + * + * @return Year as a integer + */ + public int getYear() { + return year; + } + } + + /** WeekClickHandler handles WeekClicks. */ + public interface WeekClickHandler extends EventListener, Serializable { + + /** Trigger method for the WeekClick. */ + public static final Method weekClickMethod = ReflectTools.findMethod( + WeekClickHandler.class, "weekClick", WeekClick.class); + + /** + * This method will be called when a week is clicked. + * + * @param event + * WeekClick containing the target week and year. + */ + public void weekClick(WeekClick event); + } + + /** + * EventResize is sent when an event is resized + */ + @SuppressWarnings("serial") + public class EventResize extends CalendarComponentEvent { + + public static final String EVENT_ID = CalendarEventId.EVENTRESIZE; + + private CalendarEvent calendarEvent; + + private Date startTime; + + private Date endTime; + + public EventResize(Calendar source, CalendarEvent calendarEvent, + Date startTime, Date endTime) { + super(source); + this.calendarEvent = calendarEvent; + this.startTime = startTime; + this.endTime = endTime; + } + + /** + * Get target event. + * + * @return Target event. + */ + public CalendarEvent getCalendarEvent() { + return calendarEvent; + } + + /** + * @deprecated Use {@link #getNewStart()} instead + * + * @return the new start time + */ + @Deprecated + public Date getNewStartTime() { + return startTime; + } + + /** + * Returns the updated start date/time of the event + * + * @return The new date for the event + */ + public Date getNewStart() { + return startTime; + } + + /** + * @deprecated Use {@link #getNewEnd()} instead + * + * @return the new end time + */ + @Deprecated + public Date getNewEndTime() { + return endTime; + } + + /** + * Returns the updates end date/time of the event + * + * @return The new date for the event + */ + public Date getNewEnd() { + return endTime; + } + } + + /** + * Notifier interface for event resizing. + */ + public interface EventResizeNotifier extends Serializable { + + /** + * Set a EventResizeHandler. + * + * @param handler + * EventResizeHandler to be set + */ + public void setHandler(EventResizeHandler handler); + } + + /** + * Handler for EventResize event. + */ + public interface EventResizeHandler extends EventListener, Serializable { + + /** Trigger method for the EventResize. */ + public static final Method eventResizeMethod = ReflectTools.findMethod( + EventResizeHandler.class, "eventResize", EventResize.class); + + void eventResize(EventResize event); + } + +} diff --git a/server/src/com/vaadin/ui/components/calendar/CalendarDateRange.java b/server/src/com/vaadin/ui/components/calendar/CalendarDateRange.java new file mode 100644 index 0000000000..01b766a6db --- /dev/null +++ b/server/src/com/vaadin/ui/components/calendar/CalendarDateRange.java @@ -0,0 +1,86 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.components.calendar; + +import java.io.Serializable; +import java.util.Date; +import java.util.TimeZone; + +/** + * Class for representing a date range. + * + * @since 7.1.0 + * @author Vaadin Ltd. + * + */ +@SuppressWarnings("serial") +public class CalendarDateRange implements Serializable { + + private Date start; + + private Date end; + + private final transient TimeZone tz; + + /** + * Constructor + * + * @param start + * The start date and time of the date range + * @param end + * The end date and time of the date range + */ + public CalendarDateRange(Date start, Date end, TimeZone tz) { + super(); + this.start = start; + this.end = end; + this.tz = tz; + } + + /** + * Get the start date of the date range + * + * @return the start Date of the range + */ + public Date getStart() { + return start; + } + + /** + * Get the end date of the date range + * + * @return the end Date of the range + */ + public Date getEnd() { + return end; + } + + /** + * Is a date in the date range + * + * @param date + * The date to check + * @return true if the date range contains a date start and end of range + * inclusive; false otherwise + */ + public boolean inRange(Date date) { + if (date == null) { + return false; + } + + return date.compareTo(start) >= 0 && date.compareTo(end) <= 0; + } +} diff --git a/server/src/com/vaadin/ui/components/calendar/CalendarTargetDetails.java b/server/src/com/vaadin/ui/components/calendar/CalendarTargetDetails.java new file mode 100644 index 0000000000..1a3ef67377 --- /dev/null +++ b/server/src/com/vaadin/ui/components/calendar/CalendarTargetDetails.java @@ -0,0 +1,80 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.components.calendar; + +import java.util.Date; +import java.util.Map; + +import com.vaadin.event.dd.DropTarget; +import com.vaadin.event.dd.TargetDetailsImpl; +import com.vaadin.ui.Calendar; + +/** + * Drop details for {@link com.vaadin.ui.addon.calendar.ui.Calendar Calendar}. + * When something is dropped on the Calendar, this class contains the specific + * details of the drop point. Specifically, this class gives access to the date + * where the drop happened. If the Calendar was in weekly mode, the date also + * includes the start time of the slot. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class CalendarTargetDetails extends TargetDetailsImpl { + + private boolean hasDropTime; + + public CalendarTargetDetails(Map<String, Object> rawDropData, + DropTarget dropTarget) { + super(rawDropData, dropTarget); + } + + /** + * @return true if {@link #getDropTime()} will return a date object with the + * time set to the start of the time slot where the drop happened + */ + public boolean hasDropTime() { + return hasDropTime; + } + + /** + * Does the dropped item have a time associated with it + * + * @param hasDropTime + */ + public void setHasDropTime(boolean hasDropTime) { + this.hasDropTime = hasDropTime; + } + + /** + * @return the date where the drop happened + */ + public Date getDropTime() { + if (hasDropTime) { + return (Date) getData("dropTime"); + } else { + return (Date) getData("dropDay"); + } + } + + /** + * @return the {@link com.vaadin.ui.addon.calendar.ui.Calendar Calendar} + * instance which was the target of the drop + */ + public Calendar getTargetCalendar() { + return (Calendar) getTarget(); + } +} diff --git a/server/src/com/vaadin/ui/components/calendar/ContainerEventProvider.java b/server/src/com/vaadin/ui/components/calendar/ContainerEventProvider.java new file mode 100644 index 0000000000..37ea255d27 --- /dev/null +++ b/server/src/com/vaadin/ui/components/calendar/ContainerEventProvider.java @@ -0,0 +1,577 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.components.calendar; + +import java.util.Collections; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +import com.vaadin.data.Container; +import com.vaadin.data.Container.Indexed; +import com.vaadin.data.Container.ItemSetChangeEvent; +import com.vaadin.data.Container.ItemSetChangeNotifier; +import com.vaadin.data.Item; +import com.vaadin.data.Property; +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeNotifier; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventMoveHandler; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResize; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResizeHandler; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.MoveEvent; +import com.vaadin.ui.components.calendar.event.BasicEvent; +import com.vaadin.ui.components.calendar.event.CalendarEditableEventProvider; +import com.vaadin.ui.components.calendar.event.CalendarEvent; +import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeListener; +import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeNotifier; +import com.vaadin.ui.components.calendar.event.CalendarEventProvider; +import com.vaadin.ui.components.calendar.event.CalendarEventProvider.EventSetChangeNotifier; + +/** + * A event provider which uses a {@link Container} as a datasource. Container + * used as data source. + * + * NOTE: The data source must be sorted by date! + * + * @since 7.1.0 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class ContainerEventProvider implements CalendarEditableEventProvider, + EventSetChangeNotifier, EventChangeNotifier, EventMoveHandler, + EventResizeHandler, Container.ItemSetChangeListener, + Property.ValueChangeListener { + + // Default property ids + public static final String CAPTION_PROPERTY = "caption"; + public static final String DESCRIPTION_PROPERTY = "description"; + public static final String STARTDATE_PROPERTY = "start"; + public static final String ENDDATE_PROPERTY = "end"; + public static final String STYLENAME_PROPERTY = "styleName"; + + /** + * Internal class to keep the container index which item this event + * represents + * + */ + private class ContainerCalendarEvent extends BasicEvent { + private final int index; + + public ContainerCalendarEvent(int containerIndex) { + super(); + index = containerIndex; + } + + public int getContainerIndex() { + return index; + } + } + + /** + * Listeners attached to the container + */ + private final List<EventSetChangeListener> eventSetChangeListeners = new LinkedList<CalendarEventProvider.EventSetChangeListener>(); + private final List<EventChangeListener> eventChangeListeners = new LinkedList<CalendarEvent.EventChangeListener>(); + + /** + * The event cache contains the events previously created by + * {@link #getEvents(Date, Date)} + */ + private final List<CalendarEvent> eventCache = new LinkedList<CalendarEvent>(); + + /** + * The container used as datasource + */ + private Indexed container; + + /** + * Container properties. Defaults based on using the {@link BasicEvent} + * helper class. + */ + private Object captionProperty = CAPTION_PROPERTY; + private Object descriptionProperty = DESCRIPTION_PROPERTY; + private Object startDateProperty = STARTDATE_PROPERTY; + private Object endDateProperty = ENDDATE_PROPERTY; + private Object styleNameProperty = STYLENAME_PROPERTY; + + /** + * Constructor + * + * @param container + * Container to use as a data source. + */ + public ContainerEventProvider(Container.Indexed container) { + this.container = container; + listenToContainerEvents(); + } + + /** + * Set the container data source + * + * @param container + * The container to use as datasource + * + */ + public void setContainerDataSource(Container.Indexed container) { + // Detach the previous container + detachContainerDataSource(); + + this.container = container; + listenToContainerEvents(); + } + + /** + * Returns the container used as data source + * + */ + public Container.Indexed getContainerDataSource() { + return container; + } + + /** + * Attaches listeners to the container so container events can be processed + */ + private void listenToContainerEvents() { + if (container instanceof ItemSetChangeNotifier) { + ((ItemSetChangeNotifier) container).addItemSetChangeListener(this); + } + if (container instanceof ValueChangeNotifier) { + ((ValueChangeNotifier) container).addValueChangeListener(this); + } + } + + /** + * Removes listeners from the container so no events are processed + */ + private void ignoreContainerEvents() { + if (container instanceof ItemSetChangeNotifier) { + ((ItemSetChangeNotifier) container) + .removeItemSetChangeListener(this); + } + if (container instanceof ValueChangeNotifier) { + ((ValueChangeNotifier) container).removeValueChangeListener(this); + } + } + + /** + * Converts an event in the container to an {@link CalendarEvent} + * + * @param index + * The index of the item in the container to get the event for + * @return + */ + private CalendarEvent getEvent(int index) { + + // Check the event cache first + for (CalendarEvent e : eventCache) { + if (e instanceof ContainerCalendarEvent + && ((ContainerCalendarEvent) e).getContainerIndex() == index) { + return e; + } else if (container.getIdByIndex(index) == e) { + return e; + } + } + + final Object id = container.getIdByIndex(index); + Item item = container.getItem(id); + CalendarEvent event; + if (id instanceof CalendarEvent) { + /* + * If we are using the BeanItemContainer or another container which + * stores the objects as ids then just return the instances + */ + event = (CalendarEvent) id; + + } else { + /* + * Else we use the properties to create the event + */ + BasicEvent basicEvent = new ContainerCalendarEvent(index); + + // Set values from property values + if (captionProperty != null + && item.getItemPropertyIds().contains(captionProperty)) { + basicEvent.setCaption(String.valueOf(item.getItemProperty( + captionProperty).getValue())); + } + if (descriptionProperty != null + && item.getItemPropertyIds().contains(descriptionProperty)) { + basicEvent.setDescription(String.valueOf(item.getItemProperty( + descriptionProperty).getValue())); + } + if (startDateProperty != null + && item.getItemPropertyIds().contains(startDateProperty)) { + basicEvent.setStart((Date) item.getItemProperty( + startDateProperty).getValue()); + } + if (endDateProperty != null + && item.getItemPropertyIds().contains(endDateProperty)) { + basicEvent.setEnd((Date) item.getItemProperty(endDateProperty) + .getValue()); + } + if (styleNameProperty != null + && item.getItemPropertyIds().contains(styleNameProperty)) { + basicEvent.setDescription(String.valueOf(item.getItemProperty( + descriptionProperty).getValue())); + } + event = basicEvent; + } + return event; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventProvider#getEvents(java. + * util.Date, java.util.Date) + */ + @Override + public List<CalendarEvent> getEvents(Date startDate, Date endDate) { + eventCache.clear(); + + int[] rangeIndexes = getFirstAndLastEventIndex(startDate, endDate); + for (int i = rangeIndexes[0]; i <= rangeIndexes[1] + && i < container.size(); i++) { + eventCache.add(getEvent(i)); + } + return Collections.unmodifiableList(eventCache); + } + + /** + * Get the first event for a date + * + * @param date + * The date to search for, NUll returns first event in container + * @return Returns an array where the first item is the start index and the + * second item is the end item + */ + private int[] getFirstAndLastEventIndex(Date start, Date end) { + int startIndex = 0; + int size = container.size(); + int endIndex = size - 1; + + if (start != null) { + /* + * Iterating from the start of the container, if range is in the end + * of the container then this will be slow TODO This could be + * improved by using some sort of divide and conquer algorithm + */ + while (startIndex < size) { + Object id = container.getIdByIndex(startIndex); + Item item = container.getItem(id); + Date d = (Date) item.getItemProperty(startDateProperty) + .getValue(); + if (d.compareTo(start) >= 0) { + break; + } + startIndex++; + } + } + + if (end != null) { + /* + * Iterate from the start index until range ends + */ + endIndex = startIndex; + while (endIndex < size - 1) { + Object id = container.getIdByIndex(endIndex); + Item item = container.getItem(id); + Date d = (Date) item.getItemProperty(endDateProperty) + .getValue(); + if (d == null) { + // No end date present, use start date + d = (Date) item.getItemProperty(startDateProperty) + .getValue(); + } + if (d.compareTo(end) >= 0) { + endIndex--; + break; + } + endIndex++; + } + } + + return new int[] { startIndex, endIndex }; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventProvider.EventSetChangeNotifier + * #addListener(com.vaadin.addon.calendar.event.CalendarEventProvider. + * EventSetChangeListener) + */ + @Override + public void addEventSetChangeListener(EventSetChangeListener listener) { + if (!eventSetChangeListeners.contains(listener)) { + eventSetChangeListeners.add(listener); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventProvider.EventSetChangeNotifier + * #removeListener(com.vaadin.addon.calendar.event.CalendarEventProvider. + * EventSetChangeListener) + */ + @Override + public void removeEventSetChangeListener(EventSetChangeListener listener) { + eventSetChangeListeners.remove(listener); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEvent.EventChangeNotifier#addListener + * (com.vaadin.addon.calendar.event.CalendarEvent.EventChangeListener) + */ + @Override + public void addEventChangeListener(EventChangeListener listener) { + if (eventChangeListeners.contains(listener)) { + eventChangeListeners.add(listener); + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.event.CalendarEvent.EventChangeNotifier# + * removeListener + * (com.vaadin.addon.calendar.event.CalendarEvent.EventChangeListener) + */ + @Override + public void removeEventChangeListener(EventChangeListener listener) { + eventChangeListeners.remove(listener); + } + + /** + * Get the property which provides the caption of the event + */ + public Object getCaptionProperty() { + return captionProperty; + } + + /** + * Set the property which provides the caption of the event + */ + public void setCaptionProperty(Object captionProperty) { + this.captionProperty = captionProperty; + } + + /** + * Get the property which provides the description of the event + */ + public Object getDescriptionProperty() { + return descriptionProperty; + } + + /** + * Set the property which provides the description of the event + */ + public void setDescriptionProperty(Object descriptionProperty) { + this.descriptionProperty = descriptionProperty; + } + + /** + * Get the property which provides the starting date and time of the event + */ + public Object getStartDateProperty() { + return startDateProperty; + } + + /** + * Set the property which provides the starting date and time of the event + */ + public void setStartDateProperty(Object startDateProperty) { + this.startDateProperty = startDateProperty; + } + + /** + * Get the property which provides the ending date and time of the event + */ + public Object getEndDateProperty() { + return endDateProperty; + } + + /** + * Set the property which provides the ending date and time of the event + */ + public void setEndDateProperty(Object endDateProperty) { + this.endDateProperty = endDateProperty; + } + + /** + * Get the property which provides the style name for the event + */ + public Object getStyleNameProperty() { + return styleNameProperty; + } + + /** + * Set the property which provides the style name for the event + */ + public void setStyleNameProperty(Object styleNameProperty) { + this.styleNameProperty = styleNameProperty; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.data.Container.ItemSetChangeListener#containerItemSetChange + * (com.vaadin.data.Container.ItemSetChangeEvent) + */ + @Override + public void containerItemSetChange(ItemSetChangeEvent event) { + if (event.getContainer() == container) { + // Trigger an eventset change event when the itemset changes + for (EventSetChangeListener listener : eventSetChangeListeners) { + listener.eventSetChange(new EventSetChangeEvent(this)); + } + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.data.Property.ValueChangeListener#valueChange(com.vaadin.data + * .Property.ValueChangeEvent) + */ + @Override + public void valueChange(ValueChangeEvent event) { + /* + * TODO Need to figure out how to get the item which triggered the the + * valuechange event and then trigger a EventChange event to the + * listeners + */ + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveHandler + * #eventMove + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.MoveEvent) + */ + @Override + public void eventMove(MoveEvent event) { + CalendarEvent ce = event.getCalendarEvent(); + if (eventCache.contains(ce)) { + int index; + if (ce instanceof ContainerCalendarEvent) { + index = ((ContainerCalendarEvent) ce).getContainerIndex(); + } else { + index = container.indexOfId(ce); + } + + long eventLength = ce.getEnd().getTime() - ce.getStart().getTime(); + Date newEnd = new Date(event.getNewStart().getTime() + eventLength); + + ignoreContainerEvents(); + Item item = container.getItem(container.getIdByIndex(index)); + item.getItemProperty(startDateProperty).setValue( + event.getNewStart()); + item.getItemProperty(endDateProperty).setValue(newEnd); + listenToContainerEvents(); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeHandler + * #eventResize + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResize) + */ + @Override + public void eventResize(EventResize event) { + CalendarEvent ce = event.getCalendarEvent(); + if (eventCache.contains(ce)) { + int index; + if (ce instanceof ContainerCalendarEvent) { + index = ((ContainerCalendarEvent) ce).getContainerIndex(); + } else { + index = container.indexOfId(ce); + } + ignoreContainerEvents(); + Item item = container.getItem(container.getIdByIndex(index)); + item.getItemProperty(startDateProperty).setValue( + event.getNewStart()); + item.getItemProperty(endDateProperty).setValue(event.getNewEnd()); + listenToContainerEvents(); + } + } + + /** + * If you are reusing the container which previously have been attached to + * this ContainerEventProvider call this method to remove this event + * providers container listeners before attaching it to an other + * ContainerEventProvider + */ + public void detachContainerDataSource() { + ignoreContainerEvents(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#addEvent + * (com.vaadin.addon.calendar.event.CalendarEvent) + */ + @Override + public void addEvent(CalendarEvent event) { + Item item; + try { + item = container.addItem(event); + } catch (UnsupportedOperationException uop) { + // Thrown if container does not support adding items with custom + // ids. JPAContainer for example. + item = container.getItem(container.addItem()); + } + if (item != null) { + item.getItemProperty(getCaptionProperty()).setValue( + event.getCaption()); + item.getItemProperty(getStartDateProperty()).setValue( + event.getStart()); + item.getItemProperty(getEndDateProperty()).setValue(event.getEnd()); + item.getItemProperty(getStyleNameProperty()).setValue( + event.getStyleName()); + item.getItemProperty(getDescriptionProperty()).setValue( + event.getDescription()); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#removeEvent + * (com.vaadin.addon.calendar.event.CalendarEvent) + */ + @Override + public void removeEvent(CalendarEvent event) { + container.removeItem(event); + } +} diff --git a/server/src/com/vaadin/ui/components/calendar/event/BasicEvent.java b/server/src/com/vaadin/ui/components/calendar/event/BasicEvent.java new file mode 100644 index 0000000000..3f14145f0c --- /dev/null +++ b/server/src/com/vaadin/ui/components/calendar/event/BasicEvent.java @@ -0,0 +1,265 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.components.calendar.event; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeNotifier; + +/** + * Simple implementation of + * {@link com.vaadin.addon.calendar.event.CalendarEvent CalendarEvent}. Has + * setters for all required fields and fires events when this event is changed. + * + * @since 7.1.0 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class BasicEvent implements EditableCalendarEvent, EventChangeNotifier { + + private String caption; + private String description; + private Date end; + private Date start; + private String styleName; + private transient List<EventChangeListener> listeners = new ArrayList<EventChangeListener>(); + + private boolean isAllDay; + + /** + * Default constructor + */ + public BasicEvent() { + + } + + /** + * Constructor for creating an event with the same start and end date + * + * @param caption + * The caption for the event + * @param description + * The description for the event + * @param date + * The date the event occurred + */ + public BasicEvent(String caption, String description, Date date) { + this.caption = caption; + this.description = description; + start = date; + end = date; + } + + /** + * Constructor for creating an event with a start date and an end date. + * Start date should be before the end date + * + * @param caption + * The caption for the event + * @param description + * The description for the event + * @param startDate + * The start date of the event + * @param endDate + * The end date of the event + */ + public BasicEvent(String caption, String description, Date startDate, + Date endDate) { + this.caption = caption; + this.description = description; + start = startDate; + end = endDate; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.event.CalendarEvent#getCaption() + */ + @Override + public String getCaption() { + return caption; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.event.CalendarEvent#getDescription() + */ + @Override + public String getDescription() { + return description; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.event.CalendarEvent#getEnd() + */ + @Override + public Date getEnd() { + return end; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.event.CalendarEvent#getStart() + */ + @Override + public Date getStart() { + return start; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.event.CalendarEvent#getStyleName() + */ + @Override + public String getStyleName() { + return styleName; + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.addon.calendar.event.CalendarEvent#isAllDay() + */ + @Override + public boolean isAllDay() { + return isAllDay; + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventEditor#setCaption(java.lang + * .String) + */ + @Override + public void setCaption(String caption) { + this.caption = caption; + fireEventChange(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventEditor#setDescription(java + * .lang.String) + */ + @Override + public void setDescription(String description) { + this.description = description; + fireEventChange(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventEditor#setEnd(java.util. + * Date) + */ + @Override + public void setEnd(Date end) { + this.end = end; + fireEventChange(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventEditor#setStart(java.util + * .Date) + */ + @Override + public void setStart(Date start) { + this.start = start; + fireEventChange(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventEditor#setStyleName(java + * .lang.String) + */ + @Override + public void setStyleName(String styleName) { + this.styleName = styleName; + fireEventChange(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventEditor#setAllDay(boolean) + */ + @Override + public void setAllDay(boolean isAllDay) { + this.isAllDay = isAllDay; + fireEventChange(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeListener + * ) + */ + @Override + public void addEventChangeListener(EventChangeListener listener) { + listeners.add(listener); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeNotifier + * #removeListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeListener + * ) + */ + @Override + public void removeEventChangeListener(EventChangeListener listener) { + listeners.remove(listener); + } + + /** + * Fires an event change event to the listeners. Should be triggered when + * some property of the event changes. + */ + protected void fireEventChange() { + EventChangeEvent event = new EventChangeEvent(this); + + for (EventChangeListener listener : listeners) { + listener.eventChange(event); + } + } +} diff --git a/server/src/com/vaadin/ui/components/calendar/event/BasicEventProvider.java b/server/src/com/vaadin/ui/components/calendar/event/BasicEventProvider.java new file mode 100644 index 0000000000..b2b74a5e52 --- /dev/null +++ b/server/src/com/vaadin/ui/components/calendar/event/BasicEventProvider.java @@ -0,0 +1,179 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.components.calendar.event; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +import com.vaadin.ui.components.calendar.event.CalendarEvent.EventChangeEvent; +import com.vaadin.ui.components.calendar.event.CalendarEventProvider.EventSetChangeNotifier; + +/** + * <p> + * Simple implementation of + * {@link com.vaadin.addon.calendar.event.CalendarEventProvider + * CalendarEventProvider}. Use {@link #addEvent(CalendarEvent)} and + * {@link #removeEvent(CalendarEvent)} to add / remove events. + * </p> + * + * <p> + * {@link com.vaadin.addon.calendar.event.CalendarEventProvider.EventSetChangeNotifier + * EventSetChangeNotifier} and + * {@link com.vaadin.addon.calendar.event.CalendarEvent.EventChangeListener + * EventChangeListener} are also implemented, so the Calendar is notified when + * an event is added, changed or removed. + * </p> + * + * @since 7.1.0 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class BasicEventProvider implements CalendarEditableEventProvider, + EventSetChangeNotifier, CalendarEvent.EventChangeListener { + + protected List<CalendarEvent> eventList = new ArrayList<CalendarEvent>(); + + private List<EventSetChangeListener> listeners = new ArrayList<EventSetChangeListener>(); + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEventProvider#getEvents(java. + * util.Date, java.util.Date) + */ + @Override + public List<CalendarEvent> getEvents(Date startDate, Date endDate) { + ArrayList<CalendarEvent> activeEvents = new ArrayList<CalendarEvent>(); + + for (CalendarEvent ev : eventList) { + long from = startDate.getTime(); + long to = endDate.getTime(); + + if (ev.getStart() != null && ev.getEnd() != null) { + long f = ev.getStart().getTime(); + long t = ev.getEnd().getTime(); + // Select only events that overlaps with startDate and + // endDate. + if ((f <= to && f >= from) || (t >= from && t <= to) + || (f <= from && t >= to)) { + activeEvents.add(ev); + } + } + } + + return activeEvents; + } + + /** + * Does this event provider container this event + * + * @param event + * The event to check for + * @return If this provider has the event then true is returned, else false + */ + public boolean containsEvent(BasicEvent event) { + return eventList.contains(event); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventSetChangeNotifier + * #addListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventSetChangeListener + * ) + */ + @Override + public void addEventSetChangeListener(EventSetChangeListener listener) { + listeners.add(listener); + + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventSetChangeNotifier + * #removeListener + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventSetChangeListener + * ) + */ + @Override + public void removeEventSetChangeListener(EventSetChangeListener listener) { + listeners.remove(listener); + } + + /** + * Fires a eventsetchange event. The event is fired when either an event is + * added or removed to the event provider + */ + protected void fireEventSetChange() { + EventSetChangeEvent event = new EventSetChangeEvent(this); + + for (EventSetChangeListener listener : listeners) { + listener.eventSetChange(event); + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventChangeListener + * #eventChange + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventSetChange) + */ + @Override + public void eventChange(EventChangeEvent changeEvent) { + // naive implementation + fireEventSetChange(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#addEvent + * (com.vaadin.addon.calendar.event.CalendarEvent) + */ + @Override + public void addEvent(CalendarEvent event) { + eventList.add(event); + if (event instanceof BasicEvent) { + ((BasicEvent) event).addEventChangeListener(this); + } + fireEventSetChange(); + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.event.CalendarEditableEventProvider#removeEvent + * (com.vaadin.addon.calendar.event.CalendarEvent) + */ + @Override + public void removeEvent(CalendarEvent event) { + eventList.remove(event); + if (event instanceof BasicEvent) { + ((BasicEvent) event).removeEventChangeListener(this); + } + fireEventSetChange(); + } +} diff --git a/server/src/com/vaadin/ui/components/calendar/event/CalendarEditableEventProvider.java b/server/src/com/vaadin/ui/components/calendar/event/CalendarEditableEventProvider.java new file mode 100644 index 0000000000..13e84df666 --- /dev/null +++ b/server/src/com/vaadin/ui/components/calendar/event/CalendarEditableEventProvider.java @@ -0,0 +1,42 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.ui.components.calendar.event; + +/** + * An event provider which allows adding and removing events + * + * @since 7.1.0 + * @author Vaadin Ltd. + */ +public interface CalendarEditableEventProvider extends CalendarEventProvider { + + /** + * Adds an event to the event provider + * + * @param event + * The event to add + */ + void addEvent(CalendarEvent event); + + /** + * Removes an event from the event provider + * + * @param event + * The event + */ + void removeEvent(CalendarEvent event); +} diff --git a/server/src/com/vaadin/ui/components/calendar/event/CalendarEvent.java b/server/src/com/vaadin/ui/components/calendar/event/CalendarEvent.java new file mode 100644 index 0000000000..531ee72c7f --- /dev/null +++ b/server/src/com/vaadin/ui/components/calendar/event/CalendarEvent.java @@ -0,0 +1,146 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.components.calendar.event; + +import java.io.Serializable; +import java.util.Date; + +/** + * <p> + * Event in the calendar. Customize your own event by implementing this + * interface. + * </p> + * + * <li>Start and end fields are mandatory.</li> + * + * <li>In "allDay" events longer than one day, starting and ending clock times + * are omitted in UI and only dates are shown.</li> + * + * @since 7.1.0 + * @author Vaadin Ltd. + * + */ +public interface CalendarEvent extends Serializable { + + /** + * Gets start date of event. + * + * @return Start date. + */ + public Date getStart(); + + /** + * Get end date of event. + * + * @return End date; + */ + public Date getEnd(); + + /** + * Gets caption of event. + * + * @return Caption text + */ + public String getCaption(); + + /** + * Gets description of event. Shown as a tooltip over the event. + * + * @return Description text. + */ + public String getDescription(); + + /** + * <p> + * Gets style name of event. In the client, style name will be set to the + * event's element class name and can be styled by CSS + * </p> + * Styling example:</br> <code>Java code: </br> + * event.setStyleName("color1"); + * </br></br> + * CSS:</br> + * .v-calendar-event-color1 {</br> + * background-color: #9effae;</br>}</code> + * + * @return Style name. + */ + public String getStyleName(); + + /** + * An all-day event typically does not occur at a specific time but targets + * a whole day or days. The rendering of all-day events differs from normal + * events. + * + * @return true if this event is an all-day event, false otherwise + */ + public boolean isAllDay(); + + /** + * Event to signal that an event has changed. + */ + @SuppressWarnings("serial") + public class EventChangeEvent implements Serializable { + + private CalendarEvent source; + + public EventChangeEvent(CalendarEvent source) { + this.source = source; + } + + /** + * @return the {@link com.vaadin.addon.calendar.event.CalendarEvent + * CalendarEvent} that has changed + */ + public CalendarEvent getCalendarEvent() { + return source; + } + } + + /** + * Listener for EventSetChange events. + */ + public interface EventChangeListener extends Serializable { + + /** + * Called when an Event has changed. + */ + public void eventChange(EventChangeEvent eventChangeEvent); + } + + /** + * Notifier interface for EventChange events. + */ + public interface EventChangeNotifier extends Serializable { + + /** + * Add a listener to listen for EventChangeEvents. These events are + * fired when a events properties are changed. + * + * @param listener + * The listener to add + */ + void addEventChangeListener(EventChangeListener listener); + + /** + * Remove a listener from the event provider. + * + * @param listener + * The listener to remove + */ + void removeEventChangeListener(EventChangeListener listener); + } + +} diff --git a/server/src/com/vaadin/ui/components/calendar/event/CalendarEventProvider.java b/server/src/com/vaadin/ui/components/calendar/event/CalendarEventProvider.java new file mode 100644 index 0000000000..fefb2ca9b6 --- /dev/null +++ b/server/src/com/vaadin/ui/components/calendar/event/CalendarEventProvider.java @@ -0,0 +1,112 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.components.calendar.event; + +import java.io.Serializable; +import java.util.Date; +import java.util.List; + +/** + * Interface for querying events. The Vaadin Calendar always has a + * CalendarEventProvider set. + * + * @since 7.1.0 + * @author Vaadin Ltd. + */ +public interface CalendarEventProvider extends Serializable { + /** + * <p> + * Gets all available events in the target date range between startDate and + * endDate. The Vaadin Calendar queries the events from the range that is + * shown, which is not guaranteed to be the same as the date range that is + * set. + * </p> + * + * <p> + * For example, if you set the date range to be monday 22.2.2010 - wednesday + * 24.2.2000, the used Event Provider will be queried for events between + * monday 22.2.2010 00:00 and sunday 28.2.2010 23:59. Generally you can + * expect the date range to be expanded to whole days and whole weeks. + * </p> + * + * @param startDate + * Start date + * @param endDate + * End date + * @return List of events + */ + public List<CalendarEvent> getEvents(Date startDate, Date endDate); + + /** + * Event to signal that the set of events has changed and the calendar + * should refresh its view from the + * {@link com.vaadin.addon.calendar.event.CalendarEventProvider + * CalendarEventProvider} . + * + */ + @SuppressWarnings("serial") + public class EventSetChangeEvent implements Serializable { + + private CalendarEventProvider source; + + public EventSetChangeEvent(CalendarEventProvider source) { + this.source = source; + } + + /** + * @return the + * {@link com.vaadin.addon.calendar.event.CalendarEventProvider + * CalendarEventProvider} that has changed + */ + public CalendarEventProvider getProvider() { + return source; + } + } + + /** + * Listener for EventSetChange events. + */ + public interface EventSetChangeListener extends Serializable { + + /** + * Called when the set of Events has changed. + */ + public void eventSetChange(EventSetChangeEvent changeEvent); + } + + /** + * Notifier interface for EventSetChange events. + */ + public interface EventSetChangeNotifier extends Serializable { + + /** + * Add a listener for listening to when new events are adding or removed + * from the event provider. + * + * @param listener + * The listener to add + */ + void addEventSetChangeListener(EventSetChangeListener listener); + + /** + * Remove a listener which listens to {@link EventSetChangeEvent}-events + * + * @param listener + * The listener to remove + */ + void removeEventSetChangeListener(EventSetChangeListener listener); + } +} diff --git a/server/src/com/vaadin/ui/components/calendar/event/EditableCalendarEvent.java b/server/src/com/vaadin/ui/components/calendar/event/EditableCalendarEvent.java new file mode 100644 index 0000000000..e8a27ad50f --- /dev/null +++ b/server/src/com/vaadin/ui/components/calendar/event/EditableCalendarEvent.java @@ -0,0 +1,91 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.components.calendar.event; + +import java.util.Date; + +/** + * <p> + * Extension to the basic {@link com.vaadin.addon.calendar.event.CalendarEvent + * CalendarEvent}. This interface provides setters (and thus editing + * capabilities) for all {@link com.vaadin.addon.calendar.event.CalendarEvent + * CalendarEvent} fields. For descriptions on the fields, refer to the extended + * interface. + * </p> + * + * <p> + * This interface is used by some of the basic Calendar event handlers in the + * <code>com.vaadin.addon.calendar.ui.handler</code> package to determine + * whether an event can be edited. + * </p> + * + * @since 7.1 + * @author Vaadin Ltd. + */ +public interface EditableCalendarEvent extends CalendarEvent { + + /** + * Set the visible text in the calendar for the event. + * + * @param caption + * The text to show in the calendar + */ + void setCaption(String caption); + + /** + * Set the description of the event. This is shown in the calendar when + * hoovering over the event. + * + * @param description + * The text which describes the event + */ + void setDescription(String description); + + /** + * Set the end date of the event. Must be after the start date. + * + * @param end + * The end date to set + */ + void setEnd(Date end); + + /** + * Set the start date for the event. Must be before the end date + * + * @param start + * The start date of the event + */ + void setStart(Date start); + + /** + * Set the style name for the event used for styling the event cells + * + * @param styleName + * The stylename to use + * + */ + void setStyleName(String styleName); + + /** + * Does the event span the whole day. If so then set this to true + * + * @param isAllDay + * True if the event spans the whole day. In this case the start + * and end times are ignored. + */ + void setAllDay(boolean isAllDay); + +} diff --git a/server/src/com/vaadin/ui/components/calendar/handler/BasicBackwardHandler.java b/server/src/com/vaadin/ui/components/calendar/handler/BasicBackwardHandler.java new file mode 100644 index 0000000000..65e9c94dec --- /dev/null +++ b/server/src/com/vaadin/ui/components/calendar/handler/BasicBackwardHandler.java @@ -0,0 +1,79 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.components.calendar.handler; + +import java.util.Calendar; +import java.util.Date; + +import com.vaadin.shared.ui.calendar.DateConstants; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.BackwardEvent; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.BackwardHandler; + +/** + * Implements basic functionality needed to enable backwards navigation. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class BasicBackwardHandler implements BackwardHandler { + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.BackwardHandler# + * backward + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.BackwardEvent) + */ + @Override + public void backward(BackwardEvent event) { + Date start = event.getComponent().getStartDate(); + Date end = event.getComponent().getEndDate(); + + // calculate amount to move back + int durationInDays = (int) (((end.getTime()) - start.getTime()) / DateConstants.DAYINMILLIS); + durationInDays++; + durationInDays = -durationInDays; + + // set new start and end times + Calendar javaCalendar = event.getComponent().getInternalCalendar(); + javaCalendar.setTime(start); + javaCalendar.add(java.util.Calendar.DATE, durationInDays); + Date newStart = javaCalendar.getTime(); + + javaCalendar.setTime(end); + javaCalendar.add(java.util.Calendar.DATE, durationInDays); + Date newEnd = javaCalendar.getTime(); + + setDates(event, newStart, newEnd); + } + + /** + * Set the start and end dates for the event + * + * @param event + * The event that the start and end dates should be set + * @param start + * The start date + * @param end + * The end date + */ + protected void setDates(BackwardEvent event, Date start, Date end) { + event.getComponent().setStartDate(start); + event.getComponent().setEndDate(end); + } +} diff --git a/server/src/com/vaadin/ui/components/calendar/handler/BasicDateClickHandler.java b/server/src/com/vaadin/ui/components/calendar/handler/BasicDateClickHandler.java new file mode 100644 index 0000000000..ac2470e008 --- /dev/null +++ b/server/src/com/vaadin/ui/components/calendar/handler/BasicDateClickHandler.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.components.calendar.handler; + +import java.util.Calendar; +import java.util.Date; + +import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickEvent; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickHandler; + +/** + * Implements basic functionality needed to switch to day view when a single day + * is clicked. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class BasicDateClickHandler implements DateClickHandler { + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.DateClickHandler + * #dateClick + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.DateClickEvent) + */ + @Override + public void dateClick(DateClickEvent event) { + Date clickedDate = event.getDate(); + + Calendar javaCalendar = event.getComponent().getInternalCalendar(); + javaCalendar.setTime(clickedDate); + + // as times are expanded, this is all that is needed to show one day + Date start = javaCalendar.getTime(); + Date end = javaCalendar.getTime(); + + setDates(event, start, end); + } + + /** + * Set the start and end dates for the event + * + * @param event + * The event that the start and end dates should be set + * @param start + * The start date + * @param end + * The end date + */ + protected void setDates(DateClickEvent event, Date start, Date end) { + event.getComponent().setStartDate(start); + event.getComponent().setEndDate(end); + } +} diff --git a/server/src/com/vaadin/ui/components/calendar/handler/BasicEventMoveHandler.java b/server/src/com/vaadin/ui/components/calendar/handler/BasicEventMoveHandler.java new file mode 100644 index 0000000000..ae4c5fcc12 --- /dev/null +++ b/server/src/com/vaadin/ui/components/calendar/handler/BasicEventMoveHandler.java @@ -0,0 +1,74 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.components.calendar.handler; + +import java.util.Date; + +import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventMoveHandler; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.MoveEvent; +import com.vaadin.ui.components.calendar.event.CalendarEvent; +import com.vaadin.ui.components.calendar.event.EditableCalendarEvent; + +/** + * Implements basic functionality needed to enable moving events. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class BasicEventMoveHandler implements EventMoveHandler { + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventMoveHandler + * #eventMove + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.MoveEvent) + */ + @Override + public void eventMove(MoveEvent event) { + CalendarEvent calendarEvent = event.getCalendarEvent(); + + if (calendarEvent instanceof EditableCalendarEvent) { + + EditableCalendarEvent editableEvent = (EditableCalendarEvent) calendarEvent; + + Date newFromTime = event.getNewStart(); + + // Update event dates + long length = editableEvent.getEnd().getTime() + - editableEvent.getStart().getTime(); + setDates(editableEvent, newFromTime, new Date(newFromTime.getTime() + + length)); + } + } + + /** + * Set the start and end dates for the event + * + * @param event + * The event that the start and end dates should be set + * @param start + * The start date + * @param end + * The end date + */ + protected void setDates(EditableCalendarEvent event, Date start, Date end) { + event.setStart(start); + event.setEnd(end); + } +} diff --git a/server/src/com/vaadin/ui/components/calendar/handler/BasicEventResizeHandler.java b/server/src/com/vaadin/ui/components/calendar/handler/BasicEventResizeHandler.java new file mode 100644 index 0000000000..ee7fc27360 --- /dev/null +++ b/server/src/com/vaadin/ui/components/calendar/handler/BasicEventResizeHandler.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.components.calendar.handler; + +import java.util.Date; + +import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResize; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResizeHandler; +import com.vaadin.ui.components.calendar.event.CalendarEvent; +import com.vaadin.ui.components.calendar.event.EditableCalendarEvent; + +/** + * Implements basic functionality needed to enable event resizing. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class BasicEventResizeHandler implements EventResizeHandler { + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResizeHandler + * #eventResize + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.EventResize) + */ + @Override + public void eventResize(EventResize event) { + CalendarEvent calendarEvent = event.getCalendarEvent(); + + if (calendarEvent instanceof EditableCalendarEvent) { + Date newStartTime = event.getNewStart(); + Date newEndTime = event.getNewEnd(); + + EditableCalendarEvent editableEvent = (EditableCalendarEvent) calendarEvent; + + setDates(editableEvent, newStartTime, newEndTime); + } + } + + /** + * Set the start and end dates for the event + * + * @param event + * The event that the start and end dates should be set + * @param start + * The start date + * @param end + * The end date + */ + protected void setDates(EditableCalendarEvent event, Date start, Date end) { + event.setStart(start); + event.setEnd(end); + } +} diff --git a/server/src/com/vaadin/ui/components/calendar/handler/BasicForwardHandler.java b/server/src/com/vaadin/ui/components/calendar/handler/BasicForwardHandler.java new file mode 100644 index 0000000000..e36c9e5756 --- /dev/null +++ b/server/src/com/vaadin/ui/components/calendar/handler/BasicForwardHandler.java @@ -0,0 +1,77 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.components.calendar.handler; + +import java.util.Calendar; +import java.util.Date; + +import com.vaadin.shared.ui.calendar.DateConstants; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.ForwardEvent; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.ForwardHandler; + +/** + * Implements basic functionality needed to enable forward navigation. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class BasicForwardHandler implements ForwardHandler { + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.ForwardHandler#forward + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.ForwardEvent) + */ + @Override + public void forward(ForwardEvent event) { + Date start = event.getComponent().getStartDate(); + Date end = event.getComponent().getEndDate(); + + // calculate amount to move forward + int durationInDays = (int) (((end.getTime()) - start.getTime()) / DateConstants.DAYINMILLIS); + durationInDays++; + + // set new start and end times + Calendar javaCalendar = Calendar.getInstance(); + javaCalendar.setTime(start); + javaCalendar.add(java.util.Calendar.DATE, durationInDays); + Date newStart = javaCalendar.getTime(); + + javaCalendar.setTime(end); + javaCalendar.add(java.util.Calendar.DATE, durationInDays); + Date newEnd = javaCalendar.getTime(); + + setDates(event, newStart, newEnd); + } + + /** + * Set the start and end dates for the event + * + * @param event + * The event that the start and end dates should be set + * @param start + * The start date + * @param end + * The end date + */ + protected void setDates(ForwardEvent event, Date start, Date end) { + event.getComponent().setStartDate(start); + event.getComponent().setEndDate(end); + } +} diff --git a/server/src/com/vaadin/ui/components/calendar/handler/BasicWeekClickHandler.java b/server/src/com/vaadin/ui/components/calendar/handler/BasicWeekClickHandler.java new file mode 100644 index 0000000000..846fd7dd53 --- /dev/null +++ b/server/src/com/vaadin/ui/components/calendar/handler/BasicWeekClickHandler.java @@ -0,0 +1,82 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.ui.components.calendar.handler; + +import java.util.Calendar; +import java.util.Date; +import java.util.GregorianCalendar; + +import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClick; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClickHandler; + +/** + * Implements basic functionality needed to change to week view when a week + * number is clicked. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +@SuppressWarnings("serial") +public class BasicWeekClickHandler implements WeekClickHandler { + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.addon.calendar.ui.CalendarComponentEvents.WeekClickHandler + * #weekClick + * (com.vaadin.addon.calendar.ui.CalendarComponentEvents.WeekClick) + */ + @Override + public void weekClick(WeekClick event) { + int week = event.getWeek(); + int year = event.getYear(); + + // set correct year and month + Calendar javaCalendar = event.getComponent().getInternalCalendar(); + javaCalendar.set(GregorianCalendar.YEAR, year); + javaCalendar.set(GregorianCalendar.WEEK_OF_YEAR, week); + + // starting at the beginning of the week + javaCalendar.set(GregorianCalendar.DAY_OF_WEEK, + javaCalendar.getFirstDayOfWeek()); + Date start = javaCalendar.getTime(); + + // ending at the end of the week + javaCalendar.add(GregorianCalendar.DATE, 6); + Date end = javaCalendar.getTime(); + + setDates(event, start, end); + + // times are automatically expanded, no need to worry about them + } + + /** + * Set the start and end dates for the event + * + * @param event + * The event that the start and end dates should be set + * @param start + * The start date + * @param end + * The end date + */ + protected void setDates(WeekClick event, Date start, Date end) { + event.getComponent().setStartDate(start); + event.getComponent().setEndDate(end); + } + +} diff --git a/server/src/com/vaadin/ui/components/colorpicker/ColorPickerGrid.java b/server/src/com/vaadin/ui/components/colorpicker/ColorPickerGrid.java index 9e9855afdd..9123245033 100644 --- a/server/src/com/vaadin/ui/components/colorpicker/ColorPickerGrid.java +++ b/server/src/com/vaadin/ui/components/colorpicker/ColorPickerGrid.java @@ -187,6 +187,7 @@ public class ColorPickerGrid extends AbstractComponent implements ColorSelector * @param listener * The color change listener */ + @Override public void addColorChangeListener(ColorChangeListener listener) { addListener(ColorChangeEvent.class, listener, COLOR_CHANGE_METHOD); } @@ -202,6 +203,7 @@ public class ColorPickerGrid extends AbstractComponent implements ColorSelector * @param listener * The listener */ + @Override public void removeColorChangeListener(ColorChangeListener listener) { removeListener(ColorChangeEvent.class, listener); } diff --git a/server/src/com/vaadin/ui/components/colorpicker/ColorPickerHistory.java b/server/src/com/vaadin/ui/components/colorpicker/ColorPickerHistory.java index e6edbcf40e..2902585f56 100644 --- a/server/src/com/vaadin/ui/components/colorpicker/ColorPickerHistory.java +++ b/server/src/com/vaadin/ui/components/colorpicker/ColorPickerHistory.java @@ -194,6 +194,7 @@ public class ColorPickerHistory extends CustomComponent implements * @param listener * The listener */ + @Override public void addColorChangeListener(ColorChangeListener listener) { addListener(ColorChangeEvent.class, listener, COLOR_CHANGE_METHOD); } @@ -204,6 +205,7 @@ public class ColorPickerHistory extends CustomComponent implements * @param listener * The listener */ + @Override public void removeColorChangeListener(ColorChangeListener listener) { removeListener(ColorChangeEvent.class, listener); } diff --git a/server/src/com/vaadin/ui/components/colorpicker/ColorPickerPopup.java b/server/src/com/vaadin/ui/components/colorpicker/ColorPickerPopup.java index c06ae9f6ff..fee52d1a24 100644 --- a/server/src/com/vaadin/ui/components/colorpicker/ColorPickerPopup.java +++ b/server/src/com/vaadin/ui/components/colorpicker/ColorPickerPopup.java @@ -283,6 +283,7 @@ public class ColorPickerPopup extends Window implements ClickListener, } redSlider.addValueChangeListener(new ValueChangeListener() { + @Override public void valueChange(ValueChangeEvent event) { double red = (Double) event.getProperty().getValue(); if (!updatingColors) { @@ -303,6 +304,7 @@ public class ColorPickerPopup extends Window implements ClickListener, } greenSlider.addValueChangeListener(new ValueChangeListener() { + @Override public void valueChange(ValueChangeEvent event) { double green = (Double) event.getProperty().getValue(); if (!updatingColors) { @@ -322,6 +324,7 @@ public class ColorPickerPopup extends Window implements ClickListener, } blueSlider.addValueChangeListener(new ValueChangeListener() { + @Override public void valueChange(ValueChangeEvent event) { double blue = (Double) event.getProperty().getValue(); if (!updatingColors) { @@ -380,6 +383,7 @@ public class ColorPickerPopup extends Window implements ClickListener, hueSlider.setWidth("220px"); hueSlider.setImmediate(true); hueSlider.addValueChangeListener(new ValueChangeListener() { + @Override public void valueChange(ValueChangeEvent event) { if (!updatingColors) { float hue = (Float.parseFloat(event.getProperty() @@ -417,6 +421,7 @@ public class ColorPickerPopup extends Window implements ClickListener, saturationSlider.setWidth("220px"); saturationSlider.setImmediate(true); saturationSlider.addValueChangeListener(new ValueChangeListener() { + @Override public void valueChange(ValueChangeEvent event) { if (!updatingColors) { float hue = (Float.parseFloat(hueSlider.getValue() @@ -444,6 +449,7 @@ public class ColorPickerPopup extends Window implements ClickListener, valueSlider.setWidth("220px"); valueSlider.setImmediate(true); valueSlider.addValueChangeListener(new ValueChangeListener() { + @Override public void valueChange(ValueChangeEvent event) { if (!updatingColors) { float hue = (Float.parseFloat(hueSlider.getValue() @@ -754,6 +760,7 @@ public class ColorPickerPopup extends Window implements ClickListener, /** HSV color converter */ Coordinates2Color HSVConverter = new Coordinates2Color() { + @Override public int[] calculate(Color color) { float[] hsv = color.getHSV(); @@ -769,6 +776,7 @@ public class ColorPickerPopup extends Window implements ClickListener, return new int[] { x, y }; } + @Override public Color calculate(int x, int y) { float saturation = 1f - (y / 220.0f); float value = (x / 220.0f); diff --git a/server/src/com/vaadin/util/CurrentInstance.java b/server/src/com/vaadin/util/CurrentInstance.java index 595c162e7e..60489d596e 100644 --- a/server/src/com/vaadin/util/CurrentInstance.java +++ b/server/src/com/vaadin/util/CurrentInstance.java @@ -21,8 +21,28 @@ import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; +import com.vaadin.server.VaadinPortlet; +import com.vaadin.server.VaadinPortletService; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinResponse; +import com.vaadin.server.VaadinService; +import com.vaadin.server.VaadinServlet; +import com.vaadin.server.VaadinServletService; +import com.vaadin.server.VaadinSession; +import com.vaadin.ui.UI; + /** * Keeps track of various thread local instances used by the framework. + * <p> + * Currently the framework uses the following instances: + * </p> + * <p> + * Inheritable: {@link UI}, {@link VaadinPortlet}, {@link VaadinService}, + * {@link VaadinServlet}, {@link VaadinSession}. + * </p> + * <p> + * Non-inheritable: {@link VaadinRequest}, {@link VaadinResponse}. + * </p> * * @author Vaadin Ltd * @version @VERSION@ @@ -32,6 +52,18 @@ public class CurrentInstance implements Serializable { private final Object instance; private final boolean inheritable; + private static boolean portletAvailable = false; + { + try { + /* + * VaadinPortlet depends on portlet API which is available only if + * running in a portal. + */ + portletAvailable = (VaadinPortlet.class.getName() != null); + } catch (Throwable t) { + } + } + private static InheritableThreadLocal<Map<Class<?>, CurrentInstance>> instances = new InheritableThreadLocal<Map<Class<?>, CurrentInstance>>() { @Override protected Map<Class<?>, CurrentInstance> childValue( @@ -135,7 +167,11 @@ public class CurrentInstance implements Serializable { new CurrentInstance(instance, inheritable)); if (previousInstance != null) { assert previousInstance.inheritable == inheritable : "Inheritable status mismatch for " - + type; + + type + + " (previous was " + + previousInstance.inheritable + + ", new is " + + inheritable + ")"; } } } @@ -146,4 +182,72 @@ public class CurrentInstance implements Serializable { public static void clearAll() { instances.remove(); } + + /** + * Restores the given thread locals to the given values. Note that this + * should only be used internally to restore Vaadin classes. + * + * @param old + * A Class -> Object map to set as thread locals + */ + public static void restoreThreadLocals(Map<Class<?>, CurrentInstance> old) { + for (Class c : old.keySet()) { + CurrentInstance ci = old.get(c); + set(c, ci.instance, ci.inheritable); + } + } + + /** + * Sets thread locals for the UI and all related classes + * + * @param ui + * The UI + * @return A map containing the old values of the thread locals this method + * updated. + */ + public static Map<Class<?>, CurrentInstance> setThreadLocals(UI ui) { + Map<Class<?>, CurrentInstance> old = new HashMap<Class<?>, CurrentInstance>(); + old.put(UI.class, new CurrentInstance(UI.getCurrent(), true)); + UI.setCurrent(ui); + old.putAll(setThreadLocals(ui.getSession())); + return old; + } + + /** + * Sets thread locals for the {@link VaadinSession} and all related classes + * + * @param session + * The VaadinSession + * @return A map containing the old values of the thread locals this method + * updated. + */ + public static Map<Class<?>, CurrentInstance> setThreadLocals( + VaadinSession session) { + Map<Class<?>, CurrentInstance> old = new HashMap<Class<?>, CurrentInstance>(); + old.put(VaadinSession.class, + new CurrentInstance(VaadinSession.getCurrent(), true)); + old.put(VaadinService.class, + new CurrentInstance(VaadinService.getCurrent(), true)); + VaadinService service = null; + if (session != null) { + service = session.getService(); + } + + VaadinSession.setCurrent(session); + VaadinService.setCurrent(service); + + if (service instanceof VaadinServletService) { + old.put(VaadinServlet.class, + new CurrentInstance(VaadinServlet.getCurrent(), true)); + VaadinServlet.setCurrent(((VaadinServletService) service) + .getServlet()); + } else if (portletAvailable && service instanceof VaadinPortletService) { + old.put(VaadinPortlet.class, + new CurrentInstance(VaadinPortlet.getCurrent(), true)); + VaadinPortlet.setCurrent(((VaadinPortletService) service) + .getPortlet()); + } + + return old; + } } diff --git a/server/tests/src/com/vaadin/data/DefaultFieldGroupFieldFactoryTest.java b/server/tests/src/com/vaadin/data/DefaultFieldGroupFieldFactoryTest.java new file mode 100644 index 0000000000..b319c13e4e --- /dev/null +++ b/server/tests/src/com/vaadin/data/DefaultFieldGroupFieldFactoryTest.java @@ -0,0 +1,69 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.data; + +import java.util.Date; + +import junit.framework.Assert; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.fieldgroup.DefaultFieldGroupFieldFactory; +import com.vaadin.ui.DateField; +import com.vaadin.ui.Field; +import com.vaadin.ui.InlineDateField; +import com.vaadin.ui.PopupDateField; +import com.vaadin.ui.TextField; + +public class DefaultFieldGroupFieldFactoryTest { + + private DefaultFieldGroupFieldFactory fieldFactory; + + @Before + public void setupFieldFactory() { + fieldFactory = new DefaultFieldGroupFieldFactory(); + } + + @Test + public void testDateGenerationForPopupDateField() { + Field f = fieldFactory.createField(Date.class, DateField.class); + Assert.assertNotNull(f); + Assert.assertEquals(PopupDateField.class, f.getClass()); + } + + @Test + public void testDateGenerationForInlineDateField() { + Field f = fieldFactory.createField(Date.class, InlineDateField.class); + Assert.assertNotNull(f); + Assert.assertEquals(InlineDateField.class, f.getClass()); + } + + @Test + public void testDateGenerationForTextField() { + Field f = fieldFactory.createField(Date.class, TextField.class); + Assert.assertNotNull(f); + Assert.assertEquals(TextField.class, f.getClass()); + } + + @Test + public void testDateGenerationForField() { + Field f = fieldFactory.createField(Date.class, Field.class); + Assert.assertNotNull(f); + Assert.assertEquals(PopupDateField.class, f.getClass()); + } + +} diff --git a/server/tests/src/com/vaadin/data/fieldgroup/FieldGroupDate.java b/server/tests/src/com/vaadin/data/fieldgroup/FieldGroupDate.java new file mode 100644 index 0000000000..e11b6e50f8 --- /dev/null +++ b/server/tests/src/com/vaadin/data/fieldgroup/FieldGroupDate.java @@ -0,0 +1,82 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.data.fieldgroup; + +import java.util.Date; + +import junit.framework.Assert; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.util.BeanItem; +import com.vaadin.ui.Field; +import com.vaadin.ui.PopupDateField; + +public class FieldGroupDate { + + private FieldGroup fieldGroup; + + public class TestBean { + private Date javaDate; + private java.sql.Date sqlDate; + + public TestBean(Date javaDate, java.sql.Date sqlDate) { + super(); + this.javaDate = javaDate; + this.sqlDate = sqlDate; + } + + public java.sql.Date getSqlDate() { + return sqlDate; + } + + public void setSqlDate(java.sql.Date sqlDate) { + this.sqlDate = sqlDate; + } + + public Date getJavaDate() { + return javaDate; + } + + public void setJavaDate(Date date) { + javaDate = date; + } + } + + @SuppressWarnings("deprecation") + @Before + public void setup() { + fieldGroup = new FieldGroup(); + fieldGroup.setItemDataSource(new BeanItem<TestBean>(new TestBean( + new Date(2010, 5, 7), new java.sql.Date(2011, 6, 8)))); + } + + @Test + public void testBuildAndBindDate() { + Field f = fieldGroup.buildAndBind("javaDate"); + Assert.assertNotNull(f); + Assert.assertEquals(PopupDateField.class, f.getClass()); + } + + @Test + public void testBuildAndBindSqlDate() { + Field f = fieldGroup.buildAndBind("sqlDate"); + Assert.assertNotNull(f); + Assert.assertEquals(PopupDateField.class, f.getClass()); + } + +} diff --git a/server/tests/src/com/vaadin/data/util/AbstractContainerTest.java b/server/tests/src/com/vaadin/data/util/AbstractContainerTest.java index f04acf8c46..81c90ba07b 100644 --- a/server/tests/src/com/vaadin/data/util/AbstractContainerTest.java +++ b/server/tests/src/com/vaadin/data/util/AbstractContainerTest.java @@ -314,8 +314,12 @@ public abstract class AbstractContainerTest extends TestCase { initializeContainer(container); // Filter by "contains ab" - container.addContainerFilter(new SimpleStringFilter( - FULLY_QUALIFIED_NAME, "ab", false, false)); + SimpleStringFilter filter1 = new SimpleStringFilter( + FULLY_QUALIFIED_NAME, "ab", false, false); + container.addContainerFilter(filter1); + + assertTrue(container.getContainerFilters().size() == 1); + assertEquals(filter1, container.getContainerFilters().iterator().next()); validateContainer(container, "com.vaadin.data.BufferedValidatable", "com.vaadin.ui.TabSheet", @@ -324,15 +328,21 @@ public abstract class AbstractContainerTest extends TestCase { // Filter by "contains da" (reversed as ad here) container.removeAllContainerFilters(); - container.addContainerFilter(new SimpleStringFilter( - REVERSE_FULLY_QUALIFIED_NAME, "ad", false, false)); + + assertTrue(container.getContainerFilters().isEmpty()); + + SimpleStringFilter filter2 = new SimpleStringFilter( + REVERSE_FULLY_QUALIFIED_NAME, "ad", false, false); + container.addContainerFilter(filter2); + + assertTrue(container.getContainerFilters().size() == 1); + assertEquals(filter2, container.getContainerFilters().iterator().next()); validateContainer(container, "com.vaadin.data.Buffered", "com.vaadin.server.ComponentSizeValidator", "com.vaadin.data.util.IndexedContainer", "com.vaadin.terminal.gwt.client.ui.VUriFragmentUtility", isFilteredOutItemNull(), 37); - } /** diff --git a/server/tests/src/com/vaadin/data/util/ReflectToolsGetSuperField.java b/server/tests/src/com/vaadin/data/util/ReflectToolsGetSuperField.java index efba6085ac..dc828689a8 100644 --- a/server/tests/src/com/vaadin/data/util/ReflectToolsGetSuperField.java +++ b/server/tests/src/com/vaadin/data/util/ReflectToolsGetSuperField.java @@ -19,15 +19,16 @@ public class ReflectToolsGetSuperField { class MySubClass extends MyClass { // no fields here } - + PropertysetItem item = new PropertysetItem(); - item.addItemProperty("testProperty", new ObjectProperty<String>("Value of testProperty")); - + item.addItemProperty("testProperty", new ObjectProperty<String>( + "Value of testProperty")); + MySubClass form = new MySubClass(); - + FieldGroup binder = new FieldGroup(item); binder.bindMemberFields(form); - + assertTrue("Value of testProperty".equals(form.test.getValue())); } diff --git a/server/tests/src/com/vaadin/data/util/sqlcontainer/SQLTestsConstants.java b/server/tests/src/com/vaadin/data/util/sqlcontainer/SQLTestsConstants.java index 786903f1d0..1e96d59ed5 100755 --- a/server/tests/src/com/vaadin/data/util/sqlcontainer/SQLTestsConstants.java +++ b/server/tests/src/com/vaadin/data/util/sqlcontainer/SQLTestsConstants.java @@ -45,6 +45,10 @@ public class SQLTestsConstants { public static String peopleFirst; public static String peopleSecond; public static String peopleThird; + /* Schema test creation statement(s) */ + public static String createSchema; + public static String createProductTable; + public static String dropSchema; /* Versioned -test table createion statement(s) */ public static String[] versionStatements; /* SQL Generator used during the testing */ @@ -66,6 +70,10 @@ public class SQLTestsConstants { versionStatements = new String[] { "create table versioned (id integer generated always as identity, text varchar(255), version tinyint default 0)", "alter table versioned add primary key (id)" }; + // TODO these should ideally exist for all databases + createSchema = "create schema oaas authorization DBA"; + createProductTable = "create table oaas.product (\"ID\" integer generated always as identity primary key, \"NAME\" VARCHAR(32))"; + dropSchema = "drop schema if exists oaas cascade"; break; case MYSQL: offset = 1; @@ -104,6 +112,9 @@ public class SQLTestsConstants { "CREATE TRIGGER \"mytable_modify_dt_tr\" BEFORE UPDATE" + " ON VERSIONED FOR EACH ROW" + " EXECUTE PROCEDURE \"public\".\"zz_row_version\"();" }; + createSchema = "create schema oaas"; + createProductTable = "create table oaas.product (\"ID\" serial primary key, \"NAME\" VARCHAR(32))"; + dropSchema = "drop schema oaas cascade"; break; case MSSQL: offset = 1; diff --git a/server/tests/src/com/vaadin/data/util/sqlcontainer/query/TableQueryTest.java b/server/tests/src/com/vaadin/data/util/sqlcontainer/query/TableQueryTest.java index 54db34dfd2..c275cd4363 100644 --- a/server/tests/src/com/vaadin/data/util/sqlcontainer/query/TableQueryTest.java +++ b/server/tests/src/com/vaadin/data/util/sqlcontainer/query/TableQueryTest.java @@ -661,4 +661,77 @@ public class TableQueryTest { container.commit(); } + @Test + public void construction_explicitSchema_shouldSucceed() throws SQLException { + if (SQLTestsConstants.createSchema == null + || SQLTestsConstants.createProductTable == null + || SQLTestsConstants.dropSchema == null) { + // only perform the test on the databases for which the setup and + // cleanup statements are available + return; + } + + // create schema "oaas" and table "product" in it + Connection conn = connectionPool.reserveConnection(); + Statement statement = conn.createStatement(); + try { + statement.execute(SQLTestsConstants.dropSchema); + } catch (SQLException e) { + // May fail if schema doesn't exist, which is OK. + conn.rollback(); + } + statement.execute(SQLTestsConstants.createSchema); + statement.execute(SQLTestsConstants.createProductTable); + conn.commit(); + + try { + // metadata scanning at query creation time should not fail + TableQuery tq1 = new TableQuery(null, "oaas", "product", + connectionPool, SQLTestsConstants.sqlGen); + Assert.assertNotNull(tq1); + } finally { + // cleanup - might not be an in-memory DB + statement.execute(SQLTestsConstants.dropSchema); + } + } + + @Test + public void construction_explicitCatalogAndSchema_shouldSucceed() + throws SQLException { + // not all databases support explicit catalogs, test with PostgreSQL + // using database name as catalog + if (SQLTestsConstants.db != SQLTestsConstants.DB.POSTGRESQL + || SQLTestsConstants.createSchema == null + || SQLTestsConstants.createProductTable == null + || SQLTestsConstants.dropSchema == null) { + // only perform the test on the databases for which the setup and + // cleanup statements are available + return; + } + + // create schema "oaas" and table "product" in it + Connection conn = connectionPool.reserveConnection(); + Statement statement = conn.createStatement(); + try { + statement.execute(SQLTestsConstants.dropSchema); + } catch (SQLException e) { + // May fail if schema doesn't exist, which is OK. + conn.rollback(); + } + statement.execute(SQLTestsConstants.createSchema); + statement.execute(SQLTestsConstants.createProductTable); + conn.commit(); + + try { + // metadata scanning at query creation time should not fail + // note that for most DBMS, catalog is just an optional database + // name + TableQuery tq1 = new TableQuery("sqlcontainer", "oaas", "product", + connectionPool, SQLTestsConstants.sqlGen); + Assert.assertNotNull(tq1); + } finally { + // cleanup - might not be an in-memory DB + statement.execute(SQLTestsConstants.dropSchema); + } + } }
\ No newline at end of file diff --git a/server/tests/src/com/vaadin/server/TestAbstractApplicationServletStaticFilesLocation.java b/server/tests/src/com/vaadin/server/TestAbstractApplicationServletStaticFilesLocation.java index c7330001d3..2a83f5d5b2 100644 --- a/server/tests/src/com/vaadin/server/TestAbstractApplicationServletStaticFilesLocation.java +++ b/server/tests/src/com/vaadin/server/TestAbstractApplicationServletStaticFilesLocation.java @@ -28,9 +28,11 @@ public class TestAbstractApplicationServletStaticFilesLocation extends TestCase // Workaround to avoid calling init and creating servlet config Field f = VaadinServlet.class.getDeclaredField("servletService"); f.setAccessible(true); - f.set(servlet, new VaadinServletService(servlet, + VaadinServletService service = new VaadinServletService(servlet, new DefaultDeploymentConfiguration(servlet.getClass(), - new Properties()))); + new Properties())); + service.init(); + f.set(servlet, service); } diff --git a/server/tests/src/com/vaadin/server/VaadinSessionTest.java b/server/tests/src/com/vaadin/server/VaadinSessionTest.java new file mode 100644 index 0000000000..61a1581a6f --- /dev/null +++ b/server/tests/src/com/vaadin/server/VaadinSessionTest.java @@ -0,0 +1,150 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.server; + +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import javax.servlet.http.HttpSessionBindingEvent; + +import junit.framework.Assert; + +import org.easymock.EasyMock; +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.server.ClientConnector.DetachEvent; +import com.vaadin.server.ClientConnector.DetachListener; +import com.vaadin.tests.util.MockDeploymentConfiguration; +import com.vaadin.ui.UI; +import com.vaadin.util.CurrentInstance; + +public class VaadinSessionTest { + + private VaadinSession session; + private VaadinServlet mockServlet; + private VaadinServletService mockService; + private HttpSession mockHttpSession; + private WrappedSession mockWrappedSession; + private VaadinServletRequest vaadinRequest; + private UI ui; + + @Before + public void setup() throws Exception { + mockServlet = new VaadinServlet() { + @Override + public String getServletName() { + return "mockServlet"; + }; + }; + + mockService = new VaadinServletService(mockServlet, + new MockDeploymentConfiguration()); + mockService.init(); + + mockHttpSession = EasyMock.createMock(HttpSession.class); + mockWrappedSession = new WrappedHttpSession(mockHttpSession) { + final ReentrantLock lock = new ReentrantLock(); + + @Override + public Object getAttribute(String name) { + if ("mockServlet.lock".equals(name)) { + return lock; + } + return super.getAttribute(name); + } + }; + + session = new VaadinSession(mockService); + session.storeInSession(mockService, mockWrappedSession); + + ui = new UI() { + Page page = new Page(this, getState(false).pageState) { + @Override + public void init(VaadinRequest request) { + } + }; + + @Override + protected void init(VaadinRequest request) { + } + + @Override + public Page getPage() { + return page; + } + }; + vaadinRequest = new VaadinServletRequest( + EasyMock.createMock(HttpServletRequest.class), mockService) { + @Override + public String getParameter(String name) { + if ("theme".equals(name)) { + return null; + } + + return super.getParameter(name); + } + }; + + ui.doInit(vaadinRequest, session.getNextUIid()); + + ui.setSession(session); + session.addUI(ui); + + } + + @Test + public void threadLocalsAfterUnderlyingSessionTimeout() { + + final AtomicBoolean detachCalled = new AtomicBoolean(false); + ui.addDetachListener(new DetachListener() { + @Override + public void detach(DetachEvent event) { + detachCalled.set(true); + Assert.assertEquals(ui, UI.getCurrent()); + Assert.assertEquals(ui.getPage(), Page.getCurrent()); + Assert.assertEquals(session, VaadinSession.getCurrent()); + Assert.assertEquals(mockService, VaadinService.getCurrent()); + Assert.assertEquals(mockServlet, VaadinServlet.getCurrent()); + } + }); + + session.valueUnbound(EasyMock.createMock(HttpSessionBindingEvent.class)); + Assert.assertTrue(detachCalled.get()); + } + + @Test + public void threadLocalsAfterSessionDestroy() { + final AtomicBoolean detachCalled = new AtomicBoolean(false); + ui.addDetachListener(new DetachListener() { + @Override + public void detach(DetachEvent event) { + detachCalled.set(true); + Assert.assertEquals(ui, UI.getCurrent()); + Assert.assertEquals(ui.getPage(), Page.getCurrent()); + Assert.assertEquals(session, VaadinSession.getCurrent()); + Assert.assertEquals(mockService, VaadinService.getCurrent()); + Assert.assertEquals(mockServlet, VaadinServlet.getCurrent()); + } + }); + CurrentInstance.clearAll(); + session.close(); + mockService.cleanupSession(session); + Assert.assertTrue(detachCalled.get()); + } +} diff --git a/server/tests/src/com/vaadin/tests/data/converter/ConverterFactory.java b/server/tests/src/com/vaadin/tests/data/converter/ConverterFactory.java index 5067da8861..9da8406507 100644 --- a/server/tests/src/com/vaadin/tests/data/converter/ConverterFactory.java +++ b/server/tests/src/com/vaadin/tests/data/converter/ConverterFactory.java @@ -22,6 +22,7 @@ import junit.framework.TestCase; import com.vaadin.data.util.converter.Converter; import com.vaadin.data.util.converter.DefaultConverterFactory; import com.vaadin.server.VaadinSession; +import com.vaadin.tests.util.AlwaysLockedVaadinSession; import com.vaadin.ui.TextField; public class ConverterFactory extends TestCase { @@ -66,7 +67,7 @@ public class ConverterFactory extends TestCase { public void testApplicationConverterFactoryInBackgroundThread() { VaadinSession.setCurrent(null); - final VaadinSession appWithCustomIntegerConverter = new VaadinSession( + final VaadinSession appWithCustomIntegerConverter = new AlwaysLockedVaadinSession( null); appWithCustomIntegerConverter .setConverterFactory(new ConverterFactory42()); @@ -84,7 +85,7 @@ public class ConverterFactory extends TestCase { } public void testApplicationConverterFactoryForDetachedComponent() { - final VaadinSession appWithCustomIntegerConverter = new VaadinSession( + final VaadinSession appWithCustomIntegerConverter = new AlwaysLockedVaadinSession( null); appWithCustomIntegerConverter .setConverterFactory(new ConverterFactory42()); @@ -98,11 +99,11 @@ public class ConverterFactory extends TestCase { } public void testApplicationConverterFactoryForDifferentThanCurrentApplication() { - final VaadinSession fieldAppWithCustomIntegerConverter = new VaadinSession( + final VaadinSession fieldAppWithCustomIntegerConverter = new AlwaysLockedVaadinSession( null); fieldAppWithCustomIntegerConverter .setConverterFactory(new ConverterFactory42()); - VaadinSession.setCurrent(new VaadinSession(null)); + VaadinSession.setCurrent(new AlwaysLockedVaadinSession(null)); TextField tf = new TextField("", "123") { @Override diff --git a/server/tests/src/com/vaadin/tests/data/validator/TestStringLengthValidator.java b/server/tests/src/com/vaadin/tests/data/validator/TestStringLengthValidator.java index 032b8b6d14..6b4b2b0d51 100644 --- a/server/tests/src/com/vaadin/tests/data/validator/TestStringLengthValidator.java +++ b/server/tests/src/com/vaadin/tests/data/validator/TestStringLengthValidator.java @@ -45,7 +45,7 @@ public class TestStringLengthValidator extends TestCase { validatorMinValue .isValid("This is a really long string to test that no upper bound exists")); } - + public void testNoLowerBound() { assertTrue("Didn't accept short string", validatorMaxValue.isValid("")); assertTrue("Didn't accept short string", validatorMaxValue.isValid("1")); diff --git a/server/tests/src/com/vaadin/tests/server/TestAtmosphereVersion.java b/server/tests/src/com/vaadin/tests/server/TestAtmosphereVersion.java new file mode 100644 index 0000000000..5c27ef0752 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/TestAtmosphereVersion.java @@ -0,0 +1,18 @@ +package com.vaadin.tests.server; + +import junit.framework.TestCase; + +import org.atmosphere.util.Version; + +import com.vaadin.server.Constants; + +public class TestAtmosphereVersion extends TestCase { + /** + * Test that the atmosphere version constant matches the version on our + * classpath + */ + public void testAtmosphereVersion() { + assertEquals(Constants.REQUIRED_ATMOSPHERE_VERSION, + Version.getRawVersion()); + } +} diff --git a/server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java b/server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java index 3172b759a1..90cb6b9994 100644 --- a/server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java +++ b/server/tests/src/com/vaadin/tests/server/TestClassesSerializable.java @@ -45,9 +45,7 @@ public class TestClassesSerializable extends TestCase { "com\\.vaadin\\.util\\.SerializerHelper", // fully static // class level filtering, also affecting nested classes and // interfaces - "com\\.vaadin\\.server\\.AbstractCommunicationManager.*", // - "com\\.vaadin\\.server\\.CommunicationManager.*", // - "com\\.vaadin\\.server\\.PortletCommunicationManager.*", // + "com\\.vaadin\\.server\\.LegacyCommunicationManager.*", // "com\\.vaadin\\.buildhelpers.*", // "com\\.vaadin\\.util\\.ReflectTools.*", // "com\\.vaadin\\.data\\.util\\.ReflectTools.*", // diff --git a/server/tests/src/com/vaadin/tests/server/TestSimpleMultiPartInputStream.java b/server/tests/src/com/vaadin/tests/server/TestSimpleMultiPartInputStream.java index 84247c81c1..6907594b5e 100644 --- a/server/tests/src/com/vaadin/tests/server/TestSimpleMultiPartInputStream.java +++ b/server/tests/src/com/vaadin/tests/server/TestSimpleMultiPartInputStream.java @@ -7,7 +7,7 @@ import java.util.Arrays; import junit.framework.TestCase; -import com.vaadin.server.AbstractCommunicationManager.SimpleMultiPartInputStream; +import com.vaadin.server.communication.FileUploadHandler.SimpleMultiPartInputStream; public class TestSimpleMultiPartInputStream extends TestCase { diff --git a/server/tests/src/com/vaadin/tests/server/TestStreamVariableMapping.java b/server/tests/src/com/vaadin/tests/server/TestStreamVariableMapping.java index 467a76dfa6..bee932a29f 100644 --- a/server/tests/src/com/vaadin/tests/server/TestStreamVariableMapping.java +++ b/server/tests/src/com/vaadin/tests/server/TestStreamVariableMapping.java @@ -4,10 +4,14 @@ import junit.framework.TestCase; import org.easymock.EasyMock; -import com.vaadin.server.CommunicationManager; +import com.vaadin.server.LegacyCommunicationManager; import com.vaadin.server.StreamVariable; import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinServlet; +import com.vaadin.server.VaadinServletService; import com.vaadin.server.VaadinSession; +import com.vaadin.tests.util.AlwaysLockedVaadinSession; +import com.vaadin.tests.util.MockDeploymentConfiguration; import com.vaadin.ui.ConnectorTracker; import com.vaadin.ui.UI; import com.vaadin.ui.Upload; @@ -18,11 +22,11 @@ public class TestStreamVariableMapping extends TestCase { private Upload owner; private StreamVariable streamVariable; - private CommunicationManager cm; + private LegacyCommunicationManager cm; @Override protected void setUp() throws Exception { - final VaadinSession application = new VaadinSession(null); + final VaadinSession application = new AlwaysLockedVaadinSession(null); final UI uI = new UI() { @Override protected void init(VaadinRequest request) { @@ -67,13 +71,19 @@ public class TestStreamVariableMapping extends TestCase { assertNotNull(tracker.getStreamVariable(owner.getConnectorId(), variableName)); - cm.cleanStreamVariable(owner, variableName); + tracker.cleanStreamVariable(owner.getConnectorId(), variableName); assertNull(tracker.getStreamVariable(owner.getConnectorId(), variableName)); } - private CommunicationManager createCommunicationManager() { - return new CommunicationManager(new VaadinSession(null)); + private LegacyCommunicationManager createCommunicationManager() + throws Exception { + VaadinServletService vss = new VaadinServletService( + EasyMock.createMock(VaadinServlet.class), + new MockDeploymentConfiguration()); + vss.init(); + return new LegacyCommunicationManager( + new AlwaysLockedVaadinSession(vss)); } } diff --git a/server/tests/src/com/vaadin/tests/server/clientconnector/AttachDetachListeners.java b/server/tests/src/com/vaadin/tests/server/clientconnector/AttachDetachListeners.java index 8a61ec6352..bd7053af40 100644 --- a/server/tests/src/com/vaadin/tests/server/clientconnector/AttachDetachListeners.java +++ b/server/tests/src/com/vaadin/tests/server/clientconnector/AttachDetachListeners.java @@ -18,6 +18,7 @@ import com.vaadin.server.ClientConnector.DetachListener; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinService; import com.vaadin.server.VaadinSession; +import com.vaadin.tests.util.AlwaysLockedVaadinSession; import com.vaadin.ui.Component; import com.vaadin.ui.CssLayout; import com.vaadin.ui.Label; @@ -40,7 +41,8 @@ public class AttachDetachListeners { public void setUp() { control = EasyMock.createStrictControl(); - session = new VaadinSession(control.createMock(VaadinService.class)); + session = new AlwaysLockedVaadinSession( + control.createMock(VaadinService.class)); ui = new UI() { @Override diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractfield/AbstractFieldValueConversions.java b/server/tests/src/com/vaadin/tests/server/component/abstractfield/AbstractFieldValueConversions.java index cd77101ac3..3c4d43543b 100644 --- a/server/tests/src/com/vaadin/tests/server/component/abstractfield/AbstractFieldValueConversions.java +++ b/server/tests/src/com/vaadin/tests/server/component/abstractfield/AbstractFieldValueConversions.java @@ -17,6 +17,7 @@ import com.vaadin.tests.data.bean.Address; import com.vaadin.tests.data.bean.Country; import com.vaadin.tests.data.bean.Person; import com.vaadin.tests.data.bean.Sex; +import com.vaadin.tests.util.AlwaysLockedVaadinSession; import com.vaadin.ui.CheckBox; import com.vaadin.ui.TextField; @@ -45,7 +46,7 @@ public class AbstractFieldValueConversions extends TestCase { } public void testNonmodifiedBufferedFieldConversion() { - VaadinSession.setCurrent(new VaadinSession(null)); + VaadinSession.setCurrent(new AlwaysLockedVaadinSession(null)); TextField tf = new TextField("salary"); tf.setBuffered(true); tf.setLocale(new Locale("en", "US")); @@ -61,7 +62,7 @@ public class AbstractFieldValueConversions extends TestCase { } public void testModifiedBufferedFieldConversion() { - VaadinSession.setCurrent(new VaadinSession(null)); + VaadinSession.setCurrent(new AlwaysLockedVaadinSession(null)); TextField tf = new TextField("salary"); tf.setBuffered(true); tf.setLocale(new Locale("en", "US")); @@ -129,7 +130,7 @@ public class AbstractFieldValueConversions extends TestCase { } public void testChangeReadOnlyFieldLocale() { - VaadinSession.setCurrent(new VaadinSession(null)); + VaadinSession.setCurrent(new AlwaysLockedVaadinSession(null)); TextField tf = new TextField("salary"); tf.setLocale(new Locale("en", "US")); @@ -214,7 +215,7 @@ public class AbstractFieldValueConversions extends TestCase { } public void testNumberDoubleConverterChange() { - final VaadinSession a = new VaadinSession(null); + final VaadinSession a = new AlwaysLockedVaadinSession(null); VaadinSession.setCurrent(a); TextField tf = new TextField() { @Override diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractfield/DefaultConverterFactory.java b/server/tests/src/com/vaadin/tests/server/component/abstractfield/DefaultConverterFactory.java index 698e9bcee4..bac024725f 100644 --- a/server/tests/src/com/vaadin/tests/server/component/abstractfield/DefaultConverterFactory.java +++ b/server/tests/src/com/vaadin/tests/server/component/abstractfield/DefaultConverterFactory.java @@ -11,6 +11,7 @@ import com.vaadin.tests.data.bean.Address; import com.vaadin.tests.data.bean.Country; import com.vaadin.tests.data.bean.Person; import com.vaadin.tests.data.bean.Sex; +import com.vaadin.tests.util.AlwaysLockedVaadinSession; import com.vaadin.ui.TextField; public class DefaultConverterFactory extends TestCase { @@ -53,7 +54,7 @@ public class DefaultConverterFactory extends TestCase { } public void testFloatConversion() { - VaadinSession sess = new VaadinSession(null); + VaadinSession sess = new AlwaysLockedVaadinSession(null); VaadinSession.setCurrent(sess); TextField tf = new TextField(); @@ -68,7 +69,7 @@ public class DefaultConverterFactory extends TestCase { } public void testDefaultNumberConversion() { - VaadinSession app = new VaadinSession(null); + VaadinSession app = new AlwaysLockedVaadinSession(null); VaadinSession.setCurrent(app); TextField tf = new TextField(); tf.setLocale(new Locale("en", "US")); diff --git a/server/tests/src/com/vaadin/tests/server/component/abstractfield/RemoveListenersOnDetach.java b/server/tests/src/com/vaadin/tests/server/component/abstractfield/RemoveListenersOnDetach.java index cd9b6c6631..fcf17cc499 100644 --- a/server/tests/src/com/vaadin/tests/server/component/abstractfield/RemoveListenersOnDetach.java +++ b/server/tests/src/com/vaadin/tests/server/component/abstractfield/RemoveListenersOnDetach.java @@ -9,6 +9,7 @@ import com.vaadin.data.util.AbstractProperty; import com.vaadin.data.util.converter.Converter.ConversionException; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinSession; +import com.vaadin.tests.util.AlwaysLockedVaadinSession; import com.vaadin.ui.AbstractField; import com.vaadin.ui.UI; @@ -18,7 +19,8 @@ public class RemoveListenersOnDetach { int numReadOnlyChanges = 0; AbstractField field = new AbstractField() { - final private VaadinSession application = new VaadinSession(null); + final private VaadinSession application = new AlwaysLockedVaadinSession( + null); private UI uI = new UI() { @Override diff --git a/server/tests/src/com/vaadin/tests/server/component/calendar/CalendarBasics.java b/server/tests/src/com/vaadin/tests/server/component/calendar/CalendarBasics.java new file mode 100644 index 0000000000..5926cfa1ca --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/calendar/CalendarBasics.java @@ -0,0 +1,210 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.server.component.calendar; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.TimeZone; + +import org.junit.Test; + +import com.vaadin.ui.Calendar; +import com.vaadin.ui.Calendar.TimeFormat; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.BackwardEvent; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickEvent; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventResize; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.ForwardEvent; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.MoveEvent; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClick; +import com.vaadin.ui.components.calendar.event.BasicEventProvider; +import com.vaadin.ui.components.calendar.event.CalendarEventProvider; + +/** + * Basic API tests for the calendar + */ +public class CalendarBasics { + + @Test + public void testEmptyConstructorInitialization() { + + Calendar calendar = new Calendar(); + + // The calendar should have a basic event provider with no events + CalendarEventProvider provider = calendar.getEventProvider(); + assertNotNull("Event provider should not be null", provider); + + // Basic event handlers should be registered + assertNotNull(calendar.getHandler(BackwardEvent.EVENT_ID)); + assertNotNull(calendar.getHandler(ForwardEvent.EVENT_ID)); + assertNotNull(calendar.getHandler(WeekClick.EVENT_ID)); + assertNotNull(calendar.getHandler(DateClickEvent.EVENT_ID)); + assertNotNull(calendar.getHandler(MoveEvent.EVENT_ID)); + assertNotNull(calendar.getHandler(EventResize.EVENT_ID)); + + // Calendar should have undefined size + assertTrue(calendar.getWidth() < 0); + assertTrue(calendar.getHeight() < 0); + } + + @Test + public void testConstructorWithCaption() { + final String caption = "My Calendar Caption"; + Calendar calendar = new Calendar(caption); + assertEquals(caption, calendar.getCaption()); + } + + @Test + public void testConstructorWithCustomEventProvider() { + BasicEventProvider myProvider = new BasicEventProvider(); + Calendar calendar = new Calendar(myProvider); + assertEquals(myProvider, calendar.getEventProvider()); + } + + @Test + public void testConstructorWithCustomEventProviderAndCaption() { + BasicEventProvider myProvider = new BasicEventProvider(); + final String caption = "My Calendar Caption"; + Calendar calendar = new Calendar(caption, myProvider); + assertEquals(caption, calendar.getCaption()); + assertEquals(myProvider, calendar.getEventProvider()); + } + + @Test + public void testDefaultStartAndEndDates() { + Calendar calendar = new Calendar(); + + // If no start and end date is set the calendar will display the current + // week + java.util.Calendar c = new GregorianCalendar(); + java.util.Calendar c2 = new GregorianCalendar(); + + c2.setTime(calendar.getStartDate()); + assertEquals(c.getFirstDayOfWeek(), + c2.get(java.util.Calendar.DAY_OF_WEEK)); + c2.setTime(calendar.getEndDate()); + + c.set(java.util.Calendar.DAY_OF_WEEK, c.getFirstDayOfWeek() + 6); + assertEquals(c.get(java.util.Calendar.DAY_OF_WEEK), + c2.get(java.util.Calendar.DAY_OF_WEEK)); + } + + @Test + public void testCustomStartAndEndDates() { + Calendar calendar = new Calendar(); + java.util.Calendar c = new GregorianCalendar(); + + Date start = c.getTime(); + c.add(java.util.Calendar.DATE, 3); + Date end = c.getTime(); + + calendar.setStartDate(start); + calendar.setEndDate(end); + + assertEquals(start.getTime(), calendar.getStartDate().getTime()); + assertEquals(end.getTime(), calendar.getEndDate().getTime()); + } + + @Test + public void testCustomLocale() { + Calendar calendar = new Calendar(); + calendar.setLocale(Locale.CANADA_FRENCH); + + // Setting the locale should set the internal calendars locale + assertEquals(Locale.CANADA_FRENCH, calendar.getLocale()); + java.util.Calendar c = new GregorianCalendar(Locale.CANADA_FRENCH); + assertEquals(c.getTimeZone().getRawOffset(), calendar + .getInternalCalendar().getTimeZone().getRawOffset()); + } + + @Test + public void testTimeFormat() { + Calendar calendar = new Calendar(); + + // The default timeformat depends on the current locale + calendar.setLocale(Locale.ENGLISH); + assertEquals(TimeFormat.Format12H, calendar.getTimeFormat()); + + calendar.setLocale(Locale.ITALIAN); + assertEquals(TimeFormat.Format24H, calendar.getTimeFormat()); + + // Setting a specific time format overrides the locale + calendar.setTimeFormat(TimeFormat.Format12H); + assertEquals(TimeFormat.Format12H, calendar.getTimeFormat()); + } + + @Test + public void testTimeZone() { + Calendar calendar = new Calendar(); + calendar.setLocale(Locale.CANADA_FRENCH); + + // By default the calendars timezone is returned + assertEquals(calendar.getInternalCalendar().getTimeZone(), + calendar.getTimeZone()); + + // One can override the default behaviour by specifying a timezone + TimeZone customTimeZone = TimeZone.getTimeZone("Europe/Helsinki"); + calendar.setTimeZone(customTimeZone); + assertEquals(customTimeZone, calendar.getTimeZone()); + } + + @Test + public void testVisibleDaysOfWeek() { + Calendar calendar = new Calendar(); + + // The defaults are the whole week + assertEquals(1, calendar.getFirstVisibleDayOfWeek()); + assertEquals(7, calendar.getLastVisibleDayOfWeek()); + + calendar.setFirstVisibleDayOfWeek(0); // Invalid input + assertEquals(1, calendar.getFirstVisibleDayOfWeek()); + + calendar.setLastVisibleDayOfWeek(0); // Invalid input + assertEquals(7, calendar.getLastVisibleDayOfWeek()); + + calendar.setFirstVisibleDayOfWeek(8); // Invalid input + assertEquals(1, calendar.getFirstVisibleDayOfWeek()); + + calendar.setLastVisibleDayOfWeek(8); // Invalid input + assertEquals(7, calendar.getLastVisibleDayOfWeek()); + + calendar.setFirstVisibleDayOfWeek(4); + assertEquals(4, calendar.getFirstVisibleDayOfWeek()); + + calendar.setLastVisibleDayOfWeek(6); + assertEquals(6, calendar.getLastVisibleDayOfWeek()); + + calendar.setFirstVisibleDayOfWeek(7); // Invalid since last day is 6 + assertEquals(4, calendar.getFirstVisibleDayOfWeek()); + + calendar.setLastVisibleDayOfWeek(2); // Invalid since first day is 4 + assertEquals(6, calendar.getLastVisibleDayOfWeek()); + } + + @Test + public void testVisibleHoursInDay() { + Calendar calendar = new Calendar(); + + // Defaults are the whole day + assertEquals(0, calendar.getFirstVisibleHourOfDay()); + assertEquals(23, calendar.getLastVisibleHourOfDay()); + } + +} diff --git a/server/tests/src/com/vaadin/tests/server/component/calendar/ContainerDataSource.java b/server/tests/src/com/vaadin/tests/server/component/calendar/ContainerDataSource.java new file mode 100644 index 0000000000..2bc95e371c --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/calendar/ContainerDataSource.java @@ -0,0 +1,362 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.server.component.calendar; + +import java.util.Date; +import java.util.List; + +import junit.framework.TestCase; + +import org.junit.Test; + +import com.vaadin.data.Container.Indexed; +import com.vaadin.data.Container.Sortable; +import com.vaadin.data.Item; +import com.vaadin.data.util.BeanItemContainer; +import com.vaadin.data.util.IndexedContainer; +import com.vaadin.ui.Calendar; +import com.vaadin.ui.components.calendar.ContainerEventProvider; +import com.vaadin.ui.components.calendar.event.BasicEvent; +import com.vaadin.ui.components.calendar.event.CalendarEvent; + +public class ContainerDataSource extends TestCase { + + private Calendar calendar; + + @Override + public void setUp() { + calendar = new Calendar(); + } + + /** + * Tests adding a bean item container to the Calendar + */ + @Test + public void testWithBeanItemContainer() { + + // Create a container to use as a datasource + Indexed container = createTestBeanItemContainer(); + + // Set datasource + calendar.setContainerDataSource(container); + + // Start and end dates to query for + java.util.Calendar cal = java.util.Calendar.getInstance(); + cal.setTime(((CalendarEvent) container.getIdByIndex(0)).getStart()); + Date start = cal.getTime(); + cal.add(java.util.Calendar.MONTH, 1); + Date end = cal.getTime(); + + // Test the all events are returned + List<CalendarEvent> events = calendar.getEventProvider().getEvents( + start, end); + assertEquals(container.size(), events.size()); + + // Test that a certain range is returned + cal.setTime(((CalendarEvent) container.getIdByIndex(6)).getStart()); + end = cal.getTime(); + events = calendar.getEventProvider().getEvents(start, end); + assertEquals(6, events.size()); + } + + /** + * This tests tests that if you give the Calendar an unsorted (== not sorted + * by starting date) container then the calendar should gracefully handle + * it. In this case the size of the container will be wrong. The test is + * exactly the same as {@link #testWithBeanItemContainer()} except that the + * beans has been intentionally sorted by caption instead of date. + */ + @Test + public void testWithUnsortedBeanItemContainer() { + // Create a container to use as a datasource + Indexed container = createTestBeanItemContainer(); + + // Make the container sorted by caption + ((Sortable) container).sort(new Object[] { "caption" }, + new boolean[] { true }); + + // Set data source + calendar.setContainerDataSource(container); + + // Start and end dates to query for + java.util.Calendar cal = java.util.Calendar.getInstance(); + cal.setTime(((CalendarEvent) container.getIdByIndex(0)).getStart()); + Date start = cal.getTime(); + cal.add(java.util.Calendar.MONTH, 1); + Date end = cal.getTime(); + + // Test the all events are returned + List<CalendarEvent> events = calendar.getEventProvider().getEvents( + start, end); + assertEquals(container.size(), events.size()); + + // Test that a certain range is returned + cal.setTime(((CalendarEvent) container.getIdByIndex(6)).getStart()); + end = cal.getTime(); + events = calendar.getEventProvider().getEvents(start, end); + + // The events size is 1 since the getEvents returns the wrong range + assertEquals(1, events.size()); + } + + /** + * Tests adding a Indexed container to the Calendar + */ + @Test + public void testWithIndexedContainer() { + + // Create a container to use as a datasource + Indexed container = createTestIndexedContainer(); + + // Set datasource + calendar.setContainerDataSource(container, "testCaption", + "testDescription", "testStartDate", "testEndDate", null); + + // Start and end dates to query for + java.util.Calendar cal = java.util.Calendar.getInstance(); + cal.setTime((Date) container.getItem(container.getIdByIndex(0)) + .getItemProperty("testStartDate").getValue()); + Date start = cal.getTime(); + cal.add(java.util.Calendar.MONTH, 1); + Date end = cal.getTime(); + + // Test the all events are returned + List<CalendarEvent> events = calendar.getEventProvider().getEvents( + start, end); + assertEquals(container.size(), events.size()); + + // Check that event values are present + CalendarEvent e = events.get(0); + assertEquals("Test 1", e.getCaption()); + assertEquals("Description 1", e.getDescription()); + assertTrue(e.getStart().compareTo(start) == 0); + + // Test that a certain range is returned + cal.setTime((Date) container.getItem(container.getIdByIndex(6)) + .getItemProperty("testStartDate").getValue()); + end = cal.getTime(); + events = calendar.getEventProvider().getEvents(start, end); + assertEquals(6, events.size()); + } + + @Test + public void testNullLimitsBeanItemContainer() { + // Create a container to use as a datasource + Indexed container = createTestBeanItemContainer(); + + // Start and end dates to query for + java.util.Calendar cal = java.util.Calendar.getInstance(); + cal.setTime(((CalendarEvent) container.getIdByIndex(0)).getStart()); + Date start = cal.getTime(); + cal.add(java.util.Calendar.MONTH, 1); + Date end = cal.getTime(); + + // Set datasource + calendar.setContainerDataSource(container); + + // Test null start time + List<CalendarEvent> events = calendar.getEventProvider().getEvents( + null, end); + assertEquals(container.size(), events.size()); + + // Test null end time + events = calendar.getEventProvider().getEvents(start, null); + assertEquals(container.size(), events.size()); + + // Test both null times + events = calendar.getEventProvider().getEvents(null, null); + assertEquals(container.size(), events.size()); + } + + @Test + public void testNullLimitsIndexedContainer() { + // Create a container to use as a datasource + Indexed container = createTestIndexedContainer(); + + // Start and end dates to query for + java.util.Calendar cal = java.util.Calendar.getInstance(); + cal.setTime((Date) container.getItem(container.getIdByIndex(0)) + .getItemProperty("testStartDate").getValue()); + Date start = cal.getTime(); + cal.add(java.util.Calendar.MONTH, 1); + Date end = cal.getTime(); + + // Set datasource + calendar.setContainerDataSource(container, "testCaption", + "testDescription", "testStartDate", "testEndDate", null); + + // Test null start time + List<CalendarEvent> events = calendar.getEventProvider().getEvents( + null, end); + assertEquals(container.size(), events.size()); + + // Test null end time + events = calendar.getEventProvider().getEvents(start, null); + assertEquals(container.size(), events.size()); + + // Test both null times + events = calendar.getEventProvider().getEvents(null, null); + assertEquals(container.size(), events.size()); + } + + /** + * Tests the addEvent convenience method with the default event provider + */ + @Test + public void testAddEventConvinienceMethod() { + + // Start and end dates to query for + java.util.Calendar cal = java.util.Calendar.getInstance(); + Date start = cal.getTime(); + cal.add(java.util.Calendar.MONTH, 1); + Date end = cal.getTime(); + + // Ensure no events + assertEquals(0, calendar.getEvents(start, end).size()); + + // Add an event + BasicEvent event = new BasicEvent("Test", "Test", start); + calendar.addEvent(event); + + // Ensure event exists + List<CalendarEvent> events = calendar.getEvents(start, end); + assertEquals(1, events.size()); + assertEquals(events.get(0).getCaption(), event.getCaption()); + assertEquals(events.get(0).getDescription(), event.getDescription()); + assertEquals(events.get(0).getStart(), event.getStart()); + } + + /** + * Test the removeEvent convenience method with the default event provider + */ + @Test + public void testRemoveEventConvinienceMethod() { + + // Start and end dates to query for + java.util.Calendar cal = java.util.Calendar.getInstance(); + Date start = cal.getTime(); + cal.add(java.util.Calendar.MONTH, 1); + Date end = cal.getTime(); + + // Ensure no events + assertEquals(0, calendar.getEvents(start, end).size()); + + // Add an event + CalendarEvent event = new BasicEvent("Test", "Test", start); + calendar.addEvent(event); + + // Ensure event exists + assertEquals(1, calendar.getEvents(start, end).size()); + + // Remove event + calendar.removeEvent(event); + + // Ensure no events + assertEquals(0, calendar.getEvents(start, end).size()); + } + + @Test + public void testAddEventConvinienceMethodWithCustomEventProvider() { + + // Use a container data source + calendar.setEventProvider(new ContainerEventProvider( + new BeanItemContainer<BasicEvent>(BasicEvent.class))); + + // Start and end dates to query for + java.util.Calendar cal = java.util.Calendar.getInstance(); + Date start = cal.getTime(); + cal.add(java.util.Calendar.MONTH, 1); + Date end = cal.getTime(); + + // Ensure no events + assertEquals(0, calendar.getEvents(start, end).size()); + + // Add an event + BasicEvent event = new BasicEvent("Test", "Test", start); + calendar.addEvent(event); + + // Ensure event exists + List<CalendarEvent> events = calendar.getEvents(start, end); + assertEquals(1, events.size()); + assertEquals(events.get(0).getCaption(), event.getCaption()); + assertEquals(events.get(0).getDescription(), event.getDescription()); + assertEquals(events.get(0).getStart(), event.getStart()); + } + + @Test + public void testRemoveEventConvinienceMethodWithCustomEventProvider() { + + // Use a container data source + calendar.setEventProvider(new ContainerEventProvider( + new BeanItemContainer<BasicEvent>(BasicEvent.class))); + + // Start and end dates to query for + java.util.Calendar cal = java.util.Calendar.getInstance(); + Date start = cal.getTime(); + cal.add(java.util.Calendar.MONTH, 1); + Date end = cal.getTime(); + + // Ensure no events + assertEquals(0, calendar.getEvents(start, end).size()); + + // Add an event + BasicEvent event = new BasicEvent("Test", "Test", start); + calendar.addEvent(event); + + // Ensure event exists + List<CalendarEvent> events = calendar.getEvents(start, end); + assertEquals(1, events.size()); + + // Remove event + calendar.removeEvent(event); + + // Ensure no events + assertEquals(0, calendar.getEvents(start, end).size()); + } + + private static Indexed createTestBeanItemContainer() { + BeanItemContainer<CalendarEvent> eventContainer = new BeanItemContainer<CalendarEvent>( + CalendarEvent.class); + java.util.Calendar cal = java.util.Calendar.getInstance(); + for (int i = 1; i <= 10; i++) { + eventContainer.addBean(new BasicEvent("Test " + i, "Description " + + i, cal.getTime())); + cal.add(java.util.Calendar.DAY_OF_MONTH, 2); + } + return eventContainer; + } + + private static Indexed createTestIndexedContainer() { + IndexedContainer container = new IndexedContainer(); + container.addContainerProperty("testCaption", String.class, ""); + container.addContainerProperty("testDescription", String.class, ""); + container.addContainerProperty("testStartDate", Date.class, null); + container.addContainerProperty("testEndDate", Date.class, null); + + java.util.Calendar cal = java.util.Calendar.getInstance(); + for (int i = 1; i <= 10; i++) { + Item item = container.getItem(container.addItem()); + item.getItemProperty("testCaption").setValue("Test " + i); + item.getItemProperty("testDescription") + .setValue("Description " + i); + item.getItemProperty("testStartDate").setValue(cal.getTime()); + item.getItemProperty("testEndDate").setValue(cal.getTime()); + cal.add(java.util.Calendar.DAY_OF_MONTH, 2); + } + return container; + } + +} diff --git a/server/tests/src/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java b/server/tests/src/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java index 68c1133dc0..44b77e88e2 100644 --- a/server/tests/src/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java +++ b/server/tests/src/com/vaadin/tests/server/component/fieldgroup/BeanFieldGroupTest.java @@ -3,6 +3,7 @@ package com.vaadin.tests.server.component.fieldgroup; import static org.junit.Assert.assertEquals; import org.junit.Test; + import com.vaadin.data.fieldgroup.BeanFieldGroup; public class BeanFieldGroupTest { diff --git a/server/tests/src/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBinding.java b/server/tests/src/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBinding.java new file mode 100644 index 0000000000..e571576990 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/fieldgroup/CaseInsensitiveBinding.java @@ -0,0 +1,85 @@ +package com.vaadin.tests.server.component.fieldgroup; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import com.vaadin.data.fieldgroup.FieldGroup; +import com.vaadin.data.util.ObjectProperty; +import com.vaadin.data.util.PropertysetItem; +import com.vaadin.ui.FormLayout; +import com.vaadin.ui.TextField; + +public class CaseInsensitiveBinding { + + @Test + public void caseInsensitivityAndUnderscoreRemoval() { + PropertysetItem item = new PropertysetItem(); + item.addItemProperty("LastName", new ObjectProperty<String>("Sparrow")); + + class MyForm extends FormLayout { + TextField lastName = new TextField("Last name"); + + public MyForm() { + + // Should bind to the LastName property + addComponent(lastName); + } + } + + MyForm form = new MyForm(); + + FieldGroup binder = new FieldGroup(item); + binder.bindMemberFields(form); + + assertTrue("Sparrow".equals(form.lastName.getValue())); + } + + @Test + public void UnderscoreRemoval() { + PropertysetItem item = new PropertysetItem(); + item.addItemProperty("first_name", new ObjectProperty<String>("Jack")); + + class MyForm extends FormLayout { + TextField firstName = new TextField("First name"); + + public MyForm() { + // Should bind to the first_name property + addComponent(firstName); + } + } + + MyForm form = new MyForm(); + + FieldGroup binder = new FieldGroup(item); + binder.bindMemberFields(form); + + assertTrue("Jack".equals(form.firstName.getValue())); + } + + @Test + public void perfectMatchPriority() { + PropertysetItem item = new PropertysetItem(); + item.addItemProperty("first_name", new ObjectProperty<String>( + "Not this")); + item.addItemProperty("firstName", new ObjectProperty<String>("This")); + + class MyForm extends FormLayout { + TextField firstName = new TextField("First name"); + + public MyForm() { + // should bind to the firstName property, not first_name + // property + addComponent(firstName); + } + } + + MyForm form = new MyForm(); + + FieldGroup binder = new FieldGroup(item); + binder.bindMemberFields(form); + + assertTrue("This".equals(form.firstName.getValue())); + } + +}
\ No newline at end of file diff --git a/server/tests/src/com/vaadin/tests/server/component/gridlayout/DefaultAlignment.java b/server/tests/src/com/vaadin/tests/server/component/gridlayout/DefaultAlignment.java new file mode 100644 index 0000000000..2faa65d1f2 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/gridlayout/DefaultAlignment.java @@ -0,0 +1,46 @@ +package com.vaadin.tests.server.component.gridlayout; + +import junit.framework.Assert; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.ui.Alignment; +import com.vaadin.ui.GridLayout; +import com.vaadin.ui.Label; +import com.vaadin.ui.TextField; + +public class DefaultAlignment { + + private GridLayout gridLayout; + + @Before + public void setup() { + gridLayout = new GridLayout(2, 2); + } + + @Test + public void testDefaultAlignment() { + Label label = new Label("A label"); + TextField tf = new TextField("A TextField"); + gridLayout.addComponent(label); + gridLayout.addComponent(tf); + Assert.assertEquals(Alignment.TOP_LEFT, + gridLayout.getComponentAlignment(label)); + Assert.assertEquals(Alignment.TOP_LEFT, + gridLayout.getComponentAlignment(tf)); + } + + @Test + public void testAlteredDefaultAlignment() { + Label label = new Label("A label"); + TextField tf = new TextField("A TextField"); + gridLayout.setDefaultComponentAlignment(Alignment.MIDDLE_CENTER); + gridLayout.addComponent(label); + gridLayout.addComponent(tf); + Assert.assertEquals(Alignment.MIDDLE_CENTER, + gridLayout.getComponentAlignment(label)); + Assert.assertEquals(Alignment.MIDDLE_CENTER, + gridLayout.getComponentAlignment(tf)); + } +} diff --git a/server/tests/src/com/vaadin/tests/server/component/label/LabelConverters.java b/server/tests/src/com/vaadin/tests/server/component/label/LabelConverters.java index 7fd2930865..9d71db89a6 100644 --- a/server/tests/src/com/vaadin/tests/server/component/label/LabelConverters.java +++ b/server/tests/src/com/vaadin/tests/server/component/label/LabelConverters.java @@ -21,6 +21,7 @@ import com.vaadin.data.Property; import com.vaadin.data.util.MethodProperty; import com.vaadin.server.VaadinSession; import com.vaadin.tests.data.bean.Person; +import com.vaadin.tests.util.AlwaysLockedVaadinSession; import com.vaadin.ui.Label; public class LabelConverters extends TestCase { @@ -37,7 +38,7 @@ public class LabelConverters extends TestCase { } public void testIntegerDataSource() { - VaadinSession.setCurrent(new VaadinSession(null)); + VaadinSession.setCurrent(new AlwaysLockedVaadinSession(null)); Label l = new Label("Foo"); Property ds = new MethodProperty<Integer>(Person.createTestPerson1(), "age"); diff --git a/server/tests/src/com/vaadin/tests/server/component/orderedlayout/DefaultAlignment.java b/server/tests/src/com/vaadin/tests/server/component/orderedlayout/DefaultAlignment.java new file mode 100644 index 0000000000..701373aba0 --- /dev/null +++ b/server/tests/src/com/vaadin/tests/server/component/orderedlayout/DefaultAlignment.java @@ -0,0 +1,68 @@ +package com.vaadin.tests.server.component.orderedlayout; + +import junit.framework.Assert; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.ui.AbstractOrderedLayout; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Label; +import com.vaadin.ui.TextField; +import com.vaadin.ui.VerticalLayout; + +public class DefaultAlignment { + + private VerticalLayout verticalLayout; + private HorizontalLayout horizontalLayout; + + @Before + public void setup() { + verticalLayout = new VerticalLayout(); + horizontalLayout = new HorizontalLayout(); + } + + @Test + public void testDefaultAlignmentVerticalLayout() { + testDefaultAlignment(verticalLayout); + } + + @Test + public void testDefaultAlignmentHorizontalLayout() { + testDefaultAlignment(horizontalLayout); + } + + public void testDefaultAlignment(AbstractOrderedLayout layout) { + Label label = new Label("A label"); + TextField tf = new TextField("A TextField"); + layout.addComponent(label); + layout.addComponent(tf); + Assert.assertEquals(Alignment.TOP_LEFT, + layout.getComponentAlignment(label)); + Assert.assertEquals(Alignment.TOP_LEFT, + layout.getComponentAlignment(tf)); + } + + @Test + public void testAlteredDefaultAlignmentVerticalLayout() { + testAlteredDefaultAlignment(verticalLayout); + } + + @Test + public void testAlteredDefaultAlignmentHorizontalLayout() { + testAlteredDefaultAlignment(horizontalLayout); + } + + public void testAlteredDefaultAlignment(AbstractOrderedLayout layout) { + Label label = new Label("A label"); + TextField tf = new TextField("A TextField"); + layout.setDefaultComponentAlignment(Alignment.MIDDLE_CENTER); + layout.addComponent(label); + layout.addComponent(tf); + Assert.assertEquals(Alignment.MIDDLE_CENTER, + layout.getComponentAlignment(label)); + Assert.assertEquals(Alignment.MIDDLE_CENTER, + layout.getComponentAlignment(tf)); + } +} diff --git a/server/tests/src/com/vaadin/tests/server/component/tree/TreeTest.java b/server/tests/src/com/vaadin/tests/server/component/tree/TreeTest.java index 634e6a86f3..3c9fc4c0cd 100644 --- a/server/tests/src/com/vaadin/tests/server/component/tree/TreeTest.java +++ b/server/tests/src/com/vaadin/tests/server/component/tree/TreeTest.java @@ -9,7 +9,6 @@ import java.lang.reflect.Field; import java.util.HashSet; import org.junit.Before; -import org.junit.Ignore; import org.junit.Test; import com.vaadin.data.Container; @@ -84,17 +83,12 @@ public class TreeTest { .getContainerDataSource().getClass())); } - @Ignore("This test tests that item ids which are removed are also " - + "removed from the expand list to prevent a memory leak. " - + "Fixing the memory leak cannot be done without changing some API (see #11053) " - + "so ignoring this test for the 7.0.x series.") @Test public void testRemoveExpandedItems() throws Exception { tree.expandItem("parent"); tree.expandItem("child"); - Field expandedField = tree.getClass() - .getDeclaredField("expanded"); + Field expandedField = tree.getClass().getDeclaredField("expanded"); Field expandedItemIdField = tree.getClass().getDeclaredField( "expandedItemId"); diff --git a/server/tests/src/com/vaadin/tests/server/component/ui/CustomUIClassLoader.java b/server/tests/src/com/vaadin/tests/server/component/ui/CustomUIClassLoader.java index 8884c0c27c..1df1d36cab 100644 --- a/server/tests/src/com/vaadin/tests/server/component/ui/CustomUIClassLoader.java +++ b/server/tests/src/com/vaadin/tests/server/component/ui/CustomUIClassLoader.java @@ -15,6 +15,7 @@ import com.vaadin.server.UIClassSelectionEvent; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinService; import com.vaadin.server.VaadinSession; +import com.vaadin.tests.util.AlwaysLockedVaadinSession; import com.vaadin.ui.UI; public class CustomUIClassLoader extends TestCase { @@ -113,7 +114,7 @@ public class CustomUIClassLoader extends TestCase { } private VaadinSession createStubApplication() { - return new VaadinSession(null) { + return new AlwaysLockedVaadinSession(null) { @Override public DeploymentConfiguration getConfiguration() { return createConfigurationMock(); diff --git a/server/tests/src/com/vaadin/tests/server/component/window/AddRemoveSubWindow.java b/server/tests/src/com/vaadin/tests/server/component/window/AddRemoveSubWindow.java index bf6d127a83..774eafceaf 100644 --- a/server/tests/src/com/vaadin/tests/server/component/window/AddRemoveSubWindow.java +++ b/server/tests/src/com/vaadin/tests/server/component/window/AddRemoveSubWindow.java @@ -8,6 +8,7 @@ import org.junit.Test; import com.vaadin.server.LegacyApplication; import com.vaadin.server.VaadinSession; +import com.vaadin.tests.util.AlwaysLockedVaadinSession; import com.vaadin.ui.LegacyWindow; import com.vaadin.ui.UI; import com.vaadin.ui.Window; @@ -25,7 +26,7 @@ public class AddRemoveSubWindow { @Test public void addSubWindow() { - VaadinSession.setCurrent(new VaadinSession(null)); + VaadinSession.setCurrent(new AlwaysLockedVaadinSession(null)); TestApp app = new TestApp(); app.init(); Window subWindow = new Window("Sub window"); diff --git a/server/tests/src/com/vaadin/tests/server/component/window/AttachDetachWindow.java b/server/tests/src/com/vaadin/tests/server/component/window/AttachDetachWindow.java index 63a58bcab3..485b17830f 100644 --- a/server/tests/src/com/vaadin/tests/server/component/window/AttachDetachWindow.java +++ b/server/tests/src/com/vaadin/tests/server/component/window/AttachDetachWindow.java @@ -8,6 +8,7 @@ import org.junit.Test; import com.vaadin.server.ClientConnector; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinSession; +import com.vaadin.tests.util.AlwaysLockedVaadinSession; import com.vaadin.ui.Label; import com.vaadin.ui.UI; import com.vaadin.ui.VerticalLayout; @@ -15,7 +16,7 @@ import com.vaadin.ui.Window; public class AttachDetachWindow { - private VaadinSession testApp = new VaadinSession(null); + private VaadinSession testApp = new AlwaysLockedVaadinSession(null); private interface TestContainer { public boolean attachCalled(); diff --git a/server/tests/src/com/vaadin/tests/server/componentcontainer/AddRemoveComponentTest.java b/server/tests/src/com/vaadin/tests/server/componentcontainer/AddRemoveComponentTest.java index 828404bd5a..91a302a274 100644 --- a/server/tests/src/com/vaadin/tests/server/componentcontainer/AddRemoveComponentTest.java +++ b/server/tests/src/com/vaadin/tests/server/componentcontainer/AddRemoveComponentTest.java @@ -9,6 +9,7 @@ import com.vaadin.ui.ComponentContainer; import com.vaadin.ui.CustomLayout; import com.vaadin.ui.HorizontalLayout; import com.vaadin.ui.Label; +import com.vaadin.ui.components.colorpicker.ColorPickerPreview; public class AddRemoveComponentTest extends TestCase { @@ -19,6 +20,7 @@ public class AddRemoveComponentTest extends TestCase { // No default constructor, special case containerClasses.remove(CustomLayout.class); + containerClasses.remove(ColorPickerPreview.class); testRemoveComponentFromWrongContainer(new CustomLayout("dummy")); for (Class<? extends ComponentContainer> c : containerClasses) { diff --git a/server/tests/src/com/vaadin/tests/util/AlwaysLockedVaadinSession.java b/server/tests/src/com/vaadin/tests/util/AlwaysLockedVaadinSession.java new file mode 100644 index 0000000000..9b0b524b6a --- /dev/null +++ b/server/tests/src/com/vaadin/tests/util/AlwaysLockedVaadinSession.java @@ -0,0 +1,23 @@ +package com.vaadin.tests.util; + +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +import com.vaadin.server.VaadinService; +import com.vaadin.server.VaadinSession; + +public class AlwaysLockedVaadinSession extends VaadinSession { + + private ReentrantLock lock; + + public AlwaysLockedVaadinSession(VaadinService service) { + super(service); + lock = new ReentrantLock(); + lock.lock(); + } + + @Override + public Lock getLockInstance() { + return lock; + } +} diff --git a/server/tests/src/com/vaadin/tests/util/MockDeploymentConfiguration.java b/server/tests/src/com/vaadin/tests/util/MockDeploymentConfiguration.java new file mode 100644 index 0000000000..d113efdfaf --- /dev/null +++ b/server/tests/src/com/vaadin/tests/util/MockDeploymentConfiguration.java @@ -0,0 +1,111 @@ +package com.vaadin.tests.util; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; + +import com.vaadin.server.DeploymentConfiguration; +import com.vaadin.shared.communication.PushMode; + +public class MockDeploymentConfiguration implements DeploymentConfiguration { + + private boolean productionMode = false; + private boolean xsrfProtectionEnabled = true; + + private int resourceCacheTime = 12; + private int heartbeatInterval = 300; + private boolean closeIdleSessions = false; + private PushMode pushMode = PushMode.DISABLED; + private Properties initParameters = new Properties(); + private Map<String, String> applicationOrSystemProperty = new HashMap<String, String>(); + private LegacyProperyToStringMode legacyPropertyToStringMode = LegacyProperyToStringMode.DISABLED; + + @Override + public boolean isProductionMode() { + return productionMode; + } + + public void setProductionMode(boolean productionMode) { + this.productionMode = productionMode; + } + + @Override + public boolean isXsrfProtectionEnabled() { + return xsrfProtectionEnabled; + } + + public void setXsrfProtectionEnabled(boolean xsrfProtectionEnabled) { + this.xsrfProtectionEnabled = xsrfProtectionEnabled; + } + + @Override + public int getResourceCacheTime() { + return resourceCacheTime; + } + + public void setResourceCacheTime(int resourceCacheTime) { + this.resourceCacheTime = resourceCacheTime; + } + + @Override + public int getHeartbeatInterval() { + return heartbeatInterval; + } + + public void setHeartbeatInterval(int heartbeatInterval) { + this.heartbeatInterval = heartbeatInterval; + } + + @Override + public boolean isCloseIdleSessions() { + return closeIdleSessions; + } + + public void setCloseIdleSessions(boolean closeIdleSessions) { + this.closeIdleSessions = closeIdleSessions; + } + + @Override + public PushMode getPushMode() { + return pushMode; + } + + public void setPushMode(PushMode pushMode) { + this.pushMode = pushMode; + } + + @Override + public Properties getInitParameters() { + return initParameters; + } + + public void setInitParameter(String key, String value) { + initParameters.setProperty(key, value); + } + + public void setApplicationOrSystemProperty(String key, String value) { + applicationOrSystemProperty.put(key, value); + } + + @Override + public String getApplicationOrSystemProperty(String propertyName, + String defaultValue) { + if (applicationOrSystemProperty.containsKey(propertyName)) { + return applicationOrSystemProperty.get(propertyName); + } else { + return defaultValue; + } + } + + @Override + @Deprecated + public LegacyProperyToStringMode getLegacyPropertyToStringMode() { + return legacyPropertyToStringMode; + } + + public void setLegacyPropertyToStringMode( + LegacyProperyToStringMode legacyPropertyToStringMode) { + this.legacyPropertyToStringMode = legacyPropertyToStringMode; + } + +} diff --git a/server/tests/src/com/vaadin/ui/AbstractFieldDataSourceLocaleChange.java b/server/tests/src/com/vaadin/ui/AbstractFieldDataSourceLocaleChange.java new file mode 100644 index 0000000000..9810873f0b --- /dev/null +++ b/server/tests/src/com/vaadin/ui/AbstractFieldDataSourceLocaleChange.java @@ -0,0 +1,63 @@ +package com.vaadin.ui; + +import java.text.NumberFormat; +import java.util.Locale; + +import junit.framework.Assert; + +import org.junit.Before; +import org.junit.Test; + +import com.vaadin.data.util.converter.StringToIntegerConverter; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinSession; + +public class AbstractFieldDataSourceLocaleChange { + + private VaadinSession vaadinSession; + private UI ui; + + @Before + public void setup() { + vaadinSession = new VaadinSession(null); + VaadinSession.setCurrent(vaadinSession); + ui = new UI() { + + @Override + protected void init(VaadinRequest request) { + + } + }; + ui.setSession(vaadinSession); + UI.setCurrent(ui); + } + + @Test + public void localeChangesOnAttach() { + TextField tf = new TextField(); + + ; + tf.setConverter(new StringToIntegerConverter() { + @Override + protected NumberFormat getFormat(Locale locale) { + if (locale == null) { + NumberFormat format = super.getFormat(locale); + format.setGroupingUsed(false); + format.setMinimumIntegerDigits(10); + return format; + } + return super.getFormat(locale); + } + }); + tf.setImmediate(true); + tf.setConvertedValue(10000); + Assert.assertEquals("0000010000", tf.getValue()); + + VerticalLayout vl = new VerticalLayout(); + ui.setContent(vl); + ui.setLocale(new Locale("en", "US")); + + vl.addComponent(tf); + Assert.assertEquals("10,000", tf.getValue()); + } +} diff --git a/server/tests/src/com/vaadin/ui/LabelDataSource.java b/server/tests/src/com/vaadin/ui/LabelDataSource.java index 7dcb382124..21d3e56d57 100644 --- a/server/tests/src/com/vaadin/ui/LabelDataSource.java +++ b/server/tests/src/com/vaadin/ui/LabelDataSource.java @@ -24,6 +24,7 @@ import org.junit.Test; import com.vaadin.data.util.ObjectProperty; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinSession; +import com.vaadin.tests.util.AlwaysLockedVaadinSession; public class LabelDataSource { @@ -39,7 +40,7 @@ public class LabelDataSource { @Before public void setup() { - vaadinSession = new VaadinSession(null); + vaadinSession = new AlwaysLockedVaadinSession(null); VaadinSession.setCurrent(vaadinSession); label = new Label(); diff --git a/server/tests/src/com/vaadin/util/ReflectToolsGetFieldValueByType.java b/server/tests/src/com/vaadin/util/ReflectToolsGetFieldValueByType.java index 78be9b04fb..540ffb852d 100644 --- a/server/tests/src/com/vaadin/util/ReflectToolsGetFieldValueByType.java +++ b/server/tests/src/com/vaadin/util/ReflectToolsGetFieldValueByType.java @@ -3,8 +3,6 @@ package com.vaadin.util; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; -import java.lang.reflect.InvocationTargetException; - import org.junit.Test; public class ReflectToolsGetFieldValueByType { @@ -56,8 +54,8 @@ public class ReflectToolsGetFieldValueByType { memberField = myInstance.getClass().getField("field"); // Should throw an IllegalArgument exception as the mySubClass class // doesn't have an Integer field. - ReflectTools.getJavaFieldValue(myInstance, - memberField, Integer.class); + ReflectTools.getJavaFieldValue(myInstance, memberField, + Integer.class); fail("Previous method call should have thrown an exception"); } catch (Exception e) { } diff --git a/server/tests/src/com/vaadin/util/ReflectToolsGetPrimitiveFieldValue.java b/server/tests/src/com/vaadin/util/ReflectToolsGetPrimitiveFieldValue.java index df192c51f2..1e1fafe31c 100644 --- a/server/tests/src/com/vaadin/util/ReflectToolsGetPrimitiveFieldValue.java +++ b/server/tests/src/com/vaadin/util/ReflectToolsGetPrimitiveFieldValue.java @@ -17,8 +17,8 @@ public class ReflectToolsGetPrimitiveFieldValue { Object fieldValue = new Boolean(false); try { memberField = myInstance.getClass().getField("field"); - fieldValue = ReflectTools.getJavaFieldValue(myInstance, - memberField); + fieldValue = ReflectTools + .getJavaFieldValue(myInstance, memberField); } catch (Exception e) { } assertFalse(fieldValue instanceof Boolean); diff --git a/shared/build.xml b/shared/build.xml index 6ea753afa1..8520ee6eba 100644 --- a/shared/build.xml +++ b/shared/build.xml @@ -56,8 +56,8 @@ </antcall> </target> - <target name="tests" depends="checkstyle"> - <!--<antcall target="common.tests.run" />--> + <target name="test" depends="checkstyle"> + <!--<antcall target="common.test.run" />--> <echo>WHAT? No tests for ${module.name}!</echo> </target> diff --git a/shared/ivy.xml b/shared/ivy.xml index 88020e8d09..3b044e9ab4 100644 --- a/shared/ivy.xml +++ b/shared/ivy.xml @@ -11,7 +11,7 @@ <conf name="build" /> <conf name="build-provided" /> <conf name="ide" visibility="private" /> - <conf name="tests" /> + <conf name="test" /> </configurations> <publications> <artifact type="jar" ext="jar" /> @@ -21,7 +21,7 @@ </publications> <dependencies> <dependency org="com.vaadin" name="vaadin-shared-deps" - rev="1.0.2" conf="build,ide,tests->default" /> + rev="1.0.2" conf="build,ide,test->default" /> </dependencies> </ivy-module> diff --git a/shared/src/com/vaadin/shared/ApplicationConstants.java b/shared/src/com/vaadin/shared/ApplicationConstants.java index 333833ab60..fc4abd1988 100644 --- a/shared/src/com/vaadin/shared/ApplicationConstants.java +++ b/shared/src/com/vaadin/shared/ApplicationConstants.java @@ -28,6 +28,8 @@ public class ApplicationConstants implements Serializable { public static final String HEARTBEAT_PATH = "HEARTBEAT"; + public static final String PUSH_PATH = "PUSH"; + public static final String PUBLISHED_FILE_PATH = APP_PATH + '/' + "PUBLISHED"; @@ -37,7 +39,6 @@ public class ApplicationConstants implements Serializable { + "://"; public static final String UIDL_SECURITY_TOKEN_ID = "Vaadin-Security-Key"; - public static final String PARAM_UNLOADBURST = "onunloadburst"; public static final String PARAM_ANALYZE_LAYOUTS = "analyzeLayouts"; public static final String PARAM_HIGHLIGHT_CONNECTOR = "highlightConnector"; @@ -71,4 +72,19 @@ public class ApplicationConstants implements Serializable { * </p> */ public static final String VAADIN_DIR_URL = "vaadinDir"; + + /** + * The name of the javascript containing push support. The file is located + * in the VAADIN directory. + */ + public static final String VAADIN_PUSH_JS = "vaadinPush.js"; + + /** + * Name of the parameter used to transmit the CSRF token. + */ + public static final String CSRF_TOKEN_PARAMETER = "v-csrfToken"; + + public static final int WEBSOCKET_BUFFER_SIZE = 65536; + + public static final char WEBSOCKET_MESSAGE_DELIMITER = '|'; } diff --git a/shared/src/com/vaadin/shared/JsonConstants.java b/shared/src/com/vaadin/shared/JsonConstants.java index 8a9e37f1a5..44aeac72e4 100644 --- a/shared/src/com/vaadin/shared/JsonConstants.java +++ b/shared/src/com/vaadin/shared/JsonConstants.java @@ -32,4 +32,6 @@ public class JsonConstants implements Serializable { public static final String VTYPE_SET = "q"; public static final String VTYPE_NULL = "n"; + public static final String JSON_CONTENT_TYPE = "application/json; charset=UTF-8"; + } diff --git a/shared/src/com/vaadin/shared/communication/MethodInvocation.java b/shared/src/com/vaadin/shared/communication/MethodInvocation.java index 417ced76be..d5bf8324ef 100644 --- a/shared/src/com/vaadin/shared/communication/MethodInvocation.java +++ b/shared/src/com/vaadin/shared/communication/MethodInvocation.java @@ -19,6 +19,8 @@ package com.vaadin.shared.communication; import java.io.Serializable; import java.util.Arrays; +import com.vaadin.shared.util.SharedUtil; + /** * Information needed by the framework to send an RPC method invocation from the * client to the server or vice versa. @@ -85,4 +87,29 @@ public class MethodInvocation implements Serializable { return connectorId + "-" + getInterfaceName() + "-" + getMethodName(); } + @Override + public boolean equals(Object obj) { + if (!(obj instanceof MethodInvocation)) { + return false; + } + MethodInvocation other = (MethodInvocation) obj; + if (!SharedUtil.equals(getConnectorId(), other.getConnectorId())) { + return false; + } + + if (!SharedUtil.equals(getInterfaceName(), other.getInterfaceName())) { + return false; + } + + if (!SharedUtil.equals(getMethodName(), other.getMethodName())) { + return false; + } + + if (!SharedUtil.equals(getParameters(), other.getParameters())) { + return false; + } + + return true; + + } }
\ No newline at end of file diff --git a/shared/src/com/vaadin/shared/communication/PushMode.java b/shared/src/com/vaadin/shared/communication/PushMode.java new file mode 100644 index 0000000000..3fe8b4ea3e --- /dev/null +++ b/shared/src/com/vaadin/shared/communication/PushMode.java @@ -0,0 +1,66 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.shared.communication; + +/** + * The mode of bidirectional ("push") communication that is in use. + * + * @see com.vaadin.server.DeploymentConfiguration#getPushMode() + * + * @author Vaadin Ltd + * @since 7.1 + */ +public enum PushMode { + /** + * Push is disabled. Regular AJAX requests are used to communicate between + * the client and the server. Asynchronous messages from the server are not + * possible. {@link com.vaadin.ui.UI#push() ui.push()} throws + * IllegalStateException. + * <p> + * This is the default mode unless + * {@link com.vaadin.server.DeploymentConfiguration#getPushMode() + * configured} otherwise. + */ + DISABLED, + + /** + * Push is enabled. A bidirectional channel is established between the + * client and server and used to communicate state changes and RPC + * invocations. The client is not automatically updated if the server-side + * state is asynchronously changed; {@link com.vaadin.ui.UI#push() + * ui.push()} must be explicitly called. + */ + MANUAL, + + /** + * Push is enabled. Like {@link #MANUAL}, but asynchronous changes to the + * server-side state are automatically pushed to the client once the session + * lock is released. + */ + AUTOMATIC; + + /** + * Checks whether the push mode is using push functionality + * + * @return <code>true</code> if this mode requires push functionality; + * <code>false</code> if no push functionality is used for this + * mode. + */ + public boolean isEnabled() { + return this != DISABLED; + } +} diff --git a/shared/src/com/vaadin/shared/ui/button/ButtonState.java b/shared/src/com/vaadin/shared/ui/button/ButtonState.java index a33a9f78db..7e1fd52ed7 100644 --- a/shared/src/com/vaadin/shared/ui/button/ButtonState.java +++ b/shared/src/com/vaadin/shared/ui/button/ButtonState.java @@ -37,4 +37,5 @@ public class ButtonState extends TabIndexState { * If caption should be rendered in HTML */ public boolean htmlContentAllowed = false; + public String iconAltText = ""; } diff --git a/shared/src/com/vaadin/shared/ui/calendar/CalendarClientRpc.java b/shared/src/com/vaadin/shared/ui/calendar/CalendarClientRpc.java new file mode 100644 index 0000000000..c1ff8bdda5 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/calendar/CalendarClientRpc.java @@ -0,0 +1,28 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.shared.ui.calendar; + +import com.vaadin.shared.communication.ClientRpc; + +/** + * + * @since 7.1 + * @author Vaadin Ltd. + * + */ +public interface CalendarClientRpc extends ClientRpc { + void scroll(int scrollPosition); +} diff --git a/shared/src/com/vaadin/shared/ui/calendar/CalendarEventId.java b/shared/src/com/vaadin/shared/ui/calendar/CalendarEventId.java new file mode 100644 index 0000000000..6f52aabf43 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/calendar/CalendarEventId.java @@ -0,0 +1,36 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.shared.ui.calendar; + +/** + * CalendarEventId contains static String identifiers for all Calendar events. + * These are used both in the client and server side code. + * + * @since 7.1 + * @author Vaadin Ltd. + */ +public class CalendarEventId { + + public static final String EVENTMOVE = "eventMove"; + public static final String RANGESELECT = "rangeSelect"; + public static final String FORWARD = "forward"; + public static final String BACKWARD = "backward"; + public static final String DATECLICK = "dateClick"; + public static final String WEEKCLICK = "weekClick"; + public static final String EVENTCLICK = "eventClick"; + public static final String EVENTRESIZE = "eventResize"; + public static final String ACTION = "action"; +} diff --git a/shared/src/com/vaadin/shared/ui/calendar/CalendarServerRpc.java b/shared/src/com/vaadin/shared/ui/calendar/CalendarServerRpc.java new file mode 100644 index 0000000000..5257310cbf --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/calendar/CalendarServerRpc.java @@ -0,0 +1,49 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.shared.ui.calendar; + +import com.vaadin.shared.annotations.Delayed; +import com.vaadin.shared.communication.ServerRpc; + +/** + * @since 7.1 + * @author Vaadin Ltd. + */ +public interface CalendarServerRpc extends ServerRpc { + void eventMove(int eventIndex, String newDate); + + void rangeSelect(String range); + + void forward(); + + void backward(); + + void dateClick(String date); + + void weekClick(String event); + + void eventClick(int eventIndex); + + void eventResize(int eventIndex, String newStartDate, String newEndDate); + + void actionOnEmptyCell(String actionKey, String startDate, String endDate); + + void actionOnEvent(String actionKey, String startDate, String endDate, + int eventIndex); + + @Delayed(lastOnly = true) + void scroll(int scrollPosition); +} diff --git a/shared/src/com/vaadin/shared/ui/calendar/CalendarState.java b/shared/src/com/vaadin/shared/ui/calendar/CalendarState.java new file mode 100644 index 0000000000..fab5fd828e --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/calendar/CalendarState.java @@ -0,0 +1,69 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.shared.ui.calendar; + +import java.util.List; + +import com.vaadin.shared.AbstractComponentState; + +/** + * @since 7.1.0 + * @author Vaadin Ltd. + */ +public class CalendarState extends AbstractComponentState { + + public boolean format24H; + public String[] dayNames; + public String[] monthNames; + public int firstVisibleDayOfWeek = 1; + public int lastVisibleDayOfWeek = 7; + public int firstHourOfDay = 0; + public int lastHourOfDay = 23; + public int firstDayOfWeek; + public int scroll; + public String now; + public List<CalendarState.Day> days; + public List<CalendarState.Event> events; + public List<CalendarState.Action> actions; + + public static class Day implements java.io.Serializable { + public String date; + public String localizedDateFormat; + public int dayOfWeek; + public int week; + } + + public static class Action implements java.io.Serializable { + + public String caption; + public String iconKey; + public String actionKey; + public String startDate; + public String endDate; + } + + public static class Event implements java.io.Serializable { + public int index; + public String caption; + public String dateFrom; + public String dateTo; + public String timeFrom; + public String timeTo; + public String styleName; + public String description; + public boolean allDay; + } +} diff --git a/shared/src/com/vaadin/shared/ui/calendar/DateConstants.java b/shared/src/com/vaadin/shared/ui/calendar/DateConstants.java new file mode 100644 index 0000000000..8a840274c2 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/calendar/DateConstants.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.shared.ui.calendar; + +/** + * + * @since 7.1 + * + */ +public class DateConstants { + + public static final String ACTION_DATE_FORMAT_PATTERN = "yyyy-MM-dd HH:mm:ss"; + public static final String CLIENT_DATE_FORMAT = "yyyy-MM-dd"; + public static final String CLIENT_TIME_FORMAT = "HH-mm"; + public static final long MINUTEINMILLIS = 60 * 1000; + public static final long HOURINMILLIS = 60 * MINUTEINMILLIS; + public static final long DAYINMILLIS = 24 * HOURINMILLIS; + public static final long WEEKINMILLIS = 7 * DAYINMILLIS; + +} diff --git a/shared/src/com/vaadin/shared/ui/datefield/InlineDateFieldState.java b/shared/src/com/vaadin/shared/ui/datefield/InlineDateFieldState.java index 405f89d538..d56e0d27b3 100644 --- a/shared/src/com/vaadin/shared/ui/datefield/InlineDateFieldState.java +++ b/shared/src/com/vaadin/shared/ui/datefield/InlineDateFieldState.java @@ -15,9 +15,7 @@ */ package com.vaadin.shared.ui.datefield; -import com.vaadin.shared.AbstractFieldState; - -public class InlineDateFieldState extends AbstractFieldState { +public class InlineDateFieldState extends TextualDateFieldState { { primaryStyleName = "v-inline-datefield"; } diff --git a/shared/src/com/vaadin/shared/ui/datefield/PopupDateFieldState.java b/shared/src/com/vaadin/shared/ui/datefield/PopupDateFieldState.java index 74cab3efb0..1c061b3ac3 100644 --- a/shared/src/com/vaadin/shared/ui/datefield/PopupDateFieldState.java +++ b/shared/src/com/vaadin/shared/ui/datefield/PopupDateFieldState.java @@ -16,10 +16,12 @@ package com.vaadin.shared.ui.datefield; public class PopupDateFieldState extends TextualDateFieldState { + public static final String DESCRIPTION_FOR_ASSISTIVE_DEVICES = "Arrow down key opens calendar element for choosing the date"; + { primaryStyleName = "v-datefield"; } public boolean textFieldEnabled = true; - + public String descriptionForAssistiveDevices = DESCRIPTION_FOR_ASSISTIVE_DEVICES; } diff --git a/shared/src/com/vaadin/shared/ui/datefield/TextualDateFieldState.java b/shared/src/com/vaadin/shared/ui/datefield/TextualDateFieldState.java index c34f3d8eda..11ad4cdb59 100644 --- a/shared/src/com/vaadin/shared/ui/datefield/TextualDateFieldState.java +++ b/shared/src/com/vaadin/shared/ui/datefield/TextualDateFieldState.java @@ -15,10 +15,24 @@ */ package com.vaadin.shared.ui.datefield; +import java.util.Date; + import com.vaadin.shared.AbstractFieldState; public class TextualDateFieldState extends AbstractFieldState { { primaryStyleName = "v-datefield"; } + + /* + * Start range that has been cleared, depending on the resolution of the + * date field + */ + public Date rangeStart = null; + + /* + * End range that has been cleared, depending on the resolution of the date + * field + */ + public Date rangeEnd = null; } diff --git a/shared/src/com/vaadin/shared/ui/tree/TreeConstants.java b/shared/src/com/vaadin/shared/ui/tree/TreeConstants.java index 7adc69511d..a57ca31246 100644 --- a/shared/src/com/vaadin/shared/ui/tree/TreeConstants.java +++ b/shared/src/com/vaadin/shared/ui/tree/TreeConstants.java @@ -26,6 +26,8 @@ public class TreeConstants implements Serializable { public static final String ATTRIBUTE_NODE_CAPTION = "caption"; @Deprecated public static final String ATTRIBUTE_NODE_ICON = "icon"; + @Deprecated + public static final String ATTRIBUTE_NODE_ICON_ALT = "iconalt"; @Deprecated public static final String ATTRIBUTE_ACTION_CAPTION = "caption"; diff --git a/shared/src/com/vaadin/shared/ui/ui/PageClientRpc.java b/shared/src/com/vaadin/shared/ui/ui/PageClientRpc.java index 3d8f607cb8..eb847bacd0 100644 --- a/shared/src/com/vaadin/shared/ui/ui/PageClientRpc.java +++ b/shared/src/com/vaadin/shared/ui/ui/PageClientRpc.java @@ -22,4 +22,6 @@ public interface PageClientRpc extends ClientRpc { public void setTitle(String title); + public void reload(); + } diff --git a/shared/src/com/vaadin/shared/ui/ui/PageState.java b/shared/src/com/vaadin/shared/ui/ui/PageState.java new file mode 100644 index 0000000000..0b51eb4bba --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/ui/PageState.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.shared.ui.ui; + +import java.io.Serializable; + +/** + * The shared state of a {@link com.vaadin.server.Page Page}. + * + * Note that at the moment this is not a stand-alone state class but embedded in + * {@link UIState}. This might change in the future. + * + * @since 7.1 + */ +public class PageState implements Serializable { + /** + * True if the page has browser window resize listeners. + */ + public boolean hasResizeListeners = false; +}
\ No newline at end of file diff --git a/shared/src/com/vaadin/shared/ui/ui/UIClientRpc.java b/shared/src/com/vaadin/shared/ui/ui/UIClientRpc.java new file mode 100644 index 0000000000..3067b10e24 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/ui/UIClientRpc.java @@ -0,0 +1,34 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.shared.ui.ui; + +import com.vaadin.shared.communication.ClientRpc; + +/** + * Server to Client RPC methods for UI + * + * @since 7.1 + * @author Vaadin Ltd + */ +public interface UIClientRpc extends ClientRpc { + + /** + * @since + * @param sessionExpired + */ + void uiClosed(boolean sessionExpired); + +} diff --git a/shared/src/com/vaadin/shared/ui/ui/UIServerRpc.java b/shared/src/com/vaadin/shared/ui/ui/UIServerRpc.java index 358ba2e24e..576ee83980 100644 --- a/shared/src/com/vaadin/shared/ui/ui/UIServerRpc.java +++ b/shared/src/com/vaadin/shared/ui/ui/UIServerRpc.java @@ -26,4 +26,11 @@ public interface UIServerRpc extends ClickRpc, ServerRpc { @Delayed(lastOnly = true) public void scroll(int scrollTop, int scrollLeft); + + @Delayed(lastOnly = true) + /* + * @Delayed just to get lastOnly semantics, sendPendingVariableChanges() + * should always be called to ensure the message is flushed right away. + */ + public void poll(); }
\ No newline at end of file diff --git a/shared/src/com/vaadin/shared/ui/ui/UIState.java b/shared/src/com/vaadin/shared/ui/ui/UIState.java index 9abaf47f4b..16c1ed16c7 100644 --- a/shared/src/com/vaadin/shared/ui/ui/UIState.java +++ b/shared/src/com/vaadin/shared/ui/ui/UIState.java @@ -15,12 +15,45 @@ */ package com.vaadin.shared.ui.ui; +import java.io.Serializable; + +import com.vaadin.shared.communication.PushMode; import com.vaadin.shared.ui.TabIndexState; public class UIState extends TabIndexState { + public TooltipConfigurationState tooltipConfiguration = new TooltipConfigurationState(); + public LoadingIndicatorConfigurationState loadingIndicatorConfiguration = new LoadingIndicatorConfigurationState(); + public int pollInterval = -1; + + public PushMode pushMode = PushMode.DISABLED; + + // Informing users of assistive devices, that the content of this container + // is announced automatically and does not need to be navigated into + public String overlayContainerLabel = "This content is announced automatically and does not need to be navigated into."; + + public static class LoadingIndicatorConfigurationState implements + Serializable { + public int firstDelay = 300; + public int secondDelay = 1500; + public int thirdDelay = 5000; + } + + public static class TooltipConfigurationState implements Serializable { + public int openDelay = 750; + public int quickOpenDelay = 100; + public int quickOpenTimeout = 1000; + public int closeTimeout = 300; + public int maxWidth = 500; + } + + /** + * State related to the {@link Page} class. + */ + public PageState pageState = new PageState(); + { primaryStyleName = "v-ui"; // Default is 1 for legacy reasons tabIndex = 1; } -}
\ No newline at end of file +} diff --git a/shared/src/com/vaadin/shared/ui/window/WindowMode.java b/shared/src/com/vaadin/shared/ui/window/WindowMode.java new file mode 100644 index 0000000000..04af77a086 --- /dev/null +++ b/shared/src/com/vaadin/shared/ui/window/WindowMode.java @@ -0,0 +1,39 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.shared.ui.window; + +/** + * Determines the mode of the Window. + * <p> + * A window mode decides the size and position of the Window. It can be set to + * {@link #NORMAL} or {@link #MAXIMIZED}. + * + * + * @author Vaadin Ltd + * @since 7.1 + */ +public enum WindowMode { + /** + * Normal mode. The window size and position is determined by the window + * state. + */ + NORMAL, + /** + * Maximized mode. The window is positioned in the top left corner and fills + * the whole screen. + */ + MAXIMIZED; +} diff --git a/shared/src/com/vaadin/shared/ui/window/WindowServerRpc.java b/shared/src/com/vaadin/shared/ui/window/WindowServerRpc.java index c42f91c006..cfb10ad86a 100644 --- a/shared/src/com/vaadin/shared/ui/window/WindowServerRpc.java +++ b/shared/src/com/vaadin/shared/ui/window/WindowServerRpc.java @@ -15,8 +15,13 @@ */ package com.vaadin.shared.ui.window; +import com.vaadin.shared.annotations.Delayed; import com.vaadin.shared.communication.ServerRpc; import com.vaadin.shared.ui.ClickRpc; public interface WindowServerRpc extends ClickRpc, ServerRpc { + + @Delayed(lastOnly = true) + public void windowModeChanged(WindowMode newState); + }
\ No newline at end of file diff --git a/shared/src/com/vaadin/shared/ui/window/WindowState.java b/shared/src/com/vaadin/shared/ui/window/WindowState.java index 4afc20f2b1..5a2d2b81b0 100644 --- a/shared/src/com/vaadin/shared/ui/window/WindowState.java +++ b/shared/src/com/vaadin/shared/ui/window/WindowState.java @@ -21,6 +21,7 @@ public class WindowState extends PanelState { { primaryStyleName = "v-window"; } + public boolean modal = false; public boolean resizable = true; public boolean resizeLazy = false; @@ -28,4 +29,5 @@ public class WindowState extends PanelState { public boolean centered = false;; public int positionX = -1; public int positionY = -1; + public WindowMode windowMode = WindowMode.NORMAL; }
\ No newline at end of file diff --git a/shared/src/com/vaadin/shared/util/SharedUtil.java b/shared/src/com/vaadin/shared/util/SharedUtil.java new file mode 100644 index 0000000000..2242fa4363 --- /dev/null +++ b/shared/src/com/vaadin/shared/util/SharedUtil.java @@ -0,0 +1,45 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.shared.util; + +/** + * Misc internal utility methods used by both the server and the client package. + * + * @author Vaadin Ltd + * @since 7.1 + * + */ +public class SharedUtil { + /** + * Checks if a and b are equals using {@link #equals(Object)}. Handles null + * values as well. Does not ensure that objects are of the same type. + * Assumes that the first object's equals method handle equals properly. + * + * @param o1 + * The first value to compare + * @param o2 + * The second value to compare + * @return true if the objects are equal, false otherwise + */ + public static boolean equals(Object o1, Object o2) { + if (o1 == null) { + return o2 == null; + } + + return o1.equals(o2); + } + +} diff --git a/theme-compiler/build.xml b/theme-compiler/build.xml index cd441dee70..277929d160 100644 --- a/theme-compiler/build.xml +++ b/theme-compiler/build.xml @@ -15,7 +15,7 @@ <property name="sass.parser.jj" location="src/com/vaadin/sass/internal/parser/Parser.jj" /> <path id="classpath.compile.custom"> </path> - <path id="classpath.tests.custom" /> + <path id="classpath.test.custom" /> <!--<property name="classes.exclude" value="com/vaadin/buildhelpers/**" />--> @@ -51,8 +51,8 @@ </antcall> </target> - <target name="tests" depends="checkstyle"> - <antcall target="common.tests.run" /> + <target name="test" depends="checkstyle"> + <antcall target="common.test.run" /> </target> </project>
\ No newline at end of file diff --git a/theme-compiler/ivy.xml b/theme-compiler/ivy.xml index b1a98b7101..f0646c04e6 100644 --- a/theme-compiler/ivy.xml +++ b/theme-compiler/ivy.xml @@ -11,7 +11,7 @@ <conf name="build" /> <conf name="build-provided" /> <conf name="ide" visibility="private" /> - <conf name="tests" /> + <conf name="test" /> </configurations> <publications> <artifact type="jar" ext="jar" /> @@ -23,17 +23,17 @@ <!-- LIBRARY DEPENDENCIES (compile time) --> <!-- Project modules --> <dependency org="com.vaadin" name="vaadin-shared" - rev="${vaadin.version}" conf="build,tests" /> + rev="${vaadin.version}" conf="build,test->build" /> <!-- Required build libs --> <dependency org="org.apache.commons" name="commons-jexl" - rev="2.1.1" conf="build,ide,tests->default" /> + rev="2.1.1" conf="build,ide,test->default" /> <dependency org="org.w3c.css" name="sac" rev="1.3" - conf="build,ide,tests->default" /> + conf="build,ide,test->default" /> <dependency org="net.sourceforge.cssparser" name="cssparser" - rev="0.9.5" conf="build,ide,tests->default" /> + rev="0.9.5" conf="build,ide,test->default" /> <dependency org="commons-cli" name="commons-cli" rev="1.2" - conf="build,ide,tests->default" /> + conf="build,ide,test->default" /> <!-- Provided build libs --> <dependency org="javax.servlet" name="servlet-api" @@ -41,7 +41,7 @@ <!-- Testing libs --> <dependency org="junit" name="junit" rev="4.5" - conf="tests -> default" /> + conf="test -> default" /> <!-- Internally used, for now --> <dependency org="com.carrotsearch" name="smartsprites" diff --git a/theme-compiler/ivymodule/smartsprites-ivy-0.2.3-itmill.xml b/theme-compiler/ivymodule/smartsprites-ivy-0.2.3-itmill.xml index f921bec167..29dc2d3474 100644 --- a/theme-compiler/ivymodule/smartsprites-ivy-0.2.3-itmill.xml +++ b/theme-compiler/ivymodule/smartsprites-ivy-0.2.3-itmill.xml @@ -31,7 +31,7 @@ <dependency org="com.google.collections" name="google-collections" rev="0.9" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)"/> <dependency org="args4j" name="args4j" rev="2.0.9" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)"/> <dependency org="commons-math" name="commons-math" rev="1.1" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)"/> - <dependency org="commons-io" name="commons-io" rev="1.4" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)"/> + <dependency org="commons-io" name="commons-io" rev="2.2" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)"/> <dependency org="commons-lang" name="commons-lang" rev="2.6" force="true" conf="compile->compile(*),master(*);runtime->runtime(*)"/> <dependency org="junit" name="junit" rev="4.4" force="true" conf="test->runtime(*),master(*)"/> </dependencies> diff --git a/theme-compiler/src/com/vaadin/sass/SassCompiler.java b/theme-compiler/src/com/vaadin/sass/SassCompiler.java index 48b2d24c46..6a83425ca1 100644 --- a/theme-compiler/src/com/vaadin/sass/SassCompiler.java +++ b/theme-compiler/src/com/vaadin/sass/SassCompiler.java @@ -17,7 +17,6 @@ package com.vaadin.sass; import java.io.File; -import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; @@ -49,12 +48,12 @@ public class SassCompiler { // ScssStylesheet.setStylesheetResolvers(new VaadinResolver()); ScssStylesheet scss = ScssStylesheet.get(input); - if(scss == null){ + if (scss == null) { System.err.println("The scss file " + input + " could not be found."); return; } - + scss.compile(); if (output == null) { System.out.println(scss.toString()); diff --git a/theme-compiler/src/com/vaadin/sass/internal/expression/ArithmeticExpressionEvaluator.java b/theme-compiler/src/com/vaadin/sass/internal/expression/ArithmeticExpressionEvaluator.java new file mode 100644 index 0000000000..7dbd8ae1a0 --- /dev/null +++ b/theme-compiler/src/com/vaadin/sass/internal/expression/ArithmeticExpressionEvaluator.java @@ -0,0 +1,139 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.sass.internal.expression; + +import static com.vaadin.sass.internal.parser.SCSSLexicalUnit.SCSS_VARIABLE; + +import java.util.Stack; + +import com.vaadin.sass.internal.expression.exception.ArithmeticException; +import com.vaadin.sass.internal.parser.LexicalUnitImpl; +import com.vaadin.sass.internal.parser.SCSSLexicalUnit; + +public class ArithmeticExpressionEvaluator { + private static ArithmeticExpressionEvaluator instance; + + public static ArithmeticExpressionEvaluator get() { + if (instance == null) { + instance = new ArithmeticExpressionEvaluator(); + } + return instance; + } + + private void createNewOperand(BinaryOperator operator, + Stack<Object> operands) { + Object rightOperand = operands.pop(); + operands.push(new BinaryExpression(operands.pop(), operator, + rightOperand)); + } + + public boolean containsArithmeticalOperator(LexicalUnitImpl term) { + LexicalUnitImpl current = term; + while (current != null) { + for (BinaryOperator operator : BinaryOperator.values()) { + /* + * '/' is treated as an arithmetical operator when one of its + * operands is Variable, or there is another binary operator. + * Otherwise, '/' is treated as a CSS operator. + */ + if (current.getLexicalUnitType() == operator.type) { + if (current.getLexicalUnitType() != BinaryOperator.DIV.type) { + return true; + } else { + if (current.getPreviousLexicalUnit() + .getLexicalUnitType() == SCSS_VARIABLE + || current.getNextLexicalUnit() + .getLexicalUnitType() == SCSS_VARIABLE) { + return true; + } + } + } + } + current = current.getNextLexicalUnit(); + } + return false; + } + + private Object createExpression(LexicalUnitImpl term) { + LexicalUnitImpl current = term; + boolean afterOperand = false; + Stack<Object> operands = new Stack<Object>(); + Stack<Object> operators = new Stack<Object>(); + inputTermLoop: while (current != null) { + if (afterOperand) { + if (current.getLexicalUnitType() == SCSSLexicalUnit.SCSS_OPERATOR_RIGHT_PAREN) { + Object operator = null; + while (!operators.isEmpty() + && ((operator = operators.pop()) != Parentheses.LEFT)) { + createNewOperand((BinaryOperator) operator, operands); + } + current = current.getNextLexicalUnit(); + continue; + } + afterOperand = false; + for (BinaryOperator operator : BinaryOperator.values()) { + if (current.getLexicalUnitType() == operator.type) { + while (!operators.isEmpty() + && (operators.peek() != Parentheses.LEFT) + && (((BinaryOperator) operators.peek()).precedence >= operator.precedence)) { + createNewOperand((BinaryOperator) operators.pop(), + operands); + } + operators.push(operator); + + current = current.getNextLexicalUnit(); + continue inputTermLoop; + } + } + throw new ArithmeticException(); + } + if (current.getLexicalUnitType() == SCSSLexicalUnit.SCSS_OPERATOR_LEFT_PAREN) { + operators.push(Parentheses.LEFT); + current = current.getNextLexicalUnit(); + continue; + } + afterOperand = true; + + operands.push(current); + current = current.getNextLexicalUnit(); + } + + while (!operators.isEmpty()) { + Object operator = operators.pop(); + if (operator == Parentheses.LEFT) { + throw new ArithmeticException("Unexpected \"(\" found"); + } + createNewOperand((BinaryOperator) operator, operands); + } + Object expression = operands.pop(); + if (!operands.isEmpty()) { + LexicalUnitImpl operand = (LexicalUnitImpl) operands.peek(); + throw new ArithmeticException("Unexpected operand " + + operand.toString() + " found"); + } + return expression; + } + + public LexicalUnitImpl evaluate(LexicalUnitImpl term) { + Object result = ArithmeticExpressionEvaluator.get().createExpression( + term); + if (result instanceof BinaryExpression) { + return ((BinaryExpression) result).eval(); + } + return term; + } +} diff --git a/theme-compiler/src/com/vaadin/sass/internal/expression/BinaryExpression.java b/theme-compiler/src/com/vaadin/sass/internal/expression/BinaryExpression.java new file mode 100644 index 0000000000..bfcdf6f506 --- /dev/null +++ b/theme-compiler/src/com/vaadin/sass/internal/expression/BinaryExpression.java @@ -0,0 +1,46 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.sass.internal.expression; + +import com.vaadin.sass.internal.parser.LexicalUnitImpl; + +public class BinaryExpression { + public Object leftOperand = null; + public BinaryOperator operator = null; + public Object rightOperand = null; + + public BinaryExpression(Object leftOperand, BinaryOperator operator, + Object rightOperand) { + this.leftOperand = leftOperand; + this.operator = operator; + this.rightOperand = rightOperand; + } + + public LexicalUnitImpl eval() { + LexicalUnitImpl leftValue = (leftOperand instanceof BinaryExpression) ? ((BinaryExpression) leftOperand) + .eval() : (LexicalUnitImpl) leftOperand; + LexicalUnitImpl rightValue = (rightOperand instanceof BinaryExpression) ? ((BinaryExpression) rightOperand) + .eval() : (LexicalUnitImpl) rightOperand; + return operator.eval(leftValue, rightValue); + } + + @Override + public String toString() { + return "(" + leftOperand + " " + operator.type + " " + rightOperand + + ")"; + } +} diff --git a/theme-compiler/src/com/vaadin/sass/internal/expression/BinaryOperator.java b/theme-compiler/src/com/vaadin/sass/internal/expression/BinaryOperator.java new file mode 100644 index 0000000000..15d3da797f --- /dev/null +++ b/theme-compiler/src/com/vaadin/sass/internal/expression/BinaryOperator.java @@ -0,0 +1,70 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.sass.internal.expression; + +import org.w3c.css.sac.LexicalUnit; + +import com.vaadin.sass.internal.parser.LexicalUnitImpl; + +public enum BinaryOperator { + ADD(LexicalUnit.SAC_OPERATOR_PLUS, 1) { + @Override + public LexicalUnitImpl eval(LexicalUnitImpl leftValue, + LexicalUnitImpl rightValue) { + return leftValue.add(rightValue); + } + }, + MINUS(LexicalUnit.SAC_OPERATOR_MINUS, 1) { + @Override + public LexicalUnitImpl eval(LexicalUnitImpl leftValue, + LexicalUnitImpl rightValue) { + return leftValue.minus(rightValue); + } + }, + MUL(LexicalUnit.SAC_OPERATOR_MULTIPLY, 2) { + @Override + public LexicalUnitImpl eval(LexicalUnitImpl leftValue, + LexicalUnitImpl rightValue) { + return leftValue.multiply(rightValue); + } + }, + DIV(LexicalUnit.SAC_OPERATOR_SLASH, 2) { + @Override + public LexicalUnitImpl eval(LexicalUnitImpl leftValue, + LexicalUnitImpl rightValue) { + return leftValue.divide(rightValue); + } + }, + MOD(LexicalUnit.SAC_OPERATOR_MOD, 2) { + @Override + public LexicalUnitImpl eval(LexicalUnitImpl leftValue, + LexicalUnitImpl rightValue) { + return leftValue.modulo(rightValue); + } + }; + + public final short type; + public final int precedence; + + BinaryOperator(short type, int precedence) { + this.type = type; + this.precedence = precedence; + } + + public abstract LexicalUnitImpl eval(LexicalUnitImpl leftValue, + LexicalUnitImpl rightValue); +} diff --git a/theme-compiler/src/com/vaadin/sass/internal/expression/Parentheses.java b/theme-compiler/src/com/vaadin/sass/internal/expression/Parentheses.java new file mode 100644 index 0000000000..5df8607aaf --- /dev/null +++ b/theme-compiler/src/com/vaadin/sass/internal/expression/Parentheses.java @@ -0,0 +1,21 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.sass.internal.expression; + +public enum Parentheses { + LEFT, RIGHT +} diff --git a/theme-compiler/src/com/vaadin/sass/internal/expression/exception/ArithmeticException.java b/theme-compiler/src/com/vaadin/sass/internal/expression/exception/ArithmeticException.java new file mode 100644 index 0000000000..13b6f0e936 --- /dev/null +++ b/theme-compiler/src/com/vaadin/sass/internal/expression/exception/ArithmeticException.java @@ -0,0 +1,26 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.sass.internal.expression.exception; + +public class ArithmeticException extends RuntimeException { + public ArithmeticException(String errorMsg) { + super(errorMsg); + } + + public ArithmeticException() { + super("Illegal arithmetic expression"); + } +} diff --git a/theme-compiler/src/com/vaadin/sass/internal/expression/exception/IncompatibleUnitsException.java b/theme-compiler/src/com/vaadin/sass/internal/expression/exception/IncompatibleUnitsException.java new file mode 100644 index 0000000000..bbeb0140f2 --- /dev/null +++ b/theme-compiler/src/com/vaadin/sass/internal/expression/exception/IncompatibleUnitsException.java @@ -0,0 +1,29 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.sass.internal.expression.exception; + +public class IncompatibleUnitsException extends ArithmeticException { + public IncompatibleUnitsException(String errorExpr) { + super(getErrorMsg(errorExpr)); + } + + private static String getErrorMsg(String errorExpr) { + StringBuilder builder = new StringBuilder(); + builder.append("Incompatible units found in: "); + builder.append("'").append(errorExpr).append("'"); + return builder.toString(); + } +} diff --git a/theme-compiler/src/com/vaadin/sass/internal/handler/SCSSDocumentHandler.java b/theme-compiler/src/com/vaadin/sass/internal/handler/SCSSDocumentHandler.java index 9dc6e33873..b9672b6c78 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/handler/SCSSDocumentHandler.java +++ b/theme-compiler/src/com/vaadin/sass/internal/handler/SCSSDocumentHandler.java @@ -96,4 +96,10 @@ public interface SCSSDocumentHandler extends DocumentHandler { void endKeyframeSelector(); + void contentDirective(); + + void startIncludeContentBlock(String name); + + void endIncludeContentBlock(); + } diff --git a/theme-compiler/src/com/vaadin/sass/internal/handler/SCSSDocumentHandlerImpl.java b/theme-compiler/src/com/vaadin/sass/internal/handler/SCSSDocumentHandlerImpl.java index d155d8522f..d77a404ae8 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/handler/SCSSDocumentHandlerImpl.java +++ b/theme-compiler/src/com/vaadin/sass/internal/handler/SCSSDocumentHandlerImpl.java @@ -30,6 +30,7 @@ import com.vaadin.sass.internal.ScssStylesheet; import com.vaadin.sass.internal.parser.LexicalUnitImpl; import com.vaadin.sass.internal.tree.BlockNode; import com.vaadin.sass.internal.tree.CommentNode; +import com.vaadin.sass.internal.tree.ContentNode; import com.vaadin.sass.internal.tree.ExtendNode; import com.vaadin.sass.internal.tree.FontFaceNode; import com.vaadin.sass.internal.tree.ForNode; @@ -365,4 +366,23 @@ public class SCSSDocumentHandlerImpl implements SCSSDocumentHandler { public void endKeyframeSelector() { nodeStack.pop(); } + + @Override + public void contentDirective() { + ContentNode node = new ContentNode(); + nodeStack.peek().appendChild(node); + } + + @Override + public void startIncludeContentBlock(String name) { + MixinNode node = new MixinNode(name); + nodeStack.peek().appendChild(node); + nodeStack.push(node); + + } + + @Override + public void endIncludeContentBlock() { + nodeStack.pop(); + } } diff --git a/theme-compiler/src/com/vaadin/sass/internal/parser/LexicalUnitImpl.java b/theme-compiler/src/com/vaadin/sass/internal/parser/LexicalUnitImpl.java index 7feeb6628a..498e1a941b 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/parser/LexicalUnitImpl.java +++ b/theme-compiler/src/com/vaadin/sass/internal/parser/LexicalUnitImpl.java @@ -27,6 +27,7 @@ import java.io.Serializable; import org.w3c.css.sac.LexicalUnit; +import com.vaadin.sass.internal.expression.exception.IncompatibleUnitsException; import com.vaadin.sass.internal.util.ColorUtil; import com.vaadin.sass.internal.util.DeepCopy; @@ -68,12 +69,14 @@ public class LexicalUnitImpl implements LexicalUnit, SCSSLexicalUnit, LexicalUnitImpl(int line, int column, LexicalUnitImpl previous, int i) { this(SAC_INTEGER, line, column, previous); this.i = i; + f = i; } LexicalUnitImpl(int line, int column, LexicalUnitImpl previous, short dimension, String sdimension, float f) { this(dimension, line, column, previous); this.f = f; + i = (int) f; this.dimension = dimension; this.sdimension = sdimension; } @@ -137,6 +140,7 @@ public class LexicalUnitImpl implements LexicalUnit, SCSSLexicalUnit, void setIntegerValue(int i) { this.i = i; + f = i; } @Override @@ -146,6 +150,7 @@ public class LexicalUnitImpl implements LexicalUnit, SCSSLexicalUnit, public void setFloatValue(float f) { this.f = f; + i = (int) f; } @Override @@ -364,28 +369,65 @@ public class LexicalUnitImpl implements LexicalUnit, SCSSLexicalUnit, @Override public LexicalUnitImpl divide(LexicalUnitImpl denominator) { - setFloatValue(getFloatValue() / denominator.getIntegerValue()); + if (denominator.getLexicalUnitType() != SAC_INTEGER + && denominator.getLexicalUnitType() != SAC_REAL + && getLexicalUnitType() != denominator.getLexicalUnitType()) { + throw new IncompatibleUnitsException(toString()); + } + setFloatValue(getFloatValue() / denominator.getFloatValue()); + if (getLexicalUnitType() == denominator.getLexicalUnitType()) { + setLexicalUnitType(SAC_REAL); + } + setNextLexicalUnit(denominator.getNextLexicalUnit()); return this; } @Override public LexicalUnitImpl add(LexicalUnitImpl another) { + checkAndSetUnit(another); setFloatValue(getFloatValue() + another.getFloatValue()); return this; } @Override public LexicalUnitImpl minus(LexicalUnitImpl another) { + checkAndSetUnit(another); setFloatValue(getFloatValue() - another.getFloatValue()); return this; } @Override public LexicalUnitImpl multiply(LexicalUnitImpl another) { + checkAndSetUnit(another); setFloatValue(getFloatValue() * another.getIntegerValue()); return this; } + protected void checkAndSetUnit(LexicalUnitImpl another) { + if (getLexicalUnitType() != SAC_INTEGER + && getLexicalUnitType() != SAC_REAL + && another.getLexicalUnitType() != SAC_INTEGER + && another.getLexicalUnitType() != SAC_REAL + && getLexicalUnitType() != another.getLexicalUnitType()) { + throw new IncompatibleUnitsException(toString()); + } + if (another.getLexicalUnitType() != SAC_INTEGER + && another.getLexicalUnitType() != SAC_REAL) { + setLexicalUnitType(another.getLexicalUnitType()); + } + setNextLexicalUnit(another.getNextLexicalUnit()); + } + + @Override + public LexicalUnitImpl modulo(LexicalUnitImpl another) { + if (getLexicalUnitType() != another.getLexicalUnitType()) { + throw new IncompatibleUnitsException(toString()); + } + setIntegerValue(getIntegerValue() % another.getIntegerValue()); + setNextLexicalUnit(another.getNextLexicalUnit()); + return this; + } + public void replaceValue(LexicalUnitImpl another) { // shouldn't modify 'another' directly, should only modify its copy. LexicalUnitImpl deepCopyAnother = (LexicalUnitImpl) DeepCopy @@ -470,16 +512,12 @@ public class LexicalUnitImpl implements LexicalUnit, SCSSLexicalUnit, return new LexicalUnitImpl(line, column, previous, SAC_EX, null, v); } - public static LexicalUnitImpl createPixel(float p) { - return new LexicalUnitImpl(0, 0, null, SAC_PIXEL, null, p); - } - - static LexicalUnitImpl createPX(int line, int column, + public static LexicalUnitImpl createPX(int line, int column, LexicalUnitImpl previous, float v) { return new LexicalUnitImpl(line, column, previous, SAC_PIXEL, null, v); } - static LexicalUnitImpl createCM(int line, int column, + public static LexicalUnitImpl createCM(int line, int column, LexicalUnitImpl previous, float v) { return new LexicalUnitImpl(line, column, previous, SAC_CENTIMETER, null, v); @@ -637,6 +675,39 @@ public class LexicalUnitImpl implements LexicalUnit, SCSSLexicalUnit, return new LexicalUnitImpl(SAC_OPERATOR_SLASH, line, column, previous); } + public static LexicalUnitImpl createAdd(int line, int column, + LexicalUnitImpl previous) { + return new LexicalUnitImpl(SAC_OPERATOR_PLUS, line, column, previous); + } + + public static LexicalUnitImpl createMinus(int line, int column, + LexicalUnitImpl previous) { + return new LexicalUnitImpl(SAC_OPERATOR_MINUS, line, column, previous); + } + + public static LexicalUnitImpl createMultiply(int line, int column, + LexicalUnitImpl previous) { + return new LexicalUnitImpl(SAC_OPERATOR_MULTIPLY, line, column, + previous); + } + + public static LexicalUnitImpl createModulo(int line, int column, + LexicalUnitImpl previous) { + return new LexicalUnitImpl(SAC_OPERATOR_MOD, line, column, previous); + } + + public static LexicalUnitImpl createLeftParenthesis(int line, int column, + LexicalUnitImpl previous) { + return new LexicalUnitImpl(SCSS_OPERATOR_LEFT_PAREN, line, column, + previous); + } + + public static LexicalUnitImpl createRightParenthesis(int line, int column, + LexicalUnitImpl previous) { + return new LexicalUnitImpl(SCSS_OPERATOR_LEFT_PAREN, line, column, + previous); + } + @Override public LexicalUnitImpl clone() { LexicalUnitImpl cloned = new LexicalUnitImpl(type, line, column, prev); diff --git a/theme-compiler/src/com/vaadin/sass/internal/parser/Parser.java b/theme-compiler/src/com/vaadin/sass/internal/parser/Parser.java index 981b9be2b9..d938dfefe8 100644..100755 --- a/theme-compiler/src/com/vaadin/sass/internal/parser/Parser.java +++ b/theme-compiler/src/com/vaadin/sass/internal/parser/Parser.java @@ -1,53 +1,39 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ /* Generated By:JavaCC: Do not edit this line. Parser.java */ package com.vaadin.sass.internal.parser; -import java.io.BufferedInputStream; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.Reader; -import java.net.URL; +import java.io.*; +import java.net.*; import java.util.ArrayList; import java.util.Locale; +import java.util.Map; import java.util.UUID; -import org.w3c.css.sac.CSSException; -import org.w3c.css.sac.CSSParseException; import org.w3c.css.sac.ConditionFactory; +import org.w3c.css.sac.Condition; +import org.w3c.css.sac.SelectorFactory; +import org.w3c.css.sac.SelectorList; +import org.w3c.css.sac.Selector; +import org.w3c.css.sac.SimpleSelector; import org.w3c.css.sac.DocumentHandler; -import org.w3c.css.sac.ErrorHandler; import org.w3c.css.sac.InputSource; -import org.w3c.css.sac.LexicalUnit; +import org.w3c.css.sac.ErrorHandler; +import org.w3c.css.sac.CSSException; +import org.w3c.css.sac.CSSParseException; import org.w3c.css.sac.Locator; -import org.w3c.css.sac.SelectorFactory; -import org.w3c.css.sac.SelectorList; -import org.w3c.flute.parser.selectors.ConditionFactoryImpl; +import org.w3c.css.sac.LexicalUnit; + import org.w3c.flute.parser.selectors.SelectorFactoryImpl; +import org.w3c.flute.parser.selectors.ConditionFactoryImpl; + import org.w3c.flute.util.Encoding; -import com.vaadin.sass.internal.handler.SCSSDocumentHandlerImpl; -import com.vaadin.sass.internal.tree.Node; -import com.vaadin.sass.internal.tree.VariableNode; +import com.vaadin.sass.internal.handler.*; + +import com.vaadin.sass.internal.tree.*; /** * A CSS2 parser - * + * * @author Philippe Le H�garet * @version $Revision: 1.15 $ */ @@ -78,14 +64,13 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { /** * @@TODO - * @exception CSSException - * Not yet implemented + * @exception CSSException Not yet implemented */ public void setLocale(Locale locale) throws CSSException { throw new CSSException(CSSException.SAC_NOT_SUPPORTED_ERR); } - public InputSource getInputSource() { + public InputSource getInputSource(){ return source; } @@ -93,7 +78,7 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { * Set the document handler for this parser */ public void setDocumentHandler(DocumentHandler handler) { - documentHandler = (SCSSDocumentHandlerImpl) handler; + this.documentHandler = (SCSSDocumentHandlerImpl) handler; } public void setSelectorFactory(SelectorFactory selectorFactory) { @@ -108,21 +93,18 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { * Set the error handler for this parser */ public void setErrorHandler(ErrorHandler error) { - errorHandler = error; + this.errorHandler = error; } /** * Main parse methods - * - * @param source - * the source of the style sheet. - * @exception IOException - * the source can't be parsed. - * @exception CSSException - * the source is not CSS valid. + * + * @param source the source of the style sheet. + * @exception IOException the source can't be parsed. + * @exception CSSException the source is not CSS valid. */ - public void parseStyleSheet(InputSource source) throws CSSException, - IOException { + public void parseStyleSheet(InputSource source) + throws CSSException, IOException { this.source = source; ReInit(getCharStreamWithLurk(source)); if (selectorFactory == null) { @@ -137,32 +119,25 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { /** * Convenient method for URIs. - * - * @param systemId - * the fully resolved URI of the style sheet. - * @exception IOException - * the source can't be parsed. - * @exception CSSException - * the source is not CSS valid. + * + * @param systemId the fully resolved URI of the style sheet. + * @exception IOException the source can't be parsed. + * @exception CSSException the source is not CSS valid. */ - public void parseStyleSheet(String systemId) throws CSSException, - IOException { + public void parseStyleSheet(String systemId) + throws CSSException, IOException { parseStyleSheet(new InputSource(systemId)); } /** - * This method parses only one rule (style rule or at-rule, except - * - * @charset). - * - * @param source - * the source of the rule. - * @exception IOException - * the source can't be parsed. - * @exception CSSException - * the source is not CSS valid. + * This method parses only one rule (style rule or at-rule, except @charset). + * + * @param source the source of the rule. + * @exception IOException the source can't be parsed. + * @exception CSSException the source is not CSS valid. */ - public void parseRule(InputSource source) throws CSSException, IOException { + public void parseRule(InputSource source) + throws CSSException, IOException { this.source = source; ReInit(getCharStreamWithLurk(source)); @@ -178,16 +153,13 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { /** * This method parses a style declaration (including the surrounding curly * braces). - * - * @param source - * the source of the style declaration. - * @exception IOException - * the source can't be parsed. - * @exception CSSException - * the source is not CSS valid. + * + * @param source the source of the style declaration. + * @exception IOException the source can't be parsed. + * @exception CSSException the source is not CSS valid. */ - public void parseStyleDeclaration(InputSource source) throws CSSException, - IOException { + public void parseStyleDeclaration(InputSource source) + throws CSSException, IOException { this.source = source; ReInit(getCharStreamWithLurk(source)); @@ -202,7 +174,6 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { /** * This methods returns "http://www.w3.org/TR/REC-CSS2". - * * @return the string "http://www.w3.org/TR/REC-CSS2". */ public String getParserVersion() { @@ -212,8 +183,8 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { /** * Parse methods used by DOM Level 2 implementation. */ - public void parseImportRule(InputSource source) throws CSSException, - IOException { + public void parseImportRule(InputSource source) + throws CSSException, IOException { this.source = source; ReInit(getCharStreamWithLurk(source)); @@ -226,8 +197,8 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { _parseImportRule(); } - public void parseMediaRule(InputSource source) throws CSSException, - IOException { + public void parseMediaRule(InputSource source) + throws CSSException, IOException { this.source = source; ReInit(getCharStreamWithLurk(source)); @@ -240,8 +211,8 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { _parseMediaRule(); } - public SelectorList parseSelectors(InputSource source) throws CSSException, - IOException { + public SelectorList parseSelectors(InputSource source) + throws CSSException, IOException { this.source = source; ReInit(getCharStreamWithLurk(source)); @@ -256,8 +227,8 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { return expr(); } - public boolean parsePriority(InputSource source) throws CSSException, - IOException { + public boolean parsePriority(InputSource source) + throws CSSException, IOException { this.source = source; ReInit(getCharStreamWithLurk(source)); @@ -265,8 +236,7 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { } /** - * Convert the source into a Reader. Used only by DOM Level 2 parser - * methods. + * Convert the source into a Reader. Used only by DOM Level 2 parser methods. */ private Reader getReader(InputSource source) throws IOException { if (source.getCharacterStream() != null) { @@ -278,7 +248,7 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { return new InputStreamReader(source.getByteStream(), "ASCII"); } else { return new InputStreamReader(source.getByteStream(), - source.getEncoding()); + source.getEncoding()); } } else { // systemId @@ -288,10 +258,11 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { } /** - * Convert the source into a CharStream with encoding informations. The - * encoding can be found in the InputSource or in the CSS document. Since - * this method marks the reader and make a reset after looking for the - * charset declaration, you'll find the charset declaration into the stream. + * Convert the source into a CharStream with encoding informations. + * The encoding can be found in the InputSource or in the CSS document. + * Since this method marks the reader and make a reset after looking for + * the charset declaration, you'll find the charset declaration into the + * stream. */ private CharStream getCharStreamWithLurk(InputSource source) throws CSSException, IOException { @@ -311,7 +282,7 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { } } } - // use UTF-8 as the default encoding. + //use UTF-8 as the default encoding. String encoding = source.getEncoding(); InputStream input = source.getByteStream(); if (!input.markSupported()) { @@ -321,7 +292,7 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { } // Mark either the original stream or the wrapped stream input.mark(100); - if (encoding == null) { + if(encoding == null){ encoding = "ASCII"; char c = ' '; @@ -330,15 +301,14 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { if (c == '@') { // hum, is it a charset ? - int size = 100; + int size = 100; byte[] buf = new byte[size]; input.read(buf, 0, 7); String keyword = new String(buf, 0, 7); if (keyword.equals("charset")) { // Yes, this is the charset declaration ! - // here I don't use the right declaration : white space are - // ' '. + // here I don't use the right declaration : white space are ' '. while ((c = (char) input.read()) == ' ') { // find the first quote } @@ -365,17 +335,15 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { if (c != ';') { // no semi colon at the end ? throw new CSSException("invalid charset declaration: " - + "missing semi colon"); + + "missing semi colon"); } encoding = new String(buf, 0, i); if (source.getEncoding() != null) { // compare the two encoding informations. - // For example, I don't accept to have ASCII and after - // UTF-8. + // For example, I don't accept to have ASCII and after UTF-8. // Is it really good ? That is the question. if (!encoding.equals(source.getEncoding())) { - throw new CSSException( - "invalid encoding information."); + throw new CSSException("invalid encoding information."); } } } // else no charset declaration available @@ -385,7 +353,7 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { source.setEncoding(encoding); // set the real reader of this source. source.setCharacterStream(new InputStreamReader(source.getByteStream(), - Encoding.getJavaEncoding(encoding))); + Encoding.getJavaEncoding(encoding))); // reset the stream (leave the charset declaration in the stream). input.reset(); @@ -393,7 +361,6 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { } private LocatorImpl currentLocator; - private Locator getLocator() { if (currentLocator == null) { currentLocator = new LocatorImpl(this); @@ -401,7 +368,6 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { } return currentLocator.reInit(this); } - private LocatorImpl getLocator(Token save) { if (currentLocator == null) { currentLocator = new LocatorImpl(this, save); @@ -418,8 +384,8 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { if (pe.specialConstructor) { StringBuffer errorM = new StringBuffer(); if (pe.currentToken != null) { - errorM.append("encountered \u005c"").append( - pe.currentToken.next); + errorM.append("encountered \u005c"") + .append(pe.currentToken.next); } errorM.append('"'); if (pe.expectedTokenSequences.length != 0) { @@ -435,10 +401,10 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { } } errorHandler.error(new CSSParseException(errorM.toString(), - l, e)); + l, e)); } else { - errorHandler.error(new CSSParseException(e.getMessage(), l, - e)); + errorHandler.error(new CSSParseException(e.getMessage(), + l, e)); } } else if (e == null) { errorHandler.error(new CSSParseException("error", l, null)); @@ -449,2369 +415,509 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { } private void reportWarningSkipText(Locator l, String text) { - if (errorHandler != null && text != null) { + if (errorHandler != null && text != null) { errorHandler.warning(new CSSParseException("Skipping: " + text, l)); } } - /* - * The grammar of CSS2 - */ - - /** - * The main entry for the parser. - * - * @exception ParseException - * exception during the parse - */ - final public void parserUnit() throws ParseException { - try { - documentHandler.startDocument(source); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case CHARSET_SYM: - charset(); - break; - default: - jj_la1[0] = jj_gen; - ; - } - label_1: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - case CDO: - case CDC: - case ATKEYWORD: - ; - break; - default: - jj_la1[1] = jj_gen; - break label_1; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - jj_consume_token(S); - comments(); - break; - case CDO: - case CDC: - case ATKEYWORD: - ignoreStatement(); - break; - default: - jj_la1[2] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - } - label_2: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case IMPORT_SYM: - ; - break; - default: - jj_la1[3] = jj_gen; - break label_2; - } - importDeclaration(); - label_3: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case CDO: - case CDC: - case ATKEYWORD: - ; - break; - default: - jj_la1[4] = jj_gen; - break label_3; - } - ignoreStatement(); - label_4: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[5] = jj_gen; - break label_4; - } - jj_consume_token(S); - } - } - } - afterImportDeclaration(); - jj_consume_token(0); - } finally { - documentHandler.endDocument(source); - } - } - - final public void charset() throws ParseException { - Token n; - try { - jj_consume_token(CHARSET_SYM); - label_5: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[6] = jj_gen; - break label_5; - } - jj_consume_token(S); - } - n = jj_consume_token(STRING); - label_6: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[7] = jj_gen; - break label_6; - } - jj_consume_token(S); - } - jj_consume_token(SEMICOLON); - } catch (ParseException e) { - reportError(getLocator(e.currentToken.next), e); - skipStatement(); - // reportWarningSkipText(getLocator(), skipStatement()); - - } catch (Exception e) { - reportError(getLocator(), e); - skipStatement(); - // reportWarningSkipText(getLocator(), skipStatement()); - - } - } - - final public void afterImportDeclaration() throws ParseException { - String ret; - Locator l; - label_7: while (true) { - ; - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case DEBUG_SYM: - case WARN_SYM: - debuggingDirective(); - break; - case MIXIN_SYM: - mixinDirective(); - break; - case EACH_SYM: - case IF_SYM: - controlDirective(); - break; - case INCLUDE_SYM: - includeDirective(); - break; - case PLUS: - case PRECEDES: - case SIBLING: - case LBRACKET: - case ANY: - case PARENT: - case DOT: - case COLON: - case INTERPOLATION: - case IDENT: - case HASH: - styleRule(); - break; - case MEDIA_SYM: - media(); - break; - case PAGE_SYM: - page(); - break; - case FONT_FACE_SYM: - fontFace(); - break; - case KEY_FRAME_SYM: - keyframes(); - break; - default: - jj_la1[8] = jj_gen; - if (jj_2_1(2147483647)) { - variable(); - } else { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case VARIABLE: - listModifyDirective(); - break; - default: - jj_la1[9] = jj_gen; - l = getLocator(); - ret = skipStatement(); - if ((ret == null) || (ret.length() == 0)) { - { - if (true) { - return; - } - } - } - if (ret.charAt(0) == '@') { - documentHandler.unrecognizedRule(ret); - } else { - reportWarningSkipText(l, ret); - } - } - } - } - label_8: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case CDO: - case CDC: - case ATKEYWORD: - ; - break; - default: - jj_la1[10] = jj_gen; - break label_8; - } - ignoreStatement(); - label_9: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[11] = jj_gen; - break label_9; - } - jj_consume_token(S); - } - } - } - } +/* + * The grammar of CSS2 + */ - final public void ignoreStatement() throws ParseException { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { +/** + * The main entry for the parser. + * + * @exception ParseException exception during the parse + */ + final public void parserUnit() throws ParseException { + try { + documentHandler.startDocument(source); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case CHARSET_SYM: + charset(); + break; + default: + jj_la1[0] = jj_gen; + ; + } + label_1: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: case CDO: - jj_consume_token(CDO); - break; case CDC: - jj_consume_token(CDC); - break; case ATKEYWORD: - atRuleDeclaration(); - break; + ; + break; default: - jj_la1[12] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - } - - /** - * The import statement - * - * @exception ParseException - * exception during the parse - */ - final public void importDeclaration() throws ParseException { - Token n; - String uri; - MediaListImpl ml = new MediaListImpl(); - boolean isURL = false; - try { - jj_consume_token(IMPORT_SYM); - label_10: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[13] = jj_gen; - break label_10; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case STRING: - n = jj_consume_token(STRING); - uri = convertStringIndex(n.image, 1, n.image.length() - 1); - break; - case URL: - n = jj_consume_token(URL); - isURL = true; - uri = n.image.substring(4, n.image.length() - 1).trim(); - if ((uri.charAt(0) == '"') || (uri.charAt(0) == '\u005c'')) { - uri = uri.substring(1, uri.length() - 1); - } - break; - default: - jj_la1[14] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - label_11: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[15] = jj_gen; - break label_11; - } - jj_consume_token(S); - } - mediaStatement(ml); - jj_consume_token(SEMICOLON); - label_12: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[16] = jj_gen; - break label_12; - } - jj_consume_token(S); - } - if (ml.getLength() == 0) { - // see section 6.3 of the CSS2 recommandation. - ml.addItem("all"); - } - documentHandler.importStyle(uri, ml, isURL); - } catch (ParseException e) { - reportError(getLocator(), e); - skipStatement(); - // reportWarningSkipText(getLocator(), skipStatement()); - - } - } - - /** - * @exception ParseException - * exception during the parse - */ - final public void keyframes() throws ParseException { - Token n; - boolean start = false; - String keyframeName = null; - String animationname = ""; - try { - n = jj_consume_token(KEY_FRAME_SYM); - label_13: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[17] = jj_gen; - break label_13; - } - jj_consume_token(S); - } - keyframeName = n.image; - label_14: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case IDENT: - n = jj_consume_token(IDENT); - animationname += n.image; - break; - case INTERPOLATION: - n = jj_consume_token(INTERPOLATION); - animationname += n.image; - break; - default: - jj_la1[18] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case INTERPOLATION: - case IDENT: - ; - break; - default: - jj_la1[19] = jj_gen; - break label_14; - } - } - label_15: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[20] = jj_gen; - break label_15; - } - jj_consume_token(S); - } - start = true; - documentHandler.startKeyFrames(keyframeName, animationname); - jj_consume_token(LBRACE); - label_16: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[21] = jj_gen; - break label_16; - } - jj_consume_token(S); - } - label_17: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case TO: - case FROM: - case PERCENTAGE: - ; - break; - default: - jj_la1[22] = jj_gen; - break label_17; - } - keyframeSelector(); - } - jj_consume_token(RBRACE); - label_18: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[23] = jj_gen; - break label_18; - } - jj_consume_token(S); - } - } catch (ParseException e) { - reportError(getLocator(), e); - skipStatement(); - } finally { - if (start) { - documentHandler.endKeyFrames(); - } - } - } - - final public void keyframeSelector() throws ParseException { - Token n; - boolean start = false; - try { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case FROM: - n = jj_consume_token(FROM); - break; - case TO: - n = jj_consume_token(TO); - break; - case PERCENTAGE: - n = jj_consume_token(PERCENTAGE); - break; - default: - jj_la1[24] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - label_19: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[25] = jj_gen; - break label_19; - } - jj_consume_token(S); - } - jj_consume_token(LBRACE); - label_20: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[26] = jj_gen; - break label_20; - } - jj_consume_token(S); - } - start = true; - documentHandler.startKeyframeSelector(n.image); - label_21: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case PRECEDES: - case SIBLING: - case LBRACKET: - case ANY: - case PARENT: - case DOT: - case COLON: - case INTERPOLATION: - case INCLUDE_SYM: - case DEBUG_SYM: - case WARN_SYM: - case EACH_SYM: - case IF_SYM: - case EXTEND_SYM: - case MICROSOFT_RULE: - case IDENT: - case VARIABLE: - case HASH: - case MEDIA_SYM: - case KEY_FRAME_SYM: - ; - break; - default: - jj_la1[27] = jj_gen; - break label_21; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case PRECEDES: - case SIBLING: - case LBRACKET: - case ANY: - case PARENT: - case DOT: - case COLON: - case INTERPOLATION: - case INCLUDE_SYM: - case DEBUG_SYM: - case WARN_SYM: - case EXTEND_SYM: - case IDENT: - case VARIABLE: - case HASH: - case MEDIA_SYM: - case KEY_FRAME_SYM: - ifContentStatement(); - break; - case EACH_SYM: - case IF_SYM: - controlDirective(); - break; - case MICROSOFT_RULE: - microsoftExtension(); - break; - default: - jj_la1[28] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - } - jj_consume_token(RBRACE); - label_22: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[29] = jj_gen; - break label_22; - } - jj_consume_token(S); - } - } catch (ThrowedParseException e) { - if (errorHandler != null) { - LocatorImpl li = new LocatorImpl(this, - e.e.currentToken.next.beginLine, - e.e.currentToken.next.beginColumn - 1); - reportError(li, e.e); - } - } catch (ParseException e) { - reportError(getLocator(), e); - skipStatement(); - // reportWarningSkipText(getLocator(), skipStatement()); - - } catch (TokenMgrError e) { - reportWarningSkipText(getLocator(), skipStatement()); - } finally { - if (start) { - documentHandler.endKeyframeSelector(); - } - } - } - - /** - * @exception ParseException - * exception during the parse - */ - /* see http://www.w3.org/TR/css3-mediaqueries/ */ - final public void media() throws ParseException { - boolean start = false; - String ret; - MediaListImpl ml = new MediaListImpl(); - try { - jj_consume_token(MEDIA_SYM); - label_23: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[30] = jj_gen; - break label_23; - } - jj_consume_token(S); - } - mediaStatement(ml); - start = true; - documentHandler.startMedia(ml); - jj_consume_token(LBRACE); - label_24: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[31] = jj_gen; - break label_24; - } - jj_consume_token(S); - } - label_25: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case CDO: - case LBRACE: - case DASHMATCH: - case INCLUDES: - case PLUS: - case MINUS: - case COMMA: - case SEMICOLON: - case PRECEDES: - case SIBLING: - case LBRACKET: - case ANY: - case PARENT: - case DOT: - case COLON: - case INTERPOLATION: - case NONASCII: - case DEBUG_SYM: - case WARN_SYM: - case STRING: - case IDENT: - case NUMBER: - case URL: - case PERCENTAGE: - case HASH: - case IMPORT_SYM: - case MEDIA_SYM: - case CHARSET_SYM: - case PAGE_SYM: - case FONT_FACE_SYM: - case ATKEYWORD: - case IMPORTANT_SYM: - case UNICODERANGE: - case FUNCTION: - case UNKNOWN: - ; - break; - default: - jj_la1[32] = jj_gen; - break label_25; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case DEBUG_SYM: - case WARN_SYM: - debuggingDirective(); - break; - case PLUS: - case PRECEDES: - case SIBLING: - case LBRACKET: - case ANY: - case PARENT: - case DOT: - case COLON: - case INTERPOLATION: - case IDENT: - case HASH: - styleRule(); - break; - case CDO: - case LBRACE: - case DASHMATCH: - case INCLUDES: - case MINUS: - case COMMA: - case SEMICOLON: - case NONASCII: - case STRING: - case NUMBER: - case URL: - case PERCENTAGE: - case IMPORT_SYM: - case MEDIA_SYM: - case CHARSET_SYM: - case PAGE_SYM: - case FONT_FACE_SYM: - case ATKEYWORD: - case IMPORTANT_SYM: - case UNICODERANGE: - case FUNCTION: - case UNKNOWN: - skipUnknownRule(); - break; - default: - jj_la1[33] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - } - jj_consume_token(RBRACE); - label_26: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[34] = jj_gen; - break label_26; - } - jj_consume_token(S); - } - } catch (ParseException e) { - reportError(getLocator(), e); - skipStatement(); - // reportWarningSkipText(getLocator(), skipStatement()); - - } finally { - if (start) { - documentHandler.endMedia(ml); - } - } - } - - final public void mediaStatement(MediaListImpl ml) throws ParseException { - Token t; - t = getToken(1); - // loop over comma separated parts, add each to ml - while ((t.kind != LBRACE) && (t.kind != EOF) && (t.kind != SEMICOLON)) { - StringBuffer s = new StringBuffer(); - s.append(getToken(0).image); - while ((t.kind != COMMA) && (t.kind != LBRACE) && (t.kind != EOF) - && (t.kind != SEMICOLON)) { - s.append(t.image); - getNextToken(); - t = getToken(1); - } - if (t.kind == COMMA) { - // skip the comma and the token before it that is still the - // active token - getNextToken(); - getNextToken(); - t = getToken(1); - } - String str = s.toString().trim(); - if (str.length() > 0) { - ml.addItem(str); - } + jj_la1[1] = jj_gen; + break label_1; } - } - - /** - * @exception ParseException - * exception during the parse - */ - final public String medium() throws ParseException { - Token n; - n = jj_consume_token(IDENT); - { - if (true) { - return convertIdent(n.image); - } - } - throw new Error("Missing return statement in function"); - } - - /** - * @exception ParseException - * exception during the parse - */ - final public void page() throws ParseException { - boolean start = false; - Token n = null; - String page = null; - String pseudo = null; - try { - jj_consume_token(PAGE_SYM); - label_27: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[35] = jj_gen; - break label_27; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case IDENT: - n = jj_consume_token(IDENT); - label_28: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[36] = jj_gen; - break label_28; - } - jj_consume_token(S); - } - break; - default: - jj_la1[37] = jj_gen; - ; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case COLON: - pseudo = pseudo_page(); - break; - default: - jj_la1[38] = jj_gen; - ; - } - if (n != null) { - page = convertIdent(n.image); - } - jj_consume_token(LBRACE); - label_29: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[39] = jj_gen; - break label_29; - } - jj_consume_token(S); - } - start = true; - documentHandler.startPage(page, pseudo); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case INTERPOLATION: - case IDENT: - declaration(); - break; - default: - jj_la1[40] = jj_gen; - ; - } - label_30: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case SEMICOLON: - ; - break; - default: - jj_la1[41] = jj_gen; - break label_30; - } - jj_consume_token(SEMICOLON); - label_31: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[42] = jj_gen; - break label_31; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case INTERPOLATION: - case IDENT: - declaration(); - break; - default: - jj_la1[43] = jj_gen; - ; - } - } - jj_consume_token(RBRACE); - label_32: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[44] = jj_gen; - break label_32; - } - jj_consume_token(S); - } - } catch (ParseException e) { - if (errorHandler != null) { - LocatorImpl li = new LocatorImpl(this, - e.currentToken.next.beginLine, - e.currentToken.next.beginColumn - 1); - reportError(li, e); - skipStatement(); - // reportWarningSkipText(li, skipStatement()); - } else { - skipStatement(); - } - } finally { - if (start) { - documentHandler.endPage(page, pseudo); - } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + jj_consume_token(S); + comments(); + break; + case CDO: + case CDC: + case ATKEYWORD: + ignoreStatement(); + break; + default: + jj_la1[2] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + label_2: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IMPORT_SYM: + ; + break; + default: + jj_la1[3] = jj_gen; + break label_2; } - } - - final public String pseudo_page() throws ParseException { - Token n; - jj_consume_token(COLON); - n = jj_consume_token(IDENT); - label_33: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + importDeclaration(); + label_3: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case CDO: + case CDC: + case ATKEYWORD: + ; + break; + default: + jj_la1[4] = jj_gen; + break label_3; + } + ignoreStatement(); + label_4: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case S: - ; - break; + ; + break; default: - jj_la1[45] = jj_gen; - break label_33; + jj_la1[5] = jj_gen; + break label_4; } jj_consume_token(S); - } - { - if (true) { - return convertIdent(n.image); - } - } - throw new Error("Missing return statement in function"); - } - - final public void fontFace() throws ParseException { - boolean start = false; - try { - jj_consume_token(FONT_FACE_SYM); - label_34: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[46] = jj_gen; - break label_34; - } - jj_consume_token(S); - } - jj_consume_token(LBRACE); - label_35: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[47] = jj_gen; - break label_35; - } - jj_consume_token(S); - } - start = true; - documentHandler.startFontFace(); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case INTERPOLATION: - case IDENT: - declaration(); - break; - default: - jj_la1[48] = jj_gen; - ; - } - label_36: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case SEMICOLON: - ; - break; - default: - jj_la1[49] = jj_gen; - break label_36; - } - jj_consume_token(SEMICOLON); - label_37: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[50] = jj_gen; - break label_37; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case INTERPOLATION: - case IDENT: - declaration(); - break; - default: - jj_la1[51] = jj_gen; - ; - } - } - jj_consume_token(RBRACE); - label_38: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[52] = jj_gen; - break label_38; - } - jj_consume_token(S); - } - } catch (ParseException e) { - reportError(getLocator(), e); - skipStatement(); - // reportWarningSkipText(getLocator(), skipStatement()); - - } finally { - if (start) { - documentHandler.endFontFace(); - } - } - } - - /** - * @exception ParseException - * exception during the parse - */ - final public void atRuleDeclaration() throws ParseException { - Token n; - String ret; - n = jj_consume_token(ATKEYWORD); - ret = skipStatement(); - if ((ret != null) && (ret.charAt(0) == '@')) { - documentHandler.unrecognizedRule(ret); + } + } + } + afterImportDeclaration(); + jj_consume_token(0); + } finally { + documentHandler.endDocument(source); + } + } + + final public void charset() throws ParseException { + Token n; + try { + jj_consume_token(CHARSET_SYM); + label_5: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[6] = jj_gen; + break label_5; + } + jj_consume_token(S); + } + n = jj_consume_token(STRING); + label_6: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[7] = jj_gen; + break label_6; + } + jj_consume_token(S); + } + jj_consume_token(SEMICOLON); + } catch (ParseException e) { + reportError(getLocator(e.currentToken.next), e); + skipStatement(); + // reportWarningSkipText(getLocator(), skipStatement()); + + } catch (Exception e) { + reportError(getLocator(), e); + skipStatement(); + // reportWarningSkipText(getLocator(), skipStatement()); + + } + } + + final public void afterImportDeclaration() throws ParseException { + String ret; + Locator l; + label_7: + while (true) { + ; + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case DEBUG_SYM: + case WARN_SYM: + debuggingDirective(); + break; + case MIXIN_SYM: + mixinDirective(); + break; + case EACH_SYM: + case IF_SYM: + controlDirective(); + break; + case INCLUDE_SYM: + includeDirective(); + break; + case PLUS: + case PRECEDES: + case SIBLING: + case LBRACKET: + case ANY: + case PARENT: + case DOT: + case COLON: + case INTERPOLATION: + case IDENT: + case HASH: + styleRule(); + break; + case MEDIA_SYM: + media(); + break; + case PAGE_SYM: + page(); + break; + case FONT_FACE_SYM: + fontFace(); + break; + case KEY_FRAME_SYM: + keyframes(); + break; + default: + jj_la1[8] = jj_gen; + if (jj_2_1(2147483647)) { + variable(); } else { - reportWarningSkipText(getLocator(), ret); - } - } - - final public void skipUnknownRule() throws ParseException { - Token n; - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case ATKEYWORD: - n = jj_consume_token(ATKEYWORD); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case VARIABLE: + listModifyDirective(); break; + default: + jj_la1[9] = jj_gen; + l = getLocator(); + ret = skipStatement(); + if ((ret == null) || (ret.length() == 0)) { + {if (true) return;} + } + if (ret.charAt(0) == '@') { + documentHandler.unrecognizedRule(ret); + } else { + reportWarningSkipText(l, ret); + } + } + } + } + label_8: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case CDO: - n = jj_consume_token(CDO); - break; - case CHARSET_SYM: - n = jj_consume_token(CHARSET_SYM); - break; - case COMMA: - n = jj_consume_token(COMMA); - break; - case DASHMATCH: - n = jj_consume_token(DASHMATCH); - break; - case FONT_FACE_SYM: - n = jj_consume_token(FONT_FACE_SYM); - break; - case FUNCTION: - n = jj_consume_token(FUNCTION); - break; - case IMPORTANT_SYM: - n = jj_consume_token(IMPORTANT_SYM); - break; - case IMPORT_SYM: - n = jj_consume_token(IMPORT_SYM); - break; - case INCLUDES: - n = jj_consume_token(INCLUDES); - break; - case LBRACE: - n = jj_consume_token(LBRACE); - break; - case MEDIA_SYM: - n = jj_consume_token(MEDIA_SYM); - break; - case NONASCII: - n = jj_consume_token(NONASCII); - break; - case NUMBER: - n = jj_consume_token(NUMBER); - break; - case PAGE_SYM: - n = jj_consume_token(PAGE_SYM); - break; - case PERCENTAGE: - n = jj_consume_token(PERCENTAGE); - break; - case STRING: - n = jj_consume_token(STRING); - break; - case UNICODERANGE: - n = jj_consume_token(UNICODERANGE); - break; - case URL: - n = jj_consume_token(URL); - break; - case SEMICOLON: - n = jj_consume_token(SEMICOLON); - break; - case MINUS: - n = jj_consume_token(MINUS); - break; - case UNKNOWN: - n = jj_consume_token(UNKNOWN); - break; + case CDC: + case ATKEYWORD: + ; + break; default: - jj_la1[53] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - String ret; - Locator loc = getLocator(); - ret = skipStatement(); - if ((ret != null) && (n.image.charAt(0) == '@')) { - documentHandler.unrecognizedRule(ret); - } else { - reportWarningSkipText(loc, ret); + jj_la1[10] = jj_gen; + break label_8; } - } - - /** - * @exception ParseException - * exception during the parse - */ - final public char combinator() throws ParseException { - char connector = ' '; - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case PRECEDES: - case SIBLING: - connector = combinatorChar(); + ignoreStatement(); + label_9: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; break; + default: + jj_la1[11] = jj_gen; + break label_9; + } + jj_consume_token(S); + } + } + } + } + + final public void ignoreStatement() throws ParseException { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case CDO: + jj_consume_token(CDO); + break; + case CDC: + jj_consume_token(CDC); + break; + case ATKEYWORD: + atRuleDeclaration(); + break; + default: + jj_la1[12] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + +/** + * The import statement + * + * @exception ParseException exception during the parse + */ + final public void importDeclaration() throws ParseException { + Token n; + String uri; + MediaListImpl ml = new MediaListImpl(); + boolean isURL = false; + try { + jj_consume_token(IMPORT_SYM); + label_10: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case S: - jj_consume_token(S); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case PRECEDES: - case SIBLING: - connector = combinatorChar(); - break; - default: - jj_la1[54] = jj_gen; - ; - } - break; + ; + break; default: - jj_la1[55] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - { - if (true) { - return connector; - } - } - throw new Error("Missing return statement in function"); - } - - /** to refactor combinator and reuse in selector(). */ - final public char combinatorChar() throws ParseException { - Token t; - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - t = jj_consume_token(PLUS); - break; - case PRECEDES: - t = jj_consume_token(PRECEDES); - break; - case SIBLING: - t = jj_consume_token(SIBLING); - break; + jj_la1[13] = jj_gen; + break label_10; + } + jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case STRING: + n = jj_consume_token(STRING); + uri = convertStringIndex(n.image, 1, + n.image.length() -1); + break; + case URL: + n = jj_consume_token(URL); + isURL=true; + uri = n.image.substring(4, n.image.length()-1).trim(); + if ((uri.charAt(0) == '"') + || (uri.charAt(0) == '\u005c'')) { + uri = uri.substring(1, uri.length()-1); + } + break; + default: + jj_la1[14] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + label_11: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; default: - jj_la1[56] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - label_39: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[57] = jj_gen; - break label_39; - } - jj_consume_token(S); - } - { - if (true) { - return t.image.charAt(0); - } - } - throw new Error("Missing return statement in function"); - } - - final public void microsoftExtension() throws ParseException { - Token n; - String name = ""; - String value = ""; - // This is not really taking the syntax of filter rules into account - n = jj_consume_token(MICROSOFT_RULE); - label_40: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[58] = jj_gen; - break label_40; - } - jj_consume_token(S); - } - name = n.image; - jj_consume_token(COLON); - label_41: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case IDENT: - n = jj_consume_token(IDENT); - value += n.image; - break; - case NUMBER: - n = jj_consume_token(NUMBER); - value += n.image; - break; - case STRING: - n = jj_consume_token(STRING); - value += n.image; - break; - case COMMA: - n = jj_consume_token(COMMA); - value += n.image; - break; - case INTERPOLATION: - n = jj_consume_token(INTERPOLATION); - value += n.image; - break; - case COLON: - n = jj_consume_token(COLON); - value += n.image; - break; - case FUNCTION: - n = jj_consume_token(FUNCTION); - value += n.image; - break; - case RPARAN: - n = jj_consume_token(RPARAN); - value += n.image; - break; - case EQ: - n = jj_consume_token(EQ); - value += n.image; - break; - case DOT: - n = jj_consume_token(DOT); - value += n.image; - break; - case S: - n = jj_consume_token(S); - if (value.lastIndexOf(' ') != value.length() - 1) { - value += n.image; - } - break; - default: - jj_la1[59] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - case EQ: - case COMMA: - case DOT: - case RPARAN: - case COLON: - case INTERPOLATION: - case STRING: - case IDENT: - case NUMBER: - case FUNCTION: - ; - break; - default: - jj_la1[60] = jj_gen; - break label_41; - } - } - jj_consume_token(SEMICOLON); - label_42: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[61] = jj_gen; - break label_42; - } - jj_consume_token(S); - } - documentHandler.microsoftDirective(name, value); - } - - /** - * @exception ParseException - * exception during the parse - */ - final public String property() throws ParseException { - Token t; - String s = ""; - label_43: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case IDENT: - t = jj_consume_token(IDENT); - s += t.image; - break; - case INTERPOLATION: - t = jj_consume_token(INTERPOLATION); - s += t.image; - break; - default: - jj_la1[62] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case INTERPOLATION: - case IDENT: - ; - break; - default: - jj_la1[63] = jj_gen; - break label_43; - } - } - label_44: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[64] = jj_gen; - break label_44; - } - jj_consume_token(S); - } - { - if (true) { - return s; - } - } - throw new Error("Missing return statement in function"); - } - - final public String variableName() throws ParseException { - Token n; - n = jj_consume_token(VARIABLE); - label_45: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[65] = jj_gen; - break label_45; - } - jj_consume_token(S); - } - { - if (true) { - return convertIdent(n.image.substring(1)); - } - } - throw new Error("Missing return statement in function"); - } - - final public String functionName() throws ParseException { - Token n; - n = jj_consume_token(FUNCTION); - label_46: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[66] = jj_gen; - break label_46; - } - jj_consume_token(S); - } - { - if (true) { - return convertIdent(n.image.substring(0, n.image.length() - 1)); - } - } - throw new Error("Missing return statement in function"); - } - - /** - * @exception ParseException - * exception during the parse - */ - final public void styleRule() throws ParseException { - boolean start = false; - ArrayList<String> l = null; - Token save; - Locator loc; - try { - l = selectorList(); - save = token; - jj_consume_token(LBRACE); - label_47: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[67] = jj_gen; - break label_47; - } - jj_consume_token(S); - } - start = true; - documentHandler.startSelector(l); - label_48: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case PRECEDES: - case SIBLING: - case LBRACKET: - case ANY: - case PARENT: - case DOT: - case COLON: - case INTERPOLATION: - case INCLUDE_SYM: - case DEBUG_SYM: - case WARN_SYM: - case EACH_SYM: - case IF_SYM: - case EXTEND_SYM: - case MICROSOFT_RULE: - case IDENT: - case VARIABLE: - case HASH: - case MEDIA_SYM: - case KEY_FRAME_SYM: - ; - break; - default: - jj_la1[68] = jj_gen; - break label_48; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case PRECEDES: - case SIBLING: - case LBRACKET: - case ANY: - case PARENT: - case DOT: - case COLON: - case INTERPOLATION: - case INCLUDE_SYM: - case DEBUG_SYM: - case WARN_SYM: - case EXTEND_SYM: - case IDENT: - case VARIABLE: - case HASH: - case MEDIA_SYM: - case KEY_FRAME_SYM: - ifContentStatement(); - break; - case EACH_SYM: - case IF_SYM: - controlDirective(); - break; - case MICROSOFT_RULE: - microsoftExtension(); - break; - default: - jj_la1[69] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - } - jj_consume_token(RBRACE); - label_49: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[70] = jj_gen; - break label_49; - } - jj_consume_token(S); - } - } catch (ThrowedParseException e) { - if (errorHandler != null) { - LocatorImpl li = new LocatorImpl(this, - e.e.currentToken.next.beginLine, - e.e.currentToken.next.beginColumn - 1); - reportError(li, e.e); - } - } catch (ParseException e) { - reportError(getLocator(), e); - skipStatement(); - // reportWarningSkipText(getLocator(), skipStatement()); - - } catch (TokenMgrError e) { - reportWarningSkipText(getLocator(), skipStatement()); - } finally { - if (start) { - documentHandler.endSelector(); - } - } - } - - final public ArrayList<String> selectorList() throws ParseException { - ArrayList<String> selectors = new ArrayList<String>(); - String selector; - selector = selector(); - label_50: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case COMMA: - ; - break; - default: - jj_la1[71] = jj_gen; - break label_50; - } - jj_consume_token(COMMA); - label_51: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[72] = jj_gen; - break label_51; - } - jj_consume_token(S); - } - selectors.add(selector); - selector = selector(); - } - selectors.add(selector); - { - if (true) { - return selectors; - } + jj_la1[15] = jj_gen; + break label_11; + } + jj_consume_token(S); + } + mediaStatement(ml); + jj_consume_token(SEMICOLON); + label_12: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[16] = jj_gen; + break label_12; } - throw new Error("Missing return statement in function"); - } - - /** - * @exception ParseException - * exception during the parse - */ - final public String selector() throws ParseException { - String selector = null; - char comb; - try { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case LBRACKET: - case ANY: - case PARENT: - case DOT: - case COLON: - case INTERPOLATION: - case IDENT: - case HASH: - selector = simple_selector(null, ' '); - break; - case PLUS: - case PRECEDES: - case SIBLING: - comb = combinatorChar(); - selector = simple_selector(selector, comb); - break; - default: - jj_la1[73] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - label_52: while (true) { - if (jj_2_2(2)) { - ; - } else { - break label_52; - } - comb = combinator(); - selector = simple_selector(selector, comb); - } - label_53: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[74] = jj_gen; - break label_53; - } - jj_consume_token(S); - } - { - if (true) { - return selector; - } - } - } catch (ParseException e) { - /* - * Token t = getToken(1); StringBuffer s = new StringBuffer(); - * s.append(getToken(0).image); while ((t.kind != COMMA) && (t.kind - * != SEMICOLON) && (t.kind != LBRACE) && (t.kind != EOF)) { - * s.append(t.image); getNextToken(); t = getToken(1); } - * reportWarningSkipText(getLocator(), s.toString()); - */ - Token t = getToken(1); - while ((t.kind != COMMA) && (t.kind != SEMICOLON) - && (t.kind != LBRACE) && (t.kind != EOF)) { - getNextToken(); - t = getToken(1); - } + jj_consume_token(S); + } + if (ml.getLength() == 0) { + // see section 6.3 of the CSS2 recommandation. + ml.addItem("all"); + } + documentHandler.importStyle(uri, ml, isURL); + } catch (ParseException e) { + reportError(getLocator(), e); + skipStatement(); + // reportWarningSkipText(getLocator(), skipStatement()); - { - if (true) { - throw new ThrowedParseException(e); - } - } - } - throw new Error("Missing return statement in function"); } + } - /** - * @exception ParseException - * exception during the parse - */ - final public String simple_selector(String selector, char comb) - throws ParseException { - String simple_current = null; - String cond = null; - - pseudoElt = null; - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case ANY: - case PARENT: - case INTERPOLATION: +/** + * @exception ParseException exception during the parse + */ + final public void keyframes() throws ParseException { + Token n; + boolean start = false; + String keyframeName = null; + String animationname = ""; + try { + n = jj_consume_token(KEY_FRAME_SYM); + label_13: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[17] = jj_gen; + break label_13; + } + jj_consume_token(S); + } + keyframeName = n.image; + label_14: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case IDENT: - simple_current = element_name(); - label_54: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case LBRACKET: - case DOT: - case COLON: - case HASH: - ; - break; - default: - jj_la1[75] = jj_gen; - break label_54; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case HASH: - cond = hash(cond); - break; - case DOT: - cond = _class(cond); - break; - case LBRACKET: - cond = attrib(cond); - break; - case COLON: - cond = pseudo(cond); - break; - default: - jj_la1[76] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - } - break; - case HASH: - cond = hash(cond); - label_55: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case LBRACKET: - case DOT: - case COLON: - ; - break; - default: - jj_la1[77] = jj_gen; - break label_55; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case DOT: - cond = _class(cond); - break; - case LBRACKET: - cond = attrib(cond); - break; - case COLON: - cond = pseudo(cond); - break; - default: - jj_la1[78] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - } - break; - case DOT: - cond = _class(cond); - label_56: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case LBRACKET: - case DOT: - case COLON: - case HASH: - ; - break; - default: - jj_la1[79] = jj_gen; - break label_56; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case HASH: - cond = hash(cond); - break; - case DOT: - cond = _class(cond); - break; - case LBRACKET: - cond = attrib(cond); - break; - case COLON: - cond = pseudo(cond); - break; - default: - jj_la1[80] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - } - break; - case COLON: - cond = pseudo(cond); - label_57: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case LBRACKET: - case DOT: - case COLON: - case HASH: - ; - break; - default: - jj_la1[81] = jj_gen; - break label_57; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case HASH: - cond = hash(cond); - break; - case DOT: - cond = _class(cond); - break; - case LBRACKET: - cond = attrib(cond); - break; - case COLON: - cond = pseudo(cond); - break; - default: - jj_la1[82] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - } - break; - case LBRACKET: - cond = attrib(cond); - label_58: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case LBRACKET: - case DOT: - case COLON: - case HASH: - ; - break; - default: - jj_la1[83] = jj_gen; - break label_58; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case HASH: - cond = hash(cond); - break; - case DOT: - cond = _class(cond); - break; - case LBRACKET: - cond = attrib(cond); - break; - case COLON: - cond = pseudo(cond); - break; - default: - jj_la1[84] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - } - break; + n = jj_consume_token(IDENT); + animationname += n.image; + break; + case INTERPOLATION: + n = jj_consume_token(INTERPOLATION); + animationname += n.image; + break; default: - jj_la1[85] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - if (simple_current == null) { - simple_current = ""; - } - if (cond != null) { - simple_current = simple_current + cond; + jj_la1[18] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); } - StringBuilder builder = new StringBuilder(); - switch (comb) { - case ' ': - if (selector != null) { - builder.append(selector).append(" "); - } - break; - case '+': - case '>': - case '~': - if (selector != null) { - builder.append(selector).append(" "); - } - builder.append(comb).append(" "); - break; - default: { - if (true) { - throw new ParseException("invalid state. send a bug report"); - } - } - } - builder.append(simple_current); - selector = builder.toString(); - - if (pseudoElt != null) { - selector = selector + pseudoElt; - } - { - if (true) { - return selector; - } - } - throw new Error("Missing return statement in function"); - } - - /** - * @exception ParseException - * exception during the parse - */ - final public String _class(String pred) throws ParseException { - Token t; - String s = "."; - jj_consume_token(DOT); - label_59: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case IDENT: - t = jj_consume_token(IDENT); - s += t.image; - break; - case INTERPOLATION: - t = jj_consume_token(INTERPOLATION); - s += t.image; - break; - default: - jj_la1[86] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case INTERPOLATION: - case IDENT: - ; - break; - default: - jj_la1[87] = jj_gen; - break label_59; - } - } - if (pred == null) { - { - if (true) { - return s; - } - } - } else { - { - if (true) { - return pred + s; - } - } - } - throw new Error("Missing return statement in function"); - } - - /** - * @exception ParseException - * exception during the parse - */ - final public String element_name() throws ParseException { - Token t; - String s = ""; - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case INTERPOLATION: case IDENT: - label_60: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case IDENT: - t = jj_consume_token(IDENT); - s += t.image; - break; - case INTERPOLATION: - t = jj_consume_token(INTERPOLATION); - s += t.image; - break; - default: - jj_la1[88] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case INTERPOLATION: - case IDENT: - ; - break; - default: - jj_la1[89] = jj_gen; - break label_60; - } - } - { - if (true) { - return s; - } - } - break; - case ANY: - jj_consume_token(ANY); - { - if (true) { - return "*"; - } - } - break; - case PARENT: - jj_consume_token(PARENT); - { - if (true) { - return "&"; - } - } - break; + ; + break; default: - jj_la1[90] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); + jj_la1[19] = jj_gen; + break label_14; } - throw new Error("Missing return statement in function"); - } - - /** - * @exception ParseException - * exception during the parse - */ - final public String attrib(String pred) throws ParseException { - int cases = 0; - Token att = null; - Token val = null; - String attValue = null; - jj_consume_token(LBRACKET); - label_61: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[91] = jj_gen; - break label_61; - } - jj_consume_token(S); - } - att = jj_consume_token(IDENT); - label_62: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[92] = jj_gen; - break label_62; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case DASHMATCH: - case CARETMATCH: - case DOLLARMATCH: - case STARMATCH: - case INCLUDES: - case EQ: - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case EQ: - jj_consume_token(EQ); - cases = 1; - break; - case INCLUDES: - jj_consume_token(INCLUDES); - cases = 2; - break; - case DASHMATCH: - jj_consume_token(DASHMATCH); - cases = 3; - break; - case CARETMATCH: - jj_consume_token(CARETMATCH); - cases = 4; - break; - case DOLLARMATCH: - jj_consume_token(DOLLARMATCH); - cases = 5; - break; - case STARMATCH: - jj_consume_token(STARMATCH); - cases = 6; - break; - default: - jj_la1[93] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - label_63: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[94] = jj_gen; - break label_63; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case IDENT: - val = jj_consume_token(IDENT); - attValue = val.image; - break; - case STRING: - val = jj_consume_token(STRING); - attValue = val.image; - break; - default: - jj_la1[95] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - label_64: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[96] = jj_gen; - break label_64; - } - jj_consume_token(S); - } - break; + } + label_15: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; default: - jj_la1[97] = jj_gen; - ; - } - jj_consume_token(RBRACKET); - String name = convertIdent(att.image); - String c; - switch (cases) { - case 0: - c = name; - break; - case 1: - c = name + "=" + attValue; - break; - case 2: - c = name + "~=" + attValue; - break; - case 3: - c = name + "|=" + attValue; - break; - case 4: - c = name + "^=" + attValue; - break; - case 5: - c = name + "$=" + attValue; - break; - case 6: - c = name + "*=" + attValue; - break; + jj_la1[20] = jj_gen; + break label_15; + } + jj_consume_token(S); + } + start = true; documentHandler.startKeyFrames(keyframeName, animationname); + jj_consume_token(LBRACE); + label_16: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; default: - // never reached. - c = null; - } - c = "[" + c + "]"; - if (pred == null) { - { - if (true) { - return c; - } - } - } else { - { - if (true) { - return pred + c; - } - } - } - throw new Error("Missing return statement in function"); - } - - /** - * @exception ParseException - * exception during the parse - */ - final public String pseudo(String pred) throws ParseException { - Token n; - Token param; - String d; - boolean isPseudoElement = false; - jj_consume_token(COLON); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case COLON: - jj_consume_token(COLON); - isPseudoElement = true; - break; + jj_la1[21] = jj_gen; + break label_16; + } + jj_consume_token(S); + } + label_17: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case TO: + case FROM: + case PERCENTAGE: + ; + break; default: - jj_la1[98] = jj_gen; - ; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case IDENT: - n = jj_consume_token(IDENT); - String s = ":" + convertIdent(n.image); - if (isPseudoElement) { - if (pseudoElt != null) { - { - if (true) { - throw new CSSParseException( - "duplicate pseudo element definition " + s, - getLocator()); - } - } - } else { - pseudoElt = ":" + s; - { - if (true) { - return pred; - } - } - } - } else { - String c = s; - if (pred == null) { - { - if (true) { - return c; - } - } - } else { - { - if (true) { - return pred + c; - } - } - } - } - break; - case FUNCTION: - n = jj_consume_token(FUNCTION); - label_65: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[99] = jj_gen; - break label_65; - } - jj_consume_token(S); - } - d = skipStatementUntilRightParan(); - jj_consume_token(RPARAN); - // accept anything between function and a right parenthesis - String f = convertIdent(n.image); - String colons = isPseudoElement ? "::" : ":"; - String pseudofn = colons + f + d + ")"; - if (pred == null) { - { - if (true) { - return pseudofn; - } - } - } else { - { - if (true) { - return pred + pseudofn; - } - } - } - break; + jj_la1[22] = jj_gen; + break label_17; + } + keyframeSelector(); + } + jj_consume_token(RBRACE); + label_18: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; default: - jj_la1[100] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - throw new Error("Missing return statement in function"); - } - - /** - * @exception ParseException - * exception during the parse - */ - final public String hash(String pred) throws ParseException { - Token n; - n = jj_consume_token(HASH); - String d = n.image; - if (pred == null) { - { - if (true) { - return d; - } - } - } else { - { - if (true) { - return pred + d; - } - } - } - throw new Error("Missing return statement in function"); - } - - final public void variable() throws ParseException { - String name; - LexicalUnitImpl exp = null; - boolean guarded = false; - String raw; - try { - name = variableName(); - jj_consume_token(COLON); - label_66: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[101] = jj_gen; - break label_66; - } - jj_consume_token(S); - } - exp = expr(); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case GUARDED_SYM: - guarded = guarded(); - break; - default: - jj_la1[102] = jj_gen; - ; - } - label_67: while (true) { - jj_consume_token(SEMICOLON); - label_68: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[103] = jj_gen; - break label_68; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case SEMICOLON: - ; - break; - default: - jj_la1[104] = jj_gen; - break label_67; - } - } - documentHandler.variable(name, exp, guarded); - } catch (JumpException e) { - skipAfterExpression(); - } catch (NumberFormatException e) { - if (errorHandler != null) { - errorHandler.error(new CSSParseException("Invalid number " - + e.getMessage(), getLocator(), e)); - } - reportWarningSkipText(getLocator(), skipAfterExpression()); - } catch (ParseException e) { - if (errorHandler != null) { - if (e.currentToken != null) { - LocatorImpl li = new LocatorImpl(this, - e.currentToken.next.beginLine, - e.currentToken.next.beginColumn - 1); - reportError(li, e); - } else { - reportError(getLocator(), e); - } - skipAfterExpression(); - } else { - skipAfterExpression(); - } - } - } - - final public void controlDirective() throws ParseException { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case IF_SYM: - ifDirective(); - break; - case EACH_SYM: - eachDirective(); - break; + jj_la1[23] = jj_gen; + break label_18; + } + jj_consume_token(S); + } + } catch (ParseException e) { + reportError(getLocator(), e); + skipStatement(); + } finally { + if (start) { + documentHandler.endKeyFrames(); + } + } + } + + final public void keyframeSelector() throws ParseException { + Token n; + boolean start = false; + try { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case FROM: + n = jj_consume_token(FROM); + break; + case TO: + n = jj_consume_token(TO); + break; + case PERCENTAGE: + n = jj_consume_token(PERCENTAGE); + break; + default: + jj_la1[24] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + label_19: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; default: - jj_la1[105] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - } - - final public void ifContentStatement() throws ParseException { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case INCLUDE_SYM: - includeDirective(); - break; - case MEDIA_SYM: - media(); - break; - case EXTEND_SYM: - extendDirective(); - break; + jj_la1[25] = jj_gen; + break label_19; + } + jj_consume_token(S); + } + jj_consume_token(LBRACE); + label_20: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[26] = jj_gen; + break label_20; + } + jj_consume_token(S); + } + start = true; + documentHandler.startKeyframeSelector(n.image); + label_21: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case PLUS: case PRECEDES: case SIBLING: @@ -2821,2323 +927,2684 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { case DOT: case COLON: case INTERPOLATION: + case INCLUDE_SYM: case DEBUG_SYM: case WARN_SYM: + case EACH_SYM: + case IF_SYM: + case EXTEND_SYM: + case CONTENT_SYM: + case MICROSOFT_RULE: case IDENT: + case VARIABLE: case HASH: - styleRuleOrDeclarationOrNestedProperties(); - break; + case MEDIA_SYM: case KEY_FRAME_SYM: - keyframes(); - break; + ; + break; default: - jj_la1[106] = jj_gen; - if (jj_2_3(2147483647)) { - variable(); - } else { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case VARIABLE: - listModifyDirective(); - break; - default: - jj_la1[107] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - } - } - } - - final public void ifDirective() throws ParseException { - Token n = null; - String s = null; - String evaluator = ""; - jj_consume_token(IF_SYM); - label_69: while (true) { - s = booleanExpressionToken(); - evaluator += s; - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - case EQ: - case PLUS: - case MINUS: - case PRECEDES: - case SUCCEEDS: - case DIV: - case ANY: - case LPARAN: - case RPARAN: - case COMPARE: - case OR: - case AND: - case NOT_EQ: - case IDENT: - case NUMBER: - case VARIABLE: - case CONTAINS: - ; - break; - default: - jj_la1[108] = jj_gen; - break label_69; - } - } - jj_consume_token(LBRACE); - label_70: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[109] = jj_gen; - break label_70; - } - jj_consume_token(S); - } - documentHandler.startIfElseDirective(); - documentHandler.ifDirective(evaluator); - label_71: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case PRECEDES: - case SIBLING: - case LBRACKET: - case ANY: - case PARENT: - case DOT: - case COLON: - case INTERPOLATION: - case INCLUDE_SYM: - case DEBUG_SYM: - case WARN_SYM: - case EXTEND_SYM: - case IDENT: - case VARIABLE: - case HASH: - case MEDIA_SYM: - case KEY_FRAME_SYM: - ; - break; - default: - jj_la1[110] = jj_gen; - break label_71; - } - ifContentStatement(); + jj_la1[27] = jj_gen; + break label_21; } - jj_consume_token(RBRACE); - label_72: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[111] = jj_gen; - break label_72; - } - jj_consume_token(S); - } - label_73: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case ELSE_SYM: - ; - break; - default: - jj_la1[112] = jj_gen; - break label_73; - } - elseDirective(); - } - documentHandler.endIfElseDirective(); - } - - final public void elseDirective() throws ParseException { - String evaluator = ""; - Token n = null; - String s = null; - jj_consume_token(ELSE_SYM); - label_74: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[113] = jj_gen; - break label_74; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case IF: - jj_consume_token(IF); - label_75: while (true) { - s = booleanExpressionToken(); - evaluator += s; - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - case EQ: - case PLUS: - case MINUS: - case PRECEDES: - case SUCCEEDS: - case DIV: - case ANY: - case LPARAN: - case RPARAN: - case COMPARE: - case OR: - case AND: - case NOT_EQ: - case IDENT: - case NUMBER: - case VARIABLE: - case CONTAINS: - ; - break; - default: - jj_la1[114] = jj_gen; - break label_75; - } - } - break; - default: - jj_la1[115] = jj_gen; - ; - } - jj_consume_token(LBRACE); - label_76: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[116] = jj_gen; - break label_76; - } - jj_consume_token(S); - } - if (!evaluator.trim().equals("")) { - documentHandler.ifDirective(evaluator); - } else { - documentHandler.elseDirective(); - } - label_77: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case PRECEDES: - case SIBLING: - case LBRACKET: - case ANY: - case PARENT: - case DOT: - case COLON: - case INTERPOLATION: - case INCLUDE_SYM: - case DEBUG_SYM: - case WARN_SYM: - case EXTEND_SYM: - case IDENT: - case VARIABLE: - case HASH: - case MEDIA_SYM: - case KEY_FRAME_SYM: - ; - break; - default: - jj_la1[117] = jj_gen; - break label_77; - } - ifContentStatement(); - } - jj_consume_token(RBRACE); - label_78: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[118] = jj_gen; - break label_78; - } - jj_consume_token(S); - } - } - - final public String booleanExpressionToken() throws ParseException { - Token n = null; - String s = null; - if (jj_2_4(2147483647)) { - s = containsDirective(); - } else { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case VARIABLE: - n = jj_consume_token(VARIABLE); - break; - case IDENT: - n = jj_consume_token(IDENT); - break; - case NUMBER: - n = jj_consume_token(NUMBER); - break; - case LPARAN: - n = jj_consume_token(LPARAN); - break; - case RPARAN: - n = jj_consume_token(RPARAN); - break; - case PLUS: - n = jj_consume_token(PLUS); - break; - case MINUS: - n = jj_consume_token(MINUS); - break; - case DIV: - n = jj_consume_token(DIV); - break; - case ANY: - n = jj_consume_token(ANY); - break; - case COMPARE: - n = jj_consume_token(COMPARE); - break; - case EQ: - n = jj_consume_token(EQ); - break; - case PRECEDES: - n = jj_consume_token(PRECEDES); - break; - case SUCCEEDS: - n = jj_consume_token(SUCCEEDS); - break; - case OR: - n = jj_consume_token(OR); - break; - case AND: - n = jj_consume_token(AND); - break; - case S: - n = jj_consume_token(S); - break; - case NOT_EQ: - n = jj_consume_token(NOT_EQ); - break; - default: - jj_la1[119] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - } - if (n != null) { - { - if (true) { - return n.image; - } - } - } else { - { - if (true) { - return s; - } - } - } - throw new Error("Missing return statement in function"); - } - - final public void eachDirective() throws ParseException { - Token var; - ArrayList<String> list = null; - String listVariable = null; - jj_consume_token(EACH_SYM); - label_79: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[120] = jj_gen; - break label_79; - } - jj_consume_token(S); - } - var = jj_consume_token(VARIABLE); - label_80: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[121] = jj_gen; - break label_80; - } - jj_consume_token(S); - } - jj_consume_token(EACH_IN); - label_81: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[122] = jj_gen; - break label_81; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case PRECEDES: + case SIBLING: + case LBRACKET: + case ANY: + case PARENT: + case DOT: + case COLON: + case INTERPOLATION: + case INCLUDE_SYM: + case DEBUG_SYM: + case WARN_SYM: + case EXTEND_SYM: + case CONTENT_SYM: case IDENT: - list = stringList(); - documentHandler.startEachDirective(var.image, list); - break; case VARIABLE: - listVariable = variableName(); - documentHandler.startEachDirective(var.image, listVariable); - break; + case HASH: + case MEDIA_SYM: + case KEY_FRAME_SYM: + ifContentStatement(); + break; + case EACH_SYM: + case IF_SYM: + controlDirective(); + break; + case MICROSOFT_RULE: + microsoftExtension(); + break; default: - jj_la1[123] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - jj_consume_token(LBRACE); - label_82: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[124] = jj_gen; - break label_82; - } - jj_consume_token(S); - } - label_83: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case PRECEDES: - case SIBLING: - case LBRACKET: - case ANY: - case PARENT: - case DOT: - case COLON: - case INTERPOLATION: - case INCLUDE_SYM: - case DEBUG_SYM: - case WARN_SYM: - case EXTEND_SYM: - case IDENT: - case VARIABLE: - case HASH: - case MEDIA_SYM: - case KEY_FRAME_SYM: - ; - break; - default: - jj_la1[125] = jj_gen; - break label_83; - } - ifContentStatement(); + jj_la1[28] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + jj_consume_token(RBRACE); + label_22: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[29] = jj_gen; + break label_22; } - jj_consume_token(RBRACE); - label_84: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[126] = jj_gen; - break label_84; - } - jj_consume_token(S); + jj_consume_token(S); + } + } catch (ThrowedParseException e) { + if (errorHandler != null) { + LocatorImpl li = new LocatorImpl(this, + e.e.currentToken.next.beginLine, + e.e.currentToken.next.beginColumn-1); + reportError(li, e.e); } - documentHandler.endEachDirective(); - } + } catch (ParseException e) { + reportError(getLocator(), e); + skipStatement(); + // reportWarningSkipText(getLocator(), skipStatement()); - final public ArrayList<String> stringList() throws ParseException { - ArrayList<String> strings = new ArrayList<String>(); - Token input; - input = jj_consume_token(IDENT); - label_85: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[127] = jj_gen; - break label_85; - } - jj_consume_token(S); + } catch (TokenMgrError e) { + reportWarningSkipText(getLocator(), skipStatement()); + } finally { + if (start) { + documentHandler.endKeyframeSelector(); } - strings.add(input.image); - label_86: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case COMMA: - ; - break; - default: - jj_la1[128] = jj_gen; - break label_86; - } - jj_consume_token(COMMA); - label_87: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[129] = jj_gen; - break label_87; - } - jj_consume_token(S); - } - input = jj_consume_token(IDENT); - strings.add(input.image); - label_88: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[130] = jj_gen; - break label_88; - } - jj_consume_token(S); - } - } - { - if (true) { - return strings; - } - } - throw new Error("Missing return statement in function"); } + } - final public void mixinDirective() throws ParseException { - String name; - ArrayList<VariableNode> args = null; - String body; - jj_consume_token(MIXIN_SYM); - label_89: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[131] = jj_gen; - break label_89; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { +/** + * @exception ParseException exception during the parse + */ +/* see http://www.w3.org/TR/css3-mediaqueries/ */ + final public void media() throws ParseException { + boolean start = false; + String ret; + MediaListImpl ml = new MediaListImpl(); + try { + jj_consume_token(MEDIA_SYM); + label_23: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[30] = jj_gen; + break label_23; + } + jj_consume_token(S); + } + mediaStatement(ml); + start = true; documentHandler.startMedia(ml); + jj_consume_token(LBRACE); + label_24: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[31] = jj_gen; + break label_24; + } + jj_consume_token(S); + } + label_25: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case CDO: + case LBRACE: + case DASHMATCH: + case INCLUDES: + case PLUS: + case MINUS: + case COMMA: + case SEMICOLON: + case PRECEDES: + case SIBLING: + case LBRACKET: + case ANY: + case PARENT: + case DOT: + case COLON: case INTERPOLATION: + case NONASCII: + case DEBUG_SYM: + case WARN_SYM: + case STRING: case IDENT: - name = property(); - break; + case NUMBER: + case URL: + case PERCENTAGE: + case HASH: + case IMPORT_SYM: + case MEDIA_SYM: + case CHARSET_SYM: + case PAGE_SYM: + case FONT_FACE_SYM: + case ATKEYWORD: + case IMPORTANT_SYM: + case UNICODERANGE: case FUNCTION: - name = functionName(); - args = arglist(); - jj_consume_token(RPARAN); - label_90: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[132] = jj_gen; - break label_90; - } - jj_consume_token(S); - } - break; + case UNKNOWN: + ; + break; default: - jj_la1[133] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); + jj_la1[32] = jj_gen; + break label_25; } - jj_consume_token(LBRACE); - label_91: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[134] = jj_gen; - break label_91; - } - jj_consume_token(S); - } - documentHandler.startMixinDirective(name, args); - label_92: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case PRECEDES: - case SIBLING: - case LBRACKET: - case ANY: - case PARENT: - case DOT: - case COLON: - case INTERPOLATION: - case INCLUDE_SYM: - case DEBUG_SYM: - case WARN_SYM: - case EACH_SYM: - case IF_SYM: - case EXTEND_SYM: - case IDENT: - case VARIABLE: - case HASH: - case MEDIA_SYM: - case PAGE_SYM: - case FONT_FACE_SYM: - case KEY_FRAME_SYM: - ; - break; - default: - jj_la1[135] = jj_gen; - break label_92; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case PRECEDES: - case SIBLING: - case LBRACKET: - case ANY: - case PARENT: - case DOT: - case COLON: - case INTERPOLATION: - case INCLUDE_SYM: - case DEBUG_SYM: - case WARN_SYM: - case EXTEND_SYM: - case IDENT: - case VARIABLE: - case HASH: - case MEDIA_SYM: - case KEY_FRAME_SYM: - ifContentStatement(); - break; - case EACH_SYM: - case IF_SYM: - controlDirective(); - break; - case FONT_FACE_SYM: - fontFace(); - break; - case PAGE_SYM: - page(); - break; - default: - jj_la1[136] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - } - jj_consume_token(RBRACE); - label_93: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[137] = jj_gen; - break label_93; - } - jj_consume_token(S); - } - documentHandler.endMixinDirective(name, args); - } - - final public ArrayList<VariableNode> arglist() throws ParseException { - ArrayList<VariableNode> args = new ArrayList<VariableNode>(); - VariableNode arg; - boolean hasNonOptionalArgument = false; - arg = mixinArg(); - label_94: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case COMMA: - ; - break; - default: - jj_la1[138] = jj_gen; - break label_94; - } - jj_consume_token(COMMA); - label_95: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[139] = jj_gen; - break label_95; - } - jj_consume_token(S); - } - hasNonOptionalArgument = checkMixinForNonOptionalArguments(arg, - hasNonOptionalArgument); - args.add(arg); - arg = mixinArg(); - } - hasNonOptionalArgument = checkMixinForNonOptionalArguments(arg, - hasNonOptionalArgument); - args.add(arg); - { - if (true) { - return args; - } - } - throw new Error("Missing return statement in function"); - } - - boolean checkMixinForNonOptionalArguments(VariableNode arg, - boolean hasNonOptionalArguments) throws ParseException { - boolean currentArgHasArguments = arg.getExpr() != null - && arg.getExpr().getLexicalUnitType() == LexicalUnitImpl.SCSS_VARIABLE - && arg.getExpr().getNextLexicalUnit() != null; - - if (currentArgHasArguments) { - if (hasNonOptionalArguments) { - throw new ParseException("Sass Error: Required argument $" - + arg.getName() - + " must come before any optional arguments."); - } - return hasNonOptionalArguments; - } else { - return true; - } - } - - final public VariableNode mixinArg() throws ParseException { - String name; - Token variable = null; - LexicalUnitImpl first = null; - LexicalUnitImpl prev = null; - LexicalUnitImpl next = null; - name = variableName(); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case DEBUG_SYM: + case WARN_SYM: + debuggingDirective(); + break; + case PLUS: + case PRECEDES: + case SIBLING: + case LBRACKET: + case ANY: + case PARENT: + case DOT: case COLON: - case VARIABLE: - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case COLON: - jj_consume_token(COLON); - label_96: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[140] = jj_gen; - break label_96; - } - jj_consume_token(S); - } - first = nonVariableTerm(null); - prev = first; - label_97: while (true) { - if (jj_2_5(3)) { - ; - } else { - break label_97; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case COMMA: - jj_consume_token(COMMA); - label_98: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[141] = jj_gen; - break label_98; - } - jj_consume_token(S); - } - break; - default: - jj_la1[142] = jj_gen; - ; - } - prev = nonVariableTerm(prev); - } - break; - case VARIABLE: - variable = jj_consume_token(VARIABLE); - first = LexicalUnitImpl.createVariable(token.beginLine, - token.beginColumn, prev, variable.image); - break; - default: - jj_la1[143] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - break; - default: - jj_la1[144] = jj_gen; - ; - } - VariableNode arg = new VariableNode(name, first, false); - { - if (true) { - return arg; - } - } - throw new Error("Missing return statement in function"); - } - - final public ArrayList<LexicalUnitImpl> argValuelist() - throws ParseException { - ArrayList<LexicalUnitImpl> args = new ArrayList<LexicalUnitImpl>(); - LexicalUnitImpl first = null; - LexicalUnitImpl next = null; - LexicalUnitImpl prev = null; - first = term(null); - args.add(first); - prev = first; - label_99: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case MINUS: - case DOT: - case COLON: - case TO: - case THROUGH: - case FROM: - case STRING: - case IDENT: - case NUMBER: - case URL: - case VARIABLE: - case PERCENTAGE: - case PT: - case MM: - case CM: - case PC: - case IN: - case PX: - case EMS: - case LEM: - case REM: - case EXS: - case DEG: - case RAD: - case GRAD: - case MS: - case SECOND: - case HZ: - case KHZ: - case DIMEN: - case HASH: - case UNICODERANGE: - case FUNCTION: - ; - break; - default: - jj_la1[145] = jj_gen; - break label_99; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case COLON: - jj_consume_token(COLON); - label_100: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[146] = jj_gen; - break label_100; - } - jj_consume_token(S); - } - break; - default: - jj_la1[147] = jj_gen; - ; - } - next = term(prev); - prev.setNextLexicalUnit(next); - prev = next; - } - label_101: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case COMMA: - ; - break; - default: - jj_la1[148] = jj_gen; - break label_101; - } - jj_consume_token(COMMA); - label_102: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[149] = jj_gen; - break label_102; - } - jj_consume_token(S); - } - first = term(null); - args.add(first); - prev = first; - label_103: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case MINUS: - case DOT: - case COLON: - case TO: - case THROUGH: - case FROM: - case STRING: - case IDENT: - case NUMBER: - case URL: - case VARIABLE: - case PERCENTAGE: - case PT: - case MM: - case CM: - case PC: - case IN: - case PX: - case EMS: - case LEM: - case REM: - case EXS: - case DEG: - case RAD: - case GRAD: - case MS: - case SECOND: - case HZ: - case KHZ: - case DIMEN: - case HASH: - case UNICODERANGE: - case FUNCTION: - ; - break; - default: - jj_la1[150] = jj_gen; - break label_103; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case COLON: - jj_consume_token(COLON); - label_104: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[151] = jj_gen; - break label_104; - } - jj_consume_token(S); - } - break; - default: - jj_la1[152] = jj_gen; - ; - } - next = term(prev); - prev.setNextLexicalUnit(next); - prev = next; - } - } - { - if (true) { - return args; - } - } - throw new Error("Missing return statement in function"); - } - - final public void includeDirective() throws ParseException { - String name; - ArrayList<LexicalUnitImpl> args = null; - jj_consume_token(INCLUDE_SYM); - label_105: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[153] = jj_gen; - break label_105; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { case INTERPOLATION: case IDENT: - name = property(); - break; - case VARIABLE: - name = variableName(); - name = "$" + name; - break; + case HASH: + styleRule(); + break; + case CDO: + case LBRACE: + case DASHMATCH: + case INCLUDES: + case MINUS: + case COMMA: + case SEMICOLON: + case NONASCII: + case STRING: + case NUMBER: + case URL: + case PERCENTAGE: + case IMPORT_SYM: + case MEDIA_SYM: + case CHARSET_SYM: + case PAGE_SYM: + case FONT_FACE_SYM: + case ATKEYWORD: + case IMPORTANT_SYM: + case UNICODERANGE: case FUNCTION: - name = functionName(); - args = argValuelist(); - jj_consume_token(RPARAN); - break; + case UNKNOWN: + skipUnknownRule(); + break; default: - jj_la1[154] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); + jj_la1[33] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + jj_consume_token(RBRACE); + label_26: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[34] = jj_gen; + break label_26; + } + jj_consume_token(S); + } + } catch (ParseException e) { + reportError(getLocator(), e); + skipStatement(); + // reportWarningSkipText(getLocator(), skipStatement()); + + } finally { + if (start) { + documentHandler.endMedia(ml); + } + } + } + + final public void mediaStatement(MediaListImpl ml) throws ParseException { + Token t; + t = getToken(1); + // loop over comma separated parts, add each to ml + while ((t.kind != LBRACE) && (t.kind != EOF) && (t.kind != SEMICOLON)) { + StringBuffer s = new StringBuffer(); + s.append(getToken(0).image); + while ((t.kind != COMMA) && (t.kind != LBRACE) && (t.kind != EOF) && (t.kind != SEMICOLON)) { + s.append(t.image); + getNextToken(); + t = getToken(1); } - label_106: while (true) { - jj_consume_token(SEMICOLON); - label_107: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[155] = jj_gen; - break label_107; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case SEMICOLON: - ; - break; - default: - jj_la1[156] = jj_gen; - break label_106; - } + if (t.kind == COMMA) { + // skip the comma and the token before it that is still the active token + getNextToken(); + getNextToken(); + t = getToken(1); } - documentHandler.includeDirective(name, args); - } - - final public String interpolation() throws ParseException { - Token n; - n = jj_consume_token(INTERPOLATION); - { - if (true) { - return n.image; - } + String str = s.toString().trim(); + if (str.length() > 0) { + ml.addItem(str); } - throw new Error("Missing return statement in function"); - } + } + } - final public void listModifyDirective() throws ParseException { - String list = null; - String remove = null; - String separator = null; - String variable = null; - Token n = null; - Token type = null; - // refactor, remove those 3 LOOKAHEAD(5). - n = jj_consume_token(VARIABLE); - variable = n.image; - label_108: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[157] = jj_gen; - break label_108; - } - jj_consume_token(S); - } - jj_consume_token(COLON); - label_109: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[158] = jj_gen; - break label_109; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case APPEND: - type = jj_consume_token(APPEND); - break; - case REMOVE: - type = jj_consume_token(REMOVE); - break; - case CONTAINS: - type = jj_consume_token(CONTAINS); - break; +/** + * @exception ParseException exception during the parse + */ + final public String medium() throws ParseException { + Token n; + n = jj_consume_token(IDENT); + {if (true) return convertIdent(n.image);} + throw new Error("Missing return statement in function"); + } + +/** + * @exception ParseException exception during the parse + */ + final public void page() throws ParseException { + boolean start = false; + Token n = null; + String page = null; + String pseudo = null; + try { + jj_consume_token(PAGE_SYM); + label_27: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; default: - jj_la1[159] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); + jj_la1[35] = jj_gen; + break label_27; } - label_110: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[160] = jj_gen; - break label_110; - } - jj_consume_token(S); - } - list = listModifyDirectiveArgs(0); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case RPARAN: - jj_consume_token(RPARAN); - break; - default: - jj_la1[161] = jj_gen; + jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IDENT: + n = jj_consume_token(IDENT); + label_28: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: ; - } - jj_consume_token(COMMA); - label_111: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[162] = jj_gen; - break label_111; - } - jj_consume_token(S); - } - remove = listModifyDirectiveArgs(1); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case COMMA: - jj_consume_token(COMMA); - label_112: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[163] = jj_gen; - break label_112; - } - jj_consume_token(S); - } - n = jj_consume_token(IDENT); - separator = n.image; - label_113: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[164] = jj_gen; - break label_113; - } - jj_consume_token(S); - } break; + default: + jj_la1[36] = jj_gen; + break label_28; + } + jj_consume_token(S); + } + break; + default: + jj_la1[37] = jj_gen; + ; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COLON: + pseudo = pseudo_page(); + break; + default: + jj_la1[38] = jj_gen; + ; + } + if (n != null) { + page = convertIdent(n.image); + } + jj_consume_token(LBRACE); + label_29: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; default: - jj_la1[165] = jj_gen; - ; - } - jj_consume_token(RPARAN); - switch (type.kind) { - case APPEND: - documentHandler.appendDirective(variable, list, remove, separator); - break; - case REMOVE: - documentHandler.removeDirective(variable, list, remove, separator); - break; - case CONTAINS: - if (variable == null) { - variable = "$var_" + UUID.randomUUID(); - } - documentHandler - .containsDirective(variable, list, remove, separator); - break; + jj_la1[39] = jj_gen; + break label_29; + } + jj_consume_token(S); + } + start = true; + documentHandler.startPage(page, pseudo); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTERPOLATION: + case IDENT: + declaration(); + break; + default: + jj_la1[40] = jj_gen; + ; + } + label_30: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case SEMICOLON: + ; + break; default: - break; - } - label_114: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[166] = jj_gen; - break label_114; - } - jj_consume_token(S); + jj_la1[41] = jj_gen; + break label_30; } jj_consume_token(SEMICOLON); - label_115: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[167] = jj_gen; - break label_115; - } - jj_consume_token(S); - } - } - - /** - * @exception ParseException - * exception during the parse - */ - final public void appendDirective() throws ParseException { - String list = null; - String remove = null; - String separator = null; - String variable = null; - Token n = null; - n = jj_consume_token(VARIABLE); - variable = n.image; - label_116: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[168] = jj_gen; - break label_116; - } - jj_consume_token(S); - } - jj_consume_token(COLON); - label_117: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[169] = jj_gen; - break label_117; - } - jj_consume_token(S); - } - jj_consume_token(APPEND); - label_118: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[170] = jj_gen; - break label_118; - } - jj_consume_token(S); - } - list = listModifyDirectiveArgs(0); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case RPARAN: - jj_consume_token(RPARAN); - break; - default: - jj_la1[171] = jj_gen; + label_31: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: ; - } - jj_consume_token(COMMA); - label_119: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[172] = jj_gen; - break label_119; - } - jj_consume_token(S); - } - remove = listModifyDirectiveArgs(1); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case COMMA: - jj_consume_token(COMMA); - label_120: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[173] = jj_gen; - break label_120; - } - jj_consume_token(S); - } - n = jj_consume_token(IDENT); - separator = n.image; - label_121: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[174] = jj_gen; - break label_121; - } - jj_consume_token(S); - } break; - default: - jj_la1[175] = jj_gen; - ; + default: + jj_la1[42] = jj_gen; + break label_31; + } + jj_consume_token(S); } - jj_consume_token(RPARAN); - documentHandler.appendDirective(variable, list, remove, separator); - } - - /** - * @exception ParseException - * exception during the parse - */ - final public void removeDirective() throws ParseException { - String list = null; - String remove = null; - String separator = null; - String variable = null; - Token n = null; - n = jj_consume_token(VARIABLE); - variable = n.image; - label_122: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[176] = jj_gen; - break label_122; - } - jj_consume_token(S); - } - jj_consume_token(COLON); - label_123: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[177] = jj_gen; - break label_123; - } - jj_consume_token(S); - } - jj_consume_token(REMOVE); - label_124: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[178] = jj_gen; - break label_124; - } - jj_consume_token(S); - } - list = listModifyDirectiveArgs(0); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case RPARAN: - jj_consume_token(RPARAN); - break; + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTERPOLATION: + case IDENT: + declaration(); + break; default: - jj_la1[179] = jj_gen; - ; - } - jj_consume_token(COMMA); - label_125: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[180] = jj_gen; - break label_125; - } - jj_consume_token(S); - } - remove = listModifyDirectiveArgs(1); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case COMMA: - jj_consume_token(COMMA); - label_126: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[181] = jj_gen; - break label_126; - } - jj_consume_token(S); - } - n = jj_consume_token(IDENT); - separator = n.image; - label_127: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[182] = jj_gen; - break label_127; - } - jj_consume_token(S); - } - break; + jj_la1[43] = jj_gen; + ; + } + } + jj_consume_token(RBRACE); + label_32: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; default: - jj_la1[183] = jj_gen; - ; - } - jj_consume_token(RPARAN); - documentHandler.removeDirective(variable, list, remove, separator); - } - - /** - * @exception ParseException - * exception during the parse - */ - final public String containsDirective() throws ParseException { - String list = null; - String remove = null; - String separator = null; - String variable = null; - Token n = null; - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case VARIABLE: - n = jj_consume_token(VARIABLE); - variable = n.image; - label_128: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[184] = jj_gen; - break label_128; - } - jj_consume_token(S); - } - jj_consume_token(COLON); - label_129: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[185] = jj_gen; - break label_129; - } - jj_consume_token(S); - } - break; + jj_la1[44] = jj_gen; + break label_32; + } + jj_consume_token(S); + } + } catch (ParseException e) { + if (errorHandler != null) { + LocatorImpl li = new LocatorImpl(this, + e.currentToken.next.beginLine, + e.currentToken.next.beginColumn-1); + reportError(li, e); + skipStatement(); + // reportWarningSkipText(li, skipStatement()); + } else { + skipStatement(); + } + } finally { + if (start) { + documentHandler.endPage(page, pseudo); + } + } + } + + final public String pseudo_page() throws ParseException { + Token n; + jj_consume_token(COLON); + n = jj_consume_token(IDENT); + label_33: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[45] = jj_gen; + break label_33; + } + jj_consume_token(S); + } + {if (true) return convertIdent(n.image);} + throw new Error("Missing return statement in function"); + } + + final public void fontFace() throws ParseException { + boolean start = false; + try { + jj_consume_token(FONT_FACE_SYM); + label_34: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; default: - jj_la1[186] = jj_gen; - ; - } - jj_consume_token(CONTAINS); - label_130: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[187] = jj_gen; - break label_130; - } - jj_consume_token(S); - } - list = listModifyDirectiveArgs(0); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case RPARAN: - jj_consume_token(RPARAN); - break; + jj_la1[46] = jj_gen; + break label_34; + } + jj_consume_token(S); + } + jj_consume_token(LBRACE); + label_35: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; default: - jj_la1[188] = jj_gen; - ; - } - jj_consume_token(COMMA); - label_131: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[189] = jj_gen; - break label_131; - } - jj_consume_token(S); - } - remove = listModifyDirectiveArgs(1); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case COMMA: - jj_consume_token(COMMA); - label_132: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[190] = jj_gen; - break label_132; - } - jj_consume_token(S); - } - n = jj_consume_token(IDENT); - separator = n.image; - label_133: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[191] = jj_gen; - break label_133; - } - jj_consume_token(S); - } - break; + jj_la1[47] = jj_gen; + break label_35; + } + jj_consume_token(S); + } + start = true; documentHandler.startFontFace(); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTERPOLATION: + case IDENT: + declaration(); + break; + default: + jj_la1[48] = jj_gen; + ; + } + label_36: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case SEMICOLON: + ; + break; default: - jj_la1[192] = jj_gen; - ; + jj_la1[49] = jj_gen; + break label_36; } - jj_consume_token(RPARAN); - /* - * if it is not in the form like - * "$contains : contains($items, .v-button);"for example in @if, like - * "@if (contains(a b c, b))", then create a tempvariable for contains(a - * b c, b); - */ - if (variable == null) { - variable = "$var_" + UUID.randomUUID(); + jj_consume_token(SEMICOLON); + label_37: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[50] = jj_gen; + break label_37; + } + jj_consume_token(S); } - documentHandler.containsDirective(variable, list, remove, separator); - { - if (true) { - return variable; - } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTERPOLATION: + case IDENT: + declaration(); + break; + default: + jj_la1[51] = jj_gen; + ; + } + } + jj_consume_token(RBRACE); + label_38: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[52] = jj_gen; + break label_38; } - throw new Error("Missing return statement in function"); - } - - String listModifyDirectiveArgs(int nest) throws ParseException { - String list = ""; - int nesting = nest; - Token t = null; + jj_consume_token(S); + } + } catch (ParseException e) { + reportError(getLocator(), e); + skipStatement(); + // reportWarningSkipText(getLocator(), skipStatement()); - while (true) { - t = getToken(1); - String s = t.image; - if (t.kind == VARIABLE || t.kind == IDENT) { - list += s; - } else if (s.toLowerCase().equals("auto") - || s.toLowerCase().equals("space") - || s.toLowerCase().equals("comma")) { - int i = 2; - Token temp = getToken(i); - boolean isLast = true; - while (temp.kind != SEMICOLON) { - if (temp.kind != RPARAN || temp.kind != S) { - isLast = false; - } - i++; - temp = getToken(i); - } + } finally { + if (start) { + documentHandler.endFontFace(); + } + } + } - if (isLast) { - return list; - } - } else if (t.kind == STRING) { - list += s.substring(1, s.length()).substring(0, s.length() - 2); - - } else if (t.kind == LPARAN) { - nesting++; - if (nesting > nest + 1) { - throw new CSSParseException( - "Only one ( ) pair per parameter allowed", - getLocator()); - } - } else if (t.kind == RPARAN) { - nesting--; - if (nesting == 0) { - return list; - } - } else if (t.kind == COMMA) { - if (nesting == nest) { - return list; - } else { - list += ","; - } +/** + * @exception ParseException exception during the parse + */ + final public void atRuleDeclaration() throws ParseException { + Token n; + String ret; + n = jj_consume_token(ATKEYWORD); + ret=skipStatement(); + if ((ret != null) && (ret.charAt(0) == '@')) { + documentHandler.unrecognizedRule(ret); + } else { + reportWarningSkipText(getLocator(), ret); + } + } + + final public void skipUnknownRule() throws ParseException { + Token n; + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case ATKEYWORD: + n = jj_consume_token(ATKEYWORD); + break; + case CDO: + n = jj_consume_token(CDO); + break; + case CHARSET_SYM: + n = jj_consume_token(CHARSET_SYM); + break; + case COMMA: + n = jj_consume_token(COMMA); + break; + case DASHMATCH: + n = jj_consume_token(DASHMATCH); + break; + case FONT_FACE_SYM: + n = jj_consume_token(FONT_FACE_SYM); + break; + case FUNCTION: + n = jj_consume_token(FUNCTION); + break; + case IMPORTANT_SYM: + n = jj_consume_token(IMPORTANT_SYM); + break; + case IMPORT_SYM: + n = jj_consume_token(IMPORT_SYM); + break; + case INCLUDES: + n = jj_consume_token(INCLUDES); + break; + case LBRACE: + n = jj_consume_token(LBRACE); + break; + case MEDIA_SYM: + n = jj_consume_token(MEDIA_SYM); + break; + case NONASCII: + n = jj_consume_token(NONASCII); + break; + case NUMBER: + n = jj_consume_token(NUMBER); + break; + case PAGE_SYM: + n = jj_consume_token(PAGE_SYM); + break; + case PERCENTAGE: + n = jj_consume_token(PERCENTAGE); + break; + case STRING: + n = jj_consume_token(STRING); + break; + case UNICODERANGE: + n = jj_consume_token(UNICODERANGE); + break; + case URL: + n = jj_consume_token(URL); + break; + case SEMICOLON: + n = jj_consume_token(SEMICOLON); + break; + case MINUS: + n = jj_consume_token(MINUS); + break; + case UNKNOWN: + n = jj_consume_token(UNKNOWN); + break; + default: + jj_la1[53] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + String ret; + Locator loc = getLocator(); + ret=skipStatement(); + if ((ret != null) && (n.image.charAt(0) == '@')) { + documentHandler.unrecognizedRule(ret); + } else { + reportWarningSkipText(loc, ret); + } + } - } else if (t.kind == S) { - list += " "; - } else if (t.kind == LBRACE) { - throw new CSSParseException("Invalid token,'{' found", - getLocator()); - } +/** + * @exception ParseException exception during the parse + */ + final public char combinator() throws ParseException { +char connector = ' '; + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case PRECEDES: + case SIBLING: + connector = combinatorChar(); + break; + case S: + jj_consume_token(S); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case PRECEDES: + case SIBLING: + connector = combinatorChar(); + break; + default: + jj_la1[54] = jj_gen; + ; + } + break; + default: + jj_la1[55] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + {if (true) return connector;} + throw new Error("Missing return statement in function"); + } + +/**to refactor combinator and reuse in selector().*/ + final public char combinatorChar() throws ParseException { + Token t; + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + t = jj_consume_token(PLUS); + break; + case PRECEDES: + t = jj_consume_token(PRECEDES); + break; + case SIBLING: + t = jj_consume_token(SIBLING); + break; + default: + jj_la1[56] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + label_39: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[57] = jj_gen; + break label_39; + } + jj_consume_token(S); + } + {if (true) return t.image.charAt(0);} + throw new Error("Missing return statement in function"); + } + + final public void microsoftExtension() throws ParseException { + Token n; + String name = ""; + String value = ""; + // This is not really taking the syntax of filter rules into account + n = jj_consume_token(MICROSOFT_RULE); + label_40: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[58] = jj_gen; + break label_40; + } + jj_consume_token(S); + } + name = n.image; + jj_consume_token(COLON); + label_41: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IDENT: + n = jj_consume_token(IDENT); + value += n.image; + break; + case NUMBER: + n = jj_consume_token(NUMBER); + value += n.image; + break; + case STRING: + n = jj_consume_token(STRING); + value += n.image; + break; + case COMMA: + n = jj_consume_token(COMMA); + value += n.image; + break; + case INTERPOLATION: + n = jj_consume_token(INTERPOLATION); + value += n.image; + break; + case COLON: + n = jj_consume_token(COLON); + value += n.image; + break; + case FUNCTION: + n = jj_consume_token(FUNCTION); + value += n.image; + break; + case RPARAN: + n = jj_consume_token(RPARAN); + value += n.image; + break; + case EQ: + n = jj_consume_token(EQ); + value += n.image; + break; + case DOT: + n = jj_consume_token(DOT); + value += n.image; + break; + case S: + n = jj_consume_token(S); + if(value.lastIndexOf(' ') != value.length()-1) + { value += n.image; } + break; + default: + jj_la1[59] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + case EQ: + case COMMA: + case DOT: + case RPARAN: + case COLON: + case INTERPOLATION: + case STRING: + case IDENT: + case NUMBER: + case FUNCTION: + ; + break; + default: + jj_la1[60] = jj_gen; + break label_41; + } + } + jj_consume_token(SEMICOLON); + label_42: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[61] = jj_gen; + break label_42; + } + jj_consume_token(S); + } + documentHandler.microsoftDirective(name, value); + } - getNextToken(); - } - } +/** + * @exception ParseException exception during the parse + */ + final public String property() throws ParseException { + Token t;String s = ""; + label_43: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IDENT: + t = jj_consume_token(IDENT); + s += t.image; + break; + case INTERPOLATION: + t = jj_consume_token(INTERPOLATION); + s += t.image; + break; + default: + jj_la1[62] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTERPOLATION: + case IDENT: + ; + break; + default: + jj_la1[63] = jj_gen; + break label_43; + } + } + label_44: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[64] = jj_gen; + break label_44; + } + jj_consume_token(S); + } + {if (true) return s;} + throw new Error("Missing return statement in function"); + } + + final public String variableName() throws ParseException { + Token n; + n = jj_consume_token(VARIABLE); + label_45: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[65] = jj_gen; + break label_45; + } + jj_consume_token(S); + } + {if (true) return convertIdent(n.image.substring(1));} + throw new Error("Missing return statement in function"); + } + + final public String functionName() throws ParseException { + Token n; + n = jj_consume_token(FUNCTION); + label_46: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[66] = jj_gen; + break label_46; + } + jj_consume_token(S); + } + {if (true) return convertIdent(n.image.substring(0, n.image.length()-1));} + throw new Error("Missing return statement in function"); + } - final public Node returnDirective() throws ParseException { - String raw; - raw = skipStatement(); - { - if (true) { - return null; - } +/** + * @exception ParseException exception during the parse + */ + final public void styleRule() throws ParseException { + boolean start = false; + ArrayList<String> l = null; + Token save; + Locator loc; + try { + l = selectorList(); + save = token; + jj_consume_token(LBRACE); + label_47: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[67] = jj_gen; + break label_47; + } + jj_consume_token(S); + } + start = true; + documentHandler.startSelector(l); + label_48: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case PRECEDES: + case SIBLING: + case LBRACKET: + case ANY: + case PARENT: + case DOT: + case COLON: + case INTERPOLATION: + case INCLUDE_SYM: + case DEBUG_SYM: + case WARN_SYM: + case EACH_SYM: + case IF_SYM: + case EXTEND_SYM: + case CONTENT_SYM: + case MICROSOFT_RULE: + case IDENT: + case VARIABLE: + case HASH: + case MEDIA_SYM: + case KEY_FRAME_SYM: + ; + break; + default: + jj_la1[68] = jj_gen; + break label_48; } - throw new Error("Missing return statement in function"); - } - - final public void debuggingDirective() throws ParseException { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case PRECEDES: + case SIBLING: + case LBRACKET: + case ANY: + case PARENT: + case DOT: + case COLON: + case INTERPOLATION: + case INCLUDE_SYM: case DEBUG_SYM: - debugDirective(); - break; case WARN_SYM: - warnDirective(); - break; + case EXTEND_SYM: + case CONTENT_SYM: + case IDENT: + case VARIABLE: + case HASH: + case MEDIA_SYM: + case KEY_FRAME_SYM: + ifContentStatement(); + break; + case EACH_SYM: + case IF_SYM: + controlDirective(); + break; + case MICROSOFT_RULE: + microsoftExtension(); + break; default: - jj_la1[193] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); + jj_la1[69] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + jj_consume_token(RBRACE); + label_49: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[70] = jj_gen; + break label_49; + } + jj_consume_token(S); + } + } catch (ThrowedParseException e) { + if (errorHandler != null) { + LocatorImpl li = new LocatorImpl(this, + e.e.currentToken.next.beginLine, + e.e.currentToken.next.beginColumn-1); + reportError(li, e.e); + } + } catch (ParseException e) { + reportError(getLocator(), e); + skipStatement(); + // reportWarningSkipText(getLocator(), skipStatement()); + + } catch (TokenMgrError e) { + reportWarningSkipText(getLocator(), skipStatement()); + } finally { + if (start) { + documentHandler.endSelector(); + } + } + } + + final public ArrayList<String> selectorList() throws ParseException { + ArrayList<String> selectors = new ArrayList<String>(); + String selector; + selector = selector(); + label_50: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMA: + ; + break; + default: + jj_la1[71] = jj_gen; + break label_50; + } + jj_consume_token(COMMA); + label_51: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[72] = jj_gen; + break label_51; } + jj_consume_token(S); + } + selectors.add(selector); + selector = selector(); } + selectors.add(selector); + {if (true) return selectors;} + throw new Error("Missing return statement in function"); + } - final public void debugDirective() throws ParseException { - jj_consume_token(DEBUG_SYM); - String content = skipStatementUntilSemiColon(); - // TODO should evaluate the content expression, call - // documentHandler.debugDirective() etc. - System.out.println(content); - label_134: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[194] = jj_gen; - break label_134; - } - jj_consume_token(S); - } - } +/** + * @exception ParseException exception during the parse + */ + final public String selector() throws ParseException { + String selector = null; + char comb; + try { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case LBRACKET: + case ANY: + case PARENT: + case DOT: + case COLON: + case INTERPOLATION: + case IDENT: + case HASH: + selector = simple_selector(null, ' '); + break; + case PLUS: + case PRECEDES: + case SIBLING: + comb = combinatorChar(); + selector = simple_selector(selector, comb); + break; + default: + jj_la1[73] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + label_52: + while (true) { + if (jj_2_2(2)) { + ; + } else { + break label_52; + } + comb = combinator(); + selector = simple_selector(selector, comb); + } + label_53: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[74] = jj_gen; + break label_53; + } + jj_consume_token(S); + } + {if (true) return selector;} + } catch (ParseException e) { + /* + Token t = getToken(1); + StringBuffer s = new StringBuffer(); + s.append(getToken(0).image); + while ((t.kind != COMMA) && (t.kind != SEMICOLON) + && (t.kind != LBRACE) && (t.kind != EOF)) { + s.append(t.image); + getNextToken(); + t = getToken(1); + } + reportWarningSkipText(getLocator(), s.toString()); + */ + Token t = getToken(1); + while ((t.kind != COMMA) && (t.kind != SEMICOLON) + && (t.kind != LBRACE) && (t.kind != EOF)) { + getNextToken(); + t = getToken(1); + } - final public void warnDirective() throws ParseException { - jj_consume_token(WARN_SYM); - String content = skipStatementUntilSemiColon(); - // TODO should evaluate the content expression, call - // documentHandler.warnDirective() etc. - System.err.println(content); - label_135: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[195] = jj_gen; - break label_135; - } - jj_consume_token(S); - } + {if (true) throw new ThrowedParseException(e);} } + throw new Error("Missing return statement in function"); + } - final public Node forDirective() throws ParseException { - String var; - String from; - String to; - boolean exclusive; - String body; - Token tok; - var = variableName(); - int[] toThrough = { TO, THROUGH }; - from = skipStatementUntil(toThrough); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case TO: - tok = jj_consume_token(TO); - exclusive = true; - break; - case THROUGH: - tok = jj_consume_token(THROUGH); - exclusive = false; - break; +/** + * @exception ParseException exception during the parse + */ + final public String simple_selector(String selector, char comb) throws ParseException { + String simple_current = null; + String cond = null; + + pseudoElt = null; + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case ANY: + case PARENT: + case INTERPOLATION: + case IDENT: + simple_current = element_name(); + label_54: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case LBRACKET: + case DOT: + case COLON: + case HASH: + ; + break; default: - jj_la1[196] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); + jj_la1[75] = jj_gen; + break label_54; } - to = skipStatementUntilLeftBrace(); - label_136: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[197] = jj_gen; - break label_136; - } - jj_consume_token(S); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case HASH: + cond = hash(cond); + break; + case DOT: + cond = _class(cond); + break; + case LBRACKET: + cond = attrib(cond); + break; + case COLON: + cond = pseudo(cond); + break; + default: + jj_la1[76] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + break; + case HASH: + cond = hash(cond); + label_55: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case LBRACKET: + case DOT: + case COLON: + ; + break; + default: + jj_la1[77] = jj_gen; + break label_55; } - body = skipStatement(); - { - if (true) { - return documentHandler.forDirective(var, from, to, exclusive, - body); - } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case DOT: + cond = _class(cond); + break; + case LBRACKET: + cond = attrib(cond); + break; + case COLON: + cond = pseudo(cond); + break; + default: + jj_la1[78] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + break; + case DOT: + cond = _class(cond); + label_56: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case LBRACKET: + case DOT: + case COLON: + case HASH: + ; + break; + default: + jj_la1[79] = jj_gen; + break label_56; } - throw new Error("Missing return statement in function"); - } - - final public Node whileDirective() throws ParseException { - String condition; - String body; - condition = skipStatementUntilLeftBrace(); - body = skipStatement(); - { - if (true) { - return documentHandler.whileDirective(condition, body); - } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case HASH: + cond = hash(cond); + break; + case DOT: + cond = _class(cond); + break; + case LBRACKET: + cond = attrib(cond); + break; + case COLON: + cond = pseudo(cond); + break; + default: + jj_la1[80] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + break; + case COLON: + cond = pseudo(cond); + label_57: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case LBRACKET: + case DOT: + case COLON: + case HASH: + ; + break; + default: + jj_la1[81] = jj_gen; + break label_57; } - throw new Error("Missing return statement in function"); - } - - final public void extendDirective() throws ParseException { - ArrayList<String> list; - jj_consume_token(EXTEND_SYM); - label_137: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[198] = jj_gen; - break label_137; - } - jj_consume_token(S); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case HASH: + cond = hash(cond); + break; + case DOT: + cond = _class(cond); + break; + case LBRACKET: + cond = attrib(cond); + break; + case COLON: + cond = pseudo(cond); + break; + default: + jj_la1[82] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + break; + case LBRACKET: + cond = attrib(cond); + label_58: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case LBRACKET: + case DOT: + case COLON: + case HASH: + ; + break; + default: + jj_la1[83] = jj_gen; + break label_58; } - list = selectorList(); - label_138: while (true) { - jj_consume_token(SEMICOLON); - label_139: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case HASH: + cond = hash(cond); + break; + case DOT: + cond = _class(cond); + break; + case LBRACKET: + cond = attrib(cond); + break; + case COLON: + cond = pseudo(cond); + break; + default: + jj_la1[84] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + break; + default: + jj_la1[85] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + if (simple_current == null) { + simple_current = ""; + } + if (cond != null) { + simple_current = simple_current + cond; + } + StringBuilder builder = new StringBuilder(); + switch (comb) { + case ' ': + if(selector!=null){ + builder.append(selector).append(" "); + } + break; + case '+': + case '>': + case '~': + if(selector!=null){ + builder.append(selector).append(" "); + } + builder.append(comb).append(" "); break; default: - jj_la1[199] = jj_gen; - break label_139; - } - jj_consume_token(S); + {if (true) throw new ParseException("invalid state. send a bug report");} } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case SEMICOLON: - ; - break; - default: - jj_la1[200] = jj_gen; - break label_138; - } - } - documentHandler.extendDirective(list); - } + builder.append(simple_current); + selector = builder.toString(); - Node importDirective() throws ParseException { - return null; - } - - Node charsetDirective() throws ParseException { - return null; - } - - Node mozDocumentDirective() throws ParseException { - return null; - } + if (pseudoElt != null) { + selector = selector + pseudoElt; + } + {if (true) return selector;} + throw new Error("Missing return statement in function"); + } - Node supportsDirective() throws ParseException { - return null; - } +/** + * @exception ParseException exception during the parse + */ + final public String _class(String pred) throws ParseException { + Token t; +String s = "."; + jj_consume_token(DOT); + label_59: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IDENT: + t = jj_consume_token(IDENT); + s += t.image; + break; + case INTERPOLATION: + t = jj_consume_token(INTERPOLATION); + s += t.image; + break; + default: + jj_la1[86] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTERPOLATION: + case IDENT: + ; + break; + default: + jj_la1[87] = jj_gen; + break label_59; + } + } + if (pred == null) { + {if (true) return s;} + } else { + {if (true) return pred + s;} + } + throw new Error("Missing return statement in function"); + } - final public void nestedProperties() throws ParseException { - String name; - LexicalUnit exp; - name = property(); - jj_consume_token(COLON); - label_140: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[201] = jj_gen; - break label_140; - } - jj_consume_token(S); - } - jj_consume_token(LBRACE); - label_141: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[202] = jj_gen; - break label_141; - } - jj_consume_token(S); +/** + * @exception ParseException exception during the parse + */ + final public String element_name() throws ParseException { + Token t; String s = ""; + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTERPOLATION: + case IDENT: + label_60: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IDENT: + t = jj_consume_token(IDENT); + s += t.image; + break; + case INTERPOLATION: + t = jj_consume_token(INTERPOLATION); + s += t.image; + break; + default: + jj_la1[88] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); } - documentHandler.startNestedProperties(name); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case INTERPOLATION: case IDENT: - declaration(); - break; + ; + break; default: - jj_la1[203] = jj_gen; - ; - } - label_142: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case SEMICOLON: - ; - break; - default: - jj_la1[204] = jj_gen; - break label_142; - } - jj_consume_token(SEMICOLON); - label_143: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[205] = jj_gen; - break label_143; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case INTERPOLATION: - case IDENT: - declaration(); - break; - default: - jj_la1[206] = jj_gen; - ; - } - } - jj_consume_token(RBRACE); - documentHandler.endNestedProperties(name); - label_144: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[207] = jj_gen; - break label_144; - } - jj_consume_token(S); - } - } + jj_la1[89] = jj_gen; + break label_60; + } + } + {if (true) return s;} + break; + case ANY: + jj_consume_token(ANY); + {if (true) return "*";} + break; + case PARENT: + jj_consume_token(PARENT); + {if (true) return "&";} + break; + default: + jj_la1[90] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + throw new Error("Missing return statement in function"); + } - /** - * @exception ParseException - * exception during the parse - */ - final public void styleRuleOrDeclarationOrNestedProperties() - throws ParseException { - try { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case DEBUG_SYM: - case WARN_SYM: - debuggingDirective(); - break; - default: - jj_la1[208] = jj_gen; - if (jj_2_6(2147483647)) { - styleRule(); - } else if (jj_2_7(3)) { - declarationOrNestedProperties(); - } else { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case PRECEDES: - case SIBLING: - case LBRACKET: - case ANY: - case PARENT: - case DOT: - case COLON: - case INTERPOLATION: - case IDENT: - case HASH: - styleRule(); - break; - default: - jj_la1[209] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - } - } - } catch (JumpException e) { - skipAfterExpression(); - // reportWarningSkipText(getLocator(), skipAfterExpression()); +/** + * @exception ParseException exception during the parse + */ + final public String attrib(String pred) throws ParseException { + int cases = 0; + Token att = null; + Token val = null; + String attValue = null; + jj_consume_token(LBRACKET); + label_61: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[91] = jj_gen; + break label_61; + } + jj_consume_token(S); + } + att = jj_consume_token(IDENT); + label_62: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[92] = jj_gen; + break label_62; + } + jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case DASHMATCH: + case CARETMATCH: + case DOLLARMATCH: + case STARMATCH: + case INCLUDES: + case EQ: + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case EQ: + jj_consume_token(EQ); + cases = 1; + break; + case INCLUDES: + jj_consume_token(INCLUDES); + cases = 2; + break; + case DASHMATCH: + jj_consume_token(DASHMATCH); + cases = 3; + break; + case CARETMATCH: + jj_consume_token(CARETMATCH); + cases = 4; + break; + case DOLLARMATCH: + jj_consume_token(DOLLARMATCH); + cases = 5; + break; + case STARMATCH: + jj_consume_token(STARMATCH); + cases = 6; + break; + default: + jj_la1[93] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + label_63: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[94] = jj_gen; + break label_63; + } + jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IDENT: + val = jj_consume_token(IDENT); + attValue = val.image; + break; + case STRING: + val = jj_consume_token(STRING); + attValue = val.image; + break; + default: + jj_la1[95] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + label_64: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[96] = jj_gen; + break label_64; + } + jj_consume_token(S); + } + break; + default: + jj_la1[97] = jj_gen; + ; + } + jj_consume_token(RBRACKET); + String name = convertIdent(att.image); + String c; + switch (cases) { + case 0: + c = name; + break; + case 1: + c = name + "=" + attValue; + break; + case 2: + c = name + "~=" + attValue; + break; + case 3: + c = name + "|=" +attValue; + break; + case 4: + c = name + "^=" +attValue; + break; + case 5: + c = name + "$=" +attValue; + break; + case 6: + c = name + "*=" +attValue; + break; + default: + // never reached. + c = null; + } + c = "[" + c + "]"; + if (pred == null) { + {if (true) return c;} + } else { + {if (true) return pred + c;} + } + throw new Error("Missing return statement in function"); + } - } catch (ParseException e) { - if (errorHandler != null) { - if (e.currentToken != null) { - LocatorImpl li = new LocatorImpl(this, - e.currentToken.next.beginLine, - e.currentToken.next.beginColumn - 1); - reportError(li, e); +/** + * @exception ParseException exception during the parse + */ + final public String pseudo(String pred) throws ParseException { + Token n; +Token param; +String d; +boolean isPseudoElement = false; + jj_consume_token(COLON); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COLON: + jj_consume_token(COLON); + isPseudoElement=true; + break; + default: + jj_la1[98] = jj_gen; + ; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IDENT: + n = jj_consume_token(IDENT); + String s = ":" + convertIdent(n.image); + if (isPseudoElement) { + if (pseudoElt != null) { + {if (true) throw new CSSParseException("duplicate pseudo element definition " + + s, getLocator());} } else { - reportError(getLocator(), e); + pseudoElt = ":"+s; + {if (true) return pred;} } - skipAfterExpression(); - /* - * LocatorImpl loc = (LocatorImpl) getLocator(); loc.column--; - * reportWarningSkipText(loc, skipAfterExpression()); - */ } else { - skipAfterExpression(); - } - } - } - - /** - * @exception ParseException - * exception during the parse - */ - final public void declarationOrNestedProperties() throws ParseException { - boolean important = false; - String name; - LexicalUnitImpl exp; - Token save; - String comment = null; - try { - name = property(); - save = token; - jj_consume_token(COLON); - label_145: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[210] = jj_gen; - break label_145; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case MINUS: - case DOT: - case TO: - case THROUGH: - case FROM: - case STRING: - case IDENT: - case NUMBER: - case URL: - case VARIABLE: - case PERCENTAGE: - case PT: - case MM: - case CM: - case PC: - case IN: - case PX: - case EMS: - case LEM: - case REM: - case EXS: - case DEG: - case RAD: - case GRAD: - case MS: - case SECOND: - case HZ: - case KHZ: - case DIMEN: - case HASH: - case UNICODERANGE: - case FUNCTION: - exp = expr(); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case IMPORTANT_SYM: - important = prio(); - break; - default: - jj_la1[211] = jj_gen; - ; - } - Token next = getToken(1); - if (next.kind == SEMICOLON || next.kind == RBRACE) { - while (next.kind == SEMICOLON) { - skipStatement(); - next = getToken(1); - } - if (token.specialToken != null) { - documentHandler.property(name, exp, important, - token.specialToken.image); - } else { - documentHandler.property(name, exp, important, null); - } - } - break; - case LBRACE: - jj_consume_token(LBRACE); - label_146: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[212] = jj_gen; - break label_146; - } - jj_consume_token(S); - } - documentHandler.startNestedProperties(name); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case INTERPOLATION: - case IDENT: - declaration(); - break; - default: - jj_la1[213] = jj_gen; - ; - } - label_147: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case SEMICOLON: - ; - break; - default: - jj_la1[214] = jj_gen; - break label_147; - } - jj_consume_token(SEMICOLON); - label_148: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[215] = jj_gen; - break label_148; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case INTERPOLATION: - case IDENT: - declaration(); - break; - default: - jj_la1[216] = jj_gen; - ; - } - } - jj_consume_token(RBRACE); - label_149: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[217] = jj_gen; - break label_149; - } - jj_consume_token(S); - } - documentHandler.endNestedProperties(name); - break; - default: - jj_la1[218] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - } catch (JumpException e) { - skipAfterExpression(); - // reportWarningSkipText(getLocator(), skipAfterExpression()); - - } catch (NumberFormatException e) { - if (errorHandler != null) { - errorHandler.error(new CSSParseException("Invalid number " - + e.getMessage(), getLocator(), e)); - } - reportWarningSkipText(getLocator(), skipAfterExpression()); - } catch (ParseException e) { - if (errorHandler != null) { - if (e.currentToken != null) { - LocatorImpl li = new LocatorImpl(this, - e.currentToken.next.beginLine, - e.currentToken.next.beginColumn - 1); - reportError(li, e); + String c = s; + if (pred == null) { + {if (true) return c;} } else { - reportError(getLocator(), e); + {if (true) return pred + c;} } - skipAfterExpression(); - /* - * LocatorImpl loc = (LocatorImpl) getLocator(); loc.column--; - * reportWarningSkipText(loc, skipAfterExpression()); - */ - } else { - skipAfterExpression(); } - } - } + break; + case FUNCTION: + n = jj_consume_token(FUNCTION); + label_65: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[99] = jj_gen; + break label_65; + } + jj_consume_token(S); + } + d = skipStatementUntilRightParan(); + jj_consume_token(RPARAN); + // accept anything between function and a right parenthesis + String f = convertIdent(n.image); + String colons = isPseudoElement ? "::" : ":"; + String pseudofn = colons + f + d + ")"; + if (pred == null) { + {if (true) return pseudofn;} + } else { + {if (true) return pred + pseudofn;} + } + break; + default: + jj_la1[100] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + throw new Error("Missing return statement in function"); + } - /** - * @exception ParseException - * exception during the parse - */ - final public void declaration() throws ParseException { - boolean important = false; +/** + * @exception ParseException exception during the parse + */ + final public String hash(String pred) throws ParseException { + Token n; + n = jj_consume_token(HASH); + String d = n.image; + if (pred == null) { + {if (true) return d;} + } else { + {if (true) return pred + d;} + } + throw new Error("Missing return statement in function"); + } + + final public void variable() throws ParseException { String name; - LexicalUnit exp; - Token save; - try { - name = property(); - save = token; - jj_consume_token(COLON); - label_150: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[219] = jj_gen; - break label_150; - } - jj_consume_token(S); - } - exp = expr(); - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case IMPORTANT_SYM: - important = prio(); - break; - default: - jj_la1[220] = jj_gen; - ; - } - documentHandler.property(name, exp, important); - } catch (JumpException e) { + LexicalUnitImpl exp = null; + boolean guarded = false; + String raw; + try { + name = variableName(); + jj_consume_token(COLON); + label_66: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[101] = jj_gen; + break label_66; + } + jj_consume_token(S); + } + exp = expr(); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case GUARDED_SYM: + guarded = guarded(); + break; + default: + jj_la1[102] = jj_gen; + ; + } + label_67: + while (true) { + jj_consume_token(SEMICOLON); + label_68: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[103] = jj_gen; + break label_68; + } + jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case SEMICOLON: + ; + break; + default: + jj_la1[104] = jj_gen; + break label_67; + } + } + documentHandler.variable(name, exp, guarded); + } catch (JumpException e) { skipAfterExpression(); - // reportWarningSkipText(getLocator(), skipAfterExpression()); - - } catch (NumberFormatException e) { + } catch (NumberFormatException e) { if (errorHandler != null) { errorHandler.error(new CSSParseException("Invalid number " - + e.getMessage(), getLocator(), e)); + + e.getMessage(), + getLocator(), + e)); } reportWarningSkipText(getLocator(), skipAfterExpression()); - } catch (ParseException e) { + } catch (ParseException e) { if (errorHandler != null) { if (e.currentToken != null) { - LocatorImpl li = new LocatorImpl(this, - e.currentToken.next.beginLine, - e.currentToken.next.beginColumn - 1); - reportError(li, e); + LocatorImpl li = new LocatorImpl(this, + e.currentToken.next.beginLine, + e.currentToken.next.beginColumn-1); + reportError(li, e); } else { - reportError(getLocator(), e); - } + reportError(getLocator(), e); + } skipAfterExpression(); - /* - * LocatorImpl loc = (LocatorImpl) getLocator(); loc.column--; - * reportWarningSkipText(loc, skipAfterExpression()); - */ } else { skipAfterExpression(); } - } } - - /** - * @exception ParseException - * exception during the parse - */ - final public boolean prio() throws ParseException { - jj_consume_token(IMPORTANT_SYM); - label_151: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[221] = jj_gen; - break label_151; - } - jj_consume_token(S); - } - { - if (true) { - return true; - } + } + + final public void controlDirective() throws ParseException { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IF_SYM: + ifDirective(); + break; + case EACH_SYM: + eachDirective(); + break; + default: + jj_la1[105] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + + final public void ifContentStatement() throws ParseException { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case CONTENT_SYM: + contentDirective(); + break; + case INCLUDE_SYM: + includeDirective(); + break; + case MEDIA_SYM: + media(); + break; + case EXTEND_SYM: + extendDirective(); + break; + case PLUS: + case PRECEDES: + case SIBLING: + case LBRACKET: + case ANY: + case PARENT: + case DOT: + case COLON: + case INTERPOLATION: + case DEBUG_SYM: + case WARN_SYM: + case IDENT: + case HASH: + styleRuleOrDeclarationOrNestedProperties(); + break; + case KEY_FRAME_SYM: + keyframes(); + break; + default: + jj_la1[106] = jj_gen; + if (jj_2_3(2147483647)) { + variable(); + } else { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case VARIABLE: + listModifyDirective(); + break; + default: + jj_la1[107] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + } + } + + final public void ifDirective() throws ParseException { + Token n = null; + String s = null; + String evaluator = ""; + jj_consume_token(IF_SYM); + label_69: + while (true) { + s = booleanExpressionToken(); + evaluator += s; + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + case EQ: + case PLUS: + case MINUS: + case PRECEDES: + case SUCCEEDS: + case DIV: + case ANY: + case LPARAN: + case RPARAN: + case COMPARE: + case OR: + case AND: + case NOT_EQ: + case IDENT: + case NUMBER: + case VARIABLE: + case CONTAINS: + ; + break; + default: + jj_la1[108] = jj_gen; + break label_69; + } + } + jj_consume_token(LBRACE); + label_70: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[109] = jj_gen; + break label_70; + } + jj_consume_token(S); + } + documentHandler.startIfElseDirective(); + documentHandler.ifDirective(evaluator); + label_71: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case PRECEDES: + case SIBLING: + case LBRACKET: + case ANY: + case PARENT: + case DOT: + case COLON: + case INTERPOLATION: + case INCLUDE_SYM: + case DEBUG_SYM: + case WARN_SYM: + case EXTEND_SYM: + case CONTENT_SYM: + case IDENT: + case VARIABLE: + case HASH: + case MEDIA_SYM: + case KEY_FRAME_SYM: + ; + break; + default: + jj_la1[110] = jj_gen; + break label_71; + } + ifContentStatement(); + } + jj_consume_token(RBRACE); + label_72: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[111] = jj_gen; + break label_72; + } + jj_consume_token(S); + } + label_73: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case ELSE_SYM: + ; + break; + default: + jj_la1[112] = jj_gen; + break label_73; + } + elseDirective(); + } + documentHandler.endIfElseDirective(); + } + + final public void elseDirective() throws ParseException { + String evaluator = ""; + Token n = null; + String s = null; + jj_consume_token(ELSE_SYM); + label_74: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[113] = jj_gen; + break label_74; + } + jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IF: + jj_consume_token(IF); + label_75: + while (true) { + s = booleanExpressionToken(); + evaluator += s; + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + case EQ: + case PLUS: + case MINUS: + case PRECEDES: + case SUCCEEDS: + case DIV: + case ANY: + case LPARAN: + case RPARAN: + case COMPARE: + case OR: + case AND: + case NOT_EQ: + case IDENT: + case NUMBER: + case VARIABLE: + case CONTAINS: + ; + break; + default: + jj_la1[114] = jj_gen; + break label_75; + } + } + break; + default: + jj_la1[115] = jj_gen; + ; + } + jj_consume_token(LBRACE); + label_76: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[116] = jj_gen; + break label_76; + } + jj_consume_token(S); + } + if(!evaluator.trim().equals("")){ documentHandler.ifDirective(evaluator); } + else{ documentHandler.elseDirective(); } + label_77: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case PRECEDES: + case SIBLING: + case LBRACKET: + case ANY: + case PARENT: + case DOT: + case COLON: + case INTERPOLATION: + case INCLUDE_SYM: + case DEBUG_SYM: + case WARN_SYM: + case EXTEND_SYM: + case CONTENT_SYM: + case IDENT: + case VARIABLE: + case HASH: + case MEDIA_SYM: + case KEY_FRAME_SYM: + ; + break; + default: + jj_la1[117] = jj_gen; + break label_77; + } + ifContentStatement(); + } + jj_consume_token(RBRACE); + label_78: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[118] = jj_gen; + break label_78; + } + jj_consume_token(S); + } + } + + final public String booleanExpressionToken() throws ParseException { + Token n = null; + String s = null; + if (jj_2_4(2147483647)) { + s = containsDirective(); + } else { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case VARIABLE: + n = jj_consume_token(VARIABLE); + break; + case IDENT: + n = jj_consume_token(IDENT); + break; + case NUMBER: + n = jj_consume_token(NUMBER); + break; + case LPARAN: + n = jj_consume_token(LPARAN); + break; + case RPARAN: + n = jj_consume_token(RPARAN); + break; + case PLUS: + n = jj_consume_token(PLUS); + break; + case MINUS: + n = jj_consume_token(MINUS); + break; + case DIV: + n = jj_consume_token(DIV); + break; + case ANY: + n = jj_consume_token(ANY); + break; + case COMPARE: + n = jj_consume_token(COMPARE); + break; + case EQ: + n = jj_consume_token(EQ); + break; + case PRECEDES: + n = jj_consume_token(PRECEDES); + break; + case SUCCEEDS: + n = jj_consume_token(SUCCEEDS); + break; + case OR: + n = jj_consume_token(OR); + break; + case AND: + n = jj_consume_token(AND); + break; + case S: + n = jj_consume_token(S); + break; + case NOT_EQ: + n = jj_consume_token(NOT_EQ); + break; + default: + jj_la1[119] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + if(n!=null){{if (true) return n.image;}} + else{{if (true) return s;}} + throw new Error("Missing return statement in function"); + } + + final public void eachDirective() throws ParseException { + Token var; + ArrayList<String> list = null; + String listVariable = null; + jj_consume_token(EACH_SYM); + label_79: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[120] = jj_gen; + break label_79; + } + jj_consume_token(S); + } + var = jj_consume_token(VARIABLE); + label_80: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[121] = jj_gen; + break label_80; + } + jj_consume_token(S); + } + jj_consume_token(EACH_IN); + label_81: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[122] = jj_gen; + break label_81; + } + jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IDENT: + list = stringList(); + documentHandler.startEachDirective(var.image, list); + break; + case VARIABLE: + listVariable = variableName(); + documentHandler.startEachDirective(var.image, listVariable); + break; + default: + jj_la1[123] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + jj_consume_token(LBRACE); + label_82: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[124] = jj_gen; + break label_82; + } + jj_consume_token(S); + } + label_83: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case PRECEDES: + case SIBLING: + case LBRACKET: + case ANY: + case PARENT: + case DOT: + case COLON: + case INTERPOLATION: + case INCLUDE_SYM: + case DEBUG_SYM: + case WARN_SYM: + case EXTEND_SYM: + case CONTENT_SYM: + case IDENT: + case VARIABLE: + case HASH: + case MEDIA_SYM: + case KEY_FRAME_SYM: + ; + break; + default: + jj_la1[125] = jj_gen; + break label_83; + } + ifContentStatement(); + } + jj_consume_token(RBRACE); + label_84: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[126] = jj_gen; + break label_84; + } + jj_consume_token(S); + } + documentHandler.endEachDirective(); + } + + final public ArrayList<String > stringList() throws ParseException { + ArrayList<String > strings = new ArrayList<String >(); + Token input; + input = jj_consume_token(IDENT); + label_85: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[127] = jj_gen; + break label_85; + } + jj_consume_token(S); + } + strings.add(input.image); + label_86: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMA: + ; + break; + default: + jj_la1[128] = jj_gen; + break label_86; + } + jj_consume_token(COMMA); + label_87: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[129] = jj_gen; + break label_87; + } + jj_consume_token(S); + } + input = jj_consume_token(IDENT); + strings.add(input.image); + label_88: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[130] = jj_gen; + break label_88; + } + jj_consume_token(S); + } + } + {if (true) return strings;} + throw new Error("Missing return statement in function"); + } + + final public void mixinDirective() throws ParseException { + String name; + ArrayList<VariableNode> args = null; + String body; + jj_consume_token(MIXIN_SYM); + label_89: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[131] = jj_gen; + break label_89; + } + jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTERPOLATION: + case IDENT: + name = property(); + break; + case FUNCTION: + name = functionName(); + args = arglist(); + jj_consume_token(RPARAN); + label_90: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[132] = jj_gen; + break label_90; + } + jj_consume_token(S); + } + break; + default: + jj_la1[133] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + jj_consume_token(LBRACE); + label_91: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[134] = jj_gen; + break label_91; + } + jj_consume_token(S); + } + documentHandler.startMixinDirective(name, args); + label_92: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case PRECEDES: + case SIBLING: + case LBRACKET: + case ANY: + case PARENT: + case DOT: + case COLON: + case INTERPOLATION: + case INCLUDE_SYM: + case DEBUG_SYM: + case WARN_SYM: + case EACH_SYM: + case IF_SYM: + case EXTEND_SYM: + case CONTENT_SYM: + case IDENT: + case VARIABLE: + case HASH: + case MEDIA_SYM: + case PAGE_SYM: + case FONT_FACE_SYM: + case KEY_FRAME_SYM: + ; + break; + default: + jj_la1[135] = jj_gen; + break label_92; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case PRECEDES: + case SIBLING: + case LBRACKET: + case ANY: + case PARENT: + case DOT: + case COLON: + case INTERPOLATION: + case INCLUDE_SYM: + case DEBUG_SYM: + case WARN_SYM: + case EXTEND_SYM: + case CONTENT_SYM: + case IDENT: + case VARIABLE: + case HASH: + case MEDIA_SYM: + case KEY_FRAME_SYM: + ifContentStatement(); + break; + case EACH_SYM: + case IF_SYM: + controlDirective(); + break; + case FONT_FACE_SYM: + fontFace(); + break; + case PAGE_SYM: + page(); + break; + default: + jj_la1[136] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + jj_consume_token(RBRACE); + label_93: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[137] = jj_gen; + break label_93; + } + jj_consume_token(S); + } + documentHandler.endMixinDirective(name, args); + } + + final public ArrayList<VariableNode> arglist() throws ParseException { + ArrayList<VariableNode> args = new ArrayList<VariableNode>(); + VariableNode arg; + boolean hasNonOptionalArgument = false; + arg = mixinArg(); + label_94: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMA: + ; + break; + default: + jj_la1[138] = jj_gen; + break label_94; + } + jj_consume_token(COMMA); + label_95: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[139] = jj_gen; + break label_95; } - throw new Error("Missing return statement in function"); + jj_consume_token(S); + } + hasNonOptionalArgument = checkMixinForNonOptionalArguments(arg, hasNonOptionalArgument); args.add(arg); + arg = mixinArg(); } + hasNonOptionalArgument = checkMixinForNonOptionalArguments(arg, hasNonOptionalArgument); args.add(arg); + {if (true) return args;} + throw new Error("Missing return statement in function"); + } - final public boolean guarded() throws ParseException { - jj_consume_token(GUARDED_SYM); - label_152: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[222] = jj_gen; - break label_152; - } - jj_consume_token(S); - } - { - if (true) { - return true; - } - } - throw new Error("Missing return statement in function"); - } + boolean checkMixinForNonOptionalArguments(VariableNode arg, boolean hasNonOptionalArguments) throws ParseException { + boolean currentArgHasArguments = arg.getExpr() != null && arg.getExpr().getLexicalUnitType() == LexicalUnitImpl.SCSS_VARIABLE && arg.getExpr().getNextLexicalUnit() != null; - /** - * @exception ParseException - * exception during the parse - */ - final public LexicalUnitImpl operator(LexicalUnitImpl prev) - throws ParseException { - Token n; - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case DIV: - n = jj_consume_token(DIV); - label_153: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[223] = jj_gen; - break label_153; - } - jj_consume_token(S); - } - { - if (true) { - return LexicalUnitImpl.createSlash(n.beginLine, - n.beginColumn, prev); - } - } - break; - case COMMA: - n = jj_consume_token(COMMA); - label_154: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[224] = jj_gen; - break label_154; - } - jj_consume_token(S); - } - { - if (true) { - return LexicalUnitImpl.createComma(n.beginLine, - n.beginColumn, prev); + if(currentArgHasArguments) + { + if(hasNonOptionalArguments) + { + throw new ParseException("Sass Error: Required argument $"+ arg.getName() +" must come before any optional arguments."); } - } + return hasNonOptionalArguments; + }else + { + return true; + } + } + + final public VariableNode mixinArg() throws ParseException { + String name; + Token variable = null; + LexicalUnitImpl first = null; + LexicalUnitImpl prev = null; + LexicalUnitImpl next = null; + name = variableName(); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COLON: + case VARIABLE: + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COLON: + jj_consume_token(COLON); + label_96: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; break; - default: - jj_la1[225] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); + default: + jj_la1[140] = jj_gen; + break label_96; + } + jj_consume_token(S); } - throw new Error("Missing return statement in function"); - } - - /** - * @exception ParseException - * exception during the parse - */ - final public LexicalUnitImpl expr() throws ParseException { - LexicalUnitImpl first, res; - char op; - first = term(null); - res = first; - label_155: while (true) { - if (jj_2_8(2)) { + first = nonVariableTerm(null); + prev = first; + label_97: + while (true) { + if (jj_2_5(3)) { + ; + } else { + break label_97; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMA: + jj_consume_token(COMMA); + label_98: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: ; - } else { - break label_155; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case COMMA: - case DIV: - res = operator(res); break; - default: - jj_la1[226] = jj_gen; - ; - } - res = term(res); - } - { - if (true) { - return first; - } - } - throw new Error("Missing return statement in function"); - } - - /** - * @exception ParseException - * exception during the parse - */ - final public char unaryOperator() throws ParseException { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case MINUS: - jj_consume_token(MINUS); - { - if (true) { - return '-'; - } + default: + jj_la1[141] = jj_gen; + break label_98; + } + jj_consume_token(S); } break; - case PLUS: - jj_consume_token(PLUS); - { - if (true) { - return '+'; - } - } + default: + jj_la1[142] = jj_gen; + ; + } + prev = nonVariableTerm(prev); + } + break; + case VARIABLE: + variable = jj_consume_token(VARIABLE); + first = LexicalUnitImpl.createVariable(token.beginLine, token.beginColumn, + prev, variable.image); + break; + default: + jj_la1[143] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + break; + default: + jj_la1[144] = jj_gen; + ; + } + VariableNode arg = new VariableNode(name, first, false); + {if (true) return arg;} + throw new Error("Missing return statement in function"); + } + + final public ArrayList<LexicalUnitImpl> argValuelist() throws ParseException { + ArrayList<LexicalUnitImpl> args = new ArrayList<LexicalUnitImpl>(); + LexicalUnitImpl first = null; + LexicalUnitImpl next = null; + LexicalUnitImpl prev = null; + first = term(null); + args.add(first); prev = first; + label_99: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case MINUS: + case DOT: + case COLON: + case TO: + case THROUGH: + case FROM: + case STRING: + case IDENT: + case NUMBER: + case URL: + case VARIABLE: + case PERCENTAGE: + case PT: + case MM: + case CM: + case PC: + case IN: + case PX: + case EMS: + case LEM: + case REM: + case EXS: + case DEG: + case RAD: + case GRAD: + case MS: + case SECOND: + case HZ: + case KHZ: + case DIMEN: + case HASH: + case UNICODERANGE: + case FUNCTION: + ; + break; + default: + jj_la1[145] = jj_gen; + break label_99; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COLON: + jj_consume_token(COLON); + label_100: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; break; + default: + jj_la1[146] = jj_gen; + break label_100; + } + jj_consume_token(S); + } + break; + default: + jj_la1[147] = jj_gen; + ; + } + next = term(prev); + prev.setNextLexicalUnit(next); prev = next; + } + label_101: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMA: + ; + break; + default: + jj_la1[148] = jj_gen; + break label_101; + } + jj_consume_token(COMMA); + label_102: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; default: - jj_la1[227] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - throw new Error("Missing return statement in function"); - } - - /** - * @exception ParseException - * exception during the parse - */ - final public LexicalUnitImpl term(LexicalUnitImpl prev) - throws ParseException { - LexicalUnitImpl result = null; - Token n = null; - char op = ' '; - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + jj_la1[149] = jj_gen; + break label_102; + } + jj_consume_token(S); + } + first = term(null); + args.add(first); prev = first; + label_103: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case PLUS: case MINUS: case DOT: + case COLON: case TO: case THROUGH: case FROM: @@ -5145,6 +3612,7 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { case IDENT: case NUMBER: case URL: + case VARIABLE: case PERCENTAGE: case PT: case MM: @@ -5167,395 +3635,2005 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { case HASH: case UNICODERANGE: case FUNCTION: - result = nonVariableTerm(prev); - break; - case VARIABLE: - result = variableTerm(prev); - break; + ; + break; default: - jj_la1[228] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); + jj_la1[150] = jj_gen; + break label_103; } - { - if (true) { - return result; + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COLON: + jj_consume_token(COLON); + label_104: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[151] = jj_gen; + break label_104; } + jj_consume_token(S); + } + break; + default: + jj_la1[152] = jj_gen; + ; + } + next = term(prev); + prev.setNextLexicalUnit(next); prev = next; + } + } + {if (true) return args;} + throw new Error("Missing return statement in function"); + } + + final public void includeDirective() throws ParseException { + String name; + ArrayList<LexicalUnitImpl> args=null; + jj_consume_token(INCLUDE_SYM); + label_105: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[153] = jj_gen; + break label_105; + } + jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTERPOLATION: + case IDENT: + name = property(); + break; + case VARIABLE: + name = variableName(); + name = "$"+name; + break; + case FUNCTION: + name = functionName(); + args = argValuelist(); + jj_consume_token(RPARAN); + break; + default: + jj_la1[154] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case SEMICOLON: + label_106: + while (true) { + jj_consume_token(SEMICOLON); + label_107: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[155] = jj_gen; + break label_107; + } + jj_consume_token(S); } - throw new Error("Missing return statement in function"); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case SEMICOLON: + ; + break; + default: + jj_la1[156] = jj_gen; + break label_106; + } + } + documentHandler.includeDirective(name, args); + break; + case LBRACE: + jj_consume_token(LBRACE); + label_108: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[157] = jj_gen; + break label_108; + } + jj_consume_token(S); + } + documentHandler.startIncludeContentBlock(name); + label_109: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case PRECEDES: + case SIBLING: + case LBRACKET: + case ANY: + case PARENT: + case DOT: + case COLON: + case INTERPOLATION: + case DEBUG_SYM: + case WARN_SYM: + case IDENT: + case HASH: + ; + break; + default: + jj_la1[158] = jj_gen; + break label_109; + } + styleRuleOrDeclarationOrNestedProperties(); + } + jj_consume_token(RBRACE); + label_110: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[159] = jj_gen; + break label_110; + } + jj_consume_token(S); + } + documentHandler.endIncludeContentBlock(); + break; + default: + jj_la1[160] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + + final public String interpolation() throws ParseException { + Token n; + n = jj_consume_token(INTERPOLATION); + {if (true) return n.image;} + throw new Error("Missing return statement in function"); + } + + final public void listModifyDirective() throws ParseException { + String list = null; + String remove = null; + String separator = null; + String variable = null; + Token n = null; + Token type = null; + //refactor, remove those 3 LOOKAHEAD(5). + n = jj_consume_token(VARIABLE); + variable = n.image; + label_111: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[161] = jj_gen; + break label_111; + } + jj_consume_token(S); + } + jj_consume_token(COLON); + label_112: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[162] = jj_gen; + break label_112; + } + jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case APPEND: + type = jj_consume_token(APPEND); + break; + case REMOVE: + type = jj_consume_token(REMOVE); + break; + case CONTAINS: + type = jj_consume_token(CONTAINS); + break; + default: + jj_la1[163] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + label_113: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[164] = jj_gen; + break label_113; + } + jj_consume_token(S); + } + list = listModifyDirectiveArgs(0); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case RPARAN: + jj_consume_token(RPARAN); + break; + default: + jj_la1[165] = jj_gen; + ; + } + jj_consume_token(COMMA); + label_114: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[166] = jj_gen; + break label_114; + } + jj_consume_token(S); + } + remove = listModifyDirectiveArgs(1); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMA: + jj_consume_token(COMMA); + label_115: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[167] = jj_gen; + break label_115; + } + jj_consume_token(S); + } + n = jj_consume_token(IDENT); + separator = n.image; + label_116: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[168] = jj_gen; + break label_116; + } + jj_consume_token(S); + } + break; + default: + jj_la1[169] = jj_gen; + ; + } + jj_consume_token(RPARAN); + switch (type.kind) { + case APPEND: + documentHandler.appendDirective(variable,list,remove,separator); + break; + case REMOVE: + documentHandler.removeDirective(variable,list,remove,separator); + break; + case CONTAINS: + if(variable == null){ + variable = "$var_"+UUID.randomUUID(); + } + documentHandler.containsDirective(variable,list,remove,separator); + break; + default: + break; + } + label_117: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[170] = jj_gen; + break label_117; + } + jj_consume_token(S); + } + jj_consume_token(SEMICOLON); + label_118: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[171] = jj_gen; + break label_118; + } + jj_consume_token(S); + } + } + +/** + * @exception ParseException exception during the parse + */ + final public void appendDirective() throws ParseException { + String list = null; + String remove = null; + String separator = null; + String variable = null; + Token n = null; + n = jj_consume_token(VARIABLE); + variable = n.image; + label_119: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[172] = jj_gen; + break label_119; + } + jj_consume_token(S); + } + jj_consume_token(COLON); + label_120: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[173] = jj_gen; + break label_120; + } + jj_consume_token(S); + } + jj_consume_token(APPEND); + label_121: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[174] = jj_gen; + break label_121; + } + jj_consume_token(S); + } + list = listModifyDirectiveArgs(0); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case RPARAN: + jj_consume_token(RPARAN); + break; + default: + jj_la1[175] = jj_gen; + ; + } + jj_consume_token(COMMA); + label_122: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[176] = jj_gen; + break label_122; + } + jj_consume_token(S); + } + remove = listModifyDirectiveArgs(1); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMA: + jj_consume_token(COMMA); + label_123: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[177] = jj_gen; + break label_123; + } + jj_consume_token(S); + } + n = jj_consume_token(IDENT); + separator = n.image; + label_124: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[178] = jj_gen; + break label_124; + } + jj_consume_token(S); + } + break; + default: + jj_la1[179] = jj_gen; + ; } + jj_consume_token(RPARAN); + documentHandler.appendDirective(variable,list,remove,separator); + } - final public LexicalUnitImpl variableTerm(LexicalUnitImpl prev) - throws ParseException { - LexicalUnitImpl result = null; - String varName = ""; - varName = variableName(); - result = LexicalUnitImpl.createVariable(token.beginLine, - token.beginColumn, prev, varName); - { - if (true) { - return result; - } +/** + * @exception ParseException exception during the parse + */ + final public void removeDirective() throws ParseException { + String list = null; + String remove = null; + String separator = null; + String variable = null; + Token n = null; + n = jj_consume_token(VARIABLE); + variable = n.image; + label_125: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[180] = jj_gen; + break label_125; + } + jj_consume_token(S); + } + jj_consume_token(COLON); + label_126: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[181] = jj_gen; + break label_126; + } + jj_consume_token(S); + } + jj_consume_token(REMOVE); + label_127: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[182] = jj_gen; + break label_127; + } + jj_consume_token(S); + } + list = listModifyDirectiveArgs(0); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case RPARAN: + jj_consume_token(RPARAN); + break; + default: + jj_la1[183] = jj_gen; + ; + } + jj_consume_token(COMMA); + label_128: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[184] = jj_gen; + break label_128; + } + jj_consume_token(S); + } + remove = listModifyDirectiveArgs(1); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMA: + jj_consume_token(COMMA); + label_129: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[185] = jj_gen; + break label_129; + } + jj_consume_token(S); + } + n = jj_consume_token(IDENT); + separator = n.image; + label_130: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[186] = jj_gen; + break label_130; } - throw new Error("Missing return statement in function"); + jj_consume_token(S); + } + break; + default: + jj_la1[187] = jj_gen; + ; } + jj_consume_token(RPARAN); + documentHandler.removeDirective(variable,list,remove,separator); + } - final public LexicalUnitImpl nonVariableTerm(LexicalUnitImpl prev) - throws ParseException { - LexicalUnitImpl result = null; +/** + * @exception ParseException exception during the parse + */ + final public String containsDirective() throws ParseException { + String list = null; + String remove = null; + String separator = null; + String variable = null; Token n = null; - char op = ' '; - String varName; - String s = ""; - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case MINUS: - case NUMBER: - case PERCENTAGE: - case PT: - case MM: - case CM: - case PC: - case IN: - case PX: - case EMS: - case LEM: - case REM: - case EXS: - case DEG: - case RAD: - case GRAD: - case MS: - case SECOND: - case HZ: - case KHZ: - case DIMEN: - case FUNCTION: - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case MINUS: - op = unaryOperator(); - break; - default: - jj_la1[229] = jj_gen; - ; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case NUMBER: - n = jj_consume_token(NUMBER); - result = LexicalUnitImpl.createNumber(n.beginLine, - n.beginColumn, prev, number(op, n, 0)); - break; - case PERCENTAGE: - n = jj_consume_token(PERCENTAGE); - result = LexicalUnitImpl.createPercentage(n.beginLine, - n.beginColumn, prev, number(op, n, 1)); - break; - case PT: - n = jj_consume_token(PT); - result = LexicalUnitImpl.createPT(n.beginLine, n.beginColumn, - prev, number(op, n, 2)); - break; - case CM: - n = jj_consume_token(CM); - result = LexicalUnitImpl.createCM(n.beginLine, n.beginColumn, - prev, number(op, n, 2)); - break; - case MM: - n = jj_consume_token(MM); - result = LexicalUnitImpl.createMM(n.beginLine, n.beginColumn, - prev, number(op, n, 2)); - break; - case PC: - n = jj_consume_token(PC); - result = LexicalUnitImpl.createPC(n.beginLine, n.beginColumn, - prev, number(op, n, 2)); - break; - case IN: - n = jj_consume_token(IN); - result = LexicalUnitImpl.createIN(n.beginLine, n.beginColumn, - prev, number(op, n, 2)); - break; - case PX: - n = jj_consume_token(PX); - result = LexicalUnitImpl.createPX(n.beginLine, n.beginColumn, - prev, number(op, n, 2)); - break; - case EMS: - n = jj_consume_token(EMS); - result = LexicalUnitImpl.createEMS(n.beginLine, n.beginColumn, - prev, number(op, n, 2)); - break; - case LEM: - n = jj_consume_token(LEM); - result = LexicalUnitImpl.createLEM(n.beginLine, n.beginColumn, - prev, number(op, n, 3)); - break; - case REM: - n = jj_consume_token(REM); - result = LexicalUnitImpl.createREM(n.beginLine, n.beginColumn, - prev, number(op, n, 3)); - break; - case EXS: - n = jj_consume_token(EXS); - result = LexicalUnitImpl.createEXS(n.beginLine, n.beginColumn, - prev, number(op, n, 2)); - break; - case DEG: - n = jj_consume_token(DEG); - result = LexicalUnitImpl.createDEG(n.beginLine, n.beginColumn, - prev, number(op, n, 3)); - break; - case RAD: - n = jj_consume_token(RAD); - result = LexicalUnitImpl.createRAD(n.beginLine, n.beginColumn, - prev, number(op, n, 3)); - break; - case GRAD: - n = jj_consume_token(GRAD); - result = LexicalUnitImpl.createGRAD(n.beginLine, n.beginColumn, - prev, number(op, n, 3)); - break; - case SECOND: - n = jj_consume_token(SECOND); - result = LexicalUnitImpl.createS(n.beginLine, n.beginColumn, - prev, number(op, n, 1)); - break; - case MS: - n = jj_consume_token(MS); - result = LexicalUnitImpl.createMS(n.beginLine, n.beginColumn, - prev, number(op, n, 2)); - break; - case HZ: - n = jj_consume_token(HZ); - result = LexicalUnitImpl.createHZ(n.beginLine, n.beginColumn, - prev, number(op, n, 2)); - break; - case KHZ: - n = jj_consume_token(KHZ); - result = LexicalUnitImpl.createKHZ(n.beginLine, n.beginColumn, - prev, number(op, n, 3)); - break; - case DIMEN: - n = jj_consume_token(DIMEN); - s = n.image; - int i = 0; - while (i < s.length() - && (Character.isDigit(s.charAt(i)) || (s.charAt(i) == '.'))) { - i++; - } - result = LexicalUnitImpl.createDimen(n.beginLine, - n.beginColumn, prev, Float.valueOf(s.substring(0, i)) - .floatValue(), s.substring(i)); - break; - case FUNCTION: - result = function(op, prev); - break; - default: - jj_la1[230] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } - break; - case DOT: - case TO: - case THROUGH: - case FROM: - case STRING: - case IDENT: - case URL: - case HASH: - case UNICODERANGE: - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case STRING: - n = jj_consume_token(STRING); - result = LexicalUnitImpl.createString(n.beginLine, - n.beginColumn, prev, - convertStringIndex(n.image, 1, n.image.length() - 1)); - break; - case DOT: - case TO: - case THROUGH: - case FROM: - case IDENT: - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case DOT: - jj_consume_token(DOT); - s += "."; - break; - default: - jj_la1[231] = jj_gen; - ; - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case IDENT: - n = jj_consume_token(IDENT); - break; - case TO: - n = jj_consume_token(TO); - break; - case THROUGH: - n = jj_consume_token(THROUGH); - break; - case FROM: - n = jj_consume_token(FROM); - break; - default: - jj_la1[232] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case VARIABLE: + n = jj_consume_token(VARIABLE); + variable = n.image; + label_131: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[188] = jj_gen; + break label_131; + } + jj_consume_token(S); + } + jj_consume_token(COLON); + label_132: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[189] = jj_gen; + break label_132; + } + jj_consume_token(S); + } + break; + default: + jj_la1[190] = jj_gen; + ; + } + jj_consume_token(CONTAINS); + label_133: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[191] = jj_gen; + break label_133; + } + jj_consume_token(S); + } + list = listModifyDirectiveArgs(0); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case RPARAN: + jj_consume_token(RPARAN); + break; + default: + jj_la1[192] = jj_gen; + ; + } + jj_consume_token(COMMA); + label_134: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[193] = jj_gen; + break label_134; + } + jj_consume_token(S); + } + remove = listModifyDirectiveArgs(1); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMA: + jj_consume_token(COMMA); + label_135: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[194] = jj_gen; + break label_135; + } + jj_consume_token(S); + } + n = jj_consume_token(IDENT); + separator = n.image; + label_136: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[195] = jj_gen; + break label_136; + } + jj_consume_token(S); + } + break; + default: + jj_la1[196] = jj_gen; + ; + } + jj_consume_token(RPARAN); + /* + *if it is not in the form like "$contains : contains($items, .v-button);" + *for example in @if, like "@if (contains(a b c, b))", then create a temp + *variable for contains(a b c, b); + */ + if(variable == null){ + variable = "$var_"+UUID.randomUUID(); + } + documentHandler.containsDirective(variable,list,remove,separator); + {if (true) return variable;} + throw new Error("Missing return statement in function"); + } + + String listModifyDirectiveArgs(int nest) throws ParseException { + String list = ""; + int nesting = nest; + Token t = null; + + while(true) + { + t = getToken(1); + String s = t.image; + if(t.kind == VARIABLE||t.kind == IDENT) + { + list += s; + }else if(s.toLowerCase().equals("auto")||s.toLowerCase().equals("space")||s.toLowerCase().equals("comma")) + { + int i = 2; + Token temp = getToken(i); + boolean isLast = true; + while(temp.kind != SEMICOLON) + { + if(temp.kind != RPARAN || temp.kind != S) + { + isLast = false; + } + i++; + temp = getToken(i); + } + + if(isLast) + { + return list; + } } - s += convertIdent(n.image); - if ("inherit".equals(s)) { - result = LexicalUnitImpl.createInherit(n.beginLine, - n.beginColumn, prev); - } else { - result = LexicalUnitImpl.createIdent(n.beginLine, - n.beginColumn, prev, convertIdent(n.image)); + else if(t.kind == STRING) + { + list += s.substring(1,s.length()).substring(0,s.length()-2); + + }else if(t.kind == LPARAN) + { + nesting++; + if(nesting > nest+1) + { + throw new CSSParseException("Only one ( ) pair per parameter allowed", getLocator()); + } + }else if(t.kind == RPARAN) + { + nesting--; + if(nesting == 0) + { + return list; + } + } else if(t.kind == COMMA) + { + if(nesting == nest) + { + return list; + }else + { + list += ","; + } + + }else if(t.kind == S) + { + list += " "; + } else if(t.kind == LBRACE) + { + throw new CSSParseException("Invalid token,'{' found", getLocator()); } - /* - * / Auto correction code used in the CSS Validator but must not - * be used by a conformant CSS2 parser. Common error : H1 { - * color : black background : white } - * - * Token t = getToken(1); Token semicolon = new Token(); - * semicolon.kind = SEMICOLON; semicolon.image = ";"; if (t.kind - * == COLON) { // @@SEEME. (generate a warning?) // @@SEEME if - * expression is a single ident, generate an error ? - * rejectToken(semicolon); - * - * result = prev; } / - */ + getNextToken(); + } + } + + final public Node returnDirective() throws ParseException { + String raw; + raw = skipStatement(); + {if (true) return null;} + throw new Error("Missing return statement in function"); + } + + final public void debuggingDirective() throws ParseException { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case DEBUG_SYM: + debugDirective(); + break; + case WARN_SYM: + warnDirective(); + break; + default: + jj_la1[197] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } + + final public void debugDirective() throws ParseException { + jj_consume_token(DEBUG_SYM); + String content = skipStatementUntilSemiColon(); + // TODO should evaluate the content expression, call documentHandler.debugDirective() etc. + System.out.println(content); + label_137: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[198] = jj_gen; + break label_137; + } + jj_consume_token(S); + } + } + + final public void warnDirective() throws ParseException { + jj_consume_token(WARN_SYM); + String content = skipStatementUntilSemiColon(); + // TODO should evaluate the content expression, call documentHandler.warnDirective() etc. + System.err.println(content); + label_138: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[199] = jj_gen; + break label_138; + } + jj_consume_token(S); + } + } + + final public Node forDirective() throws ParseException { + String var; + String from; + String to; + boolean exclusive; + String body; + Token tok; + var = variableName(); + int[] toThrough = {TO, THROUGH}; + from = skipStatementUntil(toThrough); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case TO: + tok = jj_consume_token(TO); + exclusive = true; + break; + case THROUGH: + tok = jj_consume_token(THROUGH); + exclusive = false; + break; + default: + jj_la1[200] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + to = skipStatementUntilLeftBrace(); + label_139: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[201] = jj_gen; + break label_139; + } + jj_consume_token(S); + } + body = skipStatement(); + {if (true) return documentHandler.forDirective(var, from, to, exclusive, body);} + throw new Error("Missing return statement in function"); + } + + final public Node whileDirective() throws ParseException { + String condition; + String body; + condition = skipStatementUntilLeftBrace(); + body = skipStatement(); + {if (true) return documentHandler.whileDirective(condition, body);} + throw new Error("Missing return statement in function"); + } + + final public void extendDirective() throws ParseException { + ArrayList<String> list; + jj_consume_token(EXTEND_SYM); + label_140: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[202] = jj_gen; + break label_140; + } + jj_consume_token(S); + } + list = selectorList(); + label_141: + while (true) { + jj_consume_token(SEMICOLON); + label_142: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[203] = jj_gen; + break label_142; + } + jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case SEMICOLON: + ; + break; + default: + jj_la1[204] = jj_gen; + break label_141; + } + } + documentHandler.extendDirective(list); + } + + final public void contentDirective() throws ParseException { + jj_consume_token(CONTENT_SYM); + label_143: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[205] = jj_gen; + break label_143; + } + jj_consume_token(S); + } + label_144: + while (true) { + jj_consume_token(SEMICOLON); + label_145: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[206] = jj_gen; + break label_145; + } + jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case SEMICOLON: + ; + break; + default: + jj_la1[207] = jj_gen; + break label_144; + } + } + documentHandler.contentDirective(); + } + + Node importDirective() throws ParseException { + return null; + } + + Node charsetDirective() throws ParseException { + return null; + } + + Node mozDocumentDirective() throws ParseException { + return null; + } + + Node supportsDirective() throws ParseException { + return null; + } + + final public void nestedProperties() throws ParseException { + String name; +LexicalUnit exp; + name = property(); + jj_consume_token(COLON); + label_146: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[208] = jj_gen; + break label_146; + } + jj_consume_token(S); + } + jj_consume_token(LBRACE); + label_147: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[209] = jj_gen; + break label_147; + } + jj_consume_token(S); + } + documentHandler.startNestedProperties(name); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTERPOLATION: + case IDENT: + declaration(); + break; + default: + jj_la1[210] = jj_gen; + ; + } + label_148: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case SEMICOLON: + ; + break; + default: + jj_la1[211] = jj_gen; + break label_148; + } + jj_consume_token(SEMICOLON); + label_149: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[212] = jj_gen; + break label_149; + } + jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTERPOLATION: + case IDENT: + declaration(); + break; + default: + jj_la1[213] = jj_gen; + ; + } + } + jj_consume_token(RBRACE); + documentHandler.endNestedProperties(name); + label_150: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[214] = jj_gen; + break label_150; + } + jj_consume_token(S); + } + } - break; - case HASH: - result = hexcolor(prev); - break; - case URL: - result = url(prev); - break; - case UNICODERANGE: - result = unicode(prev); - break; - default: - jj_la1[233] = jj_gen; - jj_consume_token(-1); - throw new ParseException(); - } +/** + * @exception ParseException exception during the parse + */ + final public void styleRuleOrDeclarationOrNestedProperties() throws ParseException { + try { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case DEBUG_SYM: + case WARN_SYM: + debuggingDirective(); + break; + default: + jj_la1[215] = jj_gen; + if (jj_2_6(2147483647)) { + styleRule(); + } else if (jj_2_7(3)) { + declarationOrNestedProperties(); + } else { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case PRECEDES: + case SIBLING: + case LBRACKET: + case ANY: + case PARENT: + case DOT: + case COLON: + case INTERPOLATION: + case IDENT: + case HASH: + styleRule(); break; - default: - jj_la1[234] = jj_gen; + default: + jj_la1[216] = jj_gen; jj_consume_token(-1); throw new ParseException(); + } + } + } + } catch (JumpException e) { + skipAfterExpression(); + // reportWarningSkipText(getLocator(), skipAfterExpression()); + + } catch (ParseException e) { + if (errorHandler != null) { + if (e.currentToken != null) { + LocatorImpl li = new LocatorImpl(this, + e.currentToken.next.beginLine, + e.currentToken.next.beginColumn-1); + reportError(li, e); + } else { + reportError(getLocator(), e); + } + skipAfterExpression(); + /* + LocatorImpl loc = (LocatorImpl) getLocator(); + loc.column--; + reportWarningSkipText(loc, skipAfterExpression()); + */ + } else { + skipAfterExpression(); + } + } + } + +/** + * @exception ParseException exception during the parse + */ + final public void declarationOrNestedProperties() throws ParseException { + boolean important = false; + String name; + LexicalUnitImpl exp; + Token save; + String comment = null; + try { + name = property(); + save = token; + jj_consume_token(COLON); + label_151: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[217] = jj_gen; + break label_151; + } + jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case MINUS: + case DOT: + case TO: + case THROUGH: + case FROM: + case STRING: + case IDENT: + case NUMBER: + case URL: + case VARIABLE: + case PERCENTAGE: + case PT: + case MM: + case CM: + case PC: + case IN: + case PX: + case EMS: + case LEM: + case REM: + case EXS: + case DEG: + case RAD: + case GRAD: + case MS: + case SECOND: + case HZ: + case KHZ: + case DIMEN: + case HASH: + case UNICODERANGE: + case FUNCTION: + exp = expr(); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IMPORTANT_SYM: + important = prio(); + break; + default: + jj_la1[218] = jj_gen; + ; + } + Token next = getToken(1); + if(next.kind == SEMICOLON || next.kind == RBRACE){ + while(next.kind == SEMICOLON){ + skipStatement(); + next = getToken(1); + } + if(token.specialToken!=null){ + documentHandler.property(name, exp, important, token.specialToken.image); + }else{ + documentHandler.property(name, exp, important, null); + } + } + break; + case LBRACE: + jj_consume_token(LBRACE); + label_152: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[219] = jj_gen; + break label_152; + } + jj_consume_token(S); + } + documentHandler.startNestedProperties(name); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTERPOLATION: + case IDENT: + declaration(); + break; + default: + jj_la1[220] = jj_gen; + ; } - label_156: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { + label_153: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case SEMICOLON: + ; + break; + default: + jj_la1[221] = jj_gen; + break label_153; + } + jj_consume_token(SEMICOLON); + label_154: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case S: - ; - break; + ; + break; default: - jj_la1[235] = jj_gen; - break label_156; + jj_la1[222] = jj_gen; + break label_154; } jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTERPOLATION: + case IDENT: + declaration(); + break; + default: + jj_la1[223] = jj_gen; + ; + } } - { - if (true) { - return result; - } - } - throw new Error("Missing return statement in function"); + jj_consume_token(RBRACE); + label_155: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[224] = jj_gen; + break label_155; + } + jj_consume_token(S); + } + documentHandler.endNestedProperties(name); + break; + default: + jj_la1[225] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + } catch (JumpException e) { + skipAfterExpression(); + // reportWarningSkipText(getLocator(), skipAfterExpression()); + + } catch (NumberFormatException e) { + if (errorHandler != null) { + errorHandler.error(new CSSParseException("Invalid number " + + e.getMessage(), + getLocator(), + e)); + } + reportWarningSkipText(getLocator(), skipAfterExpression()); + } catch (ParseException e) { + if (errorHandler != null) { + if (e.currentToken != null) { + LocatorImpl li = new LocatorImpl(this, + e.currentToken.next.beginLine, + e.currentToken.next.beginColumn-1); + reportError(li, e); + } else { + reportError(getLocator(), e); + } + skipAfterExpression(); + /* + LocatorImpl loc = (LocatorImpl) getLocator(); + loc.column--; + reportWarningSkipText(loc, skipAfterExpression()); + */ + } else { + skipAfterExpression(); + } } + } - /** - * Handle all CSS2 functions. - * - * @exception ParseException - * exception during the parse - */ - final public LexicalUnitImpl function(char operator, LexicalUnitImpl prev) - throws ParseException { - Token n; - LexicalUnit params = null; - n = jj_consume_token(FUNCTION); - label_157: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[236] = jj_gen; - break label_157; - } - jj_consume_token(S); - } - String fname = convertIdent(n.image); - if ("alpha(".equals(fname)) { - String body = skipStatementUntilSemiColon(); - { - if (true) { - return LexicalUnitImpl.createIdent(n.beginLine, - n.beginColumn, null, "alpha(" + body); - } - } - } else if ("expression(".equals(fname)) { - String body = skipStatementUntilSemiColon(); - { - if (true) { - return LexicalUnitImpl.createIdent(n.beginLine, - n.beginColumn, null, "expression(" + body); - } +/** + * @exception ParseException exception during the parse + */ + final public void declaration() throws ParseException { + boolean important = false; + String name; + LexicalUnit exp; + Token save; + try { + name = property(); + save = token; + jj_consume_token(COLON); + label_156: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[226] = jj_gen; + break label_156; + } + jj_consume_token(S); + } + exp = expr(); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IMPORTANT_SYM: + important = prio(); + break; + default: + jj_la1[227] = jj_gen; + ; + } + documentHandler.property(name, exp, important); + } catch (JumpException e) { + skipAfterExpression(); + // reportWarningSkipText(getLocator(), skipAfterExpression()); + + } catch (NumberFormatException e) { + if (errorHandler != null) { + errorHandler.error(new CSSParseException("Invalid number " + + e.getMessage(), + getLocator(), + e)); + } + reportWarningSkipText(getLocator(), skipAfterExpression()); + } catch (ParseException e) { + if (errorHandler != null) { + if (e.currentToken != null) { + LocatorImpl li = new LocatorImpl(this, + e.currentToken.next.beginLine, + e.currentToken.next.beginColumn-1); + reportError(li, e); + } else { + reportError(getLocator(), e); + } + skipAfterExpression(); + /* + LocatorImpl loc = (LocatorImpl) getLocator(); + loc.column--; + reportWarningSkipText(loc, skipAfterExpression()); + */ + } else { + skipAfterExpression(); + } + } + } + +/** + * @exception ParseException exception during the parse + */ + final public boolean prio() throws ParseException { + jj_consume_token(IMPORTANT_SYM); + label_157: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[228] = jj_gen; + break label_157; + } + jj_consume_token(S); + } + {if (true) return true;} + throw new Error("Missing return statement in function"); + } + + final public boolean guarded() throws ParseException { + jj_consume_token(GUARDED_SYM); + label_158: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[229] = jj_gen; + break label_158; + } + jj_consume_token(S); + } + {if (true) return true;} + throw new Error("Missing return statement in function"); + } + +/** + * @exception ParseException exception during the parse + */ + final public LexicalUnitImpl operator(LexicalUnitImpl prev) throws ParseException { + Token n; + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case COMMA: + /* (comments copied from basic_arithmetics.scss) + *supports: + * 1. standard arithmetic operations (+, -, *, /, %) + * 2. / is treated as css operator, unless one of its operands is variable or there is another binary arithmetic operator + *limits: + * 1. cannot mix arithmetic and css operations, e.g. "margin: 1px + 3px 2px" will fail + * 2. space between add and minus operator and their following operand is mandatory. e.g. "1 + 2" is valid, "1+2" is not + * 3. parenthesis is not supported now. + */ + n = jj_consume_token(COMMA); + label_159: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[230] = jj_gen; + break label_159; + } + jj_consume_token(S); + } + {if (true) return LexicalUnitImpl.createComma(n.beginLine, + n.beginColumn, + prev);} + break; + case DIV: + n = jj_consume_token(DIV); + label_160: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[231] = jj_gen; + break label_160; + } + jj_consume_token(S); + } + {if (true) return LexicalUnitImpl.createSlash(n.beginLine, + n.beginColumn, + prev);} + break; + case ANY: + n = jj_consume_token(ANY); + label_161: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[232] = jj_gen; + break label_161; + } + jj_consume_token(S); + } + {if (true) return LexicalUnitImpl.createMultiply(n.beginLine, + n.beginColumn, + prev);} + break; + case MOD: + n = jj_consume_token(MOD); + label_162: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[233] = jj_gen; + break label_162; + } + jj_consume_token(S); + } + {if (true) return LexicalUnitImpl.createModulo(n.beginLine, + n.beginColumn, + prev);} + break; + case PLUS: + n = jj_consume_token(PLUS); + label_163: + while (true) { + jj_consume_token(S); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[234] = jj_gen; + break label_163; + } + } + {if (true) return LexicalUnitImpl.createAdd(n.beginLine, + n.beginColumn, + prev);} + break; + case MINUS: + n = jj_consume_token(MINUS); + label_164: + while (true) { + jj_consume_token(S); + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[235] = jj_gen; + break label_164; + } + } + {if (true) return LexicalUnitImpl.createMinus(n.beginLine, + n.beginColumn, + prev);} + break; + default: + jj_la1[236] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + throw new Error("Missing return statement in function"); + } + +/** + * @exception ParseException exception during the parse + */ + final public LexicalUnitImpl expr() throws ParseException { + LexicalUnitImpl first, res; + char op; + first = term(null); + res = first; + label_165: + while (true) { + if (jj_2_8(2)) { + ; + } else { + break label_165; + } + if (jj_2_9(2)) { + res = operator(res); + } else { + ; + } + res = term(res); + } + {if (true) return first;} + throw new Error("Missing return statement in function"); + } + +/** + * @exception ParseException exception during the parse + */ + final public char unaryOperator() throws ParseException { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case MINUS: + jj_consume_token(MINUS); + {if (true) return '-';} + break; + case PLUS: + jj_consume_token(PLUS); + {if (true) return '+';} + break; + default: + jj_la1[237] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + throw new Error("Missing return statement in function"); + } + +/** + * @exception ParseException exception during the parse + */ + final public LexicalUnitImpl term(LexicalUnitImpl prev) throws ParseException { + LexicalUnitImpl result = null; + Token n = null; + char op = ' '; + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case MINUS: + case DOT: + case TO: + case THROUGH: + case FROM: + case STRING: + case IDENT: + case NUMBER: + case URL: + case PERCENTAGE: + case PT: + case MM: + case CM: + case PC: + case IN: + case PX: + case EMS: + case LEM: + case REM: + case EXS: + case DEG: + case RAD: + case GRAD: + case MS: + case SECOND: + case HZ: + case KHZ: + case DIMEN: + case HASH: + case UNICODERANGE: + case FUNCTION: + result = nonVariableTerm(prev); + break; + case VARIABLE: + result = variableTerm(prev); + break; + default: + jj_la1[238] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + {if (true) return result;} + throw new Error("Missing return statement in function"); + } + + final public LexicalUnitImpl variableTerm(LexicalUnitImpl prev) throws ParseException { + LexicalUnitImpl result = null; + String varName = ""; + varName = variableName(); + result = LexicalUnitImpl.createVariable(token.beginLine, token.beginColumn, + prev, varName); {if (true) return result;} + throw new Error("Missing return statement in function"); + } + + final public LexicalUnitImpl nonVariableTerm(LexicalUnitImpl prev) throws ParseException { +LexicalUnitImpl result = null; + Token n = null; + char op = ' '; + String varName; + String s = ""; + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case MINUS: + case NUMBER: + case PERCENTAGE: + case PT: + case MM: + case CM: + case PC: + case IN: + case PX: + case EMS: + case LEM: + case REM: + case EXS: + case DEG: + case RAD: + case GRAD: + case MS: + case SECOND: + case HZ: + case KHZ: + case DIMEN: + case FUNCTION: + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case MINUS: + op = unaryOperator(); + break; + default: + jj_la1[239] = jj_gen; + ; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case NUMBER: + n = jj_consume_token(NUMBER); + result = LexicalUnitImpl.createNumber(n.beginLine, n.beginColumn, + prev, number(op, n, 0)); + break; + case PERCENTAGE: + n = jj_consume_token(PERCENTAGE); + result = LexicalUnitImpl.createPercentage(n.beginLine, n.beginColumn, + prev, number(op, n, 1)); + break; + case PT: + n = jj_consume_token(PT); + result = LexicalUnitImpl.createPT(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); + break; + case CM: + n = jj_consume_token(CM); + result = LexicalUnitImpl.createCM(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); + break; + case MM: + n = jj_consume_token(MM); + result = LexicalUnitImpl.createMM(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); + break; + case PC: + n = jj_consume_token(PC); + result = LexicalUnitImpl.createPC(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); + break; + case IN: + n = jj_consume_token(IN); + result = LexicalUnitImpl.createIN(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); + break; + case PX: + n = jj_consume_token(PX); + result = LexicalUnitImpl.createPX(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); + break; + case EMS: + n = jj_consume_token(EMS); + result = LexicalUnitImpl.createEMS(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); + break; + case LEM: + n = jj_consume_token(LEM); + result = LexicalUnitImpl.createLEM(n.beginLine, n.beginColumn, + prev, number(op, n, 3)); + break; + case REM: + n = jj_consume_token(REM); + result = LexicalUnitImpl.createREM(n.beginLine, n.beginColumn, + prev, number(op, n, 3)); + break; + case EXS: + n = jj_consume_token(EXS); + result = LexicalUnitImpl.createEXS(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); + break; + case DEG: + n = jj_consume_token(DEG); + result = LexicalUnitImpl.createDEG(n.beginLine, n.beginColumn, + prev, number(op, n, 3)); + break; + case RAD: + n = jj_consume_token(RAD); + result = LexicalUnitImpl.createRAD(n.beginLine, n.beginColumn, + prev, number(op, n, 3)); + break; + case GRAD: + n = jj_consume_token(GRAD); + result = LexicalUnitImpl.createGRAD(n.beginLine, n.beginColumn, + prev, number(op, n, 3)); + break; + case SECOND: + n = jj_consume_token(SECOND); + result = LexicalUnitImpl.createS(n.beginLine, n.beginColumn, + prev, number(op, n, 1)); + break; + case MS: + n = jj_consume_token(MS); + result = LexicalUnitImpl.createMS(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); + break; + case HZ: + n = jj_consume_token(HZ); + result = LexicalUnitImpl.createHZ(n.beginLine, n.beginColumn, + prev, number(op, n, 2)); + break; + case KHZ: + n = jj_consume_token(KHZ); + result = LexicalUnitImpl.createKHZ(n.beginLine, n.beginColumn, + prev, number(op, n, 3)); + break; + case DIMEN: + n = jj_consume_token(DIMEN); + s = n.image; + int i = 0; + while (i < s.length() + && (Character.isDigit(s.charAt(i)) || (s.charAt(i) == '.'))) { + i++; } - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case PLUS: - case MINUS: + result = LexicalUnitImpl.createDimen(n.beginLine, n.beginColumn, prev, + Float.valueOf(s.substring(0, i)).floatValue(), + s.substring(i)); + break; + case FUNCTION: + result = function(op, prev); + break; + default: + jj_la1[240] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + break; + case DOT: + case TO: + case THROUGH: + case FROM: + case STRING: + case IDENT: + case URL: + case HASH: + case UNICODERANGE: + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case STRING: + n = jj_consume_token(STRING); + result = + LexicalUnitImpl.createString(n.beginLine, n.beginColumn, prev, + convertStringIndex(n.image, 1, + n.image.length() -1)); + break; + case DOT: + case TO: + case THROUGH: + case FROM: + case IDENT: + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { case DOT: + jj_consume_token(DOT); + s+="."; + break; + default: + jj_la1[241] = jj_gen; + ; + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IDENT: + n = jj_consume_token(IDENT); + break; case TO: + n = jj_consume_token(TO); + break; case THROUGH: + n = jj_consume_token(THROUGH); + break; case FROM: - case STRING: - case IDENT: - case NUMBER: - case URL: - case VARIABLE: - case PERCENTAGE: - case PT: - case MM: - case CM: - case PC: - case IN: - case PX: - case EMS: - case LEM: - case REM: - case EXS: - case DEG: - case RAD: - case GRAD: - case MS: - case SECOND: - case HZ: - case KHZ: - case DIMEN: - case HASH: - case UNICODERANGE: - case FUNCTION: - params = expr(); - break; + n = jj_consume_token(FROM); + break; default: - jj_la1[237] = jj_gen; - ; - } - jj_consume_token(RPARAN); + jj_la1[242] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + s += convertIdent(n.image); + if ("inherit".equals(s)) { + result = LexicalUnitImpl.createInherit(n.beginLine, n.beginColumn, + prev); + } else { + result = LexicalUnitImpl.createIdent(n.beginLine, n.beginColumn, + prev, convertIdent(n.image)); + } + + /* / + Auto correction code used in the CSS Validator but must not + be used by a conformant CSS2 parser. + * Common error : + * H1 { + * color : black + * background : white + * } + * + Token t = getToken(1); + Token semicolon = new Token(); + semicolon.kind = SEMICOLON; + semicolon.image = ";"; + if (t.kind == COLON) { + // @@SEEME. (generate a warning?) + // @@SEEME if expression is a single ident, + generate an error ? + rejectToken(semicolon); + + result = prev; + } + / */ + + break; + case HASH: + result = hexcolor(prev); + break; + case URL: + result = url(prev); + break; + case UNICODERANGE: + result = unicode(prev); + break; + default: + jj_la1[243] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + break; + default: + jj_la1[244] = jj_gen; + jj_consume_token(-1); + throw new ParseException(); + } + label_166: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[245] = jj_gen; + break label_166; + } + jj_consume_token(S); + } + {if (true) return result;} + throw new Error("Missing return statement in function"); + } + +/** + * Handle all CSS2 functions. + * @exception ParseException exception during the parse + */ + final public LexicalUnitImpl function(char operator, LexicalUnitImpl prev) throws ParseException { + Token n; + LexicalUnit params = null; + n = jj_consume_token(FUNCTION); + label_167: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[246] = jj_gen; + break label_167; + } + jj_consume_token(S); + } + String fname = convertIdent(n.image); + if("alpha(".equals(fname)){ + String body = skipStatementUntilSemiColon(); + {if (true) return LexicalUnitImpl.createIdent(n.beginLine, n.beginColumn, + null, "alpha("+body);} + }else if("expression(".equals(fname)){ + String body = skipStatementUntilSemiColon(); + {if (true) return LexicalUnitImpl.createIdent(n.beginLine, n.beginColumn, + null, "expression("+body);} + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case PLUS: + case MINUS: + case DOT: + case TO: + case THROUGH: + case FROM: + case STRING: + case IDENT: + case NUMBER: + case URL: + case VARIABLE: + case PERCENTAGE: + case PT: + case MM: + case CM: + case PC: + case IN: + case PX: + case EMS: + case LEM: + case REM: + case EXS: + case DEG: + case RAD: + case GRAD: + case MS: + case SECOND: + case HZ: + case KHZ: + case DIMEN: + case HASH: + case UNICODERANGE: + case FUNCTION: + params = expr(); + break; + default: + jj_la1[247] = jj_gen; + ; + } + jj_consume_token(RPARAN); if (operator != ' ') { - { - if (true) { - throw new CSSParseException( - "invalid operator before a function.", getLocator()); - } - } + {if (true) throw new CSSParseException("invalid operator before a function.", + getLocator());} } String f = convertIdent(n.image); LexicalUnitImpl l = (LexicalUnitImpl) params; @@ -5565,38 +5643,32 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { int i = 0; while (loop && l != null && i < 5) { switch (i) { - case 0: - case 2: - case 4: - if ((l.getLexicalUnitType() != LexicalUnit.SAC_INTEGER) + case 0: + case 2: + case 4: + if ((l.getLexicalUnitType() != LexicalUnit.SAC_INTEGER) && (l.getLexicalUnitType() != LexicalUnit.SAC_PERCENTAGE)) { - loop = false; - } - break; - case 1: - case 3: - if (l.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) { - loop = false; - } - break; - default: { - if (true) { - throw new ParseException("implementation error"); - } - } + loop = false; + } + break; + case 1: + case 3: + if (l.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) { + loop = false; + } + break; + default: + {if (true) throw new ParseException("implementation error");} } if (loop) { - l = l.getNextLexicalUnit(); - i++; + l = (LexicalUnitImpl) l.getNextLexicalUnit(); + i ++; } } if ((i == 5) && loop && (l == null)) { - { - if (true) { - return LexicalUnitImpl.createRGBColor(n.beginLine, - n.beginColumn, prev, params); - } - } + {if (true) return LexicalUnitImpl.createRGBColor(n.beginLine, + n.beginColumn, + prev, params);} } else { if (errorHandler != null) { String errorText; @@ -5604,63 +5676,54 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { if (i < 5) { if (params == null) { loc = new LocatorImpl(this, n.beginLine, - n.beginColumn - 1); + n.beginColumn-1); errorText = "not enough parameters."; } else if (l == null) { loc = new LocatorImpl(this, n.beginLine, - n.beginColumn - 1); + n.beginColumn-1); errorText = "not enough parameters: " - + params.toString(); + + params.toString(); } else { loc = new LocatorImpl(this, l.getLineNumber(), - l.getColumnNumber()); - errorText = "invalid parameter: " + l.toString(); + l.getColumnNumber()); + errorText = "invalid parameter: " + + l.toString(); } } else { loc = new LocatorImpl(this, l.getLineNumber(), - l.getColumnNumber()); - errorText = "too many parameters: " + l.toString(); + l.getColumnNumber()); + errorText = "too many parameters: " + + l.toString(); } errorHandler.error(new CSSParseException(errorText, loc)); } - { - if (true) { - throw new JumpException(); - } - } + {if (true) throw new JumpException();} } } else if ("counter".equals(f)) { int i = 0; while (loop && l != null && i < 3) { switch (i) { - case 0: - case 2: - if (l.getLexicalUnitType() != LexicalUnit.SAC_IDENT) { - loop = false; - } - break; - case 1: - if (l.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) { - loop = false; - } - break; - default: { - if (true) { - throw new ParseException("implementation error"); - } - } + case 0: + case 2: + if (l.getLexicalUnitType() != LexicalUnit.SAC_IDENT) { + loop = false; + } + break; + case 1: + if (l.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) { + loop = false; + } + break; + default: + {if (true) throw new ParseException("implementation error");} } - l = l.getNextLexicalUnit(); - i++; + l = (LexicalUnitImpl) l.getNextLexicalUnit(); + i ++; } if (((i == 1) || (i == 3)) && loop && (l == null)) { - { - if (true) { - return LexicalUnitImpl.createCounter(n.beginLine, - n.beginColumn, prev, params); - } - } + {if (true) return LexicalUnitImpl.createCounter(n.beginLine, n.beginColumn, + prev, params);} } } else if ("counters(".equals(f)) { @@ -5668,2703 +5731,2047 @@ public class Parser implements org.w3c.css.sac.Parser, ParserConstants { int i = 0; while (loop && l != null && i < 5) { switch (i) { - case 0: - case 4: - if (l.getLexicalUnitType() != LexicalUnit.SAC_IDENT) { - loop = false; - } - break; - case 2: - if (l.getLexicalUnitType() != LexicalUnit.SAC_STRING_VALUE) { - loop = false; - } - break; - case 1: - case 3: - if (l.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) { - loop = false; - } - break; - default: { - if (true) { - throw new ParseException("implementation error"); - } - } + case 0: + case 4: + if (l.getLexicalUnitType() != LexicalUnit.SAC_IDENT) { + loop = false; + } + break; + case 2: + if (l.getLexicalUnitType() != LexicalUnit.SAC_STRING_VALUE) { + loop = false; + } + break; + case 1: + case 3: + if (l.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) { + loop = false; + } + break; + default: + {if (true) throw new ParseException("implementation error");} } - l = l.getNextLexicalUnit(); - i++; + l = (LexicalUnitImpl) l.getNextLexicalUnit(); + i ++; } if (((i == 3) || (i == 5)) && loop && (l == null)) { - { - if (true) { - return LexicalUnitImpl.createCounters(n.beginLine, - n.beginColumn, prev, params); - } - } + {if (true) return LexicalUnitImpl.createCounters(n.beginLine, n.beginColumn, + prev, params);} } } else if ("attr(".equals(f)) { - if ((l != null) && (l.getNextLexicalUnit() == null) - && (l.getLexicalUnitType() == LexicalUnit.SAC_IDENT)) { - { - if (true) { - return LexicalUnitImpl.createAttr(l.getLineNumber(), - l.getColumnNumber(), prev, l.getStringValue()); - } - } + if ((l != null) + && (l.getNextLexicalUnit() == null) + && (l.getLexicalUnitType() == LexicalUnit.SAC_IDENT)) { + {if (true) return LexicalUnitImpl.createAttr(l.getLineNumber(), + l.getColumnNumber(), + prev, l.getStringValue());} } } else if ("rect(".equals(f)) { int i = 0; while (loop && l != null && i < 7) { switch (i) { - case 0: - case 2: - case 4: - case 6: - switch (l.getLexicalUnitType()) { - case LexicalUnit.SAC_INTEGER: - if (l.getIntegerValue() != 0) { + case 0: + case 2: + case 4: + case 6: + switch (l.getLexicalUnitType()) { + case LexicalUnit.SAC_INTEGER: + if (l.getIntegerValue() != 0) { + loop = false; + } + break; + case LexicalUnit.SAC_IDENT: + if (!l.getStringValue().equals("auto")) { + loop = false; + } + break; + case LexicalUnit.SAC_EM: + case LexicalUnit.SAC_EX: + case LexicalUnit.SAC_PIXEL: + case LexicalUnit.SAC_CENTIMETER: + case LexicalUnit.SAC_MILLIMETER: + case LexicalUnit.SAC_INCH: + case LexicalUnit.SAC_POINT: + case LexicalUnit.SAC_PICA: + // nothing + break; + default: loop = false; } break; - case LexicalUnit.SAC_IDENT: - if (!l.getStringValue().equals("auto")) { + case 1: + case 3: + case 5: + if (l.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) { loop = false; } break; - case LexicalUnit.SAC_EM: - case LexicalUnit.SAC_EX: - case LexicalUnit.SAC_PIXEL: - case LexicalUnit.SAC_CENTIMETER: - case LexicalUnit.SAC_MILLIMETER: - case LexicalUnit.SAC_INCH: - case LexicalUnit.SAC_POINT: - case LexicalUnit.SAC_PICA: - // nothing - break; default: - loop = false; - } - break; - case 1: - case 3: - case 5: - if (l.getLexicalUnitType() != LexicalUnit.SAC_OPERATOR_COMMA) { - loop = false; - } - break; - default: { - if (true) { - throw new ParseException("implementation error"); - } + {if (true) throw new ParseException("implementation error");} } - } - l = l.getNextLexicalUnit(); - i++; + l = (LexicalUnitImpl) l.getNextLexicalUnit(); + i ++; } if ((i == 7) && loop && (l == null)) { - { - if (true) { - return LexicalUnitImpl.createRect(n.beginLine, - n.beginColumn, prev, params); - } - } - } - } - { - if (true) { - return LexicalUnitImpl.createFunction(n.beginLine, - n.beginColumn, prev, f.substring(0, f.length() - 1), - params); - } - } - throw new Error("Missing return statement in function"); - } - - final public LexicalUnitImpl unicode(LexicalUnitImpl prev) - throws ParseException { - Token n; - n = jj_consume_token(UNICODERANGE); - LexicalUnitImpl params = null; - String s = n.image.substring(2); - int index = s.indexOf('-'); - if (index == -1) { - params = LexicalUnitImpl.createInteger(n.beginLine, n.beginColumn, - params, Integer.parseInt(s, 16)); - } else { - String s1 = s.substring(0, index); - String s2 = s.substring(index); - - params = LexicalUnitImpl.createInteger(n.beginLine, n.beginColumn, - params, Integer.parseInt(s1, 16)); - params = LexicalUnitImpl.createInteger(n.beginLine, n.beginColumn, - params, Integer.parseInt(s2, 16)); - } - - { - if (true) { - return LexicalUnitImpl.createUnicodeRange(n.beginLine, - n.beginColumn, prev, params); - } - } - throw new Error("Missing return statement in function"); - } - - final public LexicalUnitImpl url(LexicalUnitImpl prev) - throws ParseException { - Token n; - n = jj_consume_token(URL); - String urlname = n.image.substring(4, n.image.length() - 1).trim(); - { - if (true) { - return LexicalUnitImpl.createURL(n.beginLine, n.beginColumn, - prev, urlname); - } - } - throw new Error("Missing return statement in function"); - } - - /** - * @exception ParseException - * exception during the parse - */ - final public LexicalUnitImpl hexcolor(LexicalUnitImpl prev) - throws ParseException { - Token n; - n = jj_consume_token(HASH); - int r; - LexicalUnitImpl first, params = null; - String s = n.image.substring(1); - - if (s.length() != 3 && s.length() != 6) { - first = null; - { - if (true) { - throw new CSSParseException( - "invalid hexadecimal notation for RGB: " + s, - getLocator()); - } - } - } - { - if (true) { - return LexicalUnitImpl.createIdent(n.beginLine, n.beginColumn, - prev, n.image); - } - } - throw new Error("Missing return statement in function"); - } + {if (true) return LexicalUnitImpl.createRect(n.beginLine, n.beginColumn, + prev, params);} + } + } + {if (true) return LexicalUnitImpl.createFunction(n.beginLine, n.beginColumn, prev, + f.substring(0, + f.length() -1), + params);} + throw new Error("Missing return statement in function"); + } + + final public LexicalUnitImpl unicode(LexicalUnitImpl prev) throws ParseException { + Token n; + n = jj_consume_token(UNICODERANGE); + LexicalUnitImpl params = null; + String s = n.image.substring(2); + int index = s.indexOf('-'); + if (index == -1) { + params = LexicalUnitImpl.createInteger(n.beginLine, n.beginColumn, + params, Integer.parseInt(s, 16)); + } else { + String s1 = s.substring(0, index); + String s2 = s.substring(index); + + params = LexicalUnitImpl.createInteger(n.beginLine, n.beginColumn, + params, Integer.parseInt(s1, 16)); + params = LexicalUnitImpl.createInteger(n.beginLine, n.beginColumn, + params, Integer.parseInt(s2, 16)); + } + + {if (true) return LexicalUnitImpl.createUnicodeRange(n.beginLine, n.beginColumn, + prev, params);} + throw new Error("Missing return statement in function"); + } + + final public LexicalUnitImpl url(LexicalUnitImpl prev) throws ParseException { + Token n; + n = jj_consume_token(URL); + String urlname = n.image.substring(4, n.image.length()-1).trim(); + {if (true) return LexicalUnitImpl.createURL(n.beginLine, n.beginColumn, prev, urlname);} + throw new Error("Missing return statement in function"); + } - float number(char operator, Token n, int lengthUnit) throws ParseException { - String image = n.image; - float f = 0; - - if (lengthUnit != 0) { - image = image.substring(0, image.length() - lengthUnit); - } - f = Float.valueOf(image).floatValue(); - return (operator == '-') ? -f : f; - } - - String skipStatementUntilSemiColon() throws ParseException { - int[] semicolon = { SEMICOLON }; - return skipStatementUntil(semicolon); - } - - String skipStatementUntilLeftBrace() throws ParseException { - int[] lBrace = { LBRACE }; - return skipStatementUntil(lBrace); - } - - String skipStatementUntilRightParan() throws ParseException { - int[] rParan = { RPARAN }; - return skipStatementUntil(rParan); - } - - String skipStatementUntil(int[] symbols) throws ParseException { - StringBuffer s = new StringBuffer(); - boolean stop = false; - Token tok; - while (!stop) { - tok = getToken(1); - if (tok.kind == EOF) { - return null; - } - for (int sym : symbols) { - if (tok.kind == sym) { - stop = true; - break; - } - } - if (!stop) { - if (tok.image != null) { - s.append(tok.image); - } - getNextToken(); - } - } - return s.toString().trim(); - } - - String skipStatement() throws ParseException { - StringBuffer s = new StringBuffer(); - Token tok = getToken(0); - if (tok.image != null) { - s.append(tok.image); - } - while (true) { - tok = getToken(1); - if (tok.kind == EOF) { - return null; - } - s.append(tok.image); - if (tok.kind == LBRACE) { - getNextToken(); - s.append(skip_to_matching_brace()); - getNextToken(); - tok = getToken(1); - break; - } else if (tok.kind == RBRACE) { - getNextToken(); - tok = getToken(1); - break; - } else if (tok.kind == SEMICOLON) { - getNextToken(); - tok = getToken(1); - break; - } - getNextToken(); - } - - // skip white space - while (true) { - if (tok.kind != S) { - break; - } - tok = getNextToken(); - tok = getToken(1); - } - - return s.toString().trim(); - } - - String skip_to_matching_brace() throws ParseException { - StringBuffer s = new StringBuffer(); - Token tok; - int nesting = 1; - while (true) { - tok = getToken(1); - if (tok.kind == EOF) { - break; - } - s.append(tok.image); - if (tok.kind == LBRACE) { - nesting++; - } else if (tok.kind == RBRACE) { - nesting--; - if (nesting == 0) { - break; - } +/** + * @exception ParseException exception during the parse + */ + final public LexicalUnitImpl hexcolor(LexicalUnitImpl prev) throws ParseException { + Token n; + n = jj_consume_token(HASH); + int r; + LexicalUnitImpl first, params = null; + String s = n.image.substring(1); + + if(s.length()!=3 && s.length()!=6) { + first = null; + {if (true) throw new CSSParseException("invalid hexadecimal notation for RGB: " + s, + getLocator());} + } + {if (true) return LexicalUnitImpl.createIdent(n.beginLine, n.beginColumn, + prev, n.image);} + throw new Error("Missing return statement in function"); + } + + float number(char operator, Token n, int lengthUnit) throws ParseException { + String image = n.image; + float f = 0; + + if (lengthUnit != 0) { + image = image.substring(0, image.length() - lengthUnit); + } + f = Float.valueOf(image).floatValue(); + return (operator == '-')? -f: f; + } + + String skipStatementUntilSemiColon() throws ParseException { + int[] semicolon = {SEMICOLON}; + return skipStatementUntil(semicolon); + } + + String skipStatementUntilLeftBrace() throws ParseException { + int[] lBrace = {LBRACE}; + return skipStatementUntil(lBrace); + } + + String skipStatementUntilRightParan() throws ParseException { + int[] rParan = {RPARAN}; + return skipStatementUntil(rParan); + } + + String skipStatementUntil(int[] symbols) throws ParseException { + StringBuffer s = new StringBuffer(); + boolean stop = false; + Token tok; + while(!stop){ + tok = getToken(1); + if(tok.kind == EOF) { + return null; + } + for(int sym : symbols){ + if(tok.kind == sym){ + stop = true; + break; + } + } + if(!stop){ + if (tok.image != null) { + s.append(tok.image); } getNextToken(); } - return s.toString(); - } - - String convertStringIndex(String s, int start, int len) - throws ParseException { - StringBuffer buf = new StringBuffer(len); - int index = start; - - while (index < len) { - char c = s.charAt(index); - if (c == '\u005c\u005c') { - if (++index < len) { - c = s.charAt(index); - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - buf.append('\u005c\u005c'); - while (index < len) { - buf.append(s.charAt(index++)); - } - break; - case '\u005cn': - case '\u005cf': - break; - case '\u005cr': - if (index + 1 < len) { - if (s.charAt(index + 1) == '\u005cn') { - index++; - } - } - break; - default: - buf.append(c); - } - } else { - throw new CSSParseException("invalid string " + s, - getLocator()); - } - } else { - buf.append(c); - } - index++; - } - - return buf.toString(); - } - - String convertIdent(String s) throws ParseException { - return convertStringIndex(s, 0, s.length()); } + return s.toString().trim(); + } - String convertString(String s) throws ParseException { - return convertStringIndex(s, 0, s.length()); + String skipStatement() throws ParseException { + StringBuffer s = new StringBuffer(); + Token tok = getToken(0); + if (tok.image != null) { + s.append(tok.image); } - - void comments() throws ParseException { - if (token.specialToken != null) { - Token tmp_t = token.specialToken; - while (tmp_t.specialToken != null) { - tmp_t = tmp_t.specialToken; - } - while (tmp_t != null) { - documentHandler.comment(tmp_t.image); - tmp_t = tmp_t.next; - } + while (true) { + tok = getToken(1); + if (tok.kind == EOF) { + return null; } - } - - void rejectToken(Token t) throws ParseException { - Token fakeToken = new Token(); - t.next = token; - fakeToken.next = t; - token = fakeToken; - } - - String skipAfterExpression() throws ParseException { - Token t = getToken(1); - StringBuffer s = new StringBuffer(); - s.append(getToken(0).image); - - while ((t.kind != RBRACE) && (t.kind != SEMICOLON) && (t.kind != EOF)) { - s.append(t.image); + s.append(tok.image); + if (tok.kind == LBRACE) { getNextToken(); - t = getToken(1); - } - - return s.toString(); - } - - /** - * The following functions are useful for a DOM CSS implementation only and - * are not part of the general CSS2 parser. - */ - final public void _parseRule() throws ParseException { - String ret = null; - label_158: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[238] = jj_gen; - break label_158; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case IMPORT_SYM: - importDeclaration(); - break; - case DEBUG_SYM: - case WARN_SYM: - debuggingDirective(); - break; - case PLUS: - case PRECEDES: - case SIBLING: - case LBRACKET: - case ANY: - case PARENT: - case DOT: - case COLON: - case INTERPOLATION: - case IDENT: - case HASH: - styleRule(); - break; - case MEDIA_SYM: - media(); + s.append(skip_to_matching_brace()); + getNextToken(); + tok = getToken(1); break; - case PAGE_SYM: - page(); + } else if (tok.kind == RBRACE) { + getNextToken(); + tok = getToken(1); break; - case FONT_FACE_SYM: - fontFace(); + } else if (tok.kind == SEMICOLON) { + getNextToken(); + tok = getToken(1); break; - default: - jj_la1[239] = jj_gen; - ret = skipStatement(); - if ((ret == null) || (ret.length() == 0)) { - { - if (true) { - return; - } - } - } - if (ret.charAt(0) == '@') { - documentHandler.unrecognizedRule(ret); - } else { - { - if (true) { - throw new CSSParseException("unrecognize rule: " + ret, - getLocator()); - } - } - } } + getNextToken(); } - final public void _parseImportRule() throws ParseException { - label_159: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[240] = jj_gen; - break label_159; - } - jj_consume_token(S); + // skip white space + while (true) { + if (tok.kind != S) { + break; } - importDeclaration(); + tok = getNextToken(); + tok = getToken(1); } - final public void _parseMediaRule() throws ParseException { - label_160: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[241] = jj_gen; - break label_160; - } - jj_consume_token(S); - } - media(); - } + return s.toString().trim(); + } - final public void _parseDeclarationBlock() throws ParseException { - label_161: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; - break; - default: - jj_la1[242] = jj_gen; - break label_161; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case INTERPOLATION: - case IDENT: - declaration(); + String skip_to_matching_brace() throws ParseException { + StringBuffer s = new StringBuffer(); + Token tok; + int nesting = 1; + while (true) { + tok = getToken(1); + if (tok.kind == EOF) { break; - default: - jj_la1[243] = jj_gen; - ; } - label_162: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case SEMICOLON: - ; - break; - default: - jj_la1[244] = jj_gen; - break label_162; - } - jj_consume_token(SEMICOLON); - label_163: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; + s.append(tok.image); + if (tok.kind == LBRACE) { + nesting++; + } else if (tok.kind == RBRACE) { + nesting--; + if (nesting == 0) { + break; + } + } + getNextToken(); + } + return s.toString(); + } + + String convertStringIndex(String s, int start, int len) throws ParseException { + StringBuffer buf = new StringBuffer(len); + int index = start; + + while (index < len) { + char c = s.charAt(index); + if (c == '\u005c\u005c') { + if (++index < len) { + c = s.charAt(index); + switch (c) { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': + case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': + buf.append('\u005c\u005c'); + while (index < len) { + buf.append(s.charAt(index++)); + } break; - default: - jj_la1[245] = jj_gen; - break label_163; - } - jj_consume_token(S); - } - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case INTERPOLATION: - case IDENT: - declaration(); - break; - default: - jj_la1[246] = jj_gen; - ; - } - } - } - - final public ArrayList<String> _parseSelectors() throws ParseException { - ArrayList<String> p = null; - try { - label_164: while (true) { - switch ((jj_ntk == -1) ? jj_ntk() : jj_ntk) { - case S: - ; + case '\u005cn': + case '\u005cf': break; - default: - jj_la1[247] = jj_gen; - break label_164; - } - jj_consume_token(S); - } - p = selectorList(); - { - if (true) { - return p; - } - } - } catch (ThrowedParseException e) { - { - if (true) { - throw (ParseException) e.e.fillInStackTrace(); - } - } - } - throw new Error("Missing return statement in function"); - } - - private boolean jj_2_1(int xla) { - jj_la = xla; - jj_lastpos = jj_scanpos = token; - try { - return !jj_3_1(); - } catch (LookaheadSuccess ls) { - return true; - } finally { - jj_save(0, xla); - } - } - - private boolean jj_2_2(int xla) { - jj_la = xla; - jj_lastpos = jj_scanpos = token; - try { - return !jj_3_2(); - } catch (LookaheadSuccess ls) { - return true; - } finally { - jj_save(1, xla); - } - } - - private boolean jj_2_3(int xla) { - jj_la = xla; - jj_lastpos = jj_scanpos = token; - try { - return !jj_3_3(); - } catch (LookaheadSuccess ls) { - return true; - } finally { - jj_save(2, xla); - } - } - - private boolean jj_2_4(int xla) { - jj_la = xla; - jj_lastpos = jj_scanpos = token; - try { - return !jj_3_4(); - } catch (LookaheadSuccess ls) { - return true; - } finally { - jj_save(3, xla); - } - } - - private boolean jj_2_5(int xla) { - jj_la = xla; - jj_lastpos = jj_scanpos = token; - try { - return !jj_3_5(); - } catch (LookaheadSuccess ls) { - return true; - } finally { - jj_save(4, xla); - } - } - - private boolean jj_2_6(int xla) { - jj_la = xla; - jj_lastpos = jj_scanpos = token; - try { - return !jj_3_6(); - } catch (LookaheadSuccess ls) { - return true; - } finally { - jj_save(5, xla); - } - } - - private boolean jj_2_7(int xla) { - jj_la = xla; - jj_lastpos = jj_scanpos = token; - try { - return !jj_3_7(); - } catch (LookaheadSuccess ls) { - return true; - } finally { - jj_save(6, xla); - } - } - - private boolean jj_2_8(int xla) { - jj_la = xla; - jj_lastpos = jj_scanpos = token; - try { - return !jj_3_8(); - } catch (LookaheadSuccess ls) { - return true; - } finally { - jj_save(7, xla); - } - } - - private boolean jj_3R_173() { - if (jj_3R_194()) { - return true; - } - return false; - } - - private boolean jj_3R_251() { - if (jj_scan_token(PLUS)) { - return true; - } - return false; - } - - private boolean jj_3R_241() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_250()) { - jj_scanpos = xsp; - if (jj_3R_251()) { - return true; - } - } - return false; - } - - private boolean jj_3R_250() { - if (jj_scan_token(MINUS)) { - return true; - } - return false; - } - - private boolean jj_3R_246() { - if (jj_scan_token(UNICODERANGE)) { - return true; - } - return false; - } - - private boolean jj_3_8() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_173()) { - jj_scanpos = xsp; - } - if (jj_3R_174()) { - return true; - } - return false; - } - - private boolean jj_3R_176() { - if (jj_3R_174()) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_3_8()) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_199() { - if (jj_3R_198()) { - return true; - } - return false; - } - - private boolean jj_3R_198() { - Token xsp; - xsp = jj_scanpos; - if (jj_scan_token(20)) { - jj_scanpos = xsp; - if (jj_scan_token(24)) { - jj_scanpos = xsp; - if (jj_scan_token(25)) { - return true; - } - } - } - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_165() { - if (jj_3R_175()) { - return true; - } - if (jj_scan_token(COLON)) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - if (jj_3R_176()) { - return true; - } - xsp = jj_scanpos; - if (jj_3R_177()) { - jj_scanpos = xsp; - } - if (jj_3R_178()) { - return true; - } - while (true) { - xsp = jj_scanpos; - if (jj_3R_178()) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_180() { - if (jj_scan_token(S)) { - return true; - } - Token xsp; - xsp = jj_scanpos; - if (jj_3R_199()) { - jj_scanpos = xsp; - } - return false; - } - - private boolean jj_3R_236() { - if (jj_scan_token(COMMA)) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_179() { - if (jj_3R_198()) { - return true; - } - return false; - } - - private boolean jj_3R_166() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_179()) { - jj_scanpos = xsp; - if (jj_3R_180()) { - return true; - } - } - return false; - } - - private boolean jj_3R_194() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_235()) { - jj_scanpos = xsp; - if (jj_3R_236()) { - return true; - } - } - return false; - } - - private boolean jj_3R_235() { - if (jj_scan_token(DIV)) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_197() { - if (jj_scan_token(GUARDED_SYM)) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_186() { - if (jj_scan_token(VARIABLE)) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - if (jj_scan_token(COLON)) { - return true; - } - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_168() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_186()) { - jj_scanpos = xsp; - } - if (jj_scan_token(CONTAINS)) { - return true; - } - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - if (true) { - jj_la = 0; - jj_scanpos = jj_lastpos; - return false; - } - return false; - } - - private boolean jj_3R_201() { - if (jj_scan_token(HASH)) { - return true; - } - return false; - } - - private boolean jj_3R_273() { - if (jj_scan_token(IDENT)) { - return true; - } - return false; - } - - private boolean jj_3R_274() { - if (jj_scan_token(FUNCTION)) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - if (true) { - jj_la = 0; - jj_scanpos = jj_lastpos; - return false; - } - return false; - } - - private boolean jj_3R_272() { - if (jj_scan_token(COLON)) { - return true; - } - return false; - } - - private boolean jj_3R_203() { - if (jj_scan_token(COLON)) { - return true; - } - Token xsp; - xsp = jj_scanpos; - if (jj_3R_272()) { - jj_scanpos = xsp; - } - xsp = jj_scanpos; - if (jj_3R_273()) { - jj_scanpos = xsp; - if (jj_3R_274()) { - return true; - } - } - return false; - } - - private boolean jj_3_7() { - if (jj_3R_172()) { - return true; - } - return false; - } - - private boolean jj_3R_293() { - if (jj_scan_token(STRING)) { - return true; - } - return false; - } - - private boolean jj_3R_291() { - if (jj_scan_token(STARMATCH)) { - return true; - } - return false; - } - - private boolean jj_3R_292() { - if (jj_scan_token(IDENT)) { - return true; - } - return false; - } - - private boolean jj_3R_290() { - if (jj_scan_token(DOLLARMATCH)) { - return true; - } - return false; - } - - private boolean jj_3R_289() { - if (jj_scan_token(CARETMATCH)) { - return true; - } - return false; - } - - private boolean jj_3R_288() { - if (jj_scan_token(DASHMATCH)) { - return true; - } - return false; - } - - private boolean jj_3R_287() { - if (jj_scan_token(INCLUDES)) { - return true; - } - return false; - } - - private boolean jj_3R_254() { - if (jj_scan_token(INTERPOLATION)) { - return true; - } - return false; - } - - private boolean jj_3R_286() { - if (jj_scan_token(EQ)) { - return true; - } - return false; - } - - private boolean jj_3R_193() { - if (jj_scan_token(LBRACE)) { - return true; - } - return false; - } - - private boolean jj_3R_279() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_286()) { - jj_scanpos = xsp; - if (jj_3R_287()) { - jj_scanpos = xsp; - if (jj_3R_288()) { - jj_scanpos = xsp; - if (jj_3R_289()) { - jj_scanpos = xsp; - if (jj_3R_290()) { - jj_scanpos = xsp; - if (jj_3R_291()) { - return true; - } + case '\u005cr': + if (index + 1 < len) { + if (s.charAt(index + 1) == '\u005cn') { + index ++; } } + break; + default: + buf.append(c); } + } else { + throw new CSSParseException("invalid string " + s, getLocator()); } + } else { + buf.append(c); } - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - xsp = jj_scanpos; - if (jj_3R_292()) { - jj_scanpos = xsp; - if (jj_3R_293()) { - return true; - } - } - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_204() { - if (jj_scan_token(LBRACKET)) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - if (jj_scan_token(IDENT)) { - return true; - } - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - xsp = jj_scanpos; - if (jj_3R_279()) { - jj_scanpos = xsp; - } - if (jj_scan_token(RBRACKET)) { - return true; - } - return false; - } - - private boolean jj_3R_285() { - if (jj_scan_token(INTERPOLATION)) { - return true; - } - return false; - } - - private boolean jj_3R_192() { - if (jj_3R_176()) { - return true; - } - return false; - } - - private boolean jj_3R_240() { - if (jj_scan_token(PARENT)) { - return true; - } - return false; - } - - private boolean jj_3R_239() { - if (jj_scan_token(ANY)) { - return true; - } - return false; - } - - private boolean jj_3_6() { - if (jj_3R_171()) { - return true; - } - if (jj_scan_token(LBRACE)) { - return true; - } - return false; - } - - private boolean jj_3R_172() { - if (jj_3R_191()) { - return true; - } - if (jj_scan_token(COLON)) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - xsp = jj_scanpos; - if (jj_3R_192()) { - jj_scanpos = xsp; - if (jj_3R_193()) { - return true; - } - } - return false; - } - - private boolean jj_3R_249() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_253()) { - jj_scanpos = xsp; - if (jj_3R_254()) { - return true; - } - } - return false; - } - - private boolean jj_3R_253() { - if (jj_scan_token(IDENT)) { - return true; - } - return false; - } - - private boolean jj_3R_200() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_238()) { - jj_scanpos = xsp; - if (jj_3R_239()) { - jj_scanpos = xsp; - if (jj_3R_240()) { - return true; - } - } - } - return false; - } - - private boolean jj_3R_238() { - Token xsp; - if (jj_3R_249()) { - return true; - } - while (true) { - xsp = jj_scanpos; - if (jj_3R_249()) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_169() { - if (jj_scan_token(COMMA)) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_267() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_284()) { - jj_scanpos = xsp; - if (jj_3R_285()) { - return true; - } - } - return false; - } - - private boolean jj_3R_284() { - if (jj_scan_token(IDENT)) { - return true; - } - return false; - } - - private boolean jj_3R_283() { - if (jj_3R_203()) { - return true; - } - return false; - } - - private boolean jj_3_5() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_169()) { - jj_scanpos = xsp; - } - if (jj_3R_170()) { - return true; - } - return false; - } - - private boolean jj_3R_202() { - if (jj_scan_token(DOT)) { - return true; - } - Token xsp; - if (jj_3R_267()) { - return true; - } - while (true) { - xsp = jj_scanpos; - if (jj_3R_267()) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_281() { - if (jj_3R_202()) { - return true; - } - return false; - } - - private boolean jj_3R_276() { - if (jj_3R_202()) { - return true; - } - return false; - } - - private boolean jj_3R_278() { - if (jj_3R_203()) { - return true; - } - return false; - } - - private boolean jj_3R_266() { - if (jj_3R_203()) { - return true; - } - return false; - } - - private boolean jj_3R_269() { - if (jj_3R_202()) { - return true; - } - return false; - } - - private boolean jj_3R_271() { - if (jj_3R_203()) { - return true; - } - return false; - } - - private boolean jj_3R_252() { - if (jj_3R_176()) { - return true; - } - return false; - } - - private boolean jj_3R_282() { - if (jj_3R_204()) { - return true; - } - return false; - } - - private boolean jj_3R_259() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_280()) { - jj_scanpos = xsp; - if (jj_3R_281()) { - jj_scanpos = xsp; - if (jj_3R_282()) { - jj_scanpos = xsp; - if (jj_3R_283()) { - return true; - } - } - } - } - return false; - } - - private boolean jj_3R_280() { - if (jj_3R_201()) { - return true; - } - return false; - } - - private boolean jj_3R_258() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_275()) { - jj_scanpos = xsp; - if (jj_3R_276()) { - jj_scanpos = xsp; - if (jj_3R_277()) { - jj_scanpos = xsp; - if (jj_3R_278()) { - return true; - } - } - } - } - return false; - } - - private boolean jj_3R_275() { - if (jj_3R_201()) { - return true; - } - return false; - } - - private boolean jj_3R_263() { - if (jj_3R_203()) { - return true; - } - return false; - } - - private boolean jj_3R_257() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_268()) { - jj_scanpos = xsp; - if (jj_3R_269()) { - jj_scanpos = xsp; - if (jj_3R_270()) { - jj_scanpos = xsp; - if (jj_3R_271()) { - return true; - } - } - } - } - return false; - } - - private boolean jj_3R_268() { - if (jj_3R_201()) { - return true; - } - return false; - } - - private boolean jj_3R_277() { - if (jj_3R_204()) { - return true; - } - return false; - } - - private boolean jj_3R_265() { - if (jj_3R_204()) { - return true; - } - return false; - } - - private boolean jj_3R_270() { - if (jj_3R_204()) { - return true; - } - return false; - } - - private boolean jj_3R_256() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_264()) { - jj_scanpos = xsp; - if (jj_3R_265()) { - jj_scanpos = xsp; - if (jj_3R_266()) { - return true; - } - } - } - return false; - } - - private boolean jj_3R_261() { - if (jj_3R_202()) { - return true; - } - return false; - } - - private boolean jj_3R_264() { - if (jj_3R_202()) { - return true; - } - return false; - } - - private boolean jj_3R_242() { - if (jj_scan_token(FUNCTION)) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - xsp = jj_scanpos; - if (jj_3R_252()) { - jj_scanpos = xsp; - } - if (jj_scan_token(RPARAN)) { - return true; - } - return false; - } - - private boolean jj_3R_231() { - if (jj_3R_246()) { - return true; - } - return false; - } - - private boolean jj_3R_230() { - if (jj_3R_245()) { - return true; - } - return false; - } - - private boolean jj_3R_185() { - if (jj_3R_204()) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_3R_259()) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_229() { - if (jj_3R_244()) { - return true; - } - return false; - } - - private boolean jj_3R_184() { - if (jj_3R_203()) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_3R_258()) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_262() { - if (jj_3R_204()) { - return true; - } - return false; - } - - private boolean jj_3R_183() { - if (jj_3R_202()) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_3R_257()) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_182() { - if (jj_3R_201()) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_3R_256()) { - jj_scanpos = xsp; - break; - } - } - return false; + index++; } - private boolean jj_3R_255() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_260()) { - jj_scanpos = xsp; - if (jj_3R_261()) { - jj_scanpos = xsp; - if (jj_3R_262()) { - jj_scanpos = xsp; - if (jj_3R_263()) { - return true; - } - } - } - } - return false; - } + return buf.toString(); + } - private boolean jj_3R_260() { - if (jj_3R_201()) { - return true; - } - return false; - } + String convertIdent(String s) throws ParseException { + return convertStringIndex(s, 0, s.length()); + } - private boolean jj_3R_181() { - if (jj_3R_200()) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_3R_255()) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_167() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_181()) { - jj_scanpos = xsp; - if (jj_3R_182()) { - jj_scanpos = xsp; - if (jj_3R_183()) { - jj_scanpos = xsp; - if (jj_3R_184()) { - jj_scanpos = xsp; - if (jj_3R_185()) { - return true; - } - } - } - } - } - return false; - } + String convertString(String s) throws ParseException { + return convertStringIndex(s, 0, s.length()); + } - private boolean jj_3R_233() { - if (jj_3R_198()) { - return true; + void comments() throws ParseException { + if (token.specialToken != null){ + Token tmp_t = token.specialToken; + while (tmp_t.specialToken != null) tmp_t = tmp_t.specialToken; + while (tmp_t != null) { + documentHandler.comment(tmp_t.image); + tmp_t = tmp_t.next; } - if (jj_3R_167()) { - return true; - } - return false; } + } - private boolean jj_3R_243() { - if (jj_scan_token(DOT)) { - return true; - } - return false; - } + void rejectToken(Token t) throws ParseException { + Token fakeToken = new Token(); + t.next = token; + fakeToken.next = t; + token = fakeToken; + } - private boolean jj_3R_228() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_243()) { - jj_scanpos = xsp; - } - xsp = jj_scanpos; - if (jj_scan_token(72)) { - jj_scanpos = xsp; - if (jj_scan_token(50)) { - jj_scanpos = xsp; - if (jj_scan_token(51)) { - jj_scanpos = xsp; - if (jj_scan_token(53)) { - return true; - } - } - } - } - return false; - } + String skipAfterExpression() throws ParseException { + Token t = getToken(1); + StringBuffer s = new StringBuffer(); + s.append(getToken(0).image); - private boolean jj_3R_227() { - if (jj_scan_token(STRING)) { - return true; - } - return false; + while ((t.kind != RBRACE) && (t.kind != SEMICOLON) && (t.kind != EOF)) { + s.append(t.image); + getNextToken(); + t = getToken(1); } - private boolean jj_3R_226() { - if (jj_3R_242()) { - return true; - } - return false; - } + return s.toString(); + } - private boolean jj_3R_188() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_227()) { - jj_scanpos = xsp; - if (jj_3R_228()) { - jj_scanpos = xsp; - if (jj_3R_229()) { - jj_scanpos = xsp; - if (jj_3R_230()) { - jj_scanpos = xsp; - if (jj_3R_231()) { - return true; - } +/** + * The following functions are useful for a DOM CSS implementation only and are + * not part of the general CSS2 parser. + */ + final public void _parseRule() throws ParseException { + String ret = null; + label_168: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[248] = jj_gen; + break label_168; + } + jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case IMPORT_SYM: + importDeclaration(); + break; + case DEBUG_SYM: + case WARN_SYM: + debuggingDirective(); + break; + case PLUS: + case PRECEDES: + case SIBLING: + case LBRACKET: + case ANY: + case PARENT: + case DOT: + case COLON: + case INTERPOLATION: + case IDENT: + case HASH: + styleRule(); + break; + case MEDIA_SYM: + media(); + break; + case PAGE_SYM: + page(); + break; + case FONT_FACE_SYM: + fontFace(); + break; + default: + jj_la1[249] = jj_gen; + ret = skipStatement(); + if ((ret == null) || (ret.length() == 0)) { + {if (true) return;} } - } - } - } - return false; - } - - private boolean jj_3_2() { - if (jj_3R_166()) { - return true; - } - if (jj_3R_167()) { - return true; - } - return false; - } - - private boolean jj_3R_190() { - if (jj_scan_token(COMMA)) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - if (jj_3R_189()) { - return true; - } - return false; - } - - private boolean jj_3R_232() { - if (jj_3R_167()) { - return true; - } - return false; - } - - private boolean jj_3R_225() { - if (jj_scan_token(DIMEN)) { - return true; - } - return false; - } - - private boolean jj_3R_224() { - if (jj_scan_token(KHZ)) { - return true; - } - return false; - } - - private boolean jj_3R_189() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_232()) { - jj_scanpos = xsp; - if (jj_3R_233()) { - return true; - } - } - while (true) { - xsp = jj_scanpos; - if (jj_3_2()) { - jj_scanpos = xsp; - break; - } - } - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_223() { - if (jj_scan_token(HZ)) { - return true; - } - return false; - } - - private boolean jj_3R_222() { - if (jj_scan_token(MS)) { - return true; - } - return false; - } - - private boolean jj_3R_221() { - if (jj_scan_token(SECOND)) { - return true; - } - return false; - } - - private boolean jj_3R_220() { - if (jj_scan_token(GRAD)) { - return true; - } - return false; - } - - private boolean jj_3_1() { - if (jj_3R_165()) { - return true; - } - return false; - } - - private boolean jj_3R_219() { - if (jj_scan_token(RAD)) { - return true; - } - return false; - } - - private boolean jj_3R_171() { - if (jj_3R_189()) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_3R_190()) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_218() { - if (jj_scan_token(DEG)) { - return true; - } - return false; - } - - private boolean jj_3R_217() { - if (jj_scan_token(EXS)) { - return true; - } - return false; - } - - private boolean jj_3R_216() { - if (jj_scan_token(REM)) { - return true; - } - return false; - } - - private boolean jj_3_4() { - if (jj_3R_168()) { - return true; - } - return false; - } - - private boolean jj_3R_215() { - if (jj_scan_token(LEM)) { - return true; - } - return false; - } - - private boolean jj_3R_214() { - if (jj_scan_token(EMS)) { - return true; - } - return false; - } - - private boolean jj_3R_213() { - if (jj_scan_token(PX)) { - return true; - } - return false; - } - - private boolean jj_3R_212() { - if (jj_scan_token(IN)) { - return true; - } - return false; - } - - private boolean jj_3R_211() { - if (jj_scan_token(PC)) { - return true; - } - return false; - } - - private boolean jj_3R_210() { - if (jj_scan_token(MM)) { - return true; - } - return false; - } - - private boolean jj_3R_209() { - if (jj_scan_token(CM)) { - return true; - } - return false; - } - - private boolean jj_3R_248() { - if (jj_scan_token(INTERPOLATION)) { - return true; - } - return false; - } - - private boolean jj_3R_208() { - if (jj_scan_token(PT)) { - return true; - } - return false; - } - - private boolean jj_3R_207() { - if (jj_scan_token(PERCENTAGE)) { - return true; - } - return false; - } - - private boolean jj_3R_196() { - if (jj_3R_237()) { - return true; - } - return false; - } - - private boolean jj_3_3() { - if (jj_3R_165()) { - return true; - } - return false; - } - - private boolean jj_3R_206() { - if (jj_scan_token(NUMBER)) { - return true; - } - return false; - } - - private boolean jj_3R_205() { - if (jj_3R_241()) { - return true; - } - return false; - } - - private boolean jj_3R_187() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_205()) { - jj_scanpos = xsp; - } - xsp = jj_scanpos; - if (jj_3R_206()) { - jj_scanpos = xsp; - if (jj_3R_207()) { - jj_scanpos = xsp; - if (jj_3R_208()) { - jj_scanpos = xsp; - if (jj_3R_209()) { - jj_scanpos = xsp; - if (jj_3R_210()) { - jj_scanpos = xsp; - if (jj_3R_211()) { - jj_scanpos = xsp; - if (jj_3R_212()) { - jj_scanpos = xsp; - if (jj_3R_213()) { - jj_scanpos = xsp; - if (jj_3R_214()) { - jj_scanpos = xsp; - if (jj_3R_215()) { - jj_scanpos = xsp; - if (jj_3R_216()) { - jj_scanpos = xsp; - if (jj_3R_217()) { - jj_scanpos = xsp; - if (jj_3R_218()) { - jj_scanpos = xsp; - if (jj_3R_219()) { - jj_scanpos = xsp; - if (jj_3R_220()) { - jj_scanpos = xsp; - if (jj_3R_221()) { - jj_scanpos = xsp; - if (jj_3R_222()) { - jj_scanpos = xsp; - if (jj_3R_223()) { - jj_scanpos = xsp; - if (jj_3R_224()) { - jj_scanpos = xsp; - if (jj_3R_225()) { - jj_scanpos = xsp; - if (jj_3R_226()) { - return true; - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } - } + if (ret.charAt(0) == '@') { + documentHandler.unrecognizedRule(ret); + } else { + {if (true) throw new CSSParseException("unrecognize rule: " + ret, + getLocator());} } - } - } - } - return false; - } - - private boolean jj_3R_170() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_187()) { - jj_scanpos = xsp; - if (jj_3R_188()) { - return true; - } - } - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_178() { - if (jj_scan_token(SEMICOLON)) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_244() { - if (jj_scan_token(HASH)) { - return true; - } - return false; - } - - private boolean jj_3R_175() { - if (jj_scan_token(VARIABLE)) { - return true; - } - Token xsp; - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - return false; - } - - private boolean jj_3R_237() { - if (jj_3R_175()) { - return true; - } - return false; - } - - private boolean jj_3R_234() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_247()) { - jj_scanpos = xsp; - if (jj_3R_248()) { - return true; - } - } - return false; - } - - private boolean jj_3R_247() { - if (jj_scan_token(IDENT)) { - return true; - } - return false; - } - - private boolean jj_3R_245() { - if (jj_scan_token(URL)) { - return true; - } - return false; } + } + + final public void _parseImportRule() throws ParseException { + label_169: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[250] = jj_gen; + break label_169; + } + jj_consume_token(S); + } + importDeclaration(); + } + + final public void _parseMediaRule() throws ParseException { + label_170: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[251] = jj_gen; + break label_170; + } + jj_consume_token(S); + } + media(); + } + + final public void _parseDeclarationBlock() throws ParseException { + label_171: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[252] = jj_gen; + break label_171; + } + jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTERPOLATION: + case IDENT: + declaration(); + break; + default: + jj_la1[253] = jj_gen; + ; + } + label_172: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case SEMICOLON: + ; + break; + default: + jj_la1[254] = jj_gen; + break label_172; + } + jj_consume_token(SEMICOLON); + label_173: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[255] = jj_gen; + break label_173; + } + jj_consume_token(S); + } + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case INTERPOLATION: + case IDENT: + declaration(); + break; + default: + jj_la1[256] = jj_gen; + ; + } + } + } + + final public ArrayList<String> _parseSelectors() throws ParseException { + ArrayList<String> p = null; + try { + label_174: + while (true) { + switch ((jj_ntk==-1)?jj_ntk():jj_ntk) { + case S: + ; + break; + default: + jj_la1[257] = jj_gen; + break label_174; + } + jj_consume_token(S); + } + p = selectorList(); + {if (true) return p;} + } catch (ThrowedParseException e) { + {if (true) throw (ParseException) e.e.fillInStackTrace();} + } + throw new Error("Missing return statement in function"); + } + + private boolean jj_2_1(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return !jj_3_1(); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(0, xla); } + } + + private boolean jj_2_2(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return !jj_3_2(); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(1, xla); } + } + + private boolean jj_2_3(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return !jj_3_3(); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(2, xla); } + } + + private boolean jj_2_4(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return !jj_3_4(); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(3, xla); } + } + + private boolean jj_2_5(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return !jj_3_5(); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(4, xla); } + } + + private boolean jj_2_6(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return !jj_3_6(); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(5, xla); } + } + + private boolean jj_2_7(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return !jj_3_7(); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(6, xla); } + } + + private boolean jj_2_8(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return !jj_3_8(); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(7, xla); } + } + + private boolean jj_2_9(int xla) { + jj_la = xla; jj_lastpos = jj_scanpos = token; + try { return !jj_3_9(); } + catch(LookaheadSuccess ls) { return true; } + finally { jj_save(8, xla); } + } + + private boolean jj_3R_209() { + if (jj_scan_token(MOD)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_208() { + if (jj_scan_token(ANY)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_207() { + if (jj_scan_token(DIV)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_206() { + if (jj_scan_token(COMMA)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_184() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_206()) { + jj_scanpos = xsp; + if (jj_3R_207()) { + jj_scanpos = xsp; + if (jj_3R_208()) { + jj_scanpos = xsp; + if (jj_3R_209()) { + jj_scanpos = xsp; + if (jj_3R_210()) { + jj_scanpos = xsp; + if (jj_3R_211()) return true; + } + } + } + } + } + return false; + } + + private boolean jj_3R_214() { + if (jj_3R_213()) return true; + return false; + } + + private boolean jj_3R_213() { + Token xsp; + xsp = jj_scanpos; + if (jj_scan_token(20)) { + jj_scanpos = xsp; + if (jj_scan_token(24)) { + jj_scanpos = xsp; + if (jj_scan_token(25)) return true; + } + } + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_175() { + if (jj_3R_185()) return true; + if (jj_scan_token(COLON)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + if (jj_3R_186()) return true; + xsp = jj_scanpos; + if (jj_3R_187()) jj_scanpos = xsp; + if (jj_3R_188()) return true; + while (true) { + xsp = jj_scanpos; + if (jj_3R_188()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_212() { + if (jj_scan_token(GUARDED_SYM)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_190() { + if (jj_scan_token(S)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_214()) jj_scanpos = xsp; + return false; + } + + private boolean jj_3R_189() { + if (jj_3R_213()) return true; + return false; + } + + private boolean jj_3R_176() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_189()) { + jj_scanpos = xsp; + if (jj_3R_190()) return true; + } + return false; + } + + private boolean jj_3R_196() { + if (jj_scan_token(VARIABLE)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + if (jj_scan_token(COLON)) return true; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_178() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_196()) jj_scanpos = xsp; + if (jj_scan_token(CONTAINS)) return true; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + if (true) { jj_la = 0; jj_scanpos = jj_lastpos; return false;} + return false; + } + + private boolean jj_3R_216() { + if (jj_scan_token(HASH)) return true; + return false; + } + + private boolean jj_3R_286() { + if (jj_scan_token(IDENT)) return true; + return false; + } + + private boolean jj_3R_287() { + if (jj_scan_token(FUNCTION)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + if (true) { jj_la = 0; jj_scanpos = jj_lastpos; return false;} + return false; + } + + private boolean jj_3R_285() { + if (jj_scan_token(COLON)) return true; + return false; + } + + private boolean jj_3R_218() { + if (jj_scan_token(COLON)) return true; + Token xsp; + xsp = jj_scanpos; + if (jj_3R_285()) jj_scanpos = xsp; + xsp = jj_scanpos; + if (jj_3R_286()) { + jj_scanpos = xsp; + if (jj_3R_287()) return true; + } + return false; + } + + private boolean jj_3_7() { + if (jj_3R_182()) return true; + return false; + } + + private boolean jj_3R_203() { + if (jj_scan_token(LBRACE)) return true; + return false; + } + + private boolean jj_3R_306() { + if (jj_scan_token(STRING)) return true; + return false; + } + + private boolean jj_3R_304() { + if (jj_scan_token(STARMATCH)) return true; + return false; + } + + private boolean jj_3R_305() { + if (jj_scan_token(IDENT)) return true; + return false; + } + + private boolean jj_3R_303() { + if (jj_scan_token(DOLLARMATCH)) return true; + return false; + } + + private boolean jj_3R_302() { + if (jj_scan_token(CARETMATCH)) return true; + return false; + } + + private boolean jj_3R_301() { + if (jj_scan_token(DASHMATCH)) return true; + return false; + } + + private boolean jj_3R_300() { + if (jj_scan_token(INCLUDES)) return true; + return false; + } + + private boolean jj_3R_267() { + if (jj_scan_token(INTERPOLATION)) return true; + return false; + } + + private boolean jj_3R_299() { + if (jj_scan_token(EQ)) return true; + return false; + } + + private boolean jj_3R_202() { + if (jj_3R_186()) return true; + return false; + } + + private boolean jj_3R_292() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_299()) { + jj_scanpos = xsp; + if (jj_3R_300()) { + jj_scanpos = xsp; + if (jj_3R_301()) { + jj_scanpos = xsp; + if (jj_3R_302()) { + jj_scanpos = xsp; + if (jj_3R_303()) { + jj_scanpos = xsp; + if (jj_3R_304()) return true; + } + } + } + } + } + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + xsp = jj_scanpos; + if (jj_3R_305()) { + jj_scanpos = xsp; + if (jj_3R_306()) return true; + } + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3_6() { + if (jj_3R_181()) return true; + if (jj_scan_token(LBRACE)) return true; + return false; + } + + private boolean jj_3R_219() { + if (jj_scan_token(LBRACKET)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + if (jj_scan_token(IDENT)) return true; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + xsp = jj_scanpos; + if (jj_3R_292()) jj_scanpos = xsp; + if (jj_scan_token(RBRACKET)) return true; + return false; + } + + private boolean jj_3R_182() { + if (jj_3R_201()) return true; + if (jj_scan_token(COLON)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + xsp = jj_scanpos; + if (jj_3R_202()) { + jj_scanpos = xsp; + if (jj_3R_203()) return true; + } + return false; + } + + private boolean jj_3R_298() { + if (jj_scan_token(INTERPOLATION)) return true; + return false; + } + + private boolean jj_3R_253() { + if (jj_scan_token(PARENT)) return true; + return false; + } + + private boolean jj_3R_265() { + if (jj_3R_186()) return true; + return false; + } + + private boolean jj_3R_252() { + if (jj_scan_token(ANY)) return true; + return false; + } + + private boolean jj_3R_262() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_266()) { + jj_scanpos = xsp; + if (jj_3R_267()) return true; + } + return false; + } + + private boolean jj_3R_266() { + if (jj_scan_token(IDENT)) return true; + return false; + } + + private boolean jj_3R_215() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_251()) { + jj_scanpos = xsp; + if (jj_3R_252()) { + jj_scanpos = xsp; + if (jj_3R_253()) return true; + } + } + return false; + } + + private boolean jj_3R_251() { + Token xsp; + if (jj_3R_262()) return true; + while (true) { + xsp = jj_scanpos; + if (jj_3R_262()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_179() { + if (jj_scan_token(COMMA)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_255() { + if (jj_scan_token(FUNCTION)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + xsp = jj_scanpos; + if (jj_3R_265()) jj_scanpos = xsp; + if (jj_scan_token(RPARAN)) return true; + return false; + } + + private boolean jj_3R_297() { + if (jj_scan_token(IDENT)) return true; + return false; + } + + private boolean jj_3R_280() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_297()) { + jj_scanpos = xsp; + if (jj_3R_298()) return true; + } + return false; + } + + private boolean jj_3R_246() { + if (jj_3R_259()) return true; + return false; + } + + private boolean jj_3R_296() { + if (jj_3R_218()) return true; + return false; + } + + private boolean jj_3R_245() { + if (jj_3R_258()) return true; + return false; + } + + private boolean jj_3R_244() { + if (jj_3R_257()) return true; + return false; + } + + private boolean jj_3_5() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_179()) jj_scanpos = xsp; + if (jj_3R_180()) return true; + return false; + } + + private boolean jj_3R_217() { + if (jj_scan_token(DOT)) return true; + Token xsp; + if (jj_3R_280()) return true; + while (true) { + xsp = jj_scanpos; + if (jj_3R_280()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_294() { + if (jj_3R_217()) return true; + return false; + } + + private boolean jj_3R_289() { + if (jj_3R_217()) return true; + return false; + } + + private boolean jj_3R_291() { + if (jj_3R_218()) return true; + return false; + } + + private boolean jj_3R_279() { + if (jj_3R_218()) return true; + return false; + } + + private boolean jj_3R_282() { + if (jj_3R_217()) return true; + return false; + } + + private boolean jj_3R_284() { + if (jj_3R_218()) return true; + return false; + } + + private boolean jj_3R_295() { + if (jj_3R_219()) return true; + return false; + } + + private boolean jj_3R_272() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_293()) { + jj_scanpos = xsp; + if (jj_3R_294()) { + jj_scanpos = xsp; + if (jj_3R_295()) { + jj_scanpos = xsp; + if (jj_3R_296()) return true; + } + } + } + return false; + } + + private boolean jj_3R_293() { + if (jj_3R_216()) return true; + return false; + } + + private boolean jj_3R_271() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_288()) { + jj_scanpos = xsp; + if (jj_3R_289()) { + jj_scanpos = xsp; + if (jj_3R_290()) { + jj_scanpos = xsp; + if (jj_3R_291()) return true; + } + } + } + return false; + } + + private boolean jj_3R_288() { + if (jj_3R_216()) return true; + return false; + } + + private boolean jj_3R_276() { + if (jj_3R_218()) return true; + return false; + } + + private boolean jj_3R_270() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_281()) { + jj_scanpos = xsp; + if (jj_3R_282()) { + jj_scanpos = xsp; + if (jj_3R_283()) { + jj_scanpos = xsp; + if (jj_3R_284()) return true; + } + } + } + return false; + } + + private boolean jj_3R_281() { + if (jj_3R_216()) return true; + return false; + } + + private boolean jj_3R_290() { + if (jj_3R_219()) return true; + return false; + } + + private boolean jj_3R_278() { + if (jj_3R_219()) return true; + return false; + } + + private boolean jj_3R_283() { + if (jj_3R_219()) return true; + return false; + } + + private boolean jj_3R_269() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_277()) { + jj_scanpos = xsp; + if (jj_3R_278()) { + jj_scanpos = xsp; + if (jj_3R_279()) return true; + } + } + return false; + } + + private boolean jj_3R_274() { + if (jj_3R_217()) return true; + return false; + } + + private boolean jj_3R_277() { + if (jj_3R_217()) return true; + return false; + } + + private boolean jj_3R_256() { + if (jj_scan_token(DOT)) return true; + return false; + } + + private boolean jj_3R_243() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_256()) jj_scanpos = xsp; + xsp = jj_scanpos; + if (jj_scan_token(74)) { + jj_scanpos = xsp; + if (jj_scan_token(51)) { + jj_scanpos = xsp; + if (jj_scan_token(52)) { + jj_scanpos = xsp; + if (jj_scan_token(54)) return true; + } + } + } + return false; + } + + private boolean jj_3R_195() { + if (jj_3R_219()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_272()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_242() { + if (jj_scan_token(STRING)) return true; + return false; + } + + private boolean jj_3R_241() { + if (jj_3R_255()) return true; + return false; + } + + private boolean jj_3R_194() { + if (jj_3R_218()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_271()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_198() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_242()) { + jj_scanpos = xsp; + if (jj_3R_243()) { + jj_scanpos = xsp; + if (jj_3R_244()) { + jj_scanpos = xsp; + if (jj_3R_245()) { + jj_scanpos = xsp; + if (jj_3R_246()) return true; + } + } + } + } + return false; + } + + private boolean jj_3R_275() { + if (jj_3R_219()) return true; + return false; + } + + private boolean jj_3R_193() { + if (jj_3R_217()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_270()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_192() { + if (jj_3R_216()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_269()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_268() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_273()) { + jj_scanpos = xsp; + if (jj_3R_274()) { + jj_scanpos = xsp; + if (jj_3R_275()) { + jj_scanpos = xsp; + if (jj_3R_276()) return true; + } + } + } + return false; + } + + private boolean jj_3R_273() { + if (jj_3R_216()) return true; + return false; + } + + private boolean jj_3R_191() { + if (jj_3R_215()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_268()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_177() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_191()) { + jj_scanpos = xsp; + if (jj_3R_192()) { + jj_scanpos = xsp; + if (jj_3R_193()) { + jj_scanpos = xsp; + if (jj_3R_194()) { + jj_scanpos = xsp; + if (jj_3R_195()) return true; + } + } + } + } + return false; + } + + private boolean jj_3R_240() { + if (jj_scan_token(DIMEN)) return true; + return false; + } + + private boolean jj_3R_248() { + if (jj_3R_213()) return true; + if (jj_3R_177()) return true; + return false; + } + + private boolean jj_3R_239() { + if (jj_scan_token(KHZ)) return true; + return false; + } + + private boolean jj_3R_238() { + if (jj_scan_token(HZ)) return true; + return false; + } + + private boolean jj_3R_237() { + if (jj_scan_token(MS)) return true; + return false; + } + + private boolean jj_3R_236() { + if (jj_scan_token(SECOND)) return true; + return false; + } + + private boolean jj_3R_235() { + if (jj_scan_token(GRAD)) return true; + return false; + } + + private boolean jj_3R_234() { + if (jj_scan_token(RAD)) return true; + return false; + } + + private boolean jj_3R_233() { + if (jj_scan_token(DEG)) return true; + return false; + } + + private boolean jj_3R_232() { + if (jj_scan_token(EXS)) return true; + return false; + } + + private boolean jj_3R_231() { + if (jj_scan_token(REM)) return true; + return false; + } + + private boolean jj_3R_230() { + if (jj_scan_token(LEM)) return true; + return false; + } + + private boolean jj_3R_229() { + if (jj_scan_token(EMS)) return true; + return false; + } + + private boolean jj_3_2() { + if (jj_3R_176()) return true; + if (jj_3R_177()) return true; + return false; + } + + private boolean jj_3R_228() { + if (jj_scan_token(PX)) return true; + return false; + } + + private boolean jj_3R_227() { + if (jj_scan_token(IN)) return true; + return false; + } + + private boolean jj_3R_200() { + if (jj_scan_token(COMMA)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + if (jj_3R_199()) return true; + return false; + } + + private boolean jj_3R_247() { + if (jj_3R_177()) return true; + return false; + } + + private boolean jj_3R_226() { + if (jj_scan_token(PC)) return true; + return false; + } + + private boolean jj_3R_225() { + if (jj_scan_token(MM)) return true; + return false; + } + + private boolean jj_3R_199() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_247()) { + jj_scanpos = xsp; + if (jj_3R_248()) return true; + } + while (true) { + xsp = jj_scanpos; + if (jj_3_2()) { jj_scanpos = xsp; break; } + } + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_224() { + if (jj_scan_token(CM)) return true; + return false; + } + + private boolean jj_3R_223() { + if (jj_scan_token(PT)) return true; + return false; + } + + private boolean jj_3R_222() { + if (jj_scan_token(PERCENTAGE)) return true; + return false; + } + + private boolean jj_3R_205() { + if (jj_3R_250()) return true; + return false; + } + + private boolean jj_3R_221() { + if (jj_scan_token(NUMBER)) return true; + return false; + } + + private boolean jj_3_1() { + if (jj_3R_175()) return true; + return false; + } + + private boolean jj_3R_220() { + if (jj_3R_254()) return true; + return false; + } + + private boolean jj_3R_181() { + if (jj_3R_199()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3R_200()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_197() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_220()) jj_scanpos = xsp; + xsp = jj_scanpos; + if (jj_3R_221()) { + jj_scanpos = xsp; + if (jj_3R_222()) { + jj_scanpos = xsp; + if (jj_3R_223()) { + jj_scanpos = xsp; + if (jj_3R_224()) { + jj_scanpos = xsp; + if (jj_3R_225()) { + jj_scanpos = xsp; + if (jj_3R_226()) { + jj_scanpos = xsp; + if (jj_3R_227()) { + jj_scanpos = xsp; + if (jj_3R_228()) { + jj_scanpos = xsp; + if (jj_3R_229()) { + jj_scanpos = xsp; + if (jj_3R_230()) { + jj_scanpos = xsp; + if (jj_3R_231()) { + jj_scanpos = xsp; + if (jj_3R_232()) { + jj_scanpos = xsp; + if (jj_3R_233()) { + jj_scanpos = xsp; + if (jj_3R_234()) { + jj_scanpos = xsp; + if (jj_3R_235()) { + jj_scanpos = xsp; + if (jj_3R_236()) { + jj_scanpos = xsp; + if (jj_3R_237()) { + jj_scanpos = xsp; + if (jj_3R_238()) { + jj_scanpos = xsp; + if (jj_3R_239()) { + jj_scanpos = xsp; + if (jj_3R_240()) { + jj_scanpos = xsp; + if (jj_3R_241()) return true; + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + } + return false; + } + + private boolean jj_3R_180() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_197()) { + jj_scanpos = xsp; + if (jj_3R_198()) return true; + } + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_257() { + if (jj_scan_token(HASH)) return true; + return false; + } + + private boolean jj_3_4() { + if (jj_3R_178()) return true; + return false; + } + + private boolean jj_3R_250() { + if (jj_3R_185()) return true; + return false; + } + + private boolean jj_3R_258() { + if (jj_scan_token(URL)) return true; + return false; + } + + private boolean jj_3R_204() { + if (jj_3R_180()) return true; + return false; + } + + private boolean jj_3R_183() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_204()) { + jj_scanpos = xsp; + if (jj_3R_205()) return true; + } + return false; + } - private boolean jj_3R_191() { - Token xsp; - if (jj_3R_234()) { - return true; - } - while (true) { - xsp = jj_scanpos; - if (jj_3R_234()) { - jj_scanpos = xsp; - break; - } - } - while (true) { - xsp = jj_scanpos; - if (jj_scan_token(1)) { - jj_scanpos = xsp; - break; - } - } - return false; - } + private boolean jj_3R_261() { + if (jj_scan_token(INTERPOLATION)) return true; + return false; + } - private boolean jj_3R_177() { - if (jj_3R_197()) { - return true; - } - return false; - } + private boolean jj_3_9() { + if (jj_3R_184()) return true; + return false; + } - private boolean jj_3R_195() { - if (jj_3R_170()) { - return true; - } - return false; - } + private boolean jj_3_3() { + if (jj_3R_175()) return true; + return false; + } - private boolean jj_3R_174() { - Token xsp; - xsp = jj_scanpos; - if (jj_3R_195()) { - jj_scanpos = xsp; - if (jj_3R_196()) { - return true; - } - } - return false; - } - - /** Generated Token Manager. */ - public ParserTokenManager token_source; - /** Current token. */ - public Token token; - /** Next token. */ - public Token jj_nt; - private int jj_ntk; - private Token jj_scanpos, jj_lastpos; - private int jj_la; - private int jj_gen; - final private int[] jj_la1 = new int[248]; - static private int[] jj_la1_0; - static private int[] jj_la1_1; - static private int[] jj_la1_2; - static private int[] jj_la1_3; - static { - jj_la1_init_0(); - jj_la1_init_1(); - jj_la1_init_2(); - jj_la1_init_3(); - } - - private static void jj_la1_init_0() { - jj_la1_0 = new int[] { 0x0, 0xc02, 0xc02, 0x0, 0xc00, 0x2, 0x2, 0x2, - 0xd3100000, 0x0, 0xc00, 0x2, 0xc00, 0x2, 0x0, 0x2, 0x2, 0x2, - 0x0, 0x0, 0x2, 0x2, 0x0, 0x2, 0x0, 0x2, 0x2, 0xd3100000, - 0xd3100000, 0x2, 0x2, 0x2, 0xd3f45400, 0xd3f45400, 0x2, 0x2, - 0x2, 0x0, 0x0, 0x2, 0x0, 0x800000, 0x2, 0x0, 0x2, 0x2, 0x2, - 0x2, 0x0, 0x800000, 0x2, 0x0, 0x2, 0xe45400, 0x3100000, - 0x3100002, 0x3100000, 0x2, 0x2, 0x480002, 0x480002, 0x2, 0x0, - 0x0, 0x2, 0x2, 0x2, 0x2, 0xd3100000, 0xd3100000, 0x2, 0x400000, - 0x2, 0xd3100000, 0x2, 0x10000000, 0x10000000, 0x10000000, - 0x10000000, 0x10000000, 0x10000000, 0x10000000, 0x10000000, - 0x10000000, 0x10000000, 0xd0000000, 0x0, 0x0, 0x0, 0x0, - 0xc0000000, 0x2, 0x2, 0xfc000, 0x2, 0x0, 0x2, 0xfc000, 0x0, - 0x2, 0x0, 0x2, 0x0, 0x2, 0x800000, 0x0, 0xd3100000, 0x0, - 0x4d380002, 0x2, 0xd3100000, 0x2, 0x0, 0x2, 0x4d380002, 0x0, - 0x2, 0xd3100000, 0x2, 0x4d380002, 0x2, 0x2, 0x2, 0x0, 0x2, - 0xd3100000, 0x2, 0x2, 0x400000, 0x2, 0x2, 0x2, 0x2, 0x0, 0x2, - 0xd3100000, 0xd3100000, 0x2, 0x400000, 0x2, 0x2, 0x2, 0x400000, - 0x0, 0x0, 0x300000, 0x2, 0x0, 0x400000, 0x2, 0x300000, 0x2, - 0x0, 0x2, 0x0, 0x2, 0x800000, 0x2, 0x2, 0x0, 0x2, 0x0, 0x2, - 0x2, 0x2, 0x400000, 0x2, 0x2, 0x2, 0x2, 0x2, 0x0, 0x2, 0x2, - 0x2, 0x400000, 0x2, 0x2, 0x2, 0x0, 0x2, 0x2, 0x2, 0x400000, - 0x2, 0x2, 0x0, 0x2, 0x0, 0x2, 0x2, 0x2, 0x400000, 0x0, 0x2, - 0x2, 0x0, 0x2, 0x2, 0x2, 0x800000, 0x2, 0x2, 0x0, 0x800000, - 0x2, 0x0, 0x2, 0x0, 0xd3100000, 0x2, 0x0, 0x2, 0x0, 0x800000, - 0x2, 0x0, 0x2, 0x301000, 0x2, 0x0, 0x2, 0x2, 0x2, 0x2, - 0x8400000, 0x8400000, 0x300000, 0x300000, 0x300000, 0x0, 0x0, - 0x0, 0x0, 0x300000, 0x2, 0x2, 0x300000, 0x2, 0xd3100000, 0x2, - 0x2, 0x2, 0x0, 0x800000, 0x2, 0x0, 0x2, }; - } - - private static void jj_la1_init_1() { - jj_la1_1 = new int[] { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0xacc00181, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100, - 0x100, 0x0, 0x0, 0x240000, 0x0, 0x240000, 0x0, 0x0, 0xac800181, - 0xac800181, 0x0, 0x0, 0x0, 0xc000381, 0xc000381, 0x0, 0x0, 0x0, - 0x0, 0x80, 0x0, 0x100, 0x0, 0x0, 0x100, 0x0, 0x0, 0x0, 0x0, - 0x100, 0x0, 0x0, 0x100, 0x0, 0x200, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x185, 0x185, 0x0, 0x100, 0x100, 0x0, 0x0, 0x0, 0x0, - 0xac800181, 0xac800181, 0x0, 0x0, 0x0, 0x181, 0x0, 0x81, 0x81, - 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x81, 0x181, 0x100, - 0x100, 0x100, 0x100, 0x100, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xa0000000, 0xc800181, 0x0, - 0x7e, 0x0, 0xc800181, 0x0, 0x0, 0x0, 0x7e, 0x0, 0x0, 0xc800181, - 0x0, 0x7e, 0x0, 0x0, 0x0, 0x0, 0x0, 0xc800181, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x100, 0x0, 0xac800181, 0xac800181, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x80, 0x80, 0x2c0081, 0x0, 0x80, 0x0, - 0x0, 0x2c0081, 0x0, 0x80, 0x0, 0x100, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0xc000000, 0x0, - 0x0, 0xc0000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100, 0x0, 0x0, - 0x100, 0x0, 0xc000000, 0x181, 0x0, 0x0, 0x0, 0x100, 0x0, 0x0, - 0x100, 0x0, 0x2c0001, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x2c0001, 0x0, 0x0, 0x1, 0x2c0000, 0x2c0001, 0x2c0001, - 0x0, 0x0, 0x2c0001, 0x0, 0xc000181, 0x0, 0x0, 0x0, 0x100, 0x0, - 0x0, 0x100, 0x0, }; - } - - private static void jj_la1_init_2() { - jj_la1_2 = new int[] { 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100, - 0x1000, 0x0, 0x0, 0x0, 0x0, 0x880, 0x0, 0x0, 0x0, 0x100, 0x100, - 0x0, 0x0, 0x2000, 0x0, 0x2000, 0x0, 0x0, 0x1112, 0x1112, 0x0, - 0x0, 0x0, 0x2b80, 0x2b80, 0x0, 0x0, 0x0, 0x100, 0x0, 0x0, - 0x100, 0x0, 0x0, 0x100, 0x0, 0x0, 0x0, 0x0, 0x100, 0x0, 0x0, - 0x100, 0x0, 0x2a80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x380, 0x380, 0x0, - 0x100, 0x100, 0x0, 0x0, 0x0, 0x0, 0x1112, 0x1112, 0x0, 0x0, - 0x0, 0x100, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x100, 0x100, 0x100, 0x100, 0x100, 0x100, 0x0, 0x0, 0x0, - 0x0, 0x180, 0x0, 0x0, 0x0, 0x0, 0x100, 0x0, 0x40, 0x0, 0x0, - 0x0, 0x102, 0x1000, 0x1300, 0x0, 0x1102, 0x0, 0x1, 0x0, 0x1300, - 0x20, 0x0, 0x1102, 0x0, 0x1300, 0x0, 0x0, 0x0, 0x1100, 0x0, - 0x1102, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x100, 0x0, 0x1102, - 0x1102, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000, 0x1000, - 0xfffffb80, 0x0, 0x0, 0x0, 0x0, 0xfffffb80, 0x0, 0x0, 0x0, - 0x1100, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1000, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x100, 0x0, 0x0, 0x100, 0x0, 0x0, 0x100, 0x0, 0x0, 0x0, - 0x100, 0x0, 0x0, 0x100, 0x0, 0xfffffb80, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0xfffffb80, 0x0, 0xffffe200, 0x0, - 0x100, 0x980, 0xffffeb80, 0x0, 0x0, 0xfffffb80, 0x0, 0x100, - 0x0, 0x0, 0x0, 0x100, 0x0, 0x0, 0x100, 0x0, }; - } - - private static void jj_la1_init_3() { - jj_la1_3 = new int[] { 0x8, 0x80, 0x80, 0x2, 0x80, 0x0, 0x0, 0x0, 0x75, - 0x0, 0x80, 0x0, 0x80, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x45, 0x45, 0x0, 0x0, 0x0, - 0xc401bf, 0xc401bf, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0xc401be, 0x0, 0x0, 0x0, 0x0, 0x0, 0x400000, 0x400000, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x45, 0x45, 0x0, 0x0, 0x0, 0x1, - 0x0, 0x1, 0x1, 0x0, 0x0, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x400000, 0x0, 0x0, 0x0, 0x0, 0x0, 0x45, 0x0, - 0x200000, 0x0, 0x45, 0x0, 0x0, 0x0, 0x200000, 0x0, 0x0, 0x45, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x45, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x400000, 0x0, 0x75, 0x75, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x440001, 0x0, 0x0, 0x0, 0x0, 0x440001, - 0x0, 0x0, 0x0, 0x400000, 0x0, 0x0, 0x0, 0x0, 0x380000, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x1, 0x0, 0x100, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x440001, 0x0, - 0x100, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x440001, 0x0, - 0x400000, 0x0, 0x0, 0x40001, 0x440001, 0x0, 0x0, 0x440001, 0x0, - 0x37, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, }; - } - - final private JJCalls[] jj_2_rtns = new JJCalls[8]; - private boolean jj_rescan = false; - private int jj_gc = 0; - - /** Constructor with user supplied CharStream. */ - public Parser(CharStream stream) { - token_source = new ParserTokenManager(stream); - token = new Token(); - jj_ntk = -1; - jj_gen = 0; - for (int i = 0; i < 248; i++) { - jj_la1[i] = -1; - } - for (int i = 0; i < jj_2_rtns.length; i++) { - jj_2_rtns[i] = new JJCalls(); - } - } + private boolean jj_3R_264() { + if (jj_scan_token(PLUS)) return true; + return false; + } - /** Reinitialise. */ - public void ReInit(CharStream stream) { - token_source.ReInit(stream); - token = new Token(); - jj_ntk = -1; - jj_gen = 0; - for (int i = 0; i < 248; i++) { - jj_la1[i] = -1; - } - for (int i = 0; i < jj_2_rtns.length; i++) { - jj_2_rtns[i] = new JJCalls(); - } + private boolean jj_3R_254() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_263()) { + jj_scanpos = xsp; + if (jj_3R_264()) return true; } + return false; + } - /** Constructor with generated Token Manager. */ - public Parser(ParserTokenManager tm) { - token_source = tm; - token = new Token(); - jj_ntk = -1; - jj_gen = 0; - for (int i = 0; i < 248; i++) { - jj_la1[i] = -1; - } - for (int i = 0; i < jj_2_rtns.length; i++) { - jj_2_rtns[i] = new JJCalls(); - } - } + private boolean jj_3R_263() { + if (jj_scan_token(MINUS)) return true; + return false; + } - /** Reinitialise. */ - public void ReInit(ParserTokenManager tm) { - token_source = tm; - token = new Token(); - jj_ntk = -1; - jj_gen = 0; - for (int i = 0; i < 248; i++) { - jj_la1[i] = -1; - } + private boolean jj_3R_259() { + if (jj_scan_token(UNICODERANGE)) return true; + return false; + } + + private boolean jj_3R_188() { + if (jj_scan_token(SEMICOLON)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3_8() { + Token xsp; + xsp = jj_scanpos; + if (jj_3_9()) jj_scanpos = xsp; + if (jj_3R_183()) return true; + return false; + } + + private boolean jj_3R_186() { + if (jj_3R_183()) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_3_8()) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_185() { + if (jj_scan_token(VARIABLE)) return true; + Token xsp; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_249() { + Token xsp; + xsp = jj_scanpos; + if (jj_3R_260()) { + jj_scanpos = xsp; + if (jj_3R_261()) return true; + } + return false; + } + + private boolean jj_3R_260() { + if (jj_scan_token(IDENT)) return true; + return false; + } + + private boolean jj_3R_201() { + Token xsp; + if (jj_3R_249()) return true; + while (true) { + xsp = jj_scanpos; + if (jj_3R_249()) { jj_scanpos = xsp; break; } + } + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_211() { + if (jj_scan_token(MINUS)) return true; + Token xsp; + if (jj_scan_token(1)) return true; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + return false; + } + + private boolean jj_3R_187() { + if (jj_3R_212()) return true; + return false; + } + + private boolean jj_3R_210() { + if (jj_scan_token(PLUS)) return true; + Token xsp; + if (jj_scan_token(1)) return true; + while (true) { + xsp = jj_scanpos; + if (jj_scan_token(1)) { jj_scanpos = xsp; break; } + } + return false; + } + + /** Generated Token Manager. */ + public ParserTokenManager token_source; + /** Current token. */ + public Token token; + /** Next token. */ + public Token jj_nt; + private int jj_ntk; + private Token jj_scanpos, jj_lastpos; + private int jj_la; + private int jj_gen; + final private int[] jj_la1 = new int[258]; + static private int[] jj_la1_0; + static private int[] jj_la1_1; + static private int[] jj_la1_2; + static private int[] jj_la1_3; + static { + jj_la1_init_0(); + jj_la1_init_1(); + jj_la1_init_2(); + jj_la1_init_3(); + } + private static void jj_la1_init_0() { + jj_la1_0 = new int[] {0x0,0xc02,0xc02,0x0,0xc00,0x2,0x2,0x2,0x53100000,0x0,0xc00,0x2,0xc00,0x2,0x0,0x2,0x2,0x2,0x0,0x0,0x2,0x2,0x0,0x2,0x0,0x2,0x2,0x53100000,0x53100000,0x2,0x2,0x2,0x53f45400,0x53f45400,0x2,0x2,0x2,0x0,0x0,0x2,0x0,0x800000,0x2,0x0,0x2,0x2,0x2,0x2,0x0,0x800000,0x2,0x0,0x2,0xe45400,0x3100000,0x3100002,0x3100000,0x2,0x2,0x480002,0x480002,0x2,0x0,0x0,0x2,0x2,0x2,0x2,0x53100000,0x53100000,0x2,0x400000,0x2,0x53100000,0x2,0x10000000,0x10000000,0x10000000,0x10000000,0x10000000,0x10000000,0x10000000,0x10000000,0x10000000,0x10000000,0x50000000,0x0,0x0,0x0,0x0,0x40000000,0x2,0x2,0xfc000,0x2,0x0,0x2,0xfc000,0x0,0x2,0x0,0x2,0x0,0x2,0x800000,0x0,0x53100000,0x0,0x4d380002,0x2,0x53100000,0x2,0x0,0x2,0x4d380002,0x0,0x2,0x53100000,0x2,0x4d380002,0x2,0x2,0x2,0x0,0x2,0x53100000,0x2,0x2,0x400000,0x2,0x2,0x2,0x2,0x0,0x2,0x53100000,0x53100000,0x2,0x400000,0x2,0x2,0x2,0x400000,0x0,0x0,0x300000,0x2,0x0,0x400000,0x2,0x300000,0x2,0x0,0x2,0x0,0x2,0x800000,0x2,0x53100000,0x2,0x801000,0x2,0x2,0x0,0x2,0x0,0x2,0x2,0x2,0x400000,0x2,0x2,0x2,0x2,0x2,0x0,0x2,0x2,0x2,0x400000,0x2,0x2,0x2,0x0,0x2,0x2,0x2,0x400000,0x2,0x2,0x0,0x2,0x0,0x2,0x2,0x2,0x400000,0x0,0x2,0x2,0x0,0x2,0x2,0x2,0x800000,0x2,0x2,0x800000,0x2,0x2,0x0,0x800000,0x2,0x0,0x2,0x0,0x53100000,0x2,0x0,0x2,0x0,0x800000,0x2,0x0,0x2,0x301000,0x2,0x0,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0x2,0xc8700000,0x300000,0x300000,0x300000,0x0,0x0,0x0,0x0,0x300000,0x2,0x2,0x300000,0x2,0x53100000,0x2,0x2,0x2,0x0,0x800000,0x2,0x0,0x2,}; + } + private static void jj_la1_init_1() { + jj_la1_1 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x59800303,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x200,0x0,0x0,0x480000,0x0,0x480000,0x0,0x0,0x59000303,0x59000303,0x0,0x0,0x0,0x18000703,0x18000703,0x0,0x0,0x0,0x0,0x100,0x0,0x200,0x0,0x0,0x200,0x0,0x0,0x0,0x0,0x200,0x0,0x0,0x200,0x0,0x400,0x0,0x0,0x0,0x0,0x0,0x30a,0x30a,0x0,0x200,0x200,0x0,0x0,0x0,0x0,0x59000303,0x59000303,0x0,0x0,0x0,0x303,0x0,0x102,0x102,0x102,0x102,0x102,0x102,0x102,0x102,0x102,0x102,0x303,0x200,0x200,0x200,0x200,0x201,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x100,0x0,0x0,0x0,0x0,0x0,0x0,0x40000000,0x19000303,0x0,0xfc,0x0,0x19000303,0x0,0x0,0x0,0xfc,0x0,0x0,0x19000303,0x0,0xfc,0x0,0x0,0x0,0x0,0x0,0x19000303,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x59000303,0x59000303,0x0,0x0,0x0,0x0,0x0,0x0,0x100,0x100,0x580102,0x0,0x100,0x0,0x0,0x580102,0x0,0x100,0x0,0x200,0x0,0x0,0x0,0x18000303,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x8,0x0,0x0,0x0,0x0,0x18000000,0x0,0x0,0x180000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x200,0x0,0x0,0x200,0x0,0x18000000,0x303,0x0,0x0,0x0,0x200,0x0,0x0,0x200,0x0,0x580002,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x580002,0x0,0x0,0x2,0x580000,0x580002,0x580002,0x0,0x0,0x580002,0x0,0x18000303,0x0,0x0,0x0,0x200,0x0,0x0,0x200,0x0,}; + } + private static void jj_la1_init_2() { + jj_la1_2 = new int[] {0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x401,0x4000,0x0,0x0,0x0,0x0,0x2200,0x0,0x0,0x0,0x400,0x400,0x0,0x0,0x8000,0x0,0x8000,0x0,0x0,0x4465,0x4465,0x0,0x0,0x0,0xae00,0xae00,0x0,0x0,0x0,0x400,0x0,0x0,0x400,0x0,0x0,0x400,0x0,0x0,0x0,0x0,0x400,0x0,0x0,0x400,0x0,0xaa00,0x0,0x0,0x0,0x0,0x0,0xe00,0xe00,0x0,0x400,0x400,0x0,0x0,0x0,0x0,0x4465,0x4465,0x0,0x0,0x0,0x400,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x400,0x400,0x400,0x400,0x400,0x400,0x0,0x0,0x0,0x0,0x600,0x0,0x0,0x0,0x0,0x400,0x0,0x100,0x0,0x0,0x1,0x424,0x4000,0x4c00,0x0,0x4424,0x0,0x2,0x0,0x4c00,0x80,0x0,0x4424,0x0,0x4c00,0x0,0x0,0x0,0x4400,0x0,0x4424,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x400,0x0,0x4425,0x4425,0x0,0x0,0x0,0x0,0x0,0x0,0x4000,0x4000,0xffffee00,0x0,0x0,0x0,0x0,0xffffee00,0x0,0x0,0x0,0x4400,0x0,0x0,0x0,0x400,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x400,0x0,0x0,0x400,0x0,0x0,0x400,0x0,0x0,0x0,0x400,0x0,0x0,0x400,0x0,0xffffee00,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0xffffee00,0x0,0xffff8800,0x0,0x400,0x2600,0xffffae00,0x0,0x0,0xffffee00,0x0,0x400,0x0,0x0,0x0,0x400,0x0,0x0,0x400,0x0,}; + } + private static void jj_la1_init_3() { + jj_la1_3 = new int[] {0x20,0x200,0x200,0x8,0x200,0x0,0x0,0x0,0x1d4,0x0,0x200,0x0,0x200,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x114,0x114,0x0,0x0,0x0,0x31006fc,0x31006fc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x31006f8,0x0,0x0,0x0,0x0,0x0,0x1000000,0x1000000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x114,0x114,0x0,0x0,0x0,0x4,0x0,0x4,0x4,0x0,0x0,0x4,0x4,0x4,0x4,0x4,0x4,0x4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000000,0x0,0x0,0x0,0x0,0x0,0x114,0x0,0x800000,0x0,0x114,0x0,0x0,0x0,0x800000,0x0,0x0,0x114,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x114,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1000000,0x0,0x1d4,0x1d4,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1100007,0x0,0x0,0x0,0x0,0x1100007,0x0,0x0,0x0,0x1000000,0x0,0x0,0x0,0x4,0x0,0x0,0x0,0x0,0xe00000,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x4,0x0,0x400,0x0,0x0,0x0,0x0,0x0,0x0,0x1100007,0x0,0x400,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x1100007,0x0,0x1000003,0x0,0x0,0x100004,0x1100007,0x0,0x0,0x1100007,0x0,0xdc,0x0,0x0,0x0,0x0,0x0,0x0,0x0,0x0,}; + } + final private JJCalls[] jj_2_rtns = new JJCalls[9]; + private boolean jj_rescan = false; + private int jj_gc = 0; + + /** Constructor with user supplied CharStream. */ + public Parser(CharStream stream) { + token_source = new ParserTokenManager(stream); + token = new Token(); + jj_ntk = -1; + jj_gen = 0; + for (int i = 0; i < 258; i++) jj_la1[i] = -1; + for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); + } + + /** Reinitialise. */ + public void ReInit(CharStream stream) { + token_source.ReInit(stream); + token = new Token(); + jj_ntk = -1; + jj_gen = 0; + for (int i = 0; i < 258; i++) jj_la1[i] = -1; + for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); + } + + /** Constructor with generated Token Manager. */ + public Parser(ParserTokenManager tm) { + token_source = tm; + token = new Token(); + jj_ntk = -1; + jj_gen = 0; + for (int i = 0; i < 258; i++) jj_la1[i] = -1; + for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); + } + + /** Reinitialise. */ + public void ReInit(ParserTokenManager tm) { + token_source = tm; + token = new Token(); + jj_ntk = -1; + jj_gen = 0; + for (int i = 0; i < 258; i++) jj_la1[i] = -1; + for (int i = 0; i < jj_2_rtns.length; i++) jj_2_rtns[i] = new JJCalls(); + } + + private Token jj_consume_token(int kind) throws ParseException { + Token oldToken; + if ((oldToken = token).next != null) token = token.next; + else token = token.next = token_source.getNextToken(); + jj_ntk = -1; + if (token.kind == kind) { + jj_gen++; + if (++jj_gc > 100) { + jj_gc = 0; for (int i = 0; i < jj_2_rtns.length; i++) { - jj_2_rtns[i] = new JJCalls(); - } - } - - private Token jj_consume_token(int kind) throws ParseException { - Token oldToken; - if ((oldToken = token).next != null) { - token = token.next; - } else { - token = token.next = token_source.getNextToken(); - } - jj_ntk = -1; - if (token.kind == kind) { - jj_gen++; - if (++jj_gc > 100) { - jj_gc = 0; - for (int i = 0; i < jj_2_rtns.length; i++) { - JJCalls c = jj_2_rtns[i]; - while (c != null) { - if (c.gen < jj_gen) { - c.first = null; - } - c = c.next; - } - } - } - return token; - } - token = oldToken; - jj_kind = kind; - throw generateParseException(); - } - - static private final class LookaheadSuccess extends java.lang.Error { - } - - final private LookaheadSuccess jj_ls = new LookaheadSuccess(); - - private boolean jj_scan_token(int kind) { - if (jj_scanpos == jj_lastpos) { - jj_la--; - if (jj_scanpos.next == null) { - jj_lastpos = jj_scanpos = jj_scanpos.next = token_source - .getNextToken(); - } else { - jj_lastpos = jj_scanpos = jj_scanpos.next; - } - } else { - jj_scanpos = jj_scanpos.next; - } - if (jj_rescan) { - int i = 0; - Token tok = token; - while (tok != null && tok != jj_scanpos) { - i++; - tok = tok.next; - } - if (tok != null) { - jj_add_error_token(kind, i); - } - } - if (jj_scanpos.kind != kind) { - return true; - } - if (jj_la == 0 && jj_scanpos == jj_lastpos) { - throw jj_ls; - } - return false; - } - - /** Get the next Token. */ - final public Token getNextToken() { - if (token.next != null) { - token = token.next; - } else { - token = token.next = token_source.getNextToken(); - } - jj_ntk = -1; - jj_gen++; - return token; - } - - /** Get the specific Token. */ - final public Token getToken(int index) { - Token t = token; - for (int i = 0; i < index; i++) { - if (t.next != null) { - t = t.next; - } else { - t = t.next = token_source.getNextToken(); - } - } - return t; - } - - private int jj_ntk() { - if ((jj_nt = token.next) == null) { - return (jj_ntk = (token.next = token_source.getNextToken()).kind); - } else { - return (jj_ntk = jj_nt.kind); - } - } - - private java.util.List<int[]> jj_expentries = new java.util.ArrayList<int[]>(); - private int[] jj_expentry; - private int jj_kind = -1; - private int[] jj_lasttokens = new int[100]; - private int jj_endpos; - - private void jj_add_error_token(int kind, int pos) { - if (pos >= 100) { - return; - } - if (pos == jj_endpos + 1) { - jj_lasttokens[jj_endpos++] = kind; - } else if (jj_endpos != 0) { - jj_expentry = new int[jj_endpos]; - for (int i = 0; i < jj_endpos; i++) { - jj_expentry[i] = jj_lasttokens[i]; - } - jj_entries_loop: for (java.util.Iterator<?> it = jj_expentries - .iterator(); it.hasNext();) { - int[] oldentry = (int[]) (it.next()); - if (oldentry.length == jj_expentry.length) { - for (int i = 0; i < jj_expentry.length; i++) { - if (oldentry[i] != jj_expentry[i]) { - continue jj_entries_loop; - } - } - jj_expentries.add(jj_expentry); - break jj_entries_loop; - } - } - if (pos != 0) { - jj_lasttokens[(jj_endpos = pos) - 1] = kind; - } - } - } - - /** Generate ParseException. */ - public ParseException generateParseException() { - jj_expentries.clear(); - boolean[] la1tokens = new boolean[120]; - if (jj_kind >= 0) { - la1tokens[jj_kind] = true; - jj_kind = -1; - } - for (int i = 0; i < 248; i++) { - if (jj_la1[i] == jj_gen) { - for (int j = 0; j < 32; j++) { - if ((jj_la1_0[i] & (1 << j)) != 0) { - la1tokens[j] = true; - } - if ((jj_la1_1[i] & (1 << j)) != 0) { - la1tokens[32 + j] = true; - } - if ((jj_la1_2[i] & (1 << j)) != 0) { - la1tokens[64 + j] = true; - } - if ((jj_la1_3[i] & (1 << j)) != 0) { - la1tokens[96 + j] = true; - } - } - } - } - for (int i = 0; i < 120; i++) { - if (la1tokens[i]) { - jj_expentry = new int[1]; - jj_expentry[0] = i; - jj_expentries.add(jj_expentry); - } - } - jj_endpos = 0; - jj_rescan_token(); - jj_add_error_token(0, 0); - int[][] exptokseq = new int[jj_expentries.size()][]; - for (int i = 0; i < jj_expentries.size(); i++) { - exptokseq[i] = jj_expentries.get(i); - } - return new ParseException(token, exptokseq, tokenImage); - } - - /** Enable tracing. */ - final public void enable_tracing() { - } - - /** Disable tracing. */ - final public void disable_tracing() { - } - - private void jj_rescan_token() { - jj_rescan = true; - for (int i = 0; i < 8; i++) { - try { - JJCalls p = jj_2_rtns[i]; - do { - if (p.gen > jj_gen) { - jj_la = p.arg; - jj_lastpos = jj_scanpos = p.first; - switch (i) { - case 0: - jj_3_1(); - break; - case 1: - jj_3_2(); - break; - case 2: - jj_3_3(); - break; - case 3: - jj_3_4(); - break; - case 4: - jj_3_5(); - break; - case 5: - jj_3_6(); - break; - case 6: - jj_3_7(); - break; - case 7: - jj_3_8(); - break; - } - } - p = p.next; - } while (p != null); - } catch (LookaheadSuccess ls) { - } - } - jj_rescan = false; - } - - private void jj_save(int index, int xla) { - JJCalls p = jj_2_rtns[index]; - while (p.gen > jj_gen) { - if (p.next == null) { - p = p.next = new JJCalls(); - break; - } - p = p.next; - } - p.gen = jj_gen + xla - jj_la; - p.first = token; - p.arg = xla; - } - - static final class JJCalls { - int gen; - Token first; - int arg; - JJCalls next; - } + JJCalls c = jj_2_rtns[i]; + while (c != null) { + if (c.gen < jj_gen) c.first = null; + c = c.next; + } + } + } + return token; + } + token = oldToken; + jj_kind = kind; + throw generateParseException(); + } + + static private final class LookaheadSuccess extends java.lang.Error { } + final private LookaheadSuccess jj_ls = new LookaheadSuccess(); + private boolean jj_scan_token(int kind) { + if (jj_scanpos == jj_lastpos) { + jj_la--; + if (jj_scanpos.next == null) { + jj_lastpos = jj_scanpos = jj_scanpos.next = token_source.getNextToken(); + } else { + jj_lastpos = jj_scanpos = jj_scanpos.next; + } + } else { + jj_scanpos = jj_scanpos.next; + } + if (jj_rescan) { + int i = 0; Token tok = token; + while (tok != null && tok != jj_scanpos) { i++; tok = tok.next; } + if (tok != null) jj_add_error_token(kind, i); + } + if (jj_scanpos.kind != kind) return true; + if (jj_la == 0 && jj_scanpos == jj_lastpos) throw jj_ls; + return false; + } + + +/** Get the next Token. */ + final public Token getNextToken() { + if (token.next != null) token = token.next; + else token = token.next = token_source.getNextToken(); + jj_ntk = -1; + jj_gen++; + return token; + } + +/** Get the specific Token. */ + final public Token getToken(int index) { + Token t = token; + for (int i = 0; i < index; i++) { + if (t.next != null) t = t.next; + else t = t.next = token_source.getNextToken(); + } + return t; + } + + private int jj_ntk() { + if ((jj_nt=token.next) == null) + return (jj_ntk = (token.next=token_source.getNextToken()).kind); + else + return (jj_ntk = jj_nt.kind); + } + + private java.util.List<int[]> jj_expentries = new java.util.ArrayList<int[]>(); + private int[] jj_expentry; + private int jj_kind = -1; + private int[] jj_lasttokens = new int[100]; + private int jj_endpos; + + private void jj_add_error_token(int kind, int pos) { + if (pos >= 100) return; + if (pos == jj_endpos + 1) { + jj_lasttokens[jj_endpos++] = kind; + } else if (jj_endpos != 0) { + jj_expentry = new int[jj_endpos]; + for (int i = 0; i < jj_endpos; i++) { + jj_expentry[i] = jj_lasttokens[i]; + } + jj_entries_loop: for (java.util.Iterator<?> it = jj_expentries.iterator(); it.hasNext();) { + int[] oldentry = (int[])(it.next()); + if (oldentry.length == jj_expentry.length) { + for (int i = 0; i < jj_expentry.length; i++) { + if (oldentry[i] != jj_expentry[i]) { + continue jj_entries_loop; + } + } + jj_expentries.add(jj_expentry); + break jj_entries_loop; + } + } + if (pos != 0) jj_lasttokens[(jj_endpos = pos) - 1] = kind; + } + } + + /** Generate ParseException. */ + public ParseException generateParseException() { + jj_expentries.clear(); + boolean[] la1tokens = new boolean[122]; + if (jj_kind >= 0) { + la1tokens[jj_kind] = true; + jj_kind = -1; + } + for (int i = 0; i < 258; i++) { + if (jj_la1[i] == jj_gen) { + for (int j = 0; j < 32; j++) { + if ((jj_la1_0[i] & (1<<j)) != 0) { + la1tokens[j] = true; + } + if ((jj_la1_1[i] & (1<<j)) != 0) { + la1tokens[32+j] = true; + } + if ((jj_la1_2[i] & (1<<j)) != 0) { + la1tokens[64+j] = true; + } + if ((jj_la1_3[i] & (1<<j)) != 0) { + la1tokens[96+j] = true; + } + } + } + } + for (int i = 0; i < 122; i++) { + if (la1tokens[i]) { + jj_expentry = new int[1]; + jj_expentry[0] = i; + jj_expentries.add(jj_expentry); + } + } + jj_endpos = 0; + jj_rescan_token(); + jj_add_error_token(0, 0); + int[][] exptokseq = new int[jj_expentries.size()][]; + for (int i = 0; i < jj_expentries.size(); i++) { + exptokseq[i] = jj_expentries.get(i); + } + return new ParseException(token, exptokseq, tokenImage); + } + + /** Enable tracing. */ + final public void enable_tracing() { + } + + /** Disable tracing. */ + final public void disable_tracing() { + } + + private void jj_rescan_token() { + jj_rescan = true; + for (int i = 0; i < 9; i++) { + try { + JJCalls p = jj_2_rtns[i]; + do { + if (p.gen > jj_gen) { + jj_la = p.arg; jj_lastpos = jj_scanpos = p.first; + switch (i) { + case 0: jj_3_1(); break; + case 1: jj_3_2(); break; + case 2: jj_3_3(); break; + case 3: jj_3_4(); break; + case 4: jj_3_5(); break; + case 5: jj_3_6(); break; + case 6: jj_3_7(); break; + case 7: jj_3_8(); break; + case 8: jj_3_9(); break; + } + } + p = p.next; + } while (p != null); + } catch(LookaheadSuccess ls) { } + } + jj_rescan = false; + } + + private void jj_save(int index, int xla) { + JJCalls p = jj_2_rtns[index]; + while (p.gen > jj_gen) { + if (p.next == null) { p = p.next = new JJCalls(); break; } + p = p.next; + } + p.gen = jj_gen + xla - jj_la; p.first = token; p.arg = xla; + } + + static final class JJCalls { + int gen; + Token first; + int arg; + JJCalls next; + } } diff --git a/theme-compiler/src/com/vaadin/sass/internal/parser/Parser.jj b/theme-compiler/src/com/vaadin/sass/internal/parser/Parser.jj index 840a88e66e..7f86527015 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/parser/Parser.jj +++ b/theme-compiler/src/com/vaadin/sass/internal/parser/Parser.jj @@ -534,6 +534,7 @@ TOKEN : | < LBRACKET : "[" > | < RBRACKET : "]" > | < ANY : "*" > + | < MOD : "%" > | < PARENT : "&" > | < DOT : "." > | < LPARAN : "(" > @@ -603,6 +604,7 @@ TOKEN : | <EXTEND_SYM : "@extend"> | <MOZ_DOCUMENT_SYM : "@-moz-document"> | <SUPPORTS_SYM : "@supports"> + | <CONTENT_SYM : "@content"> } < DEFAULT > @@ -1531,7 +1533,7 @@ void controlDirective() : void ifContentStatement() : {} { - includeDirective() | media() | extendDirective() | styleRuleOrDeclarationOrNestedProperties() + contentDirective() | includeDirective() | media() | extendDirective() | styleRuleOrDeclarationOrNestedProperties() | keyframes() | LOOKAHEAD(variable()) variable() | listModifyDirective() } @@ -1731,8 +1733,13 @@ void includeDirective() : (<S>)* (name = property()|name = variableName(){ name = "$"+name;} |(name = functionName() - args = argValuelist()) <RPARAN>)(";"(<S>)*)+ + args = argValuelist()) <RPARAN>) + ((";"(<S>)*)+ {documentHandler.includeDirective(name, args);} + | <LBRACE> (<S>)* {documentHandler.startIncludeContentBlock(name);} + (styleRuleOrDeclarationOrNestedProperties())* + <RBRACE> (<S>)* {documentHandler.endIncludeContentBlock();} + ) } String interpolation() : @@ -1995,6 +2002,15 @@ void extendDirective() : {documentHandler.extendDirective(list);} } +void contentDirective() : +{} +{ + <CONTENT_SYM> + (<S>)* + (";"(<S>)*)+ + {documentHandler.contentDirective();} +} + JAVACODE Node importDirective(){ return null; @@ -2215,12 +2231,40 @@ boolean guarded() : LexicalUnitImpl operator(LexicalUnitImpl prev) : {Token n;} { -n="/" ( <S> )* { return LexicalUnitImpl.createSlash(n.beginLine, - n.beginColumn, - prev); } -| n="," ( <S> )* { return LexicalUnitImpl.createComma(n.beginLine, - n.beginColumn, - prev); } +/* (comments copied from basic_arithmetics.scss) +*supports: +* 1. standard arithmetic operations (+, -, *, /, %) +* 2. / is treated as css operator, unless one of its operands is variable or there is another binary arithmetic operator +*limits: +* 1. cannot mix arithmetic and css operations, e.g. "margin: 1px + 3px 2px" will fail +* 2. space between add and minus operator and their following operand is mandatory. e.g. "1 + 2" is valid, "1+2" is not +* 3. parenthesis is not supported now. +*/ +n="," ( <S> )* { return LexicalUnitImpl.createComma(n.beginLine, + n.beginColumn, + prev); } +|n="/" ( <S> )* { return LexicalUnitImpl.createSlash(n.beginLine, + n.beginColumn, + prev); } +| n="*" ( <S> )* { return LexicalUnitImpl.createMultiply(n.beginLine, + n.beginColumn, + prev); } +| n="%" ( <S> )* { return LexicalUnitImpl.createModulo(n.beginLine, + n.beginColumn, + prev); } +/* +* for '+', since it can be either a binary operator or an unary operator, +* which is ambiguous. To avoid this, the binary operator '+' always has +* a space before the following term. so '2+3' is not a valid binary expression, +* but '2 + 3' is. The same for '-' operator. +*/ + +| n="+" ( <S> )+{ return LexicalUnitImpl.createAdd(n.beginLine, + n.beginColumn, + prev); } +| n="-" ( <S> )+{ return LexicalUnitImpl.createMinus(n.beginLine, + n.beginColumn, + prev); } } /** @@ -2233,7 +2277,7 @@ LexicalUnitImpl expr() : } { first=term(null){ res = first; } - ( LOOKAHEAD(2) ( res=operator(res) )? res=term(res))* + ( LOOKAHEAD(2) ( LOOKAHEAD(2) res=operator(res) )? res=term(res))* { return first; } } diff --git a/theme-compiler/src/com/vaadin/sass/internal/parser/ParserConstants.java b/theme-compiler/src/com/vaadin/sass/internal/parser/ParserConstants.java index c55a13265f..90fe640f8b 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/parser/ParserConstants.java +++ b/theme-compiler/src/com/vaadin/sass/internal/parser/ParserConstants.java @@ -1,289 +1,379 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ /* Generated By:JavaCC: Do not edit this line. ParserConstants.java */ package com.vaadin.sass.internal.parser; + /** - * Token literal values and constants. Generated by - * org.javacc.parser.OtherFilesGen#start() + * Token literal values and constants. + * Generated by org.javacc.parser.OtherFilesGen#start() */ public interface ParserConstants { - /** End of File. */ - int EOF = 0; - /** RegularExpression Id. */ - int S = 1; - /** RegularExpression Id. */ - int FORMAL_COMMENT = 7; - /** RegularExpression Id. */ - int MULTI_LINE_COMMENT = 8; - /** RegularExpression Id. */ - int CDO = 10; - /** RegularExpression Id. */ - int CDC = 11; - /** RegularExpression Id. */ - int LBRACE = 12; - /** RegularExpression Id. */ - int RBRACE = 13; - /** RegularExpression Id. */ - int DASHMATCH = 14; - /** RegularExpression Id. */ - int CARETMATCH = 15; - /** RegularExpression Id. */ - int DOLLARMATCH = 16; - /** RegularExpression Id. */ - int STARMATCH = 17; - /** RegularExpression Id. */ - int INCLUDES = 18; - /** RegularExpression Id. */ - int EQ = 19; - /** RegularExpression Id. */ - int PLUS = 20; - /** RegularExpression Id. */ - int MINUS = 21; - /** RegularExpression Id. */ - int COMMA = 22; - /** RegularExpression Id. */ - int SEMICOLON = 23; - /** RegularExpression Id. */ - int PRECEDES = 24; - /** RegularExpression Id. */ - int SIBLING = 25; - /** RegularExpression Id. */ - int SUCCEEDS = 26; - /** RegularExpression Id. */ - int DIV = 27; - /** RegularExpression Id. */ - int LBRACKET = 28; - /** RegularExpression Id. */ - int RBRACKET = 29; - /** RegularExpression Id. */ - int ANY = 30; - /** RegularExpression Id. */ - int PARENT = 31; - /** RegularExpression Id. */ - int DOT = 32; - /** RegularExpression Id. */ - int LPARAN = 33; - /** RegularExpression Id. */ - int RPARAN = 34; - /** RegularExpression Id. */ - int COMPARE = 35; - /** RegularExpression Id. */ - int OR = 36; - /** RegularExpression Id. */ - int AND = 37; - /** RegularExpression Id. */ - int NOT_EQ = 38; - /** RegularExpression Id. */ - int COLON = 39; - /** RegularExpression Id. */ - int INTERPOLATION = 40; - /** RegularExpression Id. */ - int NONASCII = 41; - /** RegularExpression Id. */ - int H = 42; - /** RegularExpression Id. */ - int UNICODE = 43; - /** RegularExpression Id. */ - int ESCAPE = 44; - /** RegularExpression Id. */ - int NMSTART = 45; - /** RegularExpression Id. */ - int NMCHAR = 46; - /** RegularExpression Id. */ - int STRINGCHAR = 47; - /** RegularExpression Id. */ - int D = 48; - /** RegularExpression Id. */ - int NAME = 49; - /** RegularExpression Id. */ - int TO = 50; - /** RegularExpression Id. */ - int THROUGH = 51; - /** RegularExpression Id. */ - int EACH_IN = 52; - /** RegularExpression Id. */ - int FROM = 53; - /** RegularExpression Id. */ - int MIXIN_SYM = 54; - /** RegularExpression Id. */ - int INCLUDE_SYM = 55; - /** RegularExpression Id. */ - int FUNCTION_SYM = 56; - /** RegularExpression Id. */ - int RETURN_SYM = 57; - /** RegularExpression Id. */ - int DEBUG_SYM = 58; - /** RegularExpression Id. */ - int WARN_SYM = 59; - /** RegularExpression Id. */ - int FOR_SYM = 60; - /** RegularExpression Id. */ - int EACH_SYM = 61; - /** RegularExpression Id. */ - int WHILE_SYM = 62; - /** RegularExpression Id. */ - int IF_SYM = 63; - /** RegularExpression Id. */ - int ELSE_SYM = 64; - /** RegularExpression Id. */ - int EXTEND_SYM = 65; - /** RegularExpression Id. */ - int MOZ_DOCUMENT_SYM = 66; - /** RegularExpression Id. */ - int SUPPORTS_SYM = 67; - /** RegularExpression Id. */ - int MICROSOFT_RULE = 68; - /** RegularExpression Id. */ - int IF = 69; - /** RegularExpression Id. */ - int GUARDED_SYM = 70; - /** RegularExpression Id. */ - int STRING = 71; - /** RegularExpression Id. */ - int IDENT = 72; - /** RegularExpression Id. */ - int NUMBER = 73; - /** RegularExpression Id. */ - int _URL = 74; - /** RegularExpression Id. */ - int URL = 75; - /** RegularExpression Id. */ - int VARIABLE = 76; - /** RegularExpression Id. */ - int PERCENTAGE = 77; - /** RegularExpression Id. */ - int PT = 78; - /** RegularExpression Id. */ - int MM = 79; - /** RegularExpression Id. */ - int CM = 80; - /** RegularExpression Id. */ - int PC = 81; - /** RegularExpression Id. */ - int IN = 82; - /** RegularExpression Id. */ - int PX = 83; - /** RegularExpression Id. */ - int EMS = 84; - /** RegularExpression Id. */ - int LEM = 85; - /** RegularExpression Id. */ - int REM = 86; - /** RegularExpression Id. */ - int EXS = 87; - /** RegularExpression Id. */ - int DEG = 88; - /** RegularExpression Id. */ - int RAD = 89; - /** RegularExpression Id. */ - int GRAD = 90; - /** RegularExpression Id. */ - int MS = 91; - /** RegularExpression Id. */ - int SECOND = 92; - /** RegularExpression Id. */ - int HZ = 93; - /** RegularExpression Id. */ - int KHZ = 94; - /** RegularExpression Id. */ - int DIMEN = 95; - /** RegularExpression Id. */ - int HASH = 96; - /** RegularExpression Id. */ - int IMPORT_SYM = 97; - /** RegularExpression Id. */ - int MEDIA_SYM = 98; - /** RegularExpression Id. */ - int CHARSET_SYM = 99; - /** RegularExpression Id. */ - int PAGE_SYM = 100; - /** RegularExpression Id. */ - int FONT_FACE_SYM = 101; - /** RegularExpression Id. */ - int KEY_FRAME_SYM = 102; - /** RegularExpression Id. */ - int ATKEYWORD = 103; - /** RegularExpression Id. */ - int IMPORTANT_SYM = 104; - /** RegularExpression Id. */ - int RANGE0 = 105; - /** RegularExpression Id. */ - int RANGE1 = 106; - /** RegularExpression Id. */ - int RANGE2 = 107; - /** RegularExpression Id. */ - int RANGE3 = 108; - /** RegularExpression Id. */ - int RANGE4 = 109; - /** RegularExpression Id. */ - int RANGE5 = 110; - /** RegularExpression Id. */ - int RANGE6 = 111; - /** RegularExpression Id. */ - int RANGE = 112; - /** RegularExpression Id. */ - int UNI = 113; - /** RegularExpression Id. */ - int UNICODERANGE = 114; - /** RegularExpression Id. */ - int REMOVE = 115; - /** RegularExpression Id. */ - int APPEND = 116; - /** RegularExpression Id. */ - int CONTAINS = 117; - /** RegularExpression Id. */ - int FUNCTION = 118; - /** RegularExpression Id. */ - int UNKNOWN = 119; + /** End of File. */ + int EOF = 0; + /** RegularExpression Id. */ + int S = 1; + /** RegularExpression Id. */ + int FORMAL_COMMENT = 7; + /** RegularExpression Id. */ + int MULTI_LINE_COMMENT = 8; + /** RegularExpression Id. */ + int CDO = 10; + /** RegularExpression Id. */ + int CDC = 11; + /** RegularExpression Id. */ + int LBRACE = 12; + /** RegularExpression Id. */ + int RBRACE = 13; + /** RegularExpression Id. */ + int DASHMATCH = 14; + /** RegularExpression Id. */ + int CARETMATCH = 15; + /** RegularExpression Id. */ + int DOLLARMATCH = 16; + /** RegularExpression Id. */ + int STARMATCH = 17; + /** RegularExpression Id. */ + int INCLUDES = 18; + /** RegularExpression Id. */ + int EQ = 19; + /** RegularExpression Id. */ + int PLUS = 20; + /** RegularExpression Id. */ + int MINUS = 21; + /** RegularExpression Id. */ + int COMMA = 22; + /** RegularExpression Id. */ + int SEMICOLON = 23; + /** RegularExpression Id. */ + int PRECEDES = 24; + /** RegularExpression Id. */ + int SIBLING = 25; + /** RegularExpression Id. */ + int SUCCEEDS = 26; + /** RegularExpression Id. */ + int DIV = 27; + /** RegularExpression Id. */ + int LBRACKET = 28; + /** RegularExpression Id. */ + int RBRACKET = 29; + /** RegularExpression Id. */ + int ANY = 30; + /** RegularExpression Id. */ + int MOD = 31; + /** RegularExpression Id. */ + int PARENT = 32; + /** RegularExpression Id. */ + int DOT = 33; + /** RegularExpression Id. */ + int LPARAN = 34; + /** RegularExpression Id. */ + int RPARAN = 35; + /** RegularExpression Id. */ + int COMPARE = 36; + /** RegularExpression Id. */ + int OR = 37; + /** RegularExpression Id. */ + int AND = 38; + /** RegularExpression Id. */ + int NOT_EQ = 39; + /** RegularExpression Id. */ + int COLON = 40; + /** RegularExpression Id. */ + int INTERPOLATION = 41; + /** RegularExpression Id. */ + int NONASCII = 42; + /** RegularExpression Id. */ + int H = 43; + /** RegularExpression Id. */ + int UNICODE = 44; + /** RegularExpression Id. */ + int ESCAPE = 45; + /** RegularExpression Id. */ + int NMSTART = 46; + /** RegularExpression Id. */ + int NMCHAR = 47; + /** RegularExpression Id. */ + int STRINGCHAR = 48; + /** RegularExpression Id. */ + int D = 49; + /** RegularExpression Id. */ + int NAME = 50; + /** RegularExpression Id. */ + int TO = 51; + /** RegularExpression Id. */ + int THROUGH = 52; + /** RegularExpression Id. */ + int EACH_IN = 53; + /** RegularExpression Id. */ + int FROM = 54; + /** RegularExpression Id. */ + int MIXIN_SYM = 55; + /** RegularExpression Id. */ + int INCLUDE_SYM = 56; + /** RegularExpression Id. */ + int FUNCTION_SYM = 57; + /** RegularExpression Id. */ + int RETURN_SYM = 58; + /** RegularExpression Id. */ + int DEBUG_SYM = 59; + /** RegularExpression Id. */ + int WARN_SYM = 60; + /** RegularExpression Id. */ + int FOR_SYM = 61; + /** RegularExpression Id. */ + int EACH_SYM = 62; + /** RegularExpression Id. */ + int WHILE_SYM = 63; + /** RegularExpression Id. */ + int IF_SYM = 64; + /** RegularExpression Id. */ + int ELSE_SYM = 65; + /** RegularExpression Id. */ + int EXTEND_SYM = 66; + /** RegularExpression Id. */ + int MOZ_DOCUMENT_SYM = 67; + /** RegularExpression Id. */ + int SUPPORTS_SYM = 68; + /** RegularExpression Id. */ + int CONTENT_SYM = 69; + /** RegularExpression Id. */ + int MICROSOFT_RULE = 70; + /** RegularExpression Id. */ + int IF = 71; + /** RegularExpression Id. */ + int GUARDED_SYM = 72; + /** RegularExpression Id. */ + int STRING = 73; + /** RegularExpression Id. */ + int IDENT = 74; + /** RegularExpression Id. */ + int NUMBER = 75; + /** RegularExpression Id. */ + int _URL = 76; + /** RegularExpression Id. */ + int URL = 77; + /** RegularExpression Id. */ + int VARIABLE = 78; + /** RegularExpression Id. */ + int PERCENTAGE = 79; + /** RegularExpression Id. */ + int PT = 80; + /** RegularExpression Id. */ + int MM = 81; + /** RegularExpression Id. */ + int CM = 82; + /** RegularExpression Id. */ + int PC = 83; + /** RegularExpression Id. */ + int IN = 84; + /** RegularExpression Id. */ + int PX = 85; + /** RegularExpression Id. */ + int EMS = 86; + /** RegularExpression Id. */ + int LEM = 87; + /** RegularExpression Id. */ + int REM = 88; + /** RegularExpression Id. */ + int EXS = 89; + /** RegularExpression Id. */ + int DEG = 90; + /** RegularExpression Id. */ + int RAD = 91; + /** RegularExpression Id. */ + int GRAD = 92; + /** RegularExpression Id. */ + int MS = 93; + /** RegularExpression Id. */ + int SECOND = 94; + /** RegularExpression Id. */ + int HZ = 95; + /** RegularExpression Id. */ + int KHZ = 96; + /** RegularExpression Id. */ + int DIMEN = 97; + /** RegularExpression Id. */ + int HASH = 98; + /** RegularExpression Id. */ + int IMPORT_SYM = 99; + /** RegularExpression Id. */ + int MEDIA_SYM = 100; + /** RegularExpression Id. */ + int CHARSET_SYM = 101; + /** RegularExpression Id. */ + int PAGE_SYM = 102; + /** RegularExpression Id. */ + int FONT_FACE_SYM = 103; + /** RegularExpression Id. */ + int KEY_FRAME_SYM = 104; + /** RegularExpression Id. */ + int ATKEYWORD = 105; + /** RegularExpression Id. */ + int IMPORTANT_SYM = 106; + /** RegularExpression Id. */ + int RANGE0 = 107; + /** RegularExpression Id. */ + int RANGE1 = 108; + /** RegularExpression Id. */ + int RANGE2 = 109; + /** RegularExpression Id. */ + int RANGE3 = 110; + /** RegularExpression Id. */ + int RANGE4 = 111; + /** RegularExpression Id. */ + int RANGE5 = 112; + /** RegularExpression Id. */ + int RANGE6 = 113; + /** RegularExpression Id. */ + int RANGE = 114; + /** RegularExpression Id. */ + int UNI = 115; + /** RegularExpression Id. */ + int UNICODERANGE = 116; + /** RegularExpression Id. */ + int REMOVE = 117; + /** RegularExpression Id. */ + int APPEND = 118; + /** RegularExpression Id. */ + int CONTAINS = 119; + /** RegularExpression Id. */ + int FUNCTION = 120; + /** RegularExpression Id. */ + int UNKNOWN = 121; - /** Lexical state. */ - int DEFAULT = 0; - /** Lexical state. */ - int IN_SINGLE_LINE_COMMENT = 1; - /** Lexical state. */ - int IN_FORMAL_COMMENT = 2; - /** Lexical state. */ - int IN_MULTI_LINE_COMMENT = 3; + /** Lexical state. */ + int DEFAULT = 0; + /** Lexical state. */ + int IN_SINGLE_LINE_COMMENT = 1; + /** Lexical state. */ + int IN_FORMAL_COMMENT = 2; + /** Lexical state. */ + int IN_MULTI_LINE_COMMENT = 3; - /** Literal token values. */ - String[] tokenImage = { "<EOF>", "<S>", "\"//\"", "<token of kind 3>", - "<token of kind 4>", "<token of kind 5>", "\"/*\"", "\"*/\"", - "\"*/\"", "<token of kind 9>", "\"<!--\"", "\"-->\"", "\"{\"", - "\"}\"", "\"|=\"", "\"^=\"", "\"$=\"", "\"*=\"", "\"~=\"", "\"=\"", - "\"+\"", "\"-\"", "\",\"", "\";\"", "\">\"", "\"~\"", "\"<\"", - "\"/\"", "\"[\"", "\"]\"", "\"*\"", "\"&\"", "\".\"", "\"(\"", - "\")\"", "\"==\"", "\"||\"", "\"&&\"", "\"!=\"", "\":\"", - "<INTERPOLATION>", "<NONASCII>", "<H>", "<UNICODE>", "<ESCAPE>", - "<NMSTART>", "<NMCHAR>", "<STRINGCHAR>", "<D>", "<NAME>", "\"to\"", - "\"through\"", "\"in\"", "\"from\"", "\"@mixin\"", "\"@include\"", - "\"@function\"", "\"@return\"", "\"@debug\"", "\"@warn\"", - "\"@for\"", "\"@each\"", "\"@while\"", "\"@if\"", "\"@else\"", - "\"@extend\"", "\"@-moz-document\"", "\"@supports\"", - "<MICROSOFT_RULE>", "\"if\"", "<GUARDED_SYM>", "<STRING>", - "<IDENT>", "<NUMBER>", "<_URL>", "<URL>", "<VARIABLE>", - "<PERCENTAGE>", "<PT>", "<MM>", "<CM>", "<PC>", "<IN>", "<PX>", - "<EMS>", "<LEM>", "<REM>", "<EXS>", "<DEG>", "<RAD>", "<GRAD>", - "<MS>", "<SECOND>", "<HZ>", "<KHZ>", "<DIMEN>", "<HASH>", - "\"@import\"", "\"@media\"", "\"@charset\"", "\"@page\"", - "\"@font-face\"", "<KEY_FRAME_SYM>", "<ATKEYWORD>", - "<IMPORTANT_SYM>", "<RANGE0>", "<RANGE1>", "<RANGE2>", "<RANGE3>", - "<RANGE4>", "<RANGE5>", "<RANGE6>", "<RANGE>", "<UNI>", - "<UNICODERANGE>", "<REMOVE>", "<APPEND>", "<CONTAINS>", - "<FUNCTION>", "<UNKNOWN>", }; + /** Literal token values. */ + String[] tokenImage = { + "<EOF>", + "<S>", + "\"//\"", + "<token of kind 3>", + "<token of kind 4>", + "<token of kind 5>", + "\"/*\"", + "\"*/\"", + "\"*/\"", + "<token of kind 9>", + "\"<!--\"", + "\"-->\"", + "\"{\"", + "\"}\"", + "\"|=\"", + "\"^=\"", + "\"$=\"", + "\"*=\"", + "\"~=\"", + "\"=\"", + "\"+\"", + "\"-\"", + "\",\"", + "\";\"", + "\">\"", + "\"~\"", + "\"<\"", + "\"/\"", + "\"[\"", + "\"]\"", + "\"*\"", + "\"%\"", + "\"&\"", + "\".\"", + "\"(\"", + "\")\"", + "\"==\"", + "\"||\"", + "\"&&\"", + "\"!=\"", + "\":\"", + "<INTERPOLATION>", + "<NONASCII>", + "<H>", + "<UNICODE>", + "<ESCAPE>", + "<NMSTART>", + "<NMCHAR>", + "<STRINGCHAR>", + "<D>", + "<NAME>", + "\"to\"", + "\"through\"", + "\"in\"", + "\"from\"", + "\"@mixin\"", + "\"@include\"", + "\"@function\"", + "\"@return\"", + "\"@debug\"", + "\"@warn\"", + "\"@for\"", + "\"@each\"", + "\"@while\"", + "\"@if\"", + "\"@else\"", + "\"@extend\"", + "\"@-moz-document\"", + "\"@supports\"", + "\"@content\"", + "<MICROSOFT_RULE>", + "\"if\"", + "<GUARDED_SYM>", + "<STRING>", + "<IDENT>", + "<NUMBER>", + "<_URL>", + "<URL>", + "<VARIABLE>", + "<PERCENTAGE>", + "<PT>", + "<MM>", + "<CM>", + "<PC>", + "<IN>", + "<PX>", + "<EMS>", + "<LEM>", + "<REM>", + "<EXS>", + "<DEG>", + "<RAD>", + "<GRAD>", + "<MS>", + "<SECOND>", + "<HZ>", + "<KHZ>", + "<DIMEN>", + "<HASH>", + "\"@import\"", + "\"@media\"", + "\"@charset\"", + "\"@page\"", + "\"@font-face\"", + "<KEY_FRAME_SYM>", + "<ATKEYWORD>", + "<IMPORTANT_SYM>", + "<RANGE0>", + "<RANGE1>", + "<RANGE2>", + "<RANGE3>", + "<RANGE4>", + "<RANGE5>", + "<RANGE6>", + "<RANGE>", + "<UNI>", + "<UNICODERANGE>", + "<REMOVE>", + "<APPEND>", + "<CONTAINS>", + "<FUNCTION>", + "<UNKNOWN>", + }; } diff --git a/theme-compiler/src/com/vaadin/sass/internal/parser/ParserTokenManager.java b/theme-compiler/src/com/vaadin/sass/internal/parser/ParserTokenManager.java index 9ff123c808..6271673d5d 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/parser/ParserTokenManager.java +++ b/theme-compiler/src/com/vaadin/sass/internal/parser/ParserTokenManager.java @@ -1,6030 +1,5060 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ /* Generated By:JavaCC: Do not edit this line. ParserTokenManager.java */ package com.vaadin.sass.internal.parser; +import java.io.*; +import java.net.*; +import java.util.ArrayList; +import java.util.Locale; +import java.util.Map; +import java.util.UUID; +import org.w3c.css.sac.ConditionFactory; +import org.w3c.css.sac.Condition; +import org.w3c.css.sac.SelectorFactory; +import org.w3c.css.sac.SelectorList; +import org.w3c.css.sac.Selector; +import org.w3c.css.sac.SimpleSelector; +import org.w3c.css.sac.DocumentHandler; +import org.w3c.css.sac.InputSource; +import org.w3c.css.sac.ErrorHandler; +import org.w3c.css.sac.CSSException; +import org.w3c.css.sac.CSSParseException; +import org.w3c.css.sac.Locator; +import org.w3c.css.sac.LexicalUnit; +import org.w3c.flute.parser.selectors.SelectorFactoryImpl; +import org.w3c.flute.parser.selectors.ConditionFactoryImpl; +import org.w3c.flute.util.Encoding; +import com.vaadin.sass.internal.handler.*; +import com.vaadin.sass.internal.tree.*; /** Token Manager. */ -public class ParserTokenManager implements ParserConstants { +public class ParserTokenManager implements ParserConstants +{ - /** Debug output. */ - public java.io.PrintStream debugStream = System.out; - - /** Set debug output. */ - public void setDebugStream(java.io.PrintStream ds) { - debugStream = ds; - } - - private final int jjStopStringLiteralDfa_0(int pos, long active0, - long active1) { - switch (pos) { - case 0: - if ((active0 & 0x1c000000000000L) != 0L || (active1 & 0x20L) != 0L) { - jjmatchedKind = 72; - return 517; - } - if ((active0 & 0x4000000000L) != 0L) { - return 518; - } - if ((active0 & 0x10000L) != 0L) { - return 79; - } - if ((active0 & 0x200800L) != 0L) { - return 42; - } - if ((active0 & 0x20000000000000L) != 0L) { - jjmatchedKind = 72; - return 33; - } - if ((active0 & 0x8000044L) != 0L) { - return 3; - } - if ((active0 & 0xffc0000000000000L) != 0L - || (active1 & 0x3e0000000fL) != 0L) { - return 166; - } - if ((active0 & 0x100000000L) != 0L) { - return 519; - } - return -1; - case 1: - if ((active1 & 0x4L) != 0L) { - return 178; - } - if ((active0 & 0xffc0000000000000L) != 0L - || (active1 & 0x3e0000000bL) != 0L) { - jjmatchedKind = 103; - jjmatchedPos = 1; - return 520; - } - if ((active0 & 0x40L) != 0L) { - return 1; - } - if ((active0 & 0x28000000000000L) != 0L) { - jjmatchedKind = 72; - jjmatchedPos = 1; - return 517; - } - if ((active0 & 0x14000000000000L) != 0L || (active1 & 0x20L) != 0L) { - return 517; - } - return -1; - case 2: - if ((active0 & 0x7fc0000000000000L) != 0L - || (active1 & 0x3e0000000bL) != 0L) { - jjmatchedKind = 103; - jjmatchedPos = 2; - return 520; - } - if ((active0 & 0x8000000000000000L) != 0L) { - return 520; - } - if ((active0 & 0x28000000000000L) != 0L) { - jjmatchedKind = 72; - jjmatchedPos = 2; - return 517; - } - if ((active1 & 0x4L) != 0L) { - jjmatchedKind = 103; - jjmatchedPos = 2; - return 177; - } - return -1; - case 3: - if ((active0 & 0x6fc0000000000000L) != 0L - || (active1 & 0x3e0000000bL) != 0L) { - jjmatchedKind = 103; - jjmatchedPos = 3; - return 520; - } - if ((active0 & 0x1000000000000000L) != 0L) { - return 520; - } - if ((active1 & 0x4L) != 0L) { - jjmatchedKind = 103; - jjmatchedPos = 3; - return 176; - } - if ((active0 & 0x20000000000000L) != 0L) { - return 517; - } - if ((active0 & 0x8000000000000L) != 0L) { - jjmatchedKind = 72; - jjmatchedPos = 3; - return 517; - } - return -1; - case 4: - if ((active1 & 0x4L) != 0L) { - jjmatchedKind = 103; - jjmatchedPos = 4; - return 175; - } - if ((active0 & 0x8000000000000L) != 0L) { - jjmatchedKind = 72; - jjmatchedPos = 4; - return 517; - } - if ((active0 & 0x2800000000000000L) != 0L - || (active1 & 0x1000000001L) != 0L) { - return 520; - } - if ((active0 & 0x47c0000000000000L) != 0L - || (active1 & 0x2e0000000aL) != 0L) { - jjmatchedKind = 103; - jjmatchedPos = 4; - return 520; - } - return -1; - case 5: - if ((active0 & 0x4440000000000000L) != 0L - || (active1 & 0x400000000L) != 0L) { - return 520; - } - if ((active1 & 0x4L) != 0L) { - jjmatchedKind = 103; - jjmatchedPos = 5; - return 174; - } - if ((active0 & 0x380000000000000L) != 0L - || (active1 & 0x2a0000000aL) != 0L) { - jjmatchedKind = 103; - jjmatchedPos = 5; - return 520; - } - if ((active0 & 0x8000000000000L) != 0L) { - jjmatchedKind = 72; - jjmatchedPos = 5; - return 517; - } - return -1; - case 6: - if ((active0 & 0x200000000000000L) != 0L - || (active1 & 0x200000002L) != 0L) { - return 520; - } - if ((active0 & 0x180000000000000L) != 0L - || (active1 & 0x280000000cL) != 0L) { - jjmatchedKind = 103; - jjmatchedPos = 6; - return 520; - } - if ((active0 & 0x8000000000000L) != 0L) { - return 517; - } - return -1; - case 7: - if ((active0 & 0x100000000000000L) != 0L - || (active1 & 0x200000000cL) != 0L) { - jjmatchedKind = 103; - jjmatchedPos = 7; - return 520; - } - if ((active0 & 0x80000000000000L) != 0L - || (active1 & 0x800000000L) != 0L) { - return 520; - } - return -1; - case 8: - if ((active1 & 0x2000000004L) != 0L) { - jjmatchedKind = 103; - jjmatchedPos = 8; - return 520; - } - if ((active0 & 0x100000000000000L) != 0L || (active1 & 0x8L) != 0L) { - return 520; - } - return -1; - case 9: - if ((active1 & 0x4L) != 0L) { - jjmatchedKind = 103; - jjmatchedPos = 9; - return 520; - } - if ((active1 & 0x2000000000L) != 0L) { - return 520; - } - return -1; - case 10: - if ((active1 & 0x4L) != 0L) { - jjmatchedKind = 103; - jjmatchedPos = 10; - return 520; - } - return -1; - case 11: - if ((active1 & 0x4L) != 0L) { - jjmatchedKind = 103; - jjmatchedPos = 11; - return 520; - } - return -1; - case 12: - if ((active1 & 0x4L) != 0L) { - jjmatchedKind = 103; - jjmatchedPos = 12; - return 520; - } - return -1; - default: - return -1; - } - } - - private final int jjStartNfa_0(int pos, long active0, long active1) { - return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0, active1), - pos + 1); - } - - private int jjStopAtPos(int pos, int kind) { - jjmatchedKind = kind; - jjmatchedPos = pos; - return pos + 1; - } - - private int jjMoveStringLiteralDfa0_0() { - switch (curChar) { - case 33: - return jjMoveStringLiteralDfa1_0(0x4000000000L, 0x0L); - case 36: - return jjMoveStringLiteralDfa1_0(0x10000L, 0x0L); - case 38: - jjmatchedKind = 31; - return jjMoveStringLiteralDfa1_0(0x2000000000L, 0x0L); - case 40: - return jjStopAtPos(0, 33); - case 41: - return jjStopAtPos(0, 34); - case 42: - jjmatchedKind = 30; - return jjMoveStringLiteralDfa1_0(0x20000L, 0x0L); - case 43: - return jjStopAtPos(0, 20); - case 44: - return jjStopAtPos(0, 22); - case 45: - jjmatchedKind = 21; - return jjMoveStringLiteralDfa1_0(0x800L, 0x0L); - case 46: - return jjStartNfaWithStates_0(0, 32, 519); - case 47: - jjmatchedKind = 27; - return jjMoveStringLiteralDfa1_0(0x44L, 0x0L); - case 58: - return jjStopAtPos(0, 39); - case 59: - return jjStopAtPos(0, 23); - case 60: - jjmatchedKind = 26; - return jjMoveStringLiteralDfa1_0(0x400L, 0x0L); - case 61: - jjmatchedKind = 19; - return jjMoveStringLiteralDfa1_0(0x800000000L, 0x0L); - case 62: - return jjStopAtPos(0, 24); - case 64: - return jjMoveStringLiteralDfa1_0(0xffc0000000000000L, 0x3e0000000fL); - case 91: - return jjStopAtPos(0, 28); - case 93: - return jjStopAtPos(0, 29); - case 94: - return jjMoveStringLiteralDfa1_0(0x8000L, 0x0L); - case 70: - case 102: - return jjMoveStringLiteralDfa1_0(0x20000000000000L, 0x0L); - case 73: - case 105: - return jjMoveStringLiteralDfa1_0(0x10000000000000L, 0x20L); - case 84: - case 116: - return jjMoveStringLiteralDfa1_0(0xc000000000000L, 0x0L); - case 123: - return jjStopAtPos(0, 12); - case 124: - return jjMoveStringLiteralDfa1_0(0x1000004000L, 0x0L); - case 125: - return jjStopAtPos(0, 13); - case 126: - jjmatchedKind = 25; - return jjMoveStringLiteralDfa1_0(0x40000L, 0x0L); - default: - return jjMoveNfa_0(4, 0); - } - } - - private int jjMoveStringLiteralDfa1_0(long active0, long active1) { - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_0(0, active0, active1); - return 1; - } - switch (curChar) { - case 33: - return jjMoveStringLiteralDfa2_0(active0, 0x400L, active1, 0L); - case 38: - if ((active0 & 0x2000000000L) != 0L) { - return jjStopAtPos(1, 37); - } - break; - case 42: - if ((active0 & 0x40L) != 0L) { - return jjStartNfaWithStates_0(1, 6, 1); - } - break; - case 45: - return jjMoveStringLiteralDfa2_0(active0, 0x800L, active1, 0x4L); - case 47: - if ((active0 & 0x4L) != 0L) { - return jjStopAtPos(1, 2); - } - break; - case 61: - if ((active0 & 0x4000L) != 0L) { - return jjStopAtPos(1, 14); - } else if ((active0 & 0x8000L) != 0L) { - return jjStopAtPos(1, 15); - } else if ((active0 & 0x10000L) != 0L) { - return jjStopAtPos(1, 16); - } else if ((active0 & 0x20000L) != 0L) { - return jjStopAtPos(1, 17); - } else if ((active0 & 0x40000L) != 0L) { - return jjStopAtPos(1, 18); - } else if ((active0 & 0x800000000L) != 0L) { - return jjStopAtPos(1, 35); - } else if ((active0 & 0x4000000000L) != 0L) { - return jjStopAtPos(1, 38); - } - break; - case 67: - case 99: - return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x800000000L); - case 68: - case 100: - return jjMoveStringLiteralDfa2_0(active0, 0x400000000000000L, - active1, 0L); - case 69: - case 101: - return jjMoveStringLiteralDfa2_0(active0, 0x2000000000000000L, - active1, 0x3L); - case 70: - case 102: - if ((active1 & 0x20L) != 0L) { - return jjStartNfaWithStates_0(1, 69, 517); - } - return jjMoveStringLiteralDfa2_0(active0, 0x1100000000000000L, - active1, 0x2000000000L); - case 72: - case 104: - return jjMoveStringLiteralDfa2_0(active0, 0x8000000000000L, - active1, 0L); - case 73: - case 105: - return jjMoveStringLiteralDfa2_0(active0, 0x8080000000000000L, - active1, 0x200000000L); - case 77: - case 109: - return jjMoveStringLiteralDfa2_0(active0, 0x40000000000000L, - active1, 0x400000000L); - case 78: - case 110: - if ((active0 & 0x10000000000000L) != 0L) { - return jjStartNfaWithStates_0(1, 52, 517); - } - break; - case 79: - case 111: - if ((active0 & 0x4000000000000L) != 0L) { - return jjStartNfaWithStates_0(1, 50, 517); - } - break; - case 80: - case 112: - return jjMoveStringLiteralDfa2_0(active0, 0L, active1, - 0x1000000000L); - case 82: - case 114: - return jjMoveStringLiteralDfa2_0(active0, 0x220000000000000L, - active1, 0L); - case 83: - case 115: - return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x8L); - case 87: - case 119: - return jjMoveStringLiteralDfa2_0(active0, 0x4800000000000000L, - active1, 0L); - case 124: - if ((active0 & 0x1000000000L) != 0L) { - return jjStopAtPos(1, 36); - } - break; - default: - break; - } - return jjStartNfa_0(0, active0, active1); - } - - private int jjMoveStringLiteralDfa2_0(long old0, long active0, long old1, - long active1) { - if (((active0 &= old0) | (active1 &= old1)) == 0L) { - return jjStartNfa_0(0, old0, old1); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_0(1, active0, active1); - return 2; - } - switch (curChar) { - case 45: - return jjMoveStringLiteralDfa3_0(active0, 0x400L, active1, 0L); - case 62: - if ((active0 & 0x800L) != 0L) { - return jjStopAtPos(2, 11); - } - break; - case 65: - case 97: - return jjMoveStringLiteralDfa3_0(active0, 0x2800000000000000L, - active1, 0x1000000000L); - case 69: - case 101: - return jjMoveStringLiteralDfa3_0(active0, 0x600000000000000L, - active1, 0x400000000L); - case 70: - case 102: - if ((active0 & 0x8000000000000000L) != 0L) { - return jjStartNfaWithStates_0(2, 63, 520); - } - break; - case 72: - case 104: - return jjMoveStringLiteralDfa3_0(active0, 0x4000000000000000L, - active1, 0x800000000L); - case 73: - case 105: - return jjMoveStringLiteralDfa3_0(active0, 0x40000000000000L, - active1, 0L); - case 76: - case 108: - return jjMoveStringLiteralDfa3_0(active0, 0L, active1, 0x1L); - case 77: - case 109: - return jjMoveStringLiteralDfa3_0(active0, 0L, active1, 0x200000004L); - case 78: - case 110: - return jjMoveStringLiteralDfa3_0(active0, 0x80000000000000L, - active1, 0L); - case 79: - case 111: - return jjMoveStringLiteralDfa3_0(active0, 0x1020000000000000L, - active1, 0x2000000000L); - case 82: - case 114: - return jjMoveStringLiteralDfa3_0(active0, 0x8000000000000L, - active1, 0L); - case 85: - case 117: - return jjMoveStringLiteralDfa3_0(active0, 0x100000000000000L, - active1, 0x8L); - case 88: - case 120: - return jjMoveStringLiteralDfa3_0(active0, 0L, active1, 0x2L); - default: - break; - } - return jjStartNfa_0(1, active0, active1); - } - - private int jjMoveStringLiteralDfa3_0(long old0, long active0, long old1, - long active1) { - if (((active0 &= old0) | (active1 &= old1)) == 0L) { - return jjStartNfa_0(1, old0, old1); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_0(2, active0, active1); + /** Debug output. */ + public java.io.PrintStream debugStream = System.out; + /** Set debug output. */ + public void setDebugStream(java.io.PrintStream ds) { debugStream = ds; } +private final int jjStopStringLiteralDfa_0(int pos, long active0, long active1) +{ + switch (pos) + { + case 0: + if ((active0 & 0x40000000000000L) != 0L) + { + jjmatchedKind = 74; + return 33; + } + if ((active0 & 0x8000000000L) != 0L) + return 517; + if ((active0 & 0x10000L) != 0L) + return 79; + if ((active0 & 0x200800L) != 0L) + return 42; + if ((active0 & 0x8000044L) != 0L) return 3; - } - switch (curChar) { - case 45: - if ((active0 & 0x400L) != 0L) { - return jjStopAtPos(3, 10); - } - break; - case 65: - case 97: - return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x800000000L); - case 66: - case 98: - return jjMoveStringLiteralDfa4_0(active0, 0x400000000000000L, - active1, 0L); - case 67: - case 99: - return jjMoveStringLiteralDfa4_0(active0, 0x2080000000000000L, - active1, 0L); - case 68: - case 100: - return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x400000000L); - case 71: - case 103: - return jjMoveStringLiteralDfa4_0(active0, 0L, active1, - 0x1000000000L); - case 73: - case 105: - return jjMoveStringLiteralDfa4_0(active0, 0x4000000000000000L, - active1, 0L); - case 77: - case 109: - if ((active0 & 0x20000000000000L) != 0L) { - return jjStartNfaWithStates_0(3, 53, 517); - } - break; - case 78: - case 110: - return jjMoveStringLiteralDfa4_0(active0, 0x100000000000000L, - active1, 0x2000000000L); - case 79: - case 111: - return jjMoveStringLiteralDfa4_0(active0, 0x8000000000000L, - active1, 0x4L); - case 80: - case 112: - return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x200000008L); - case 82: - case 114: - if ((active0 & 0x1000000000000000L) != 0L) { - return jjStartNfaWithStates_0(3, 60, 520); - } - return jjMoveStringLiteralDfa4_0(active0, 0x800000000000000L, - active1, 0L); - case 83: - case 115: - return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x1L); - case 84: - case 116: - return jjMoveStringLiteralDfa4_0(active0, 0x200000000000000L, - active1, 0x2L); - case 88: - case 120: - return jjMoveStringLiteralDfa4_0(active0, 0x40000000000000L, - active1, 0L); - default: - break; - } - return jjStartNfa_0(2, active0, active1); - } - - private int jjMoveStringLiteralDfa4_0(long old0, long active0, long old1, - long active1) { - if (((active0 &= old0) | (active1 &= old1)) == 0L) { - return jjStartNfa_0(2, old0, old1); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_0(3, active0, active1); - return 4; - } - switch (curChar) { - case 67: - case 99: - return jjMoveStringLiteralDfa5_0(active0, 0x100000000000000L, - active1, 0L); - case 69: - case 101: - if ((active1 & 0x1L) != 0L) { - return jjStartNfaWithStates_0(4, 64, 520); - } else if ((active1 & 0x1000000000L) != 0L) { - return jjStartNfaWithStates_0(4, 100, 520); - } - return jjMoveStringLiteralDfa5_0(active0, 0L, active1, 0x2L); - case 72: - case 104: - if ((active0 & 0x2000000000000000L) != 0L) { - return jjStartNfaWithStates_0(4, 61, 520); - } - break; - case 73: - case 105: - return jjMoveStringLiteralDfa5_0(active0, 0x40000000000000L, - active1, 0x400000000L); - case 76: - case 108: - return jjMoveStringLiteralDfa5_0(active0, 0x4080000000000000L, - active1, 0L); - case 78: - case 110: - if ((active0 & 0x800000000000000L) != 0L) { - return jjStartNfaWithStates_0(4, 59, 520); - } - break; - case 79: - case 111: - return jjMoveStringLiteralDfa5_0(active0, 0L, active1, 0x200000000L); - case 80: - case 112: - return jjMoveStringLiteralDfa5_0(active0, 0L, active1, 0x8L); - case 82: - case 114: - return jjMoveStringLiteralDfa5_0(active0, 0L, active1, 0x800000000L); - case 84: - case 116: - return jjMoveStringLiteralDfa5_0(active0, 0L, active1, - 0x2000000000L); - case 85: - case 117: - return jjMoveStringLiteralDfa5_0(active0, 0x608000000000000L, - active1, 0L); - case 90: - case 122: - return jjMoveStringLiteralDfa5_0(active0, 0L, active1, 0x4L); - default: - break; - } - return jjStartNfa_0(3, active0, active1); - } - - private int jjMoveStringLiteralDfa5_0(long old0, long active0, long old1, - long active1) { - if (((active0 &= old0) | (active1 &= old1)) == 0L) { - return jjStartNfa_0(3, old0, old1); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_0(4, active0, active1); - return 5; - } - switch (curChar) { - case 45: - return jjMoveStringLiteralDfa6_0(active0, 0L, active1, - 0x2000000004L); - case 65: - case 97: - if ((active1 & 0x400000000L) != 0L) { - return jjStartNfaWithStates_0(5, 98, 520); - } - break; - case 69: - case 101: - if ((active0 & 0x4000000000000000L) != 0L) { - return jjStartNfaWithStates_0(5, 62, 520); - } - break; - case 71: - case 103: - if ((active0 & 0x400000000000000L) != 0L) { - return jjStartNfaWithStates_0(5, 58, 520); - } - return jjMoveStringLiteralDfa6_0(active0, 0x8000000000000L, - active1, 0L); - case 78: - case 110: - if ((active0 & 0x40000000000000L) != 0L) { - return jjStartNfaWithStates_0(5, 54, 520); - } - return jjMoveStringLiteralDfa6_0(active0, 0L, active1, 0x2L); - case 79: - case 111: - return jjMoveStringLiteralDfa6_0(active0, 0L, active1, 0x8L); - case 82: - case 114: - return jjMoveStringLiteralDfa6_0(active0, 0x200000000000000L, - active1, 0x200000000L); - case 83: - case 115: - return jjMoveStringLiteralDfa6_0(active0, 0L, active1, 0x800000000L); - case 84: - case 116: - return jjMoveStringLiteralDfa6_0(active0, 0x100000000000000L, - active1, 0L); - case 85: - case 117: - return jjMoveStringLiteralDfa6_0(active0, 0x80000000000000L, - active1, 0L); - default: - break; - } - return jjStartNfa_0(4, active0, active1); - } - - private int jjMoveStringLiteralDfa6_0(long old0, long active0, long old1, - long active1) { - if (((active0 &= old0) | (active1 &= old1)) == 0L) { - return jjStartNfa_0(4, old0, old1); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_0(5, active0, active1); - return 6; - } - switch (curChar) { - case 68: - case 100: - if ((active1 & 0x2L) != 0L) { - return jjStartNfaWithStates_0(6, 65, 520); - } - return jjMoveStringLiteralDfa7_0(active0, 0x80000000000000L, - active1, 0x4L); - case 69: - case 101: - return jjMoveStringLiteralDfa7_0(active0, 0L, active1, 0x800000000L); - case 70: - case 102: - return jjMoveStringLiteralDfa7_0(active0, 0L, active1, - 0x2000000000L); - case 72: - case 104: - if ((active0 & 0x8000000000000L) != 0L) { - return jjStartNfaWithStates_0(6, 51, 517); - } - break; - case 73: - case 105: - return jjMoveStringLiteralDfa7_0(active0, 0x100000000000000L, - active1, 0L); - case 78: - case 110: - if ((active0 & 0x200000000000000L) != 0L) { - return jjStartNfaWithStates_0(6, 57, 520); - } - break; - case 82: - case 114: - return jjMoveStringLiteralDfa7_0(active0, 0L, active1, 0x8L); - case 84: - case 116: - if ((active1 & 0x200000000L) != 0L) { - return jjStartNfaWithStates_0(6, 97, 520); - } - break; - default: - break; - } - return jjStartNfa_0(5, active0, active1); - } - - private int jjMoveStringLiteralDfa7_0(long old0, long active0, long old1, - long active1) { - if (((active0 &= old0) | (active1 &= old1)) == 0L) { - return jjStartNfa_0(5, old0, old1); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_0(6, active0, active1); - return 7; - } - switch (curChar) { - case 65: - case 97: - return jjMoveStringLiteralDfa8_0(active0, 0L, active1, - 0x2000000000L); - case 69: - case 101: - if ((active0 & 0x80000000000000L) != 0L) { - return jjStartNfaWithStates_0(7, 55, 520); - } - break; - case 79: - case 111: - return jjMoveStringLiteralDfa8_0(active0, 0x100000000000000L, - active1, 0x4L); - case 84: - case 116: - if ((active1 & 0x800000000L) != 0L) { - return jjStartNfaWithStates_0(7, 99, 520); - } - return jjMoveStringLiteralDfa8_0(active0, 0L, active1, 0x8L); - default: - break; - } - return jjStartNfa_0(6, active0, active1); - } - - private int jjMoveStringLiteralDfa8_0(long old0, long active0, long old1, - long active1) { - if (((active0 &= old0) | (active1 &= old1)) == 0L) { - return jjStartNfa_0(6, old0, old1); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_0(7, active0, active1); - return 8; - } - switch (curChar) { - case 67: - case 99: - return jjMoveStringLiteralDfa9_0(active0, 0L, active1, - 0x2000000004L); - case 78: - case 110: - if ((active0 & 0x100000000000000L) != 0L) { - return jjStartNfaWithStates_0(8, 56, 520); - } - break; - case 83: - case 115: - if ((active1 & 0x8L) != 0L) { - return jjStartNfaWithStates_0(8, 67, 520); - } - break; - default: - break; - } - return jjStartNfa_0(7, active0, active1); - } - - private int jjMoveStringLiteralDfa9_0(long old0, long active0, long old1, - long active1) { - if (((active0 &= old0) | (active1 &= old1)) == 0L) { - return jjStartNfa_0(7, old0, old1); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_0(8, 0L, active1); - return 9; - } - switch (curChar) { - case 69: - case 101: - if ((active1 & 0x2000000000L) != 0L) { - return jjStartNfaWithStates_0(9, 101, 520); - } - break; - case 85: - case 117: - return jjMoveStringLiteralDfa10_0(active1, 0x4L); - default: - break; - } - return jjStartNfa_0(8, 0L, active1); - } - - private int jjMoveStringLiteralDfa10_0(long old1, long active1) { - if (((active1 &= old1)) == 0L) { - return jjStartNfa_0(8, 0L, old1); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_0(9, 0L, active1); - return 10; - } - switch (curChar) { - case 77: - case 109: - return jjMoveStringLiteralDfa11_0(active1, 0x4L); - default: - break; - } - return jjStartNfa_0(9, 0L, active1); - } - - private int jjMoveStringLiteralDfa11_0(long old1, long active1) { - if (((active1 &= old1)) == 0L) { - return jjStartNfa_0(9, 0L, old1); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_0(10, 0L, active1); - return 11; - } - switch (curChar) { - case 69: - case 101: - return jjMoveStringLiteralDfa12_0(active1, 0x4L); - default: - break; - } - return jjStartNfa_0(10, 0L, active1); - } - - private int jjMoveStringLiteralDfa12_0(long old1, long active1) { - if (((active1 &= old1)) == 0L) { - return jjStartNfa_0(10, 0L, old1); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_0(11, 0L, active1); - return 12; - } - switch (curChar) { - case 78: - case 110: - return jjMoveStringLiteralDfa13_0(active1, 0x4L); - default: - break; - } - return jjStartNfa_0(11, 0L, active1); - } - - private int jjMoveStringLiteralDfa13_0(long old1, long active1) { - if (((active1 &= old1)) == 0L) { - return jjStartNfa_0(11, 0L, old1); - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - jjStopStringLiteralDfa_0(12, 0L, active1); - return 13; - } - switch (curChar) { - case 84: - case 116: - if ((active1 & 0x4L) != 0L) { - return jjStartNfaWithStates_0(13, 66, 520); - } - break; - default: - break; - } - return jjStartNfa_0(12, 0L, active1); - } - - private int jjStartNfaWithStates_0(int pos, int kind, int state) { - jjmatchedKind = kind; - jjmatchedPos = pos; - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - return pos + 1; - } - return jjMoveNfa_0(state, pos + 1); - } - - static final long[] jjbitVec0 = { 0x0L, 0x0L, 0xffffffffffffffffL, - 0xffffffffffffffffL }; - - private int jjMoveNfa_0(int startState, int curPos) { - int startsAt = 0; - jjnewStateCnt = 517; - int i = 1; - jjstateSet[0] = startState; - int kind = 0x7fffffff; - for (;;) { - if (++jjround == 0x7fffffff) { - ReInitRounds(); - } - if (curChar < 64) { - long l = 1L << curChar; - do { - switch (jjstateSet[--i]) { - case 520: - case 113: - if ((0x3ff200000000000L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - break; - case 166: - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 112; - } - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 217; - } - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 205; - } - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 189; - } - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 178; - } - break; - case 174: - if ((0x3ff200000000000L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - break; - case 4: - if ((0x3ff000000000000L & l) != 0L) { - if (kind > 73) { - kind = 73; - } - jjCheckNAddStates(0, 81); - } else if ((0x100003600L & l) != 0L) { - if (kind > 1) { - kind = 1; - } - jjCheckNAdd(0); - } else if (curChar == 46) { - jjCheckNAddStates(82, 101); - } else if (curChar == 45) { - jjAddStates(102, 103); - } else if (curChar == 33) { - jjCheckNAddStates(104, 107); - } else if (curChar == 35) { - jjCheckNAddTwoStates(100, 101); - } else if (curChar == 36) { - jjCheckNAddStates(108, 111); - } else if (curChar == 39) { - jjCheckNAddStates(112, 115); - } else if (curChar == 34) { - jjCheckNAddStates(116, 119); - } else if (curChar == 47) { - jjstateSet[jjnewStateCnt++] = 3; - } - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 42; - } else if (curChar == 35) { - jjstateSet[jjnewStateCnt++] = 5; - } - break; - case 518: - if ((0x100003600L & l) != 0L) { - jjCheckNAddTwoStates(251, 260); - } - if ((0x100003600L & l) != 0L) { - jjCheckNAddTwoStates(243, 250); - } - break; - case 517: - if ((0x3ff200000000000L & l) != 0L) { - jjCheckNAddStates(120, 123); - } else if ((0x100003600L & l) != 0L) { - jjCheckNAddTwoStates(231, 232); - } else if (curChar == 40) { - if (kind > 118) { - kind = 118; - } - } - if ((0x3ff200000000000L & l) != 0L) { - if (kind > 72) { - kind = 72; - } - jjCheckNAddTwoStates(220, 221); - } - break; - case 175: - if ((0x3ff200000000000L & l) != 0L) { - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - } - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 174; - } - break; - case 33: - if ((0x3ff200000000000L & l) != 0L) { - jjCheckNAddStates(120, 123); - } else if ((0x100003600L & l) != 0L) { - jjCheckNAddTwoStates(231, 232); - } else if (curChar == 40) { - if (kind > 118) { - kind = 118; - } - } - if ((0x3ff200000000000L & l) != 0L) { - if (kind > 72) { - kind = 72; - } - jjCheckNAddTwoStates(220, 221); - } - break; - case 176: - if ((0x3ff200000000000L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - break; - case 519: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(124, 128); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(322, 325); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(319, 321); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(317, 318); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(314, 316); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(309, 313); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(305, 308); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(301, 304); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(298, 300); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(294, 297); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(290, 293); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(287, 289); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(284, 286); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(281, 283); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(278, 280); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(275, 277); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(272, 274); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(269, 271); - } - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(267, 268); - } - if ((0x3ff000000000000L & l) != 0L) { - if (kind > 73) { - kind = 73; - } - jjCheckNAdd(266); - } - break; - case 177: - if ((0x3ff200000000000L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - break; - case 79: - if (curChar == 45) { - jjCheckNAdd(80); - } - break; - case 0: - if ((0x100003600L & l) == 0L) { - break; - } - if (kind > 1) { - kind = 1; - } - jjCheckNAdd(0); - break; - case 1: - if (curChar == 42) { - jjstateSet[jjnewStateCnt++] = 2; - } - break; - case 2: - if ((0xffff7fffffffffffL & l) != 0L && kind > 5) { - kind = 5; - } - break; - case 3: - if (curChar == 42) { - jjstateSet[jjnewStateCnt++] = 1; - } - break; - case 6: - if (curChar == 36) { - jjCheckNAddStates(129, 132); - } - break; - case 7: - if (curChar == 45) { - jjCheckNAdd(8); - } - break; - case 9: - if ((0x3ff200000000000L & l) != 0L) { - jjCheckNAddStates(133, 135); - } - break; - case 12: - if ((0xffffffff00000000L & l) != 0L) { - jjCheckNAddStates(133, 135); - } - break; - case 13: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(136, 140); - } - break; - case 14: - if ((0x100003600L & l) != 0L) { - jjCheckNAddStates(133, 135); - } - break; - case 15: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(141, 148); - } - break; - case 16: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(149, 152); - } - break; - case 17: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(153, 157); - } - break; - case 18: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(158, 163); - } - break; - case 19: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(164, 170); - } - break; - case 22: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(171, 175); - } - break; - case 23: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(176, 183); - } - break; - case 24: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(184, 187); - } - break; - case 25: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(188, 192); - } - break; - case 26: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(193, 198); - } - break; - case 27: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(199, 205); - } - break; - case 28: - if (curChar == 35) { - jjstateSet[jjnewStateCnt++] = 5; - } - break; - case 40: - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 39; - } - break; - case 43: - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 42; - } - break; - case 44: - if (curChar == 34) { - jjCheckNAddStates(116, 119); - } - break; - case 45: - if ((0xfffffffb00000200L & l) != 0L) { - jjCheckNAddStates(116, 119); - } - break; - case 46: - if (curChar == 34 && kind > 71) { - kind = 71; - } - break; - case 48: - if (curChar == 12) { - jjCheckNAddStates(116, 119); - } - break; - case 50: - if ((0xffffffff00000000L & l) != 0L) { - jjCheckNAddStates(116, 119); - } - break; - case 51: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(206, 211); - } - break; - case 52: - if ((0x100003600L & l) != 0L) { - jjCheckNAddStates(116, 119); - } - break; - case 53: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(212, 220); - } - break; - case 54: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(221, 225); - } - break; - case 55: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(226, 231); - } - break; - case 56: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(232, 238); - } - break; - case 57: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(239, 246); - } - break; - case 58: - if (curChar == 13) { - jjCheckNAddStates(116, 119); - } - break; - case 59: - if (curChar == 10) { - jjCheckNAddStates(116, 119); - } - break; - case 60: - if (curChar == 13) { - jjstateSet[jjnewStateCnt++] = 59; - } - break; - case 61: - if (curChar == 39) { - jjCheckNAddStates(112, 115); - } - break; - case 62: - if ((0xffffff7f00000200L & l) != 0L) { - jjCheckNAddStates(112, 115); - } - break; - case 63: - if (curChar == 39 && kind > 71) { - kind = 71; - } - break; - case 65: - if (curChar == 12) { - jjCheckNAddStates(112, 115); - } - break; - case 67: - if ((0xffffffff00000000L & l) != 0L) { - jjCheckNAddStates(112, 115); - } - break; - case 68: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(247, 252); - } - break; - case 69: - if ((0x100003600L & l) != 0L) { - jjCheckNAddStates(112, 115); - } - break; - case 70: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(253, 261); - } - break; - case 71: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(262, 266); - } - break; - case 72: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(267, 272); - } - break; - case 73: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(273, 279); - } - break; - case 74: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(280, 287); - } - break; - case 75: - if (curChar == 13) { - jjCheckNAddStates(112, 115); - } - break; - case 76: - if (curChar == 10) { - jjCheckNAddStates(112, 115); - } - break; - case 77: - if (curChar == 13) { - jjstateSet[jjnewStateCnt++] = 76; - } - break; - case 78: - if (curChar == 36) { - jjCheckNAddStates(108, 111); - } - break; - case 81: - if ((0x3ff200000000000L & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddTwoStates(81, 82); - break; - case 83: - if ((0xffffffff00000000L & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddTwoStates(81, 82); - break; - case 84: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(288, 291); - break; - case 85: - if ((0x100003600L & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddTwoStates(81, 82); - break; - case 86: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(292, 298); - break; - case 87: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(299, 301); - break; - case 88: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(302, 305); - break; - case 89: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(306, 310); - break; - case 90: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(311, 316); - break; - case 93: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(317, 320); - break; - case 94: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(321, 327); - break; - case 95: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(328, 330); - break; - case 96: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(331, 334); - break; - case 97: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(335, 339); - break; - case 98: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(340, 345); - break; - case 99: - if (curChar == 35) { - jjCheckNAddTwoStates(100, 101); - } - break; - case 100: - if ((0x3ff200000000000L & l) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddTwoStates(100, 101); - break; - case 102: - if ((0xffffffff00000000L & l) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddTwoStates(100, 101); - break; - case 103: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddStates(346, 349); - break; - case 104: - if ((0x100003600L & l) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddTwoStates(100, 101); - break; - case 105: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddStates(350, 356); - break; - case 106: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddStates(357, 359); - break; - case 107: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddStates(360, 363); - break; - case 108: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddStates(364, 368); - break; - case 109: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddStates(369, 374); - break; - case 111: - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 112; - } - break; - case 115: - if ((0xffffffff00000000L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - break; - case 116: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(375, 378); - break; - case 117: - if ((0x100003600L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - break; - case 118: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(379, 385); - break; - case 119: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(386, 388); - break; - case 120: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(389, 392); - break; - case 121: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(393, 397); - break; - case 122: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(398, 403); - break; - case 125: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(404, 407); - break; - case 126: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(408, 414); - break; - case 127: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(415, 417); - break; - case 128: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(418, 421); - break; - case 129: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(422, 426); - break; - case 130: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(427, 432); - break; - case 132: - if ((0x100003600L & l) != 0L) { - jjAddStates(433, 434); - } - break; - case 133: - if (curChar == 40 && kind > 115) { - kind = 115; - } - break; - case 140: - if ((0x100003600L & l) != 0L) { - jjAddStates(435, 436); - } - break; - case 141: - if (curChar == 40 && kind > 116) { - kind = 116; - } - break; - case 148: - if ((0x100003600L & l) != 0L) { - jjAddStates(437, 438); - } - break; - case 149: - if (curChar == 40 && kind > 117) { - kind = 117; - } - break; - case 179: - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 178; - } - break; - case 188: - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 187; - } - break; - case 190: - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 189; - } - break; - case 199: - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 198; - } - break; - case 206: - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 205; - } - break; - case 215: - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 214; - } - break; - case 218: - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 217; - } - break; - case 220: - if ((0x3ff200000000000L & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddTwoStates(220, 221); - break; - case 222: - if ((0xffffffff00000000L & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddTwoStates(220, 221); - break; - case 223: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(439, 442); - break; - case 224: - if ((0x100003600L & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddTwoStates(220, 221); - break; - case 225: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(443, 449); - break; - case 226: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(450, 452); - break; - case 227: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(453, 456); - break; - case 228: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(457, 461); - break; - case 229: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(462, 467); - break; - case 230: - if ((0x3ff200000000000L & l) != 0L) { - jjCheckNAddStates(120, 123); - } - break; - case 231: - if ((0x100003600L & l) != 0L) { - jjCheckNAddTwoStates(231, 232); - } - break; - case 232: - if (curChar == 40 && kind > 118) { - kind = 118; - } - break; - case 234: - if ((0xffffffff00000000L & l) != 0L) { - jjCheckNAddStates(120, 123); - } - break; - case 235: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(468, 472); - } - break; - case 236: - if ((0x100003600L & l) != 0L) { - jjCheckNAddStates(120, 123); - } - break; - case 237: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(473, 480); - } - break; - case 238: - case 452: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(481, 484); - } - break; - case 239: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(485, 489); - } - break; - case 240: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(490, 495); - } - break; - case 241: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(496, 502); - } - break; - case 242: - if (curChar == 33) { - jjCheckNAddStates(104, 107); - } - break; - case 243: - if ((0x100003600L & l) != 0L) { - jjCheckNAddTwoStates(243, 250); - } - break; - case 251: - if ((0x100003600L & l) != 0L) { - jjCheckNAddTwoStates(251, 260); - } - break; - case 261: - if (curChar == 45) { - jjAddStates(102, 103); - } - break; - case 265: - if (curChar == 46) { - jjCheckNAddStates(82, 101); - } - break; - case 266: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 73) { - kind = 73; - } - jjCheckNAdd(266); - break; - case 267: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(267, 268); - } - break; - case 268: - if (curChar == 37 && kind > 77) { - kind = 77; - } - break; - case 269: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(269, 271); - } - break; - case 272: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(272, 274); - } - break; - case 275: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(275, 277); - } - break; - case 278: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(278, 280); - } - break; - case 281: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(281, 283); - } - break; - case 284: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(284, 286); - } - break; - case 287: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(287, 289); - } - break; - case 290: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(290, 293); - } - break; - case 294: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(294, 297); - } - break; - case 298: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(298, 300); - } - break; - case 301: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(301, 304); - } - break; - case 305: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(305, 308); - } - break; - case 309: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(309, 313); - } - break; - case 314: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(314, 316); - } - break; - case 317: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(317, 318); - } - break; - case 319: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(319, 321); - } - break; - case 322: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(322, 325); - } - break; - case 326: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(124, 128); - } - break; - case 327: - if (curChar == 45) { - jjCheckNAdd(328); - } - break; - case 329: - if ((0x3ff200000000000L & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddTwoStates(329, 330); - break; - case 331: - if ((0xffffffff00000000L & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddTwoStates(329, 330); - break; - case 332: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(503, 506); - break; - case 333: - if ((0x100003600L & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddTwoStates(329, 330); - break; - case 334: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(507, 513); - break; - case 335: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(514, 516); - break; - case 336: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(517, 520); - break; - case 337: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(521, 525); - break; - case 338: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(526, 531); - break; - case 341: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(532, 535); - break; - case 342: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(536, 542); - break; - case 343: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(543, 545); - break; - case 344: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(546, 549); - break; - case 345: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(550, 554); - break; - case 346: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(555, 560); - break; - case 348: - if (curChar == 40) { - jjCheckNAddStates(561, 566); - } - break; - case 349: - if ((0xfffffc7a00000000L & l) != 0L) { - jjCheckNAddStates(567, 570); - } - break; - case 350: - if ((0x100003600L & l) != 0L) { - jjCheckNAddTwoStates(350, 351); - } - break; - case 351: - if (curChar == 41 && kind > 75) { - kind = 75; - } - break; - case 353: - if ((0xffffffff00000000L & l) != 0L) { - jjCheckNAddStates(567, 570); - } - break; - case 354: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(571, 575); - } - break; - case 355: - if ((0x100003600L & l) != 0L) { - jjCheckNAddStates(567, 570); - } - break; - case 356: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(576, 583); - } - break; - case 357: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(584, 587); - } - break; - case 358: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(588, 592); - } - break; - case 359: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(593, 598); - } - break; - case 360: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(599, 605); - } - break; - case 361: - if (curChar == 39) { - jjCheckNAddStates(606, 609); - } - break; - case 362: - if ((0xffffff7f00000200L & l) != 0L) { - jjCheckNAddStates(606, 609); - } - break; - case 363: - if (curChar == 39) { - jjCheckNAddTwoStates(350, 351); - } - break; - case 365: - if (curChar == 12) { - jjCheckNAddStates(606, 609); - } - break; - case 367: - if ((0xffffffff00000000L & l) != 0L) { - jjCheckNAddStates(606, 609); - } - break; - case 368: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(610, 615); - } - break; - case 369: - if ((0x100003600L & l) != 0L) { - jjCheckNAddStates(606, 609); - } - break; - case 370: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(616, 624); - } - break; - case 371: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(625, 629); - } - break; - case 372: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(630, 635); - } - break; - case 373: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(636, 642); - } - break; - case 374: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(643, 650); - } - break; - case 375: - if (curChar == 13) { - jjCheckNAddStates(606, 609); - } - break; - case 376: - if (curChar == 10) { - jjCheckNAddStates(606, 609); - } - break; - case 377: - if (curChar == 13) { - jjstateSet[jjnewStateCnt++] = 376; - } - break; - case 378: - if (curChar == 34) { - jjCheckNAddStates(651, 654); - } - break; - case 379: - if ((0xfffffffb00000200L & l) != 0L) { - jjCheckNAddStates(651, 654); - } - break; - case 380: - if (curChar == 34) { - jjCheckNAddTwoStates(350, 351); - } - break; - case 382: - if (curChar == 12) { - jjCheckNAddStates(651, 654); - } - break; - case 384: - if ((0xffffffff00000000L & l) != 0L) { - jjCheckNAddStates(651, 654); - } - break; - case 385: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(655, 660); - } - break; - case 386: - if ((0x100003600L & l) != 0L) { - jjCheckNAddStates(651, 654); - } - break; - case 387: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(661, 669); - } - break; - case 388: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(670, 674); - } - break; - case 389: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(675, 680); - } - break; - case 390: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(681, 687); - } - break; - case 391: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(688, 695); - } - break; - case 392: - if (curChar == 13) { - jjCheckNAddStates(651, 654); - } - break; - case 393: - if (curChar == 10) { - jjCheckNAddStates(651, 654); - } - break; - case 394: - if (curChar == 13) { - jjstateSet[jjnewStateCnt++] = 393; - } - break; - case 395: - if ((0x100003600L & l) != 0L) { - jjCheckNAddStates(696, 702); - } - break; - case 398: - if (curChar == 43) { - jjAddStates(703, 704); - } - break; - case 399: - if (curChar != 63) { - break; - } - if (kind > 114) { - kind = 114; - } - jjstateSet[jjnewStateCnt++] = 400; - break; - case 400: - if (curChar != 63) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAddStates(705, 708); - break; - case 401: - if (curChar == 63 && kind > 114) { - kind = 114; - } - break; - case 402: - case 417: - case 421: - case 424: - case 427: - if (curChar != 63) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAdd(401); - break; - case 403: - if (curChar != 63) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAddTwoStates(401, 402); - break; - case 404: - if (curChar != 63) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAddStates(709, 711); - break; - case 405: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjAddStates(712, 717); - break; - case 406: - if ((0x3ff000000000000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 407; - } - break; - case 407: - if ((0x3ff000000000000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 408; - } - break; - case 408: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAdd(409); - } - break; - case 409: - if ((0x3ff000000000000L & l) != 0L && kind > 114) { - kind = 114; - } - break; - case 410: - if ((0x3ff000000000000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 411; - } - break; - case 411: - if ((0x3ff000000000000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 412; - } - break; - case 412: - if ((0x3ff000000000000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 413; - } - break; - case 413: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAdd(401); - break; - case 414: - if ((0x3ff000000000000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 415; - } - break; - case 415: - if ((0x3ff000000000000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 416; - } - break; - case 416: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjstateSet[jjnewStateCnt++] = 417; - break; - case 418: - if ((0x3ff000000000000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 419; - } - break; - case 419: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjstateSet[jjnewStateCnt++] = 420; - break; - case 420: - if (curChar != 63) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAddTwoStates(401, 421); - break; - case 422: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjstateSet[jjnewStateCnt++] = 423; - break; - case 423: - if (curChar != 63) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAddStates(718, 720); - break; - case 425: - if (curChar != 63) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAddTwoStates(401, 424); - break; - case 426: - if (curChar != 63) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAddStates(721, 724); - break; - case 428: - if (curChar != 63) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAddTwoStates(401, 427); - break; - case 429: - if (curChar != 63) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAddStates(725, 727); - break; - case 430: - if (curChar == 43) { - jjstateSet[jjnewStateCnt++] = 431; - } - break; - case 431: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(432, 438); - } - break; - case 432: - if (curChar == 45) { - jjstateSet[jjnewStateCnt++] = 433; - } - break; - case 433: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjstateSet[jjnewStateCnt++] = 434; - break; - case 434: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAddStates(728, 731); - break; - case 435: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAdd(409); - break; - case 436: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAddTwoStates(409, 435); - break; - case 437: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAddStates(732, 734); - break; - case 438: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(735, 739); - } - break; - case 439: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAdd(432); - } - break; - case 440: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(439, 432); - } - break; - case 441: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(740, 742); - } - break; - case 442: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(743, 746); - } - break; - case 444: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(747, 750); - break; - case 445: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(751, 757); - break; - case 446: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(758, 760); - break; - case 447: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(761, 764); - break; - case 448: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(765, 769); - break; - case 449: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(770, 775); - break; - case 450: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(776, 780); - } - break; - case 451: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(781, 788); - } - break; - case 453: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(789, 793); - } - break; - case 454: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(794, 799); - } - break; - case 455: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(800, 806); - } - break; - case 456: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 73) { - kind = 73; - } - jjCheckNAddStates(0, 81); - break; - case 457: - if ((0x3ff000000000000L & l) == 0L) { - break; - } - if (kind > 73) { - kind = 73; - } - jjCheckNAdd(457); - break; - case 458: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(458, 459); - } - break; - case 459: - if (curChar == 46) { - jjCheckNAdd(266); - } - break; - case 460: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(460, 268); - } - break; - case 461: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(461, 462); - } - break; - case 462: - if (curChar == 46) { - jjCheckNAdd(267); - } - break; - case 463: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(463, 271); - } - break; - case 464: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(464, 465); - } - break; - case 465: - if (curChar == 46) { - jjCheckNAdd(269); - } - break; - case 466: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(466, 274); - } - break; - case 467: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(467, 468); - } - break; - case 468: - if (curChar == 46) { - jjCheckNAdd(272); - } - break; - case 469: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(469, 277); - } - break; - case 470: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(470, 471); - } - break; - case 471: - if (curChar == 46) { - jjCheckNAdd(275); - } - break; - case 472: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(472, 280); - } - break; - case 473: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(473, 474); - } - break; - case 474: - if (curChar == 46) { - jjCheckNAdd(278); - } - break; - case 475: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(475, 283); - } - break; - case 476: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(476, 477); - } - break; - case 477: - if (curChar == 46) { - jjCheckNAdd(281); - } - break; - case 478: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(478, 286); - } - break; - case 479: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(479, 480); - } - break; - case 480: - if (curChar == 46) { - jjCheckNAdd(284); - } - break; - case 481: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(481, 289); - } - break; - case 482: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(482, 483); - } - break; - case 483: - if (curChar == 46) { - jjCheckNAdd(287); - } - break; - case 484: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(484, 293); - } - break; - case 485: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(485, 486); - } - break; - case 486: - if (curChar == 46) { - jjCheckNAdd(290); - } - break; - case 487: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(487, 297); - } - break; - case 488: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(488, 489); - } - break; - case 489: - if (curChar == 46) { - jjCheckNAdd(294); - } - break; - case 490: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(490, 300); - } - break; - case 491: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(491, 492); - } - break; - case 492: - if (curChar == 46) { - jjCheckNAdd(298); - } - break; - case 493: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(493, 304); - } - break; - case 494: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(494, 495); - } - break; - case 495: - if (curChar == 46) { - jjCheckNAdd(301); - } - break; - case 496: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(496, 308); - } - break; - case 497: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(497, 498); - } - break; - case 498: - if (curChar == 46) { - jjCheckNAdd(305); - } - break; - case 499: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(499, 313); - } - break; - case 500: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(500, 501); - } - break; - case 501: - if (curChar == 46) { - jjCheckNAdd(309); - } - break; - case 502: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(502, 316); - } - break; - case 503: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(503, 504); - } - break; - case 504: - if (curChar == 46) { - jjCheckNAdd(314); - } - break; - case 505: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(505, 318); - } - break; - case 506: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(506, 507); - } - break; - case 507: - if (curChar == 46) { - jjCheckNAdd(317); - } - break; - case 508: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(508, 321); - } - break; - case 509: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(509, 510); - } - break; - case 510: - if (curChar == 46) { - jjCheckNAdd(319); - } - break; - case 511: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(511, 325); - } - break; - case 512: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(512, 513); - } - break; - case 513: - if (curChar == 46) { - jjCheckNAdd(322); - } - break; - case 514: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddStates(807, 811); - } - break; - case 515: - if ((0x3ff000000000000L & l) != 0L) { - jjCheckNAddTwoStates(515, 516); - } - break; - case 516: - if (curChar == 46) { - jjCheckNAdd(326); - } - break; - default: - break; - } - } while (i != startsAt); - } else if (curChar < 128) { - long l = 1L << (curChar & 077); - do { - switch (jjstateSet[--i]) { - case 520: - if ((0x7fffffe87fffffeL & l) != 0L) { - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - } else if (curChar == 92) { - jjCheckNAddTwoStates(115, 116); - } - break; - case 166: - if ((0x7fffffe07fffffeL & l) != 0L) { - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - } else if (curChar == 92) { - jjCheckNAddTwoStates(115, 125); - } - if ((0x80000000800L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 165; - } - break; - case 174: - if ((0x7fffffe87fffffeL & l) != 0L) { - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - } else if (curChar == 92) { - jjCheckNAddTwoStates(115, 116); - } - if ((0x80000000800L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 173; - } - break; - case 4: - if ((0x7fffffe07fffffeL & l) != 0L) { - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(812, 817); - } else if (curChar == 92) { - jjCheckNAddStates(818, 821); - } else if (curChar == 64) { - jjAddStates(822, 826); - } - if ((0x20000000200000L & l) != 0L) { - jjAddStates(827, 829); - } else if ((0x800000008L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 155; - } else if ((0x200000002L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 145; - } else if ((0x4000000040000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 137; - } else if ((0x4000000040L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 33; - } else if (curChar == 64) { - jjAddStates(830, 833); - } - break; - case 518: - if ((0x20000000200L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 259; - } else if ((0x1000000010L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 249; - } - break; - case 178: - if ((0x7fffffe07fffffeL & l) != 0L) { - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - } - if ((0x200000002000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 216; - } else if ((0x80000000800000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 204; - } else if ((0x800000008000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 188; - } - if ((0x200000002000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 177; - } - break; - case 517: - if ((0x7fffffe87fffffeL & l) != 0L) { - jjCheckNAddStates(120, 123); - } else if (curChar == 92) { - jjCheckNAddTwoStates(222, 223); - } - if ((0x7fffffe87fffffeL & l) != 0L) { - if (kind > 72) { - kind = 72; - } - jjCheckNAddTwoStates(220, 221); - } else if (curChar == 92) { - jjCheckNAddTwoStates(234, 235); - } - break; - case 175: - if ((0x7fffffe87fffffeL & l) != 0L) { - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - } else if (curChar == 92) { - jjCheckNAddTwoStates(115, 116); - } - break; - case 33: - if ((0x7fffffe87fffffeL & l) != 0L) { - jjCheckNAddStates(120, 123); - } else if (curChar == 92) { - jjCheckNAddTwoStates(222, 223); - } - if ((0x7fffffe87fffffeL & l) != 0L) { - if (kind > 72) { - kind = 72; - } - jjCheckNAddTwoStates(220, 221); - } else if (curChar == 92) { - jjCheckNAddTwoStates(234, 235); - } - if ((0x20000000200L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 32; - } - break; - case 176: - if ((0x7fffffe87fffffeL & l) != 0L) { - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - } else if (curChar == 92) { - jjCheckNAddTwoStates(115, 116); - } - if ((0x400000004000000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 175; - } - break; - case 42: - if ((0x7fffffe07fffffeL & l) != 0L) { - jjCheckNAddStates(120, 123); - } - if ((0x7fffffe07fffffeL & l) != 0L) { - if (kind > 72) { - kind = 72; - } - jjCheckNAddTwoStates(220, 221); - } - if ((0x200000002000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 41; - } - break; - case 177: - if ((0x7fffffe87fffffeL & l) != 0L) { - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - } else if (curChar == 92) { - jjCheckNAddTwoStates(115, 116); - } - if ((0x8000000080000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 215; - } else if ((0x800000008000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 176; - } - break; - case 79: - if ((0x7fffffe07fffffeL & l) != 0L) { - if (kind > 76) { - kind = 76; - } - jjCheckNAddTwoStates(81, 82); - } else if (curChar == 92) { - jjCheckNAddTwoStates(83, 93); - } - break; - case 2: - if (kind > 5) { - kind = 5; - } - break; - case 5: - if (curChar == 123) { - jjstateSet[jjnewStateCnt++] = 6; - } - break; - case 8: - if ((0x7fffffe07fffffeL & l) != 0L) { - jjCheckNAddStates(133, 135); - } - break; - case 9: - if ((0x7fffffe87fffffeL & l) != 0L) { - jjCheckNAddStates(133, 135); - } - break; - case 10: - if (curChar == 125 && kind > 40) { - kind = 40; - } - break; - case 11: - if (curChar == 92) { - jjCheckNAddTwoStates(12, 13); - } - break; - case 12: - if ((0x7fffffffffffffffL & l) != 0L) { - jjCheckNAddStates(133, 135); - } - break; - case 13: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(136, 140); - } - break; - case 15: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(141, 148); - } - break; - case 16: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(149, 152); - } - break; - case 17: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(153, 157); - } - break; - case 18: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(158, 163); - } - break; - case 19: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(164, 170); - } - break; - case 21: - if (curChar == 92) { - jjCheckNAddTwoStates(12, 22); - } - break; - case 22: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(171, 175); - } - break; - case 23: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(176, 183); - } - break; - case 24: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(184, 187); - } - break; - case 25: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(188, 192); - } - break; - case 26: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(193, 198); - } - break; - case 27: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(199, 205); - } - break; - case 29: - if ((0x4000000040000L & l) != 0L && kind > 68) { - kind = 68; - } - break; - case 30: - case 35: - if ((0x2000000020L & l) != 0L) { - jjCheckNAdd(29); - } - break; - case 31: - if ((0x10000000100000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 30; - } - break; - case 32: - if ((0x100000001000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 31; - } - break; - case 34: - if ((0x4000000040L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 33; - } - break; - case 36: - if ((0x10000000100000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 35; - } - break; - case 37: - if ((0x100000001000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 36; - } - break; - case 38: - if ((0x20000000200L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 37; - } - break; - case 39: - if ((0x4000000040L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 38; - } - break; - case 41: - if ((0x8000000080000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 40; - } - break; - case 45: - case 50: - if ((0x7fffffffffffffffL & l) != 0L) { - jjCheckNAddStates(116, 119); - } - break; - case 47: - if (curChar == 92) { - jjAddStates(834, 837); - } - break; - case 49: - if (curChar == 92) { - jjAddStates(838, 839); - } - break; - case 51: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(206, 211); - } - break; - case 53: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(212, 220); - } - break; - case 54: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(221, 225); - } - break; - case 55: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(226, 231); - } - break; - case 56: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(232, 238); - } - break; - case 57: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(239, 246); - } - break; - case 62: - case 67: - if ((0x7fffffffffffffffL & l) != 0L) { - jjCheckNAddStates(112, 115); - } - break; - case 64: - if (curChar == 92) { - jjAddStates(840, 843); - } - break; - case 66: - if (curChar == 92) { - jjAddStates(844, 845); - } - break; - case 68: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(247, 252); - } - break; - case 70: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(253, 261); - } - break; - case 71: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(262, 266); - } - break; - case 72: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(267, 272); - } - break; - case 73: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(273, 279); - } - break; - case 74: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(280, 287); - } - break; - case 80: - if ((0x7fffffe07fffffeL & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddTwoStates(81, 82); - break; - case 81: - if ((0x7fffffe87fffffeL & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddTwoStates(81, 82); - break; - case 82: - if (curChar == 92) { - jjCheckNAddTwoStates(83, 84); - } - break; - case 83: - if ((0x7fffffffffffffffL & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddTwoStates(81, 82); - break; - case 84: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(288, 291); - break; - case 86: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(292, 298); - break; - case 87: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(299, 301); - break; - case 88: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(302, 305); - break; - case 89: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(306, 310); - break; - case 90: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(311, 316); - break; - case 92: - if (curChar == 92) { - jjCheckNAddTwoStates(83, 93); - } - break; - case 93: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(317, 320); - break; - case 94: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(321, 327); - break; - case 95: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(328, 330); - break; - case 96: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(331, 334); - break; - case 97: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(335, 339); - break; - case 98: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddStates(340, 345); - break; - case 100: - if ((0x7fffffe87fffffeL & l) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddTwoStates(100, 101); - break; - case 101: - if (curChar == 92) { - jjAddStates(846, 847); - } - break; - case 102: - if ((0x7fffffffffffffffL & l) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddTwoStates(100, 101); - break; - case 103: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddStates(346, 349); - break; - case 105: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddStates(350, 356); - break; - case 106: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddStates(357, 359); - break; - case 107: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddStates(360, 363); - break; - case 108: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddStates(364, 368); - break; - case 109: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddStates(369, 374); - break; - case 110: - if (curChar == 64) { - jjAddStates(830, 833); - } - break; - case 112: - if ((0x7fffffe07fffffeL & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - break; - case 113: - if ((0x7fffffe87fffffeL & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - break; - case 114: - if (curChar == 92) { - jjCheckNAddTwoStates(115, 116); - } - break; - case 115: - if ((0x7fffffffffffffffL & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - break; - case 116: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(375, 378); - break; - case 118: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(379, 385); - break; - case 119: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(386, 388); - break; - case 120: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(389, 392); - break; - case 121: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(393, 397); - break; - case 122: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(398, 403); - break; - case 124: - if (curChar == 92) { - jjCheckNAddTwoStates(115, 125); - } - break; - case 125: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(404, 407); - break; - case 126: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(408, 414); - break; - case 127: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(415, 417); - break; - case 128: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(418, 421); - break; - case 129: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(422, 426); - break; - case 130: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddStates(427, 432); - break; - case 131: - if ((0x2000000020L & l) != 0L) { - jjAddStates(433, 434); - } - break; - case 134: - if ((0x40000000400000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 131; - } - break; - case 135: - if ((0x800000008000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 134; - } - break; - case 136: - if ((0x200000002000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 135; - } - break; - case 137: - if ((0x2000000020L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 136; - } - break; - case 138: - if ((0x4000000040000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 137; - } - break; - case 139: - if ((0x1000000010L & l) != 0L) { - jjAddStates(435, 436); - } - break; - case 142: - if ((0x400000004000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 139; - } - break; - case 143: - if ((0x2000000020L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 142; - } - break; - case 144: - if ((0x1000000010000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 143; - } - break; - case 145: - if ((0x1000000010000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 144; - } - break; - case 146: - if ((0x200000002L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 145; - } - break; - case 147: - if ((0x8000000080000L & l) != 0L) { - jjAddStates(437, 438); - } - break; - case 150: - if ((0x400000004000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 147; - } - break; - case 151: - if ((0x20000000200L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 150; - } - break; - case 152: - if ((0x200000002L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 151; - } - break; - case 153: - if ((0x10000000100000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 152; - } - break; - case 154: - if ((0x400000004000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 153; - } - break; - case 155: - if ((0x800000008000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 154; - } - break; - case 156: - if ((0x800000008L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 155; - } - break; - case 157: - if (curChar == 64) { - jjAddStates(822, 826); - } - break; - case 158: - if ((0x8000000080000L & l) != 0L && kind > 102) { - kind = 102; - } - break; - case 159: - case 167: - case 180: - case 191: - case 207: - if ((0x2000000020L & l) != 0L) { - jjCheckNAdd(158); - } - break; - case 160: - if ((0x200000002000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 159; - } - break; - case 161: - if ((0x200000002L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 160; - } - break; - case 162: - if ((0x4000000040000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 161; - } - break; - case 163: - if ((0x4000000040L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 162; - } - break; - case 164: - if ((0x200000002000000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 163; - } - break; - case 165: - if ((0x2000000020L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 164; - } - break; - case 168: - if ((0x200000002000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 167; - } - break; - case 169: - if ((0x200000002L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 168; - } - break; - case 170: - if ((0x4000000040000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 169; - } - break; - case 171: - if ((0x4000000040L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 170; - } - break; - case 172: - if ((0x200000002000000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 171; - } - break; - case 173: - if ((0x2000000020L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 172; - } - break; - case 181: - if ((0x200000002000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 180; - } - break; - case 182: - if ((0x200000002L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 181; - } - break; - case 183: - if ((0x4000000040000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 182; - } - break; - case 184: - if ((0x4000000040L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 183; - } - break; - case 185: - if ((0x200000002000000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 184; - } - break; - case 186: - if ((0x2000000020L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 185; - } - break; - case 187: - if ((0x80000000800L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 186; - } - break; - case 189: - if ((0x800000008000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 188; - } - break; - case 192: - if ((0x200000002000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 191; - } - break; - case 193: - if ((0x200000002L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 192; - } - break; - case 194: - if ((0x4000000040000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 193; - } - break; - case 195: - if ((0x4000000040L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 194; - } - break; - case 196: - if ((0x200000002000000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 195; - } - break; - case 197: - if ((0x2000000020L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 196; - } - break; - case 198: - if ((0x80000000800L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 197; - } - break; - case 200: - if ((0x10000000100000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 199; - } - break; - case 201: - if ((0x20000000200L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 200; - } - break; - case 202: - if ((0x80000000800L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 201; - } - break; - case 203: - if ((0x400000004L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 202; - } - break; - case 204: - if ((0x2000000020L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 203; - } - break; - case 205: - if ((0x80000000800000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 204; - } - break; - case 208: - if ((0x200000002000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 207; - } - break; - case 209: - if ((0x200000002L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 208; - } - break; - case 210: - if ((0x4000000040000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 209; - } - break; - case 211: - if ((0x4000000040L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 210; - } - break; - case 212: - if ((0x200000002000000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 211; - } - break; - case 213: - if ((0x2000000020L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 212; - } - break; - case 214: - if ((0x80000000800L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 213; - } - break; - case 216: - if ((0x8000000080000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 215; - } - break; - case 217: - if ((0x200000002000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 216; - } - break; - case 220: - if ((0x7fffffe87fffffeL & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddTwoStates(220, 221); - break; - case 221: - if (curChar == 92) { - jjCheckNAddTwoStates(222, 223); - } - break; - case 222: - if ((0x7fffffffffffffffL & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddTwoStates(220, 221); - break; - case 223: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(439, 442); - break; - case 225: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(443, 449); - break; - case 226: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(450, 452); - break; - case 227: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(453, 456); - break; - case 228: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(457, 461); - break; - case 229: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(462, 467); - break; - case 230: - if ((0x7fffffe87fffffeL & l) != 0L) { - jjCheckNAddStates(120, 123); - } - break; - case 233: - if (curChar == 92) { - jjCheckNAddTwoStates(234, 235); - } - break; - case 234: - if ((0x7fffffffffffffffL & l) != 0L) { - jjCheckNAddStates(120, 123); - } - break; - case 235: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(468, 472); - } - break; - case 237: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(473, 480); - } - break; - case 238: - case 452: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(481, 484); - } - break; - case 239: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(485, 489); - } - break; - case 240: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(490, 495); - } - break; - case 241: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(496, 502); - } - break; - case 244: - if ((0x10000000100000L & l) != 0L && kind > 70) { - kind = 70; - } - break; - case 245: - if ((0x100000001000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 244; - } - break; - case 246: - if ((0x20000000200000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 245; - } - break; - case 247: - if ((0x200000002L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 246; - } - break; - case 248: - if ((0x4000000040L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 247; - } - break; - case 249: - if ((0x2000000020L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 248; - } - break; - case 250: - if ((0x1000000010L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 249; - } - break; - case 252: - if ((0x10000000100000L & l) != 0L && kind > 104) { - kind = 104; - } - break; - case 253: - if ((0x400000004000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 252; - } - break; - case 254: - if ((0x200000002L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 253; - } - break; - case 255: - if ((0x10000000100000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 254; - } - break; - case 256: - if ((0x4000000040000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 255; - } - break; - case 257: - if ((0x800000008000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 256; - } - break; - case 258: - if ((0x1000000010000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 257; - } - break; - case 259: - if ((0x200000002000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 258; - } - break; - case 260: - if ((0x20000000200L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 259; - } - break; - case 262: - if ((0x7fffffe07fffffeL & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddTwoStates(220, 221); - break; - case 263: - if ((0x7fffffe07fffffeL & l) != 0L) { - jjCheckNAddStates(120, 123); - } - break; - case 264: - if ((0x7fffffe07fffffeL & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(812, 817); - break; - case 270: - if ((0x10000000100000L & l) != 0L && kind > 78) { - kind = 78; - } - break; - case 271: - if ((0x1000000010000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 270; - } - break; - case 273: - if ((0x200000002000L & l) != 0L && kind > 79) { - kind = 79; - } - break; - case 274: - if ((0x200000002000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 273; - } - break; - case 276: - if ((0x200000002000L & l) != 0L && kind > 80) { - kind = 80; - } - break; - case 277: - if ((0x800000008L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 276; - } - break; - case 279: - if ((0x800000008L & l) != 0L && kind > 81) { - kind = 81; - } - break; - case 280: - if ((0x1000000010000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 279; - } - break; - case 282: - if ((0x400000004000L & l) != 0L && kind > 82) { - kind = 82; - } - break; - case 283: - if ((0x20000000200L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 282; - } - break; - case 285: - if ((0x100000001000000L & l) != 0L && kind > 83) { - kind = 83; - } - break; - case 286: - if ((0x1000000010000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 285; - } - break; - case 288: - if ((0x200000002000L & l) != 0L && kind > 84) { - kind = 84; - } - break; - case 289: - if ((0x2000000020L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 288; - } - break; - case 291: - if ((0x200000002000L & l) != 0L && kind > 85) { - kind = 85; - } - break; - case 292: - if ((0x2000000020L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 291; - } - break; - case 293: - if ((0x100000001000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 292; - } - break; - case 295: - if ((0x200000002000L & l) != 0L && kind > 86) { - kind = 86; - } - break; - case 296: - if ((0x2000000020L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 295; - } - break; - case 297: - if ((0x4000000040000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 296; - } - break; - case 299: - if ((0x100000001000000L & l) != 0L && kind > 87) { - kind = 87; - } - break; - case 300: - if ((0x2000000020L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 299; - } - break; - case 302: - if ((0x8000000080L & l) != 0L && kind > 88) { - kind = 88; - } - break; - case 303: - if ((0x2000000020L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 302; - } - break; - case 304: - if ((0x1000000010L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 303; - } - break; - case 306: - if ((0x1000000010L & l) != 0L && kind > 89) { - kind = 89; - } - break; - case 307: - if ((0x200000002L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 306; - } - break; - case 308: - if ((0x4000000040000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 307; - } - break; - case 310: - if ((0x1000000010L & l) != 0L && kind > 90) { - kind = 90; - } - break; - case 311: - if ((0x200000002L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 310; - } - break; - case 312: - if ((0x4000000040000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 311; - } - break; - case 313: - if ((0x8000000080L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 312; - } - break; - case 315: - if ((0x8000000080000L & l) != 0L && kind > 91) { - kind = 91; - } - break; - case 316: - if ((0x200000002000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 315; - } - break; - case 318: - if ((0x8000000080000L & l) != 0L && kind > 92) { - kind = 92; - } - break; - case 320: - if ((0x400000004000000L & l) != 0L && kind > 93) { - kind = 93; - } - break; - case 321: - if ((0x10000000100L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 320; - } - break; - case 323: - if ((0x400000004000000L & l) != 0L && kind > 94) { - kind = 94; - } - break; - case 324: - if ((0x10000000100L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 323; - } - break; - case 325: - if ((0x80000000800L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 324; - } - break; - case 328: - if ((0x7fffffe07fffffeL & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddTwoStates(329, 330); - break; - case 329: - if ((0x7fffffe87fffffeL & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddTwoStates(329, 330); - break; - case 330: - if (curChar == 92) { - jjCheckNAddTwoStates(331, 332); - } - break; - case 331: - if ((0x7fffffffffffffffL & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddTwoStates(329, 330); - break; - case 332: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(503, 506); - break; - case 334: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(507, 513); - break; - case 335: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(514, 516); - break; - case 336: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(517, 520); - break; - case 337: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(521, 525); - break; - case 338: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(526, 531); - break; - case 340: - if (curChar == 92) { - jjCheckNAddTwoStates(331, 341); - } - break; - case 341: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(532, 535); - break; - case 342: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(536, 542); - break; - case 343: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(543, 545); - break; - case 344: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(546, 549); - break; - case 345: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(550, 554); - break; - case 346: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddStates(555, 560); - break; - case 347: - if ((0x20000000200000L & l) != 0L) { - jjAddStates(827, 829); - } - break; - case 349: - case 353: - if ((0x7fffffffffffffffL & l) != 0L) { - jjCheckNAddStates(567, 570); - } - break; - case 352: - if (curChar == 92) { - jjAddStates(848, 849); - } - break; - case 354: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(571, 575); - } - break; - case 356: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(576, 583); - } - break; - case 357: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(584, 587); - } - break; - case 358: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(588, 592); - } - break; - case 359: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(593, 598); - } - break; - case 360: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(599, 605); - } - break; - case 362: - case 367: - if ((0x7fffffffffffffffL & l) != 0L) { - jjCheckNAddStates(606, 609); - } - break; - case 364: - if (curChar == 92) { - jjAddStates(850, 853); - } - break; - case 366: - if (curChar == 92) { - jjAddStates(854, 855); - } - break; - case 368: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(610, 615); - } - break; - case 370: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(616, 624); - } - break; - case 371: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(625, 629); - } - break; - case 372: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(630, 635); - } - break; - case 373: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(636, 642); - } - break; - case 374: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(643, 650); - } - break; - case 379: - case 384: - if ((0x7fffffffffffffffL & l) != 0L) { - jjCheckNAddStates(651, 654); - } - break; - case 381: - if (curChar == 92) { - jjAddStates(856, 859); - } - break; - case 383: - if (curChar == 92) { - jjAddStates(860, 861); - } - break; - case 385: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(655, 660); - } - break; - case 387: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(661, 669); - } - break; - case 388: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(670, 674); - } - break; - case 389: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(675, 680); - } - break; - case 390: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(681, 687); - } - break; - case 391: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(688, 695); - } - break; - case 396: - if ((0x100000001000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 348; - } - break; - case 397: - if ((0x4000000040000L & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 396; - } - break; - case 405: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjAddStates(712, 717); - break; - case 406: - if ((0x7e0000007eL & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 407; - } - break; - case 407: - if ((0x7e0000007eL & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 408; - } - break; - case 408: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAdd(409); - } - break; - case 409: - if ((0x7e0000007eL & l) != 0L && kind > 114) { - kind = 114; - } - break; - case 410: - if ((0x7e0000007eL & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 411; - } - break; - case 411: - if ((0x7e0000007eL & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 412; - } - break; - case 412: - if ((0x7e0000007eL & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 413; - } - break; - case 413: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjstateSet[jjnewStateCnt++] = 401; - break; - case 414: - if ((0x7e0000007eL & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 415; - } - break; - case 415: - if ((0x7e0000007eL & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 416; - } - break; - case 416: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjstateSet[jjnewStateCnt++] = 417; - break; - case 418: - if ((0x7e0000007eL & l) != 0L) { - jjstateSet[jjnewStateCnt++] = 419; - } - break; - case 419: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjstateSet[jjnewStateCnt++] = 420; - break; - case 422: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjstateSet[jjnewStateCnt++] = 423; - break; - case 431: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddTwoStates(432, 438); - } - break; - case 433: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjstateSet[jjnewStateCnt++] = 434; - break; - case 434: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAddStates(728, 731); - break; - case 435: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAdd(409); - break; - case 436: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAddTwoStates(409, 435); - break; - case 437: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 114) { - kind = 114; - } - jjCheckNAddStates(732, 734); - break; - case 438: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(735, 739); - } - break; - case 439: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAdd(432); - } - break; - case 440: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddTwoStates(439, 432); - } - break; - case 441: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(740, 742); - } - break; - case 442: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(743, 746); - } - break; - case 443: - if (curChar == 92) { - jjCheckNAddStates(818, 821); - } - break; - case 444: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(747, 750); - break; - case 445: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(751, 757); - break; - case 446: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(758, 760); - break; - case 447: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(761, 764); - break; - case 448: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(765, 769); - break; - case 449: - if ((0x7e0000007eL & l) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddStates(770, 775); - break; - case 450: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(776, 780); - } - break; - case 451: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(781, 788); - } - break; - case 453: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(789, 793); - } - break; - case 454: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(794, 799); - } - break; - case 455: - if ((0x7e0000007eL & l) != 0L) { - jjCheckNAddStates(800, 806); - } - break; - default: - break; - } - } while (i != startsAt); - } else { - int i2 = (curChar & 0xff) >> 6; - long l2 = 1L << (curChar & 077); - do { - switch (jjstateSet[--i]) { - case 520: - case 113: - case 115: - if ((jjbitVec0[i2] & l2) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - break; - case 166: - if ((jjbitVec0[i2] & l2) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - break; - case 174: - if ((jjbitVec0[i2] & l2) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - break; - case 4: - if ((jjbitVec0[i2] & l2) == 0L) { - break; - } - if (kind > 41) { - kind = 41; - } - jjCheckNAddStates(812, 817); - break; - case 517: - if ((jjbitVec0[i2] & l2) != 0L) { - if (kind > 72) { - kind = 72; - } - jjCheckNAddTwoStates(220, 221); - } - if ((jjbitVec0[i2] & l2) != 0L) { - jjCheckNAddStates(120, 123); - } - break; - case 175: - if ((jjbitVec0[i2] & l2) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - break; - case 33: - if ((jjbitVec0[i2] & l2) != 0L) { - if (kind > 72) { - kind = 72; - } - jjCheckNAddTwoStates(220, 221); - } - if ((jjbitVec0[i2] & l2) != 0L) { - jjCheckNAddStates(120, 123); - } - break; - case 176: - if ((jjbitVec0[i2] & l2) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - break; - case 177: - if ((jjbitVec0[i2] & l2) == 0L) { - break; - } - if (kind > 103) { - kind = 103; - } - jjCheckNAddTwoStates(113, 114); - break; - case 79: - case 81: - case 83: - if ((jjbitVec0[i2] & l2) == 0L) { - break; - } - if (kind > 76) { - kind = 76; - } - jjCheckNAddTwoStates(81, 82); - break; - case 2: - if ((jjbitVec0[i2] & l2) != 0L && kind > 5) { - kind = 5; - } - break; - case 9: - case 12: - case 20: - if ((jjbitVec0[i2] & l2) != 0L) { - jjCheckNAddStates(133, 135); - } - break; - case 45: - case 50: - if ((jjbitVec0[i2] & l2) != 0L) { - jjCheckNAddStates(116, 119); - } - break; - case 62: - case 67: - if ((jjbitVec0[i2] & l2) != 0L) { - jjCheckNAddStates(112, 115); - } - break; - case 100: - case 102: - if ((jjbitVec0[i2] & l2) == 0L) { - break; - } - if (kind > 96) { - kind = 96; - } - jjCheckNAddTwoStates(100, 101); - break; - case 220: - case 222: - if ((jjbitVec0[i2] & l2) == 0L) { - break; - } - if (kind > 72) { - kind = 72; - } - jjCheckNAddTwoStates(220, 221); - break; - case 230: - case 234: - if ((jjbitVec0[i2] & l2) != 0L) { - jjCheckNAddStates(120, 123); - } - break; - case 329: - case 331: - case 339: - if ((jjbitVec0[i2] & l2) == 0L) { - break; - } - if (kind > 95) { - kind = 95; - } - jjCheckNAddTwoStates(329, 330); - break; - case 349: - case 353: - if ((jjbitVec0[i2] & l2) != 0L) { - jjCheckNAddStates(567, 570); - } - break; - case 362: - case 367: - if ((jjbitVec0[i2] & l2) != 0L) { - jjCheckNAddStates(606, 609); - } - break; - case 379: - case 384: - if ((jjbitVec0[i2] & l2) != 0L) { - jjCheckNAddStates(651, 654); - } - break; - default: - break; - } - } while (i != startsAt); - } - if (kind != 0x7fffffff) { - jjmatchedKind = kind; - jjmatchedPos = curPos; - kind = 0x7fffffff; - } - ++curPos; - if ((i = jjnewStateCnt) == (startsAt = 517 - (jjnewStateCnt = startsAt))) { - return curPos; - } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - return curPos; - } - } - } - - private int jjMoveStringLiteralDfa0_3() { - switch (curChar) { - case 42: - return jjMoveStringLiteralDfa1_3(0x100L); - default: + if ((active0 & 0xff80000000000000L) != 0L || (active1 & 0xf80000003fL) != 0L) + return 166; + if ((active0 & 0x38000000000000L) != 0L || (active1 & 0x80L) != 0L) + { + jjmatchedKind = 74; + return 518; + } + if ((active0 & 0x200000000L) != 0L) + return 519; + return -1; + case 1: + if ((active0 & 0x50000000000000L) != 0L) + { + jjmatchedKind = 74; + jjmatchedPos = 1; + return 518; + } + if ((active1 & 0x8L) != 0L) + return 178; + if ((active0 & 0xff80000000000000L) != 0L || (active1 & 0xf800000037L) != 0L) + { + jjmatchedKind = 105; + jjmatchedPos = 1; + return 520; + } + if ((active0 & 0x40L) != 0L) return 1; - } - } - - private int jjMoveStringLiteralDfa1_3(long active0) { - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - return 1; - } - switch (curChar) { - case 47: - if ((active0 & 0x100L) != 0L) { - return jjStopAtPos(1, 8); - } - break; - default: - return 2; - } - return 2; - } - - private int jjMoveStringLiteralDfa0_1() { - return jjMoveNfa_1(0, 0); - } - - private int jjMoveNfa_1(int startState, int curPos) { - int startsAt = 0; - jjnewStateCnt = 4; - int i = 1; - jjstateSet[0] = startState; - int kind = 0x7fffffff; - for (;;) { - if (++jjround == 0x7fffffff) { - ReInitRounds(); + if ((active0 & 0x28000000000000L) != 0L || (active1 & 0x80L) != 0L) + return 518; + return -1; + case 2: + if ((active1 & 0x8L) != 0L) + { + jjmatchedKind = 105; + jjmatchedPos = 2; + return 177; + } + if ((active1 & 0x1L) != 0L) + return 520; + if ((active0 & 0xff80000000000000L) != 0L || (active1 & 0xf800000036L) != 0L) + { + jjmatchedKind = 105; + jjmatchedPos = 2; + return 520; + } + if ((active0 & 0x50000000000000L) != 0L) + { + jjmatchedKind = 74; + jjmatchedPos = 2; + return 518; + } + return -1; + case 3: + if ((active0 & 0x10000000000000L) != 0L) + { + jjmatchedKind = 74; + jjmatchedPos = 3; + return 518; + } + if ((active1 & 0x8L) != 0L) + { + jjmatchedKind = 105; + jjmatchedPos = 3; + return 176; + } + if ((active0 & 0xdf80000000000000L) != 0L || (active1 & 0xf800000036L) != 0L) + { + jjmatchedKind = 105; + jjmatchedPos = 3; + return 520; + } + if ((active0 & 0x2000000000000000L) != 0L) + return 520; + if ((active0 & 0x40000000000000L) != 0L) + return 518; + return -1; + case 4: + if ((active0 & 0x8f80000000000000L) != 0L || (active1 & 0xb800000034L) != 0L) + { + jjmatchedKind = 105; + jjmatchedPos = 4; + return 520; + } + if ((active0 & 0x5000000000000000L) != 0L || (active1 & 0x4000000002L) != 0L) + return 520; + if ((active0 & 0x10000000000000L) != 0L) + { + jjmatchedKind = 74; + jjmatchedPos = 4; + return 518; + } + if ((active1 & 0x8L) != 0L) + { + jjmatchedKind = 105; + jjmatchedPos = 4; + return 175; + } + return -1; + case 5: + if ((active0 & 0x700000000000000L) != 0L || (active1 & 0xa800000034L) != 0L) + { + jjmatchedKind = 105; + jjmatchedPos = 5; + return 520; + } + if ((active0 & 0x10000000000000L) != 0L) + { + jjmatchedKind = 74; + jjmatchedPos = 5; + return 518; + } + if ((active1 & 0x8L) != 0L) + { + jjmatchedKind = 105; + jjmatchedPos = 5; + return 174; + } + if ((active0 & 0x8880000000000000L) != 0L || (active1 & 0x1000000000L) != 0L) + return 520; + return -1; + case 6: + if ((active0 & 0x400000000000000L) != 0L || (active1 & 0x800000004L) != 0L) + return 520; + if ((active0 & 0x300000000000000L) != 0L || (active1 & 0xa000000038L) != 0L) + { + jjmatchedKind = 105; + jjmatchedPos = 6; + return 520; + } + if ((active0 & 0x10000000000000L) != 0L) + return 518; + return -1; + case 7: + if ((active0 & 0x100000000000000L) != 0L || (active1 & 0x2000000020L) != 0L) + return 520; + if ((active0 & 0x200000000000000L) != 0L || (active1 & 0x8000000018L) != 0L) + { + jjmatchedKind = 105; + jjmatchedPos = 7; + return 520; + } + return -1; + case 8: + if ((active0 & 0x200000000000000L) != 0L || (active1 & 0x10L) != 0L) + return 520; + if ((active1 & 0x8000000008L) != 0L) + { + jjmatchedKind = 105; + jjmatchedPos = 8; + return 520; + } + return -1; + case 9: + if ((active1 & 0x8L) != 0L) + { + jjmatchedKind = 105; + jjmatchedPos = 9; + return 520; + } + if ((active1 & 0x8000000000L) != 0L) + return 520; + return -1; + case 10: + if ((active1 & 0x8L) != 0L) + { + jjmatchedKind = 105; + jjmatchedPos = 10; + return 520; + } + return -1; + case 11: + if ((active1 & 0x8L) != 0L) + { + jjmatchedKind = 105; + jjmatchedPos = 11; + return 520; + } + return -1; + case 12: + if ((active1 & 0x8L) != 0L) + { + jjmatchedKind = 105; + jjmatchedPos = 12; + return 520; + } + return -1; + default : + return -1; + } +} +private final int jjStartNfa_0(int pos, long active0, long active1) +{ + return jjMoveNfa_0(jjStopStringLiteralDfa_0(pos, active0, active1), pos + 1); +} +private int jjStopAtPos(int pos, int kind) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + return pos + 1; +} +private int jjMoveStringLiteralDfa0_0() +{ + switch(curChar) + { + case 33: + return jjMoveStringLiteralDfa1_0(0x8000000000L, 0x0L); + case 36: + return jjMoveStringLiteralDfa1_0(0x10000L, 0x0L); + case 37: + return jjStopAtPos(0, 31); + case 38: + jjmatchedKind = 32; + return jjMoveStringLiteralDfa1_0(0x4000000000L, 0x0L); + case 40: + return jjStopAtPos(0, 34); + case 41: + return jjStopAtPos(0, 35); + case 42: + jjmatchedKind = 30; + return jjMoveStringLiteralDfa1_0(0x20000L, 0x0L); + case 43: + return jjStopAtPos(0, 20); + case 44: + return jjStopAtPos(0, 22); + case 45: + jjmatchedKind = 21; + return jjMoveStringLiteralDfa1_0(0x800L, 0x0L); + case 46: + return jjStartNfaWithStates_0(0, 33, 519); + case 47: + jjmatchedKind = 27; + return jjMoveStringLiteralDfa1_0(0x44L, 0x0L); + case 58: + return jjStopAtPos(0, 40); + case 59: + return jjStopAtPos(0, 23); + case 60: + jjmatchedKind = 26; + return jjMoveStringLiteralDfa1_0(0x400L, 0x0L); + case 61: + jjmatchedKind = 19; + return jjMoveStringLiteralDfa1_0(0x1000000000L, 0x0L); + case 62: + return jjStopAtPos(0, 24); + case 64: + return jjMoveStringLiteralDfa1_0(0xff80000000000000L, 0xf80000003fL); + case 91: + return jjStopAtPos(0, 28); + case 93: + return jjStopAtPos(0, 29); + case 94: + return jjMoveStringLiteralDfa1_0(0x8000L, 0x0L); + case 70: + case 102: + return jjMoveStringLiteralDfa1_0(0x40000000000000L, 0x0L); + case 73: + case 105: + return jjMoveStringLiteralDfa1_0(0x20000000000000L, 0x80L); + case 84: + case 116: + return jjMoveStringLiteralDfa1_0(0x18000000000000L, 0x0L); + case 123: + return jjStopAtPos(0, 12); + case 124: + return jjMoveStringLiteralDfa1_0(0x2000004000L, 0x0L); + case 125: + return jjStopAtPos(0, 13); + case 126: + jjmatchedKind = 25; + return jjMoveStringLiteralDfa1_0(0x40000L, 0x0L); + default : + return jjMoveNfa_0(4, 0); + } +} +private int jjMoveStringLiteralDfa1_0(long active0, long active1) +{ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(0, active0, active1); + return 1; + } + switch(curChar) + { + case 33: + return jjMoveStringLiteralDfa2_0(active0, 0x400L, active1, 0L); + case 38: + if ((active0 & 0x4000000000L) != 0L) + return jjStopAtPos(1, 38); + break; + case 42: + if ((active0 & 0x40L) != 0L) + return jjStartNfaWithStates_0(1, 6, 1); + break; + case 45: + return jjMoveStringLiteralDfa2_0(active0, 0x800L, active1, 0x8L); + case 47: + if ((active0 & 0x4L) != 0L) + return jjStopAtPos(1, 2); + break; + case 61: + if ((active0 & 0x4000L) != 0L) + return jjStopAtPos(1, 14); + else if ((active0 & 0x8000L) != 0L) + return jjStopAtPos(1, 15); + else if ((active0 & 0x10000L) != 0L) + return jjStopAtPos(1, 16); + else if ((active0 & 0x20000L) != 0L) + return jjStopAtPos(1, 17); + else if ((active0 & 0x40000L) != 0L) + return jjStopAtPos(1, 18); + else if ((active0 & 0x1000000000L) != 0L) + return jjStopAtPos(1, 36); + else if ((active0 & 0x8000000000L) != 0L) + return jjStopAtPos(1, 39); + break; + case 67: + case 99: + return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x2000000020L); + case 68: + case 100: + return jjMoveStringLiteralDfa2_0(active0, 0x800000000000000L, active1, 0L); + case 69: + case 101: + return jjMoveStringLiteralDfa2_0(active0, 0x4000000000000000L, active1, 0x6L); + case 70: + case 102: + if ((active1 & 0x80L) != 0L) + return jjStartNfaWithStates_0(1, 71, 518); + return jjMoveStringLiteralDfa2_0(active0, 0x2200000000000000L, active1, 0x8000000000L); + case 72: + case 104: + return jjMoveStringLiteralDfa2_0(active0, 0x10000000000000L, active1, 0L); + case 73: + case 105: + return jjMoveStringLiteralDfa2_0(active0, 0x100000000000000L, active1, 0x800000001L); + case 77: + case 109: + return jjMoveStringLiteralDfa2_0(active0, 0x80000000000000L, active1, 0x1000000000L); + case 78: + case 110: + if ((active0 & 0x20000000000000L) != 0L) + return jjStartNfaWithStates_0(1, 53, 518); + break; + case 79: + case 111: + if ((active0 & 0x8000000000000L) != 0L) + return jjStartNfaWithStates_0(1, 51, 518); + break; + case 80: + case 112: + return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x4000000000L); + case 82: + case 114: + return jjMoveStringLiteralDfa2_0(active0, 0x440000000000000L, active1, 0L); + case 83: + case 115: + return jjMoveStringLiteralDfa2_0(active0, 0L, active1, 0x10L); + case 87: + case 119: + return jjMoveStringLiteralDfa2_0(active0, 0x9000000000000000L, active1, 0L); + case 124: + if ((active0 & 0x2000000000L) != 0L) + return jjStopAtPos(1, 37); + break; + default : + break; + } + return jjStartNfa_0(0, active0, active1); +} +private int jjMoveStringLiteralDfa2_0(long old0, long active0, long old1, long active1) +{ + if (((active0 &= old0) | (active1 &= old1)) == 0L) + return jjStartNfa_0(0, old0, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(1, active0, active1); + return 2; + } + switch(curChar) + { + case 45: + return jjMoveStringLiteralDfa3_0(active0, 0x400L, active1, 0L); + case 62: + if ((active0 & 0x800L) != 0L) + return jjStopAtPos(2, 11); + break; + case 65: + case 97: + return jjMoveStringLiteralDfa3_0(active0, 0x5000000000000000L, active1, 0x4000000000L); + case 69: + case 101: + return jjMoveStringLiteralDfa3_0(active0, 0xc00000000000000L, active1, 0x1000000000L); + case 70: + case 102: + if ((active1 & 0x1L) != 0L) + return jjStartNfaWithStates_0(2, 64, 520); + break; + case 72: + case 104: + return jjMoveStringLiteralDfa3_0(active0, 0x8000000000000000L, active1, 0x2000000000L); + case 73: + case 105: + return jjMoveStringLiteralDfa3_0(active0, 0x80000000000000L, active1, 0L); + case 76: + case 108: + return jjMoveStringLiteralDfa3_0(active0, 0L, active1, 0x2L); + case 77: + case 109: + return jjMoveStringLiteralDfa3_0(active0, 0L, active1, 0x800000008L); + case 78: + case 110: + return jjMoveStringLiteralDfa3_0(active0, 0x100000000000000L, active1, 0L); + case 79: + case 111: + return jjMoveStringLiteralDfa3_0(active0, 0x2040000000000000L, active1, 0x8000000020L); + case 82: + case 114: + return jjMoveStringLiteralDfa3_0(active0, 0x10000000000000L, active1, 0L); + case 85: + case 117: + return jjMoveStringLiteralDfa3_0(active0, 0x200000000000000L, active1, 0x10L); + case 88: + case 120: + return jjMoveStringLiteralDfa3_0(active0, 0L, active1, 0x4L); + default : + break; + } + return jjStartNfa_0(1, active0, active1); +} +private int jjMoveStringLiteralDfa3_0(long old0, long active0, long old1, long active1) +{ + if (((active0 &= old0) | (active1 &= old1)) == 0L) + return jjStartNfa_0(1, old0, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(2, active0, active1); + return 3; + } + switch(curChar) + { + case 45: + if ((active0 & 0x400L) != 0L) + return jjStopAtPos(3, 10); + break; + case 65: + case 97: + return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x2000000000L); + case 66: + case 98: + return jjMoveStringLiteralDfa4_0(active0, 0x800000000000000L, active1, 0L); + case 67: + case 99: + return jjMoveStringLiteralDfa4_0(active0, 0x4100000000000000L, active1, 0L); + case 68: + case 100: + return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x1000000000L); + case 71: + case 103: + return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x4000000000L); + case 73: + case 105: + return jjMoveStringLiteralDfa4_0(active0, 0x8000000000000000L, active1, 0L); + case 77: + case 109: + if ((active0 & 0x40000000000000L) != 0L) + return jjStartNfaWithStates_0(3, 54, 518); + break; + case 78: + case 110: + return jjMoveStringLiteralDfa4_0(active0, 0x200000000000000L, active1, 0x8000000020L); + case 79: + case 111: + return jjMoveStringLiteralDfa4_0(active0, 0x10000000000000L, active1, 0x8L); + case 80: + case 112: + return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x800000010L); + case 82: + case 114: + if ((active0 & 0x2000000000000000L) != 0L) + return jjStartNfaWithStates_0(3, 61, 520); + return jjMoveStringLiteralDfa4_0(active0, 0x1000000000000000L, active1, 0L); + case 83: + case 115: + return jjMoveStringLiteralDfa4_0(active0, 0L, active1, 0x2L); + case 84: + case 116: + return jjMoveStringLiteralDfa4_0(active0, 0x400000000000000L, active1, 0x4L); + case 88: + case 120: + return jjMoveStringLiteralDfa4_0(active0, 0x80000000000000L, active1, 0L); + default : + break; + } + return jjStartNfa_0(2, active0, active1); +} +private int jjMoveStringLiteralDfa4_0(long old0, long active0, long old1, long active1) +{ + if (((active0 &= old0) | (active1 &= old1)) == 0L) + return jjStartNfa_0(2, old0, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(3, active0, active1); + return 4; + } + switch(curChar) + { + case 67: + case 99: + return jjMoveStringLiteralDfa5_0(active0, 0x200000000000000L, active1, 0L); + case 69: + case 101: + if ((active1 & 0x2L) != 0L) + return jjStartNfaWithStates_0(4, 65, 520); + else if ((active1 & 0x4000000000L) != 0L) + return jjStartNfaWithStates_0(4, 102, 520); + return jjMoveStringLiteralDfa5_0(active0, 0L, active1, 0x4L); + case 72: + case 104: + if ((active0 & 0x4000000000000000L) != 0L) + return jjStartNfaWithStates_0(4, 62, 520); + break; + case 73: + case 105: + return jjMoveStringLiteralDfa5_0(active0, 0x80000000000000L, active1, 0x1000000000L); + case 76: + case 108: + return jjMoveStringLiteralDfa5_0(active0, 0x8100000000000000L, active1, 0L); + case 78: + case 110: + if ((active0 & 0x1000000000000000L) != 0L) + return jjStartNfaWithStates_0(4, 60, 520); + break; + case 79: + case 111: + return jjMoveStringLiteralDfa5_0(active0, 0L, active1, 0x800000000L); + case 80: + case 112: + return jjMoveStringLiteralDfa5_0(active0, 0L, active1, 0x10L); + case 82: + case 114: + return jjMoveStringLiteralDfa5_0(active0, 0L, active1, 0x2000000000L); + case 84: + case 116: + return jjMoveStringLiteralDfa5_0(active0, 0L, active1, 0x8000000020L); + case 85: + case 117: + return jjMoveStringLiteralDfa5_0(active0, 0xc10000000000000L, active1, 0L); + case 90: + case 122: + return jjMoveStringLiteralDfa5_0(active0, 0L, active1, 0x8L); + default : + break; + } + return jjStartNfa_0(3, active0, active1); +} +private int jjMoveStringLiteralDfa5_0(long old0, long active0, long old1, long active1) +{ + if (((active0 &= old0) | (active1 &= old1)) == 0L) + return jjStartNfa_0(3, old0, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(4, active0, active1); + return 5; + } + switch(curChar) + { + case 45: + return jjMoveStringLiteralDfa6_0(active0, 0L, active1, 0x8000000008L); + case 65: + case 97: + if ((active1 & 0x1000000000L) != 0L) + return jjStartNfaWithStates_0(5, 100, 520); + break; + case 69: + case 101: + if ((active0 & 0x8000000000000000L) != 0L) + return jjStartNfaWithStates_0(5, 63, 520); + return jjMoveStringLiteralDfa6_0(active0, 0L, active1, 0x20L); + case 71: + case 103: + if ((active0 & 0x800000000000000L) != 0L) + return jjStartNfaWithStates_0(5, 59, 520); + return jjMoveStringLiteralDfa6_0(active0, 0x10000000000000L, active1, 0L); + case 78: + case 110: + if ((active0 & 0x80000000000000L) != 0L) + return jjStartNfaWithStates_0(5, 55, 520); + return jjMoveStringLiteralDfa6_0(active0, 0L, active1, 0x4L); + case 79: + case 111: + return jjMoveStringLiteralDfa6_0(active0, 0L, active1, 0x10L); + case 82: + case 114: + return jjMoveStringLiteralDfa6_0(active0, 0x400000000000000L, active1, 0x800000000L); + case 83: + case 115: + return jjMoveStringLiteralDfa6_0(active0, 0L, active1, 0x2000000000L); + case 84: + case 116: + return jjMoveStringLiteralDfa6_0(active0, 0x200000000000000L, active1, 0L); + case 85: + case 117: + return jjMoveStringLiteralDfa6_0(active0, 0x100000000000000L, active1, 0L); + default : + break; + } + return jjStartNfa_0(4, active0, active1); +} +private int jjMoveStringLiteralDfa6_0(long old0, long active0, long old1, long active1) +{ + if (((active0 &= old0) | (active1 &= old1)) == 0L) + return jjStartNfa_0(4, old0, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(5, active0, active1); + return 6; + } + switch(curChar) + { + case 68: + case 100: + if ((active1 & 0x4L) != 0L) + return jjStartNfaWithStates_0(6, 66, 520); + return jjMoveStringLiteralDfa7_0(active0, 0x100000000000000L, active1, 0x8L); + case 69: + case 101: + return jjMoveStringLiteralDfa7_0(active0, 0L, active1, 0x2000000000L); + case 70: + case 102: + return jjMoveStringLiteralDfa7_0(active0, 0L, active1, 0x8000000000L); + case 72: + case 104: + if ((active0 & 0x10000000000000L) != 0L) + return jjStartNfaWithStates_0(6, 52, 518); + break; + case 73: + case 105: + return jjMoveStringLiteralDfa7_0(active0, 0x200000000000000L, active1, 0L); + case 78: + case 110: + if ((active0 & 0x400000000000000L) != 0L) + return jjStartNfaWithStates_0(6, 58, 520); + return jjMoveStringLiteralDfa7_0(active0, 0L, active1, 0x20L); + case 82: + case 114: + return jjMoveStringLiteralDfa7_0(active0, 0L, active1, 0x10L); + case 84: + case 116: + if ((active1 & 0x800000000L) != 0L) + return jjStartNfaWithStates_0(6, 99, 520); + break; + default : + break; + } + return jjStartNfa_0(5, active0, active1); +} +private int jjMoveStringLiteralDfa7_0(long old0, long active0, long old1, long active1) +{ + if (((active0 &= old0) | (active1 &= old1)) == 0L) + return jjStartNfa_0(5, old0, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(6, active0, active1); + return 7; + } + switch(curChar) + { + case 65: + case 97: + return jjMoveStringLiteralDfa8_0(active0, 0L, active1, 0x8000000000L); + case 69: + case 101: + if ((active0 & 0x100000000000000L) != 0L) + return jjStartNfaWithStates_0(7, 56, 520); + break; + case 79: + case 111: + return jjMoveStringLiteralDfa8_0(active0, 0x200000000000000L, active1, 0x8L); + case 84: + case 116: + if ((active1 & 0x20L) != 0L) + return jjStartNfaWithStates_0(7, 69, 520); + else if ((active1 & 0x2000000000L) != 0L) + return jjStartNfaWithStates_0(7, 101, 520); + return jjMoveStringLiteralDfa8_0(active0, 0L, active1, 0x10L); + default : + break; + } + return jjStartNfa_0(6, active0, active1); +} +private int jjMoveStringLiteralDfa8_0(long old0, long active0, long old1, long active1) +{ + if (((active0 &= old0) | (active1 &= old1)) == 0L) + return jjStartNfa_0(6, old0, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(7, active0, active1); + return 8; + } + switch(curChar) + { + case 67: + case 99: + return jjMoveStringLiteralDfa9_0(active0, 0L, active1, 0x8000000008L); + case 78: + case 110: + if ((active0 & 0x200000000000000L) != 0L) + return jjStartNfaWithStates_0(8, 57, 520); + break; + case 83: + case 115: + if ((active1 & 0x10L) != 0L) + return jjStartNfaWithStates_0(8, 68, 520); + break; + default : + break; + } + return jjStartNfa_0(7, active0, active1); +} +private int jjMoveStringLiteralDfa9_0(long old0, long active0, long old1, long active1) +{ + if (((active0 &= old0) | (active1 &= old1)) == 0L) + return jjStartNfa_0(7, old0, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(8, 0L, active1); + return 9; + } + switch(curChar) + { + case 69: + case 101: + if ((active1 & 0x8000000000L) != 0L) + return jjStartNfaWithStates_0(9, 103, 520); + break; + case 85: + case 117: + return jjMoveStringLiteralDfa10_0(active1, 0x8L); + default : + break; + } + return jjStartNfa_0(8, 0L, active1); +} +private int jjMoveStringLiteralDfa10_0(long old1, long active1) +{ + if (((active1 &= old1)) == 0L) + return jjStartNfa_0(8, 0L, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(9, 0L, active1); + return 10; + } + switch(curChar) + { + case 77: + case 109: + return jjMoveStringLiteralDfa11_0(active1, 0x8L); + default : + break; + } + return jjStartNfa_0(9, 0L, active1); +} +private int jjMoveStringLiteralDfa11_0(long old1, long active1) +{ + if (((active1 &= old1)) == 0L) + return jjStartNfa_0(9, 0L, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(10, 0L, active1); + return 11; + } + switch(curChar) + { + case 69: + case 101: + return jjMoveStringLiteralDfa12_0(active1, 0x8L); + default : + break; + } + return jjStartNfa_0(10, 0L, active1); +} +private int jjMoveStringLiteralDfa12_0(long old1, long active1) +{ + if (((active1 &= old1)) == 0L) + return jjStartNfa_0(10, 0L, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(11, 0L, active1); + return 12; + } + switch(curChar) + { + case 78: + case 110: + return jjMoveStringLiteralDfa13_0(active1, 0x8L); + default : + break; + } + return jjStartNfa_0(11, 0L, active1); +} +private int jjMoveStringLiteralDfa13_0(long old1, long active1) +{ + if (((active1 &= old1)) == 0L) + return jjStartNfa_0(11, 0L, old1); + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + jjStopStringLiteralDfa_0(12, 0L, active1); + return 13; + } + switch(curChar) + { + case 84: + case 116: + if ((active1 & 0x8L) != 0L) + return jjStartNfaWithStates_0(13, 67, 520); + break; + default : + break; + } + return jjStartNfa_0(12, 0L, active1); +} +private int jjStartNfaWithStates_0(int pos, int kind, int state) +{ + jjmatchedKind = kind; + jjmatchedPos = pos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return pos + 1; } + return jjMoveNfa_0(state, pos + 1); +} +static final long[] jjbitVec0 = { + 0x0L, 0x0L, 0xffffffffffffffffL, 0xffffffffffffffffL +}; +private int jjMoveNfa_0(int startState, int curPos) +{ + int startsAt = 0; + jjnewStateCnt = 517; + int i = 1; + jjstateSet[0] = startState; + int kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + do + { + switch(jjstateSet[--i]) + { + case 520: + case 113: + if ((0x3ff200000000000L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + break; + case 166: + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 112; + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 217; + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 205; + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 189; + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 178; + break; + case 174: + if ((0x3ff200000000000L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + break; + case 4: + if ((0x3ff000000000000L & l) != 0L) + { + if (kind > 75) + kind = 75; + jjCheckNAddStates(0, 81); + } + else if ((0x100003600L & l) != 0L) + { + if (kind > 1) + kind = 1; + jjCheckNAdd(0); + } + else if (curChar == 46) + jjCheckNAddStates(82, 101); + else if (curChar == 45) + jjAddStates(102, 103); + else if (curChar == 33) + jjCheckNAddStates(104, 107); + else if (curChar == 35) + jjCheckNAddTwoStates(100, 101); + else if (curChar == 36) + jjCheckNAddStates(108, 111); + else if (curChar == 39) + jjCheckNAddStates(112, 115); + else if (curChar == 34) + jjCheckNAddStates(116, 119); + else if (curChar == 47) + jjstateSet[jjnewStateCnt++] = 3; + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 42; + else if (curChar == 35) + jjstateSet[jjnewStateCnt++] = 5; + break; + case 517: + if ((0x100003600L & l) != 0L) + jjCheckNAddTwoStates(251, 260); + if ((0x100003600L & l) != 0L) + jjCheckNAddTwoStates(243, 250); + break; + case 518: + if ((0x3ff200000000000L & l) != 0L) + jjCheckNAddStates(120, 123); + else if ((0x100003600L & l) != 0L) + jjCheckNAddTwoStates(231, 232); + else if (curChar == 40) + { + if (kind > 120) + kind = 120; + } + if ((0x3ff200000000000L & l) != 0L) + { + if (kind > 74) + kind = 74; + jjCheckNAddTwoStates(220, 221); + } + break; + case 175: + if ((0x3ff200000000000L & l) != 0L) + { + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + } + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 174; + break; + case 33: + if ((0x3ff200000000000L & l) != 0L) + jjCheckNAddStates(120, 123); + else if ((0x100003600L & l) != 0L) + jjCheckNAddTwoStates(231, 232); + else if (curChar == 40) + { + if (kind > 120) + kind = 120; + } + if ((0x3ff200000000000L & l) != 0L) + { + if (kind > 74) + kind = 74; + jjCheckNAddTwoStates(220, 221); + } + break; + case 176: + if ((0x3ff200000000000L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + break; + case 519: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(124, 128); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(322, 325); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(319, 321); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(317, 318); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(314, 316); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(309, 313); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(305, 308); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(301, 304); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(298, 300); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(294, 297); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(290, 293); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(287, 289); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(284, 286); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(281, 283); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(278, 280); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(275, 277); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(272, 274); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(269, 271); + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(267, 268); + if ((0x3ff000000000000L & l) != 0L) + { + if (kind > 75) + kind = 75; + jjCheckNAdd(266); + } + break; + case 177: + if ((0x3ff200000000000L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + break; + case 79: + if (curChar == 45) + jjCheckNAdd(80); + break; + case 0: + if ((0x100003600L & l) == 0L) + break; + if (kind > 1) + kind = 1; + jjCheckNAdd(0); + break; + case 1: + if (curChar == 42) + jjstateSet[jjnewStateCnt++] = 2; + break; + case 2: + if ((0xffff7fffffffffffL & l) != 0L && kind > 5) + kind = 5; + break; + case 3: + if (curChar == 42) + jjstateSet[jjnewStateCnt++] = 1; + break; + case 6: + if (curChar == 36) + jjCheckNAddStates(129, 132); + break; + case 7: + if (curChar == 45) + jjCheckNAdd(8); + break; + case 9: + if ((0x3ff200000000000L & l) != 0L) + jjCheckNAddStates(133, 135); + break; + case 12: + if ((0xffffffff00000000L & l) != 0L) + jjCheckNAddStates(133, 135); + break; + case 13: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(136, 140); + break; + case 14: + if ((0x100003600L & l) != 0L) + jjCheckNAddStates(133, 135); + break; + case 15: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(141, 148); + break; + case 16: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(149, 152); + break; + case 17: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(153, 157); + break; + case 18: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(158, 163); + break; + case 19: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(164, 170); + break; + case 22: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(171, 175); + break; + case 23: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(176, 183); + break; + case 24: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(184, 187); + break; + case 25: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(188, 192); + break; + case 26: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(193, 198); + break; + case 27: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(199, 205); + break; + case 28: + if (curChar == 35) + jjstateSet[jjnewStateCnt++] = 5; + break; + case 40: + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 39; + break; + case 43: + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 42; + break; + case 44: + if (curChar == 34) + jjCheckNAddStates(116, 119); + break; + case 45: + if ((0xfffffffb00000200L & l) != 0L) + jjCheckNAddStates(116, 119); + break; + case 46: + if (curChar == 34 && kind > 73) + kind = 73; + break; + case 48: + if (curChar == 12) + jjCheckNAddStates(116, 119); + break; + case 50: + if ((0xffffffff00000000L & l) != 0L) + jjCheckNAddStates(116, 119); + break; + case 51: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(206, 211); + break; + case 52: + if ((0x100003600L & l) != 0L) + jjCheckNAddStates(116, 119); + break; + case 53: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(212, 220); + break; + case 54: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(221, 225); + break; + case 55: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(226, 231); + break; + case 56: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(232, 238); + break; + case 57: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(239, 246); + break; + case 58: + if (curChar == 13) + jjCheckNAddStates(116, 119); + break; + case 59: + if (curChar == 10) + jjCheckNAddStates(116, 119); + break; + case 60: + if (curChar == 13) + jjstateSet[jjnewStateCnt++] = 59; + break; + case 61: + if (curChar == 39) + jjCheckNAddStates(112, 115); + break; + case 62: + if ((0xffffff7f00000200L & l) != 0L) + jjCheckNAddStates(112, 115); + break; + case 63: + if (curChar == 39 && kind > 73) + kind = 73; + break; + case 65: + if (curChar == 12) + jjCheckNAddStates(112, 115); + break; + case 67: + if ((0xffffffff00000000L & l) != 0L) + jjCheckNAddStates(112, 115); + break; + case 68: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(247, 252); + break; + case 69: + if ((0x100003600L & l) != 0L) + jjCheckNAddStates(112, 115); + break; + case 70: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(253, 261); + break; + case 71: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(262, 266); + break; + case 72: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(267, 272); + break; + case 73: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(273, 279); + break; + case 74: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(280, 287); + break; + case 75: + if (curChar == 13) + jjCheckNAddStates(112, 115); + break; + case 76: + if (curChar == 10) + jjCheckNAddStates(112, 115); + break; + case 77: + if (curChar == 13) + jjstateSet[jjnewStateCnt++] = 76; + break; + case 78: + if (curChar == 36) + jjCheckNAddStates(108, 111); + break; + case 81: + if ((0x3ff200000000000L & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddTwoStates(81, 82); + break; + case 83: + if ((0xffffffff00000000L & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddTwoStates(81, 82); + break; + case 84: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(288, 291); + break; + case 85: + if ((0x100003600L & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddTwoStates(81, 82); + break; + case 86: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(292, 298); + break; + case 87: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(299, 301); + break; + case 88: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(302, 305); + break; + case 89: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(306, 310); + break; + case 90: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(311, 316); + break; + case 93: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(317, 320); + break; + case 94: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(321, 327); + break; + case 95: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(328, 330); + break; + case 96: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(331, 334); + break; + case 97: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(335, 339); + break; + case 98: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(340, 345); + break; + case 99: + if (curChar == 35) + jjCheckNAddTwoStates(100, 101); + break; + case 100: + if ((0x3ff200000000000L & l) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddTwoStates(100, 101); + break; + case 102: + if ((0xffffffff00000000L & l) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddTwoStates(100, 101); + break; + case 103: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddStates(346, 349); + break; + case 104: + if ((0x100003600L & l) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddTwoStates(100, 101); + break; + case 105: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddStates(350, 356); + break; + case 106: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddStates(357, 359); + break; + case 107: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddStates(360, 363); + break; + case 108: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddStates(364, 368); + break; + case 109: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddStates(369, 374); + break; + case 111: + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 112; + break; + case 115: + if ((0xffffffff00000000L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + break; + case 116: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(375, 378); + break; + case 117: + if ((0x100003600L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + break; + case 118: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(379, 385); + break; + case 119: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(386, 388); + break; + case 120: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(389, 392); + break; + case 121: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(393, 397); + break; + case 122: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(398, 403); + break; + case 125: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(404, 407); + break; + case 126: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(408, 414); + break; + case 127: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(415, 417); + break; + case 128: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(418, 421); + break; + case 129: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(422, 426); + break; + case 130: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(427, 432); + break; + case 132: + if ((0x100003600L & l) != 0L) + jjAddStates(433, 434); + break; + case 133: + if (curChar == 40 && kind > 117) + kind = 117; + break; + case 140: + if ((0x100003600L & l) != 0L) + jjAddStates(435, 436); + break; + case 141: + if (curChar == 40 && kind > 118) + kind = 118; + break; + case 148: + if ((0x100003600L & l) != 0L) + jjAddStates(437, 438); + break; + case 149: + if (curChar == 40 && kind > 119) + kind = 119; + break; + case 179: + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 178; + break; + case 188: + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 187; + break; + case 190: + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 189; + break; + case 199: + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 198; + break; + case 206: + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 205; + break; + case 215: + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 214; + break; + case 218: + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 217; + break; + case 220: + if ((0x3ff200000000000L & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddTwoStates(220, 221); + break; + case 222: + if ((0xffffffff00000000L & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddTwoStates(220, 221); + break; + case 223: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(439, 442); + break; + case 224: + if ((0x100003600L & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddTwoStates(220, 221); + break; + case 225: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(443, 449); + break; + case 226: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(450, 452); + break; + case 227: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(453, 456); + break; + case 228: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(457, 461); + break; + case 229: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(462, 467); + break; + case 230: + if ((0x3ff200000000000L & l) != 0L) + jjCheckNAddStates(120, 123); + break; + case 231: + if ((0x100003600L & l) != 0L) + jjCheckNAddTwoStates(231, 232); + break; + case 232: + if (curChar == 40 && kind > 120) + kind = 120; + break; + case 234: + if ((0xffffffff00000000L & l) != 0L) + jjCheckNAddStates(120, 123); + break; + case 235: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(468, 472); + break; + case 236: + if ((0x100003600L & l) != 0L) + jjCheckNAddStates(120, 123); + break; + case 237: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(473, 480); + break; + case 238: + case 452: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(481, 484); + break; + case 239: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(485, 489); + break; + case 240: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(490, 495); + break; + case 241: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(496, 502); + break; + case 242: + if (curChar == 33) + jjCheckNAddStates(104, 107); + break; + case 243: + if ((0x100003600L & l) != 0L) + jjCheckNAddTwoStates(243, 250); + break; + case 251: + if ((0x100003600L & l) != 0L) + jjCheckNAddTwoStates(251, 260); + break; + case 261: + if (curChar == 45) + jjAddStates(102, 103); + break; + case 265: + if (curChar == 46) + jjCheckNAddStates(82, 101); + break; + case 266: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 75) + kind = 75; + jjCheckNAdd(266); + break; + case 267: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(267, 268); + break; + case 268: + if (curChar == 37 && kind > 79) + kind = 79; + break; + case 269: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(269, 271); + break; + case 272: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(272, 274); + break; + case 275: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(275, 277); + break; + case 278: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(278, 280); + break; + case 281: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(281, 283); + break; + case 284: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(284, 286); + break; + case 287: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(287, 289); + break; + case 290: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(290, 293); + break; + case 294: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(294, 297); + break; + case 298: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(298, 300); + break; + case 301: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(301, 304); + break; + case 305: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(305, 308); + break; + case 309: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(309, 313); + break; + case 314: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(314, 316); + break; + case 317: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(317, 318); + break; + case 319: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(319, 321); + break; + case 322: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(322, 325); + break; + case 326: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(124, 128); + break; + case 327: + if (curChar == 45) + jjCheckNAdd(328); + break; + case 329: + if ((0x3ff200000000000L & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddTwoStates(329, 330); + break; + case 331: + if ((0xffffffff00000000L & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddTwoStates(329, 330); + break; + case 332: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(503, 506); + break; + case 333: + if ((0x100003600L & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddTwoStates(329, 330); + break; + case 334: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(507, 513); + break; + case 335: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(514, 516); + break; + case 336: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(517, 520); + break; + case 337: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(521, 525); + break; + case 338: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(526, 531); + break; + case 341: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(532, 535); + break; + case 342: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(536, 542); + break; + case 343: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(543, 545); + break; + case 344: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(546, 549); + break; + case 345: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(550, 554); + break; + case 346: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(555, 560); + break; + case 348: + if (curChar == 40) + jjCheckNAddStates(561, 566); + break; + case 349: + if ((0xfffffc7a00000000L & l) != 0L) + jjCheckNAddStates(567, 570); + break; + case 350: + if ((0x100003600L & l) != 0L) + jjCheckNAddTwoStates(350, 351); + break; + case 351: + if (curChar == 41 && kind > 77) + kind = 77; + break; + case 353: + if ((0xffffffff00000000L & l) != 0L) + jjCheckNAddStates(567, 570); + break; + case 354: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(571, 575); + break; + case 355: + if ((0x100003600L & l) != 0L) + jjCheckNAddStates(567, 570); + break; + case 356: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(576, 583); + break; + case 357: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(584, 587); + break; + case 358: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(588, 592); + break; + case 359: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(593, 598); + break; + case 360: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(599, 605); + break; + case 361: + if (curChar == 39) + jjCheckNAddStates(606, 609); + break; + case 362: + if ((0xffffff7f00000200L & l) != 0L) + jjCheckNAddStates(606, 609); + break; + case 363: + if (curChar == 39) + jjCheckNAddTwoStates(350, 351); + break; + case 365: + if (curChar == 12) + jjCheckNAddStates(606, 609); + break; + case 367: + if ((0xffffffff00000000L & l) != 0L) + jjCheckNAddStates(606, 609); + break; + case 368: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(610, 615); + break; + case 369: + if ((0x100003600L & l) != 0L) + jjCheckNAddStates(606, 609); + break; + case 370: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(616, 624); + break; + case 371: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(625, 629); + break; + case 372: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(630, 635); + break; + case 373: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(636, 642); + break; + case 374: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(643, 650); + break; + case 375: + if (curChar == 13) + jjCheckNAddStates(606, 609); + break; + case 376: + if (curChar == 10) + jjCheckNAddStates(606, 609); + break; + case 377: + if (curChar == 13) + jjstateSet[jjnewStateCnt++] = 376; + break; + case 378: + if (curChar == 34) + jjCheckNAddStates(651, 654); + break; + case 379: + if ((0xfffffffb00000200L & l) != 0L) + jjCheckNAddStates(651, 654); + break; + case 380: + if (curChar == 34) + jjCheckNAddTwoStates(350, 351); + break; + case 382: + if (curChar == 12) + jjCheckNAddStates(651, 654); + break; + case 384: + if ((0xffffffff00000000L & l) != 0L) + jjCheckNAddStates(651, 654); + break; + case 385: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(655, 660); + break; + case 386: + if ((0x100003600L & l) != 0L) + jjCheckNAddStates(651, 654); + break; + case 387: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(661, 669); + break; + case 388: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(670, 674); + break; + case 389: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(675, 680); + break; + case 390: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(681, 687); + break; + case 391: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(688, 695); + break; + case 392: + if (curChar == 13) + jjCheckNAddStates(651, 654); + break; + case 393: + if (curChar == 10) + jjCheckNAddStates(651, 654); + break; + case 394: + if (curChar == 13) + jjstateSet[jjnewStateCnt++] = 393; + break; + case 395: + if ((0x100003600L & l) != 0L) + jjCheckNAddStates(696, 702); + break; + case 398: + if (curChar == 43) + jjAddStates(703, 704); + break; + case 399: + if (curChar != 63) + break; + if (kind > 116) + kind = 116; + jjstateSet[jjnewStateCnt++] = 400; + break; + case 400: + if (curChar != 63) + break; + if (kind > 116) + kind = 116; + jjCheckNAddStates(705, 708); + break; + case 401: + if (curChar == 63 && kind > 116) + kind = 116; + break; + case 402: + case 417: + case 421: + case 424: + case 427: + if (curChar != 63) + break; + if (kind > 116) + kind = 116; + jjCheckNAdd(401); + break; + case 403: + if (curChar != 63) + break; + if (kind > 116) + kind = 116; + jjCheckNAddTwoStates(401, 402); + break; + case 404: + if (curChar != 63) + break; + if (kind > 116) + kind = 116; + jjCheckNAddStates(709, 711); + break; + case 405: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjAddStates(712, 717); + break; + case 406: + if ((0x3ff000000000000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 407; + break; + case 407: + if ((0x3ff000000000000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 408; + break; + case 408: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAdd(409); + break; + case 409: + if ((0x3ff000000000000L & l) != 0L && kind > 116) + kind = 116; + break; + case 410: + if ((0x3ff000000000000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 411; + break; + case 411: + if ((0x3ff000000000000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 412; + break; + case 412: + if ((0x3ff000000000000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 413; + break; + case 413: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjCheckNAdd(401); + break; + case 414: + if ((0x3ff000000000000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 415; + break; + case 415: + if ((0x3ff000000000000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 416; + break; + case 416: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjstateSet[jjnewStateCnt++] = 417; + break; + case 418: + if ((0x3ff000000000000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 419; + break; + case 419: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjstateSet[jjnewStateCnt++] = 420; + break; + case 420: + if (curChar != 63) + break; + if (kind > 116) + kind = 116; + jjCheckNAddTwoStates(401, 421); + break; + case 422: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjstateSet[jjnewStateCnt++] = 423; + break; + case 423: + if (curChar != 63) + break; + if (kind > 116) + kind = 116; + jjCheckNAddStates(718, 720); + break; + case 425: + if (curChar != 63) + break; + if (kind > 116) + kind = 116; + jjCheckNAddTwoStates(401, 424); + break; + case 426: + if (curChar != 63) + break; + if (kind > 116) + kind = 116; + jjCheckNAddStates(721, 724); + break; + case 428: + if (curChar != 63) + break; + if (kind > 116) + kind = 116; + jjCheckNAddTwoStates(401, 427); + break; + case 429: + if (curChar != 63) + break; + if (kind > 116) + kind = 116; + jjCheckNAddStates(725, 727); + break; + case 430: + if (curChar == 43) + jjstateSet[jjnewStateCnt++] = 431; + break; + case 431: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(432, 438); + break; + case 432: + if (curChar == 45) + jjstateSet[jjnewStateCnt++] = 433; + break; + case 433: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjstateSet[jjnewStateCnt++] = 434; + break; + case 434: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjCheckNAddStates(728, 731); + break; + case 435: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjCheckNAdd(409); + break; + case 436: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjCheckNAddTwoStates(409, 435); + break; + case 437: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjCheckNAddStates(732, 734); + break; + case 438: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(735, 739); + break; + case 439: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAdd(432); + break; + case 440: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(439, 432); + break; + case 441: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(740, 742); + break; + case 442: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(743, 746); + break; + case 444: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(747, 750); + break; + case 445: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(751, 757); + break; + case 446: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(758, 760); + break; + case 447: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(761, 764); + break; + case 448: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(765, 769); + break; + case 449: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(770, 775); + break; + case 450: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(776, 780); + break; + case 451: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(781, 788); + break; + case 453: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(789, 793); + break; + case 454: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(794, 799); + break; + case 455: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(800, 806); + break; + case 456: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 75) + kind = 75; + jjCheckNAddStates(0, 81); + break; + case 457: + if ((0x3ff000000000000L & l) == 0L) + break; + if (kind > 75) + kind = 75; + jjCheckNAdd(457); + break; + case 458: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(458, 459); + break; + case 459: + if (curChar == 46) + jjCheckNAdd(266); + break; + case 460: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(460, 268); + break; + case 461: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(461, 462); + break; + case 462: + if (curChar == 46) + jjCheckNAdd(267); + break; + case 463: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(463, 271); + break; + case 464: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(464, 465); + break; + case 465: + if (curChar == 46) + jjCheckNAdd(269); + break; + case 466: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(466, 274); + break; + case 467: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(467, 468); + break; + case 468: + if (curChar == 46) + jjCheckNAdd(272); + break; + case 469: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(469, 277); + break; + case 470: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(470, 471); + break; + case 471: + if (curChar == 46) + jjCheckNAdd(275); + break; + case 472: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(472, 280); + break; + case 473: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(473, 474); + break; + case 474: + if (curChar == 46) + jjCheckNAdd(278); + break; + case 475: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(475, 283); + break; + case 476: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(476, 477); + break; + case 477: + if (curChar == 46) + jjCheckNAdd(281); + break; + case 478: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(478, 286); + break; + case 479: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(479, 480); + break; + case 480: + if (curChar == 46) + jjCheckNAdd(284); + break; + case 481: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(481, 289); + break; + case 482: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(482, 483); + break; + case 483: + if (curChar == 46) + jjCheckNAdd(287); + break; + case 484: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(484, 293); + break; + case 485: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(485, 486); + break; + case 486: + if (curChar == 46) + jjCheckNAdd(290); + break; + case 487: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(487, 297); + break; + case 488: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(488, 489); + break; + case 489: + if (curChar == 46) + jjCheckNAdd(294); + break; + case 490: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(490, 300); + break; + case 491: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(491, 492); + break; + case 492: + if (curChar == 46) + jjCheckNAdd(298); + break; + case 493: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(493, 304); + break; + case 494: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(494, 495); + break; + case 495: + if (curChar == 46) + jjCheckNAdd(301); + break; + case 496: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(496, 308); + break; + case 497: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(497, 498); + break; + case 498: + if (curChar == 46) + jjCheckNAdd(305); + break; + case 499: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(499, 313); + break; + case 500: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(500, 501); + break; + case 501: + if (curChar == 46) + jjCheckNAdd(309); + break; + case 502: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(502, 316); + break; + case 503: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(503, 504); + break; + case 504: + if (curChar == 46) + jjCheckNAdd(314); + break; + case 505: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(505, 318); + break; + case 506: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(506, 507); + break; + case 507: + if (curChar == 46) + jjCheckNAdd(317); + break; + case 508: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(508, 321); + break; + case 509: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(509, 510); + break; + case 510: + if (curChar == 46) + jjCheckNAdd(319); + break; + case 511: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(511, 325); + break; + case 512: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(512, 513); + break; + case 513: + if (curChar == 46) + jjCheckNAdd(322); + break; + case 514: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddStates(807, 811); + break; + case 515: + if ((0x3ff000000000000L & l) != 0L) + jjCheckNAddTwoStates(515, 516); + break; + case 516: + if (curChar == 46) + jjCheckNAdd(326); + break; + default : break; } - if (curChar < 64) { - long l = 1L << curChar; - do { - switch (jjstateSet[--i]) { - case 0: - if ((0xffffffffffffdbffL & l) != 0L) { - if (kind > 3) { - kind = 3; - } - } else if ((0x2400L & l) != 0L) { - if (kind > 4) { - kind = 4; - } - } - if (curChar == 13) { - jjstateSet[jjnewStateCnt++] = 2; - } - break; - case 1: - if ((0x2400L & l) != 0L && kind > 4) { - kind = 4; - } - break; - case 2: - if (curChar == 10 && kind > 4) { - kind = 4; - } - break; - case 3: - if (curChar == 13) { - jjstateSet[jjnewStateCnt++] = 2; - } - break; - default: - break; - } - } while (i != startsAt); - } else if (curChar < 128) { - long l = 1L << (curChar & 077); - do { - switch (jjstateSet[--i]) { - case 0: - kind = 3; - break; - default: - break; - } - } while (i != startsAt); - } else { - int i2 = (curChar & 0xff) >> 6; - long l2 = 1L << (curChar & 077); - do { - switch (jjstateSet[--i]) { - case 0: - if ((jjbitVec0[i2] & l2) != 0L && kind > 3) { - kind = 3; - } - break; - default: - break; - } - } while (i != startsAt); + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 520: + if ((0x7fffffe87fffffeL & l) != 0L) + { + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + } + else if (curChar == 92) + jjCheckNAddTwoStates(115, 116); + break; + case 166: + if ((0x7fffffe07fffffeL & l) != 0L) + { + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + } + else if (curChar == 92) + jjCheckNAddTwoStates(115, 125); + if ((0x80000000800L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 165; + break; + case 174: + if ((0x7fffffe87fffffeL & l) != 0L) + { + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + } + else if (curChar == 92) + jjCheckNAddTwoStates(115, 116); + if ((0x80000000800L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 173; + break; + case 4: + if ((0x7fffffe07fffffeL & l) != 0L) + { + if (kind > 74) + kind = 74; + jjCheckNAddStates(812, 817); + } + else if (curChar == 92) + jjCheckNAddStates(818, 821); + else if (curChar == 64) + jjAddStates(822, 826); + if ((0x20000000200000L & l) != 0L) + jjAddStates(827, 829); + else if ((0x800000008L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 155; + else if ((0x200000002L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 145; + else if ((0x4000000040000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 137; + else if ((0x4000000040L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 33; + else if (curChar == 64) + jjAddStates(830, 833); + break; + case 517: + if ((0x20000000200L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 259; + else if ((0x1000000010L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 249; + break; + case 178: + if ((0x7fffffe07fffffeL & l) != 0L) + { + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + } + if ((0x200000002000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 216; + else if ((0x80000000800000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 204; + else if ((0x800000008000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 188; + if ((0x200000002000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 177; + break; + case 518: + if ((0x7fffffe87fffffeL & l) != 0L) + jjCheckNAddStates(120, 123); + else if (curChar == 92) + jjCheckNAddTwoStates(222, 223); + if ((0x7fffffe87fffffeL & l) != 0L) + { + if (kind > 74) + kind = 74; + jjCheckNAddTwoStates(220, 221); + } + else if (curChar == 92) + jjCheckNAddTwoStates(234, 235); + break; + case 175: + if ((0x7fffffe87fffffeL & l) != 0L) + { + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + } + else if (curChar == 92) + jjCheckNAddTwoStates(115, 116); + break; + case 33: + if ((0x7fffffe87fffffeL & l) != 0L) + jjCheckNAddStates(120, 123); + else if (curChar == 92) + jjCheckNAddTwoStates(222, 223); + if ((0x7fffffe87fffffeL & l) != 0L) + { + if (kind > 74) + kind = 74; + jjCheckNAddTwoStates(220, 221); + } + else if (curChar == 92) + jjCheckNAddTwoStates(234, 235); + if ((0x20000000200L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 32; + break; + case 176: + if ((0x7fffffe87fffffeL & l) != 0L) + { + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + } + else if (curChar == 92) + jjCheckNAddTwoStates(115, 116); + if ((0x400000004000000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 175; + break; + case 42: + if ((0x7fffffe07fffffeL & l) != 0L) + jjCheckNAddStates(120, 123); + if ((0x7fffffe07fffffeL & l) != 0L) + { + if (kind > 74) + kind = 74; + jjCheckNAddTwoStates(220, 221); + } + if ((0x200000002000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 41; + break; + case 177: + if ((0x7fffffe87fffffeL & l) != 0L) + { + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + } + else if (curChar == 92) + jjCheckNAddTwoStates(115, 116); + if ((0x8000000080000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 215; + else if ((0x800000008000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 176; + break; + case 79: + if ((0x7fffffe07fffffeL & l) != 0L) + { + if (kind > 78) + kind = 78; + jjCheckNAddTwoStates(81, 82); + } + else if (curChar == 92) + jjCheckNAddTwoStates(83, 93); + break; + case 2: + if (kind > 5) + kind = 5; + break; + case 5: + if (curChar == 123) + jjstateSet[jjnewStateCnt++] = 6; + break; + case 8: + if ((0x7fffffe07fffffeL & l) != 0L) + jjCheckNAddStates(133, 135); + break; + case 9: + if ((0x7fffffe87fffffeL & l) != 0L) + jjCheckNAddStates(133, 135); + break; + case 10: + if (curChar == 125 && kind > 41) + kind = 41; + break; + case 11: + if (curChar == 92) + jjCheckNAddTwoStates(12, 13); + break; + case 12: + if ((0x7fffffffffffffffL & l) != 0L) + jjCheckNAddStates(133, 135); + break; + case 13: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(136, 140); + break; + case 15: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(141, 148); + break; + case 16: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(149, 152); + break; + case 17: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(153, 157); + break; + case 18: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(158, 163); + break; + case 19: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(164, 170); + break; + case 21: + if (curChar == 92) + jjCheckNAddTwoStates(12, 22); + break; + case 22: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(171, 175); + break; + case 23: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(176, 183); + break; + case 24: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(184, 187); + break; + case 25: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(188, 192); + break; + case 26: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(193, 198); + break; + case 27: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(199, 205); + break; + case 29: + if ((0x4000000040000L & l) != 0L && kind > 70) + kind = 70; + break; + case 30: + case 35: + if ((0x2000000020L & l) != 0L) + jjCheckNAdd(29); + break; + case 31: + if ((0x10000000100000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 30; + break; + case 32: + if ((0x100000001000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 31; + break; + case 34: + if ((0x4000000040L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 33; + break; + case 36: + if ((0x10000000100000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 35; + break; + case 37: + if ((0x100000001000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 36; + break; + case 38: + if ((0x20000000200L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 37; + break; + case 39: + if ((0x4000000040L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 38; + break; + case 41: + if ((0x8000000080000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 40; + break; + case 45: + case 50: + if ((0x7fffffffffffffffL & l) != 0L) + jjCheckNAddStates(116, 119); + break; + case 47: + if (curChar == 92) + jjAddStates(834, 837); + break; + case 49: + if (curChar == 92) + jjAddStates(838, 839); + break; + case 51: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(206, 211); + break; + case 53: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(212, 220); + break; + case 54: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(221, 225); + break; + case 55: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(226, 231); + break; + case 56: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(232, 238); + break; + case 57: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(239, 246); + break; + case 62: + case 67: + if ((0x7fffffffffffffffL & l) != 0L) + jjCheckNAddStates(112, 115); + break; + case 64: + if (curChar == 92) + jjAddStates(840, 843); + break; + case 66: + if (curChar == 92) + jjAddStates(844, 845); + break; + case 68: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(247, 252); + break; + case 70: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(253, 261); + break; + case 71: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(262, 266); + break; + case 72: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(267, 272); + break; + case 73: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(273, 279); + break; + case 74: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(280, 287); + break; + case 80: + if ((0x7fffffe07fffffeL & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddTwoStates(81, 82); + break; + case 81: + if ((0x7fffffe87fffffeL & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddTwoStates(81, 82); + break; + case 82: + if (curChar == 92) + jjCheckNAddTwoStates(83, 84); + break; + case 83: + if ((0x7fffffffffffffffL & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddTwoStates(81, 82); + break; + case 84: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(288, 291); + break; + case 86: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(292, 298); + break; + case 87: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(299, 301); + break; + case 88: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(302, 305); + break; + case 89: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(306, 310); + break; + case 90: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(311, 316); + break; + case 92: + if (curChar == 92) + jjCheckNAddTwoStates(83, 93); + break; + case 93: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(317, 320); + break; + case 94: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(321, 327); + break; + case 95: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(328, 330); + break; + case 96: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(331, 334); + break; + case 97: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(335, 339); + break; + case 98: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddStates(340, 345); + break; + case 100: + if ((0x7fffffe87fffffeL & l) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddTwoStates(100, 101); + break; + case 101: + if (curChar == 92) + jjAddStates(846, 847); + break; + case 102: + if ((0x7fffffffffffffffL & l) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddTwoStates(100, 101); + break; + case 103: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddStates(346, 349); + break; + case 105: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddStates(350, 356); + break; + case 106: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddStates(357, 359); + break; + case 107: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddStates(360, 363); + break; + case 108: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddStates(364, 368); + break; + case 109: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddStates(369, 374); + break; + case 110: + if (curChar == 64) + jjAddStates(830, 833); + break; + case 112: + if ((0x7fffffe07fffffeL & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + break; + case 113: + if ((0x7fffffe87fffffeL & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + break; + case 114: + if (curChar == 92) + jjCheckNAddTwoStates(115, 116); + break; + case 115: + if ((0x7fffffffffffffffL & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + break; + case 116: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(375, 378); + break; + case 118: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(379, 385); + break; + case 119: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(386, 388); + break; + case 120: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(389, 392); + break; + case 121: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(393, 397); + break; + case 122: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(398, 403); + break; + case 124: + if (curChar == 92) + jjCheckNAddTwoStates(115, 125); + break; + case 125: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(404, 407); + break; + case 126: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(408, 414); + break; + case 127: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(415, 417); + break; + case 128: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(418, 421); + break; + case 129: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(422, 426); + break; + case 130: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddStates(427, 432); + break; + case 131: + if ((0x2000000020L & l) != 0L) + jjAddStates(433, 434); + break; + case 134: + if ((0x40000000400000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 131; + break; + case 135: + if ((0x800000008000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 134; + break; + case 136: + if ((0x200000002000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 135; + break; + case 137: + if ((0x2000000020L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 136; + break; + case 138: + if ((0x4000000040000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 137; + break; + case 139: + if ((0x1000000010L & l) != 0L) + jjAddStates(435, 436); + break; + case 142: + if ((0x400000004000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 139; + break; + case 143: + if ((0x2000000020L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 142; + break; + case 144: + if ((0x1000000010000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 143; + break; + case 145: + if ((0x1000000010000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 144; + break; + case 146: + if ((0x200000002L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 145; + break; + case 147: + if ((0x8000000080000L & l) != 0L) + jjAddStates(437, 438); + break; + case 150: + if ((0x400000004000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 147; + break; + case 151: + if ((0x20000000200L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 150; + break; + case 152: + if ((0x200000002L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 151; + break; + case 153: + if ((0x10000000100000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 152; + break; + case 154: + if ((0x400000004000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 153; + break; + case 155: + if ((0x800000008000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 154; + break; + case 156: + if ((0x800000008L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 155; + break; + case 157: + if (curChar == 64) + jjAddStates(822, 826); + break; + case 158: + if ((0x8000000080000L & l) != 0L && kind > 104) + kind = 104; + break; + case 159: + case 167: + case 180: + case 191: + case 207: + if ((0x2000000020L & l) != 0L) + jjCheckNAdd(158); + break; + case 160: + if ((0x200000002000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 159; + break; + case 161: + if ((0x200000002L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 160; + break; + case 162: + if ((0x4000000040000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 161; + break; + case 163: + if ((0x4000000040L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 162; + break; + case 164: + if ((0x200000002000000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 163; + break; + case 165: + if ((0x2000000020L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 164; + break; + case 168: + if ((0x200000002000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 167; + break; + case 169: + if ((0x200000002L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 168; + break; + case 170: + if ((0x4000000040000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 169; + break; + case 171: + if ((0x4000000040L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 170; + break; + case 172: + if ((0x200000002000000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 171; + break; + case 173: + if ((0x2000000020L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 172; + break; + case 181: + if ((0x200000002000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 180; + break; + case 182: + if ((0x200000002L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 181; + break; + case 183: + if ((0x4000000040000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 182; + break; + case 184: + if ((0x4000000040L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 183; + break; + case 185: + if ((0x200000002000000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 184; + break; + case 186: + if ((0x2000000020L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 185; + break; + case 187: + if ((0x80000000800L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 186; + break; + case 189: + if ((0x800000008000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 188; + break; + case 192: + if ((0x200000002000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 191; + break; + case 193: + if ((0x200000002L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 192; + break; + case 194: + if ((0x4000000040000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 193; + break; + case 195: + if ((0x4000000040L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 194; + break; + case 196: + if ((0x200000002000000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 195; + break; + case 197: + if ((0x2000000020L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 196; + break; + case 198: + if ((0x80000000800L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 197; + break; + case 200: + if ((0x10000000100000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 199; + break; + case 201: + if ((0x20000000200L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 200; + break; + case 202: + if ((0x80000000800L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 201; + break; + case 203: + if ((0x400000004L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 202; + break; + case 204: + if ((0x2000000020L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 203; + break; + case 205: + if ((0x80000000800000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 204; + break; + case 208: + if ((0x200000002000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 207; + break; + case 209: + if ((0x200000002L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 208; + break; + case 210: + if ((0x4000000040000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 209; + break; + case 211: + if ((0x4000000040L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 210; + break; + case 212: + if ((0x200000002000000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 211; + break; + case 213: + if ((0x2000000020L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 212; + break; + case 214: + if ((0x80000000800L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 213; + break; + case 216: + if ((0x8000000080000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 215; + break; + case 217: + if ((0x200000002000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 216; + break; + case 220: + if ((0x7fffffe87fffffeL & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddTwoStates(220, 221); + break; + case 221: + if (curChar == 92) + jjCheckNAddTwoStates(222, 223); + break; + case 222: + if ((0x7fffffffffffffffL & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddTwoStates(220, 221); + break; + case 223: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(439, 442); + break; + case 225: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(443, 449); + break; + case 226: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(450, 452); + break; + case 227: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(453, 456); + break; + case 228: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(457, 461); + break; + case 229: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(462, 467); + break; + case 230: + if ((0x7fffffe87fffffeL & l) != 0L) + jjCheckNAddStates(120, 123); + break; + case 233: + if (curChar == 92) + jjCheckNAddTwoStates(234, 235); + break; + case 234: + if ((0x7fffffffffffffffL & l) != 0L) + jjCheckNAddStates(120, 123); + break; + case 235: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(468, 472); + break; + case 237: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(473, 480); + break; + case 238: + case 452: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(481, 484); + break; + case 239: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(485, 489); + break; + case 240: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(490, 495); + break; + case 241: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(496, 502); + break; + case 244: + if ((0x10000000100000L & l) != 0L && kind > 72) + kind = 72; + break; + case 245: + if ((0x100000001000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 244; + break; + case 246: + if ((0x20000000200000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 245; + break; + case 247: + if ((0x200000002L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 246; + break; + case 248: + if ((0x4000000040L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 247; + break; + case 249: + if ((0x2000000020L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 248; + break; + case 250: + if ((0x1000000010L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 249; + break; + case 252: + if ((0x10000000100000L & l) != 0L && kind > 106) + kind = 106; + break; + case 253: + if ((0x400000004000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 252; + break; + case 254: + if ((0x200000002L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 253; + break; + case 255: + if ((0x10000000100000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 254; + break; + case 256: + if ((0x4000000040000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 255; + break; + case 257: + if ((0x800000008000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 256; + break; + case 258: + if ((0x1000000010000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 257; + break; + case 259: + if ((0x200000002000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 258; + break; + case 260: + if ((0x20000000200L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 259; + break; + case 262: + if ((0x7fffffe07fffffeL & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddTwoStates(220, 221); + break; + case 263: + if ((0x7fffffe07fffffeL & l) != 0L) + jjCheckNAddStates(120, 123); + break; + case 264: + if ((0x7fffffe07fffffeL & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(812, 817); + break; + case 270: + if ((0x10000000100000L & l) != 0L && kind > 80) + kind = 80; + break; + case 271: + if ((0x1000000010000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 270; + break; + case 273: + if ((0x200000002000L & l) != 0L && kind > 81) + kind = 81; + break; + case 274: + if ((0x200000002000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 273; + break; + case 276: + if ((0x200000002000L & l) != 0L && kind > 82) + kind = 82; + break; + case 277: + if ((0x800000008L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 276; + break; + case 279: + if ((0x800000008L & l) != 0L && kind > 83) + kind = 83; + break; + case 280: + if ((0x1000000010000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 279; + break; + case 282: + if ((0x400000004000L & l) != 0L && kind > 84) + kind = 84; + break; + case 283: + if ((0x20000000200L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 282; + break; + case 285: + if ((0x100000001000000L & l) != 0L && kind > 85) + kind = 85; + break; + case 286: + if ((0x1000000010000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 285; + break; + case 288: + if ((0x200000002000L & l) != 0L && kind > 86) + kind = 86; + break; + case 289: + if ((0x2000000020L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 288; + break; + case 291: + if ((0x200000002000L & l) != 0L && kind > 87) + kind = 87; + break; + case 292: + if ((0x2000000020L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 291; + break; + case 293: + if ((0x100000001000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 292; + break; + case 295: + if ((0x200000002000L & l) != 0L && kind > 88) + kind = 88; + break; + case 296: + if ((0x2000000020L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 295; + break; + case 297: + if ((0x4000000040000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 296; + break; + case 299: + if ((0x100000001000000L & l) != 0L && kind > 89) + kind = 89; + break; + case 300: + if ((0x2000000020L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 299; + break; + case 302: + if ((0x8000000080L & l) != 0L && kind > 90) + kind = 90; + break; + case 303: + if ((0x2000000020L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 302; + break; + case 304: + if ((0x1000000010L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 303; + break; + case 306: + if ((0x1000000010L & l) != 0L && kind > 91) + kind = 91; + break; + case 307: + if ((0x200000002L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 306; + break; + case 308: + if ((0x4000000040000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 307; + break; + case 310: + if ((0x1000000010L & l) != 0L && kind > 92) + kind = 92; + break; + case 311: + if ((0x200000002L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 310; + break; + case 312: + if ((0x4000000040000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 311; + break; + case 313: + if ((0x8000000080L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 312; + break; + case 315: + if ((0x8000000080000L & l) != 0L && kind > 93) + kind = 93; + break; + case 316: + if ((0x200000002000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 315; + break; + case 318: + if ((0x8000000080000L & l) != 0L && kind > 94) + kind = 94; + break; + case 320: + if ((0x400000004000000L & l) != 0L && kind > 95) + kind = 95; + break; + case 321: + if ((0x10000000100L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 320; + break; + case 323: + if ((0x400000004000000L & l) != 0L && kind > 96) + kind = 96; + break; + case 324: + if ((0x10000000100L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 323; + break; + case 325: + if ((0x80000000800L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 324; + break; + case 328: + if ((0x7fffffe07fffffeL & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddTwoStates(329, 330); + break; + case 329: + if ((0x7fffffe87fffffeL & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddTwoStates(329, 330); + break; + case 330: + if (curChar == 92) + jjCheckNAddTwoStates(331, 332); + break; + case 331: + if ((0x7fffffffffffffffL & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddTwoStates(329, 330); + break; + case 332: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(503, 506); + break; + case 334: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(507, 513); + break; + case 335: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(514, 516); + break; + case 336: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(517, 520); + break; + case 337: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(521, 525); + break; + case 338: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(526, 531); + break; + case 340: + if (curChar == 92) + jjCheckNAddTwoStates(331, 341); + break; + case 341: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(532, 535); + break; + case 342: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(536, 542); + break; + case 343: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(543, 545); + break; + case 344: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(546, 549); + break; + case 345: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(550, 554); + break; + case 346: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddStates(555, 560); + break; + case 347: + if ((0x20000000200000L & l) != 0L) + jjAddStates(827, 829); + break; + case 349: + case 353: + if ((0x7fffffffffffffffL & l) != 0L) + jjCheckNAddStates(567, 570); + break; + case 352: + if (curChar == 92) + jjAddStates(848, 849); + break; + case 354: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(571, 575); + break; + case 356: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(576, 583); + break; + case 357: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(584, 587); + break; + case 358: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(588, 592); + break; + case 359: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(593, 598); + break; + case 360: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(599, 605); + break; + case 362: + case 367: + if ((0x7fffffffffffffffL & l) != 0L) + jjCheckNAddStates(606, 609); + break; + case 364: + if (curChar == 92) + jjAddStates(850, 853); + break; + case 366: + if (curChar == 92) + jjAddStates(854, 855); + break; + case 368: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(610, 615); + break; + case 370: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(616, 624); + break; + case 371: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(625, 629); + break; + case 372: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(630, 635); + break; + case 373: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(636, 642); + break; + case 374: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(643, 650); + break; + case 379: + case 384: + if ((0x7fffffffffffffffL & l) != 0L) + jjCheckNAddStates(651, 654); + break; + case 381: + if (curChar == 92) + jjAddStates(856, 859); + break; + case 383: + if (curChar == 92) + jjAddStates(860, 861); + break; + case 385: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(655, 660); + break; + case 387: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(661, 669); + break; + case 388: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(670, 674); + break; + case 389: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(675, 680); + break; + case 390: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(681, 687); + break; + case 391: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(688, 695); + break; + case 396: + if ((0x100000001000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 348; + break; + case 397: + if ((0x4000000040000L & l) != 0L) + jjstateSet[jjnewStateCnt++] = 396; + break; + case 405: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjAddStates(712, 717); + break; + case 406: + if ((0x7e0000007eL & l) != 0L) + jjstateSet[jjnewStateCnt++] = 407; + break; + case 407: + if ((0x7e0000007eL & l) != 0L) + jjstateSet[jjnewStateCnt++] = 408; + break; + case 408: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAdd(409); + break; + case 409: + if ((0x7e0000007eL & l) != 0L && kind > 116) + kind = 116; + break; + case 410: + if ((0x7e0000007eL & l) != 0L) + jjstateSet[jjnewStateCnt++] = 411; + break; + case 411: + if ((0x7e0000007eL & l) != 0L) + jjstateSet[jjnewStateCnt++] = 412; + break; + case 412: + if ((0x7e0000007eL & l) != 0L) + jjstateSet[jjnewStateCnt++] = 413; + break; + case 413: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjstateSet[jjnewStateCnt++] = 401; + break; + case 414: + if ((0x7e0000007eL & l) != 0L) + jjstateSet[jjnewStateCnt++] = 415; + break; + case 415: + if ((0x7e0000007eL & l) != 0L) + jjstateSet[jjnewStateCnt++] = 416; + break; + case 416: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjstateSet[jjnewStateCnt++] = 417; + break; + case 418: + if ((0x7e0000007eL & l) != 0L) + jjstateSet[jjnewStateCnt++] = 419; + break; + case 419: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjstateSet[jjnewStateCnt++] = 420; + break; + case 422: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjstateSet[jjnewStateCnt++] = 423; + break; + case 431: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddTwoStates(432, 438); + break; + case 433: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjstateSet[jjnewStateCnt++] = 434; + break; + case 434: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjCheckNAddStates(728, 731); + break; + case 435: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjCheckNAdd(409); + break; + case 436: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjCheckNAddTwoStates(409, 435); + break; + case 437: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 116) + kind = 116; + jjCheckNAddStates(732, 734); + break; + case 438: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(735, 739); + break; + case 439: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAdd(432); + break; + case 440: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddTwoStates(439, 432); + break; + case 441: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(740, 742); + break; + case 442: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(743, 746); + break; + case 443: + if (curChar == 92) + jjCheckNAddStates(818, 821); + break; + case 444: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(747, 750); + break; + case 445: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(751, 757); + break; + case 446: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(758, 760); + break; + case 447: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(761, 764); + break; + case 448: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(765, 769); + break; + case 449: + if ((0x7e0000007eL & l) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddStates(770, 775); + break; + case 450: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(776, 780); + break; + case 451: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(781, 788); + break; + case 453: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(789, 793); + break; + case 454: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(794, 799); + break; + case 455: + if ((0x7e0000007eL & l) != 0L) + jjCheckNAddStates(800, 806); + break; + default : break; } - if (kind != 0x7fffffff) { - jjmatchedKind = kind; - jjmatchedPos = curPos; - kind = 0x7fffffff; + } while(i != startsAt); + } + else + { + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 520: + case 113: + case 115: + if ((jjbitVec0[i2] & l2) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + break; + case 166: + if ((jjbitVec0[i2] & l2) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + break; + case 174: + if ((jjbitVec0[i2] & l2) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + break; + case 4: + if ((jjbitVec0[i2] & l2) == 0L) + break; + if (kind > 42) + kind = 42; + jjCheckNAddStates(812, 817); + break; + case 518: + if ((jjbitVec0[i2] & l2) != 0L) + { + if (kind > 74) + kind = 74; + jjCheckNAddTwoStates(220, 221); + } + if ((jjbitVec0[i2] & l2) != 0L) + jjCheckNAddStates(120, 123); + break; + case 175: + if ((jjbitVec0[i2] & l2) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + break; + case 33: + if ((jjbitVec0[i2] & l2) != 0L) + { + if (kind > 74) + kind = 74; + jjCheckNAddTwoStates(220, 221); + } + if ((jjbitVec0[i2] & l2) != 0L) + jjCheckNAddStates(120, 123); + break; + case 176: + if ((jjbitVec0[i2] & l2) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + break; + case 177: + if ((jjbitVec0[i2] & l2) == 0L) + break; + if (kind > 105) + kind = 105; + jjCheckNAddTwoStates(113, 114); + break; + case 79: + case 81: + case 83: + if ((jjbitVec0[i2] & l2) == 0L) + break; + if (kind > 78) + kind = 78; + jjCheckNAddTwoStates(81, 82); + break; + case 2: + if ((jjbitVec0[i2] & l2) != 0L && kind > 5) + kind = 5; + break; + case 9: + case 12: + case 20: + if ((jjbitVec0[i2] & l2) != 0L) + jjCheckNAddStates(133, 135); + break; + case 45: + case 50: + if ((jjbitVec0[i2] & l2) != 0L) + jjCheckNAddStates(116, 119); + break; + case 62: + case 67: + if ((jjbitVec0[i2] & l2) != 0L) + jjCheckNAddStates(112, 115); + break; + case 100: + case 102: + if ((jjbitVec0[i2] & l2) == 0L) + break; + if (kind > 98) + kind = 98; + jjCheckNAddTwoStates(100, 101); + break; + case 220: + case 222: + if ((jjbitVec0[i2] & l2) == 0L) + break; + if (kind > 74) + kind = 74; + jjCheckNAddTwoStates(220, 221); + break; + case 230: + case 234: + if ((jjbitVec0[i2] & l2) != 0L) + jjCheckNAddStates(120, 123); + break; + case 329: + case 331: + case 339: + if ((jjbitVec0[i2] & l2) == 0L) + break; + if (kind > 97) + kind = 97; + jjCheckNAddTwoStates(329, 330); + break; + case 349: + case 353: + if ((jjbitVec0[i2] & l2) != 0L) + jjCheckNAddStates(567, 570); + break; + case 362: + case 367: + if ((jjbitVec0[i2] & l2) != 0L) + jjCheckNAddStates(606, 609); + break; + case 379: + case 384: + if ((jjbitVec0[i2] & l2) != 0L) + jjCheckNAddStates(651, 654); + break; + default : break; } - ++curPos; - if ((i = jjnewStateCnt) == (startsAt = 4 - (jjnewStateCnt = startsAt))) { - return curPos; + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 517 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private int jjMoveStringLiteralDfa0_3() +{ + switch(curChar) + { + case 42: + return jjMoveStringLiteralDfa1_3(0x100L); + default : + return 1; + } +} +private int jjMoveStringLiteralDfa1_3(long active0) +{ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + return 1; + } + switch(curChar) + { + case 47: + if ((active0 & 0x100L) != 0L) + return jjStopAtPos(1, 8); + break; + default : + return 2; + } + return 2; +} +private int jjMoveStringLiteralDfa0_1() +{ + return jjMoveNfa_1(0, 0); +} +private int jjMoveNfa_1(int startState, int curPos) +{ + int startsAt = 0; + jjnewStateCnt = 4; + int i = 1; + jjstateSet[0] = startState; + int kind = 0x7fffffff; + for (;;) + { + if (++jjround == 0x7fffffff) + ReInitRounds(); + if (curChar < 64) + { + long l = 1L << curChar; + do + { + switch(jjstateSet[--i]) + { + case 0: + if ((0xffffffffffffdbffL & l) != 0L) + { + if (kind > 3) + kind = 3; + } + else if ((0x2400L & l) != 0L) + { + if (kind > 4) + kind = 4; + } + if (curChar == 13) + jjstateSet[jjnewStateCnt++] = 2; + break; + case 1: + if ((0x2400L & l) != 0L && kind > 4) + kind = 4; + break; + case 2: + if (curChar == 10 && kind > 4) + kind = 4; + break; + case 3: + if (curChar == 13) + jjstateSet[jjnewStateCnt++] = 2; + break; + default : break; } - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - return curPos; + } while(i != startsAt); + } + else if (curChar < 128) + { + long l = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 0: + kind = 3; + break; + default : break; } - } - } - - private int jjMoveStringLiteralDfa0_2() { - switch (curChar) { - case 42: - return jjMoveStringLiteralDfa1_2(0x80L); - default: - return 1; - } - } - - private int jjMoveStringLiteralDfa1_2(long active0) { - try { - curChar = input_stream.readChar(); - } catch (java.io.IOException e) { - return 1; - } - switch (curChar) { - case 47: - if ((active0 & 0x80L) != 0L) { - return jjStopAtPos(1, 7); + } while(i != startsAt); + } + else + { + int i2 = (curChar & 0xff) >> 6; + long l2 = 1L << (curChar & 077); + do + { + switch(jjstateSet[--i]) + { + case 0: + if ((jjbitVec0[i2] & l2) != 0L && kind > 3) + kind = 3; + break; + default : break; } - break; - default: - return 2; - } - return 2; - } - - static final int[] jjnextStates = { 457, 458, 459, 460, 461, 462, 268, 463, - 464, 465, 271, 466, 467, 468, 274, 469, 470, 471, 277, 472, 473, - 474, 280, 475, 476, 477, 283, 478, 479, 480, 286, 481, 482, 483, - 289, 484, 485, 486, 293, 487, 488, 489, 297, 490, 491, 492, 300, - 493, 494, 495, 304, 496, 497, 498, 308, 499, 500, 501, 313, 502, - 503, 504, 316, 505, 506, 507, 318, 508, 509, 510, 321, 511, 512, - 513, 325, 514, 515, 516, 327, 328, 339, 340, 266, 267, 269, 272, - 275, 278, 281, 284, 287, 290, 294, 298, 301, 305, 309, 314, 317, - 319, 322, 326, 262, 263, 243, 250, 251, 260, 79, 80, 91, 92, 62, - 63, 64, 66, 45, 46, 47, 49, 230, 231, 232, 233, 326, 327, 328, 339, - 340, 7, 8, 20, 21, 9, 10, 11, 9, 14, 10, 11, 15, 9, 16, 14, 10, 11, - 17, 18, 19, 9, 14, 10, 11, 9, 16, 14, 10, 11, 9, 16, 14, 10, 11, - 17, 9, 16, 14, 10, 11, 17, 18, 14, 9, 10, 11, 23, 24, 14, 9, 10, - 11, 25, 26, 27, 14, 9, 10, 11, 24, 14, 9, 10, 11, 24, 14, 9, 10, - 11, 25, 24, 14, 9, 10, 11, 25, 26, 45, 52, 46, 47, 49, 53, 45, 54, - 52, 46, 47, 49, 55, 56, 57, 45, 52, 46, 47, 49, 45, 54, 52, 46, 47, - 49, 45, 54, 52, 46, 47, 49, 55, 45, 54, 52, 46, 47, 49, 55, 56, 62, - 69, 63, 64, 66, 70, 62, 71, 69, 63, 64, 66, 72, 73, 74, 62, 69, 63, - 64, 66, 62, 71, 69, 63, 64, 66, 62, 71, 69, 63, 64, 66, 72, 62, 71, - 69, 63, 64, 66, 72, 73, 81, 85, 82, 86, 81, 87, 85, 82, 88, 89, 90, - 81, 85, 82, 81, 87, 85, 82, 81, 87, 85, 82, 88, 81, 87, 85, 82, 88, - 89, 85, 81, 82, 94, 95, 85, 81, 82, 96, 97, 98, 85, 81, 82, 95, 85, - 81, 82, 95, 85, 81, 82, 96, 95, 85, 81, 82, 96, 97, 100, 104, 101, - 105, 100, 106, 104, 101, 107, 108, 109, 100, 104, 101, 100, 106, - 104, 101, 100, 106, 104, 101, 107, 100, 106, 104, 101, 107, 108, - 113, 117, 114, 118, 113, 119, 117, 114, 120, 121, 122, 113, 117, - 114, 113, 119, 117, 114, 113, 119, 117, 114, 120, 113, 119, 117, - 114, 120, 121, 117, 113, 114, 126, 127, 117, 113, 114, 128, 129, - 130, 117, 113, 114, 127, 117, 113, 114, 127, 117, 113, 114, 128, - 127, 117, 113, 114, 128, 129, 132, 133, 140, 141, 148, 149, 220, - 224, 221, 225, 220, 226, 224, 221, 227, 228, 229, 220, 224, 221, - 220, 226, 224, 221, 220, 226, 224, 221, 227, 220, 226, 224, 221, - 227, 228, 230, 232, 233, 236, 237, 230, 238, 232, 233, 236, 239, - 240, 241, 230, 232, 233, 236, 230, 238, 232, 233, 236, 230, 238, - 232, 233, 236, 239, 230, 238, 232, 233, 236, 239, 240, 329, 333, - 330, 334, 329, 335, 333, 330, 336, 337, 338, 329, 333, 330, 329, - 335, 333, 330, 329, 335, 333, 330, 336, 329, 335, 333, 330, 336, - 337, 333, 329, 330, 342, 343, 333, 329, 330, 344, 345, 346, 333, - 329, 330, 343, 333, 329, 330, 343, 333, 329, 330, 344, 343, 333, - 329, 330, 344, 345, 349, 361, 378, 351, 352, 395, 349, 350, 351, - 352, 349, 351, 352, 355, 356, 349, 357, 351, 352, 355, 358, 359, - 360, 349, 351, 352, 355, 349, 357, 351, 352, 355, 349, 357, 351, - 352, 355, 358, 349, 357, 351, 352, 355, 358, 359, 362, 363, 364, - 366, 362, 369, 363, 364, 366, 370, 362, 371, 369, 363, 364, 366, - 372, 373, 374, 362, 369, 363, 364, 366, 362, 371, 369, 363, 364, - 366, 362, 371, 369, 363, 364, 366, 372, 362, 371, 369, 363, 364, - 366, 372, 373, 379, 380, 381, 383, 379, 386, 380, 381, 383, 387, - 379, 388, 386, 380, 381, 383, 389, 390, 391, 379, 386, 380, 381, - 383, 379, 388, 386, 380, 381, 383, 379, 388, 386, 380, 381, 383, - 389, 379, 388, 386, 380, 381, 383, 389, 390, 349, 361, 378, 350, - 351, 352, 395, 399, 405, 401, 402, 403, 404, 401, 402, 403, 406, - 410, 414, 418, 422, 426, 401, 424, 425, 401, 427, 428, 429, 401, - 427, 428, 409, 435, 436, 437, 409, 435, 436, 439, 432, 440, 441, - 442, 439, 432, 440, 439, 432, 440, 441, 224, 220, 221, 445, 446, - 224, 220, 221, 447, 448, 449, 224, 220, 221, 446, 224, 220, 221, - 446, 224, 220, 221, 447, 446, 224, 220, 221, 447, 448, 230, 232, - 233, 236, 451, 452, 230, 232, 233, 236, 453, 454, 455, 452, 230, - 232, 233, 236, 452, 230, 232, 233, 236, 453, 452, 230, 232, 233, - 236, 453, 454, 514, 327, 328, 339, 340, 220, 230, 231, 232, 233, - 221, 222, 444, 234, 450, 166, 179, 190, 206, 218, 397, 398, 430, - 111, 112, 123, 124, 48, 58, 60, 59, 50, 51, 65, 75, 77, 76, 67, 68, - 102, 103, 353, 354, 365, 375, 377, 376, 367, 368, 382, 392, 394, - 393, 384, 385, }; - - /** Token literal values. */ - public static final String[] jjstrLiteralImages = { "", null, null, null, - null, null, null, null, null, null, "\74\41\55\55", "\55\55\76", - "\173", "\175", "\174\75", "\136\75", "\44\75", "\52\75", - "\176\75", "\75", "\53", "\55", "\54", "\73", "\76", "\176", "\74", - "\57", "\133", "\135", "\52", "\46", "\56", "\50", "\51", "\75\75", - "\174\174", "\46\46", "\41\75", "\72", null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null, null, }; - - /** Lexer state names. */ - public static final String[] lexStateNames = { "DEFAULT", - "IN_SINGLE_LINE_COMMENT", "IN_FORMAL_COMMENT", - "IN_MULTI_LINE_COMMENT", }; - - /** Lex State array. */ - public static final int[] jjnewLexState = { -1, -1, 1, -1, 0, 2, 3, 0, 0, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, }; - static final long[] jjtoToken = { 0xfffc03fffffffc03L, 0xfc01fffffffbffL, }; - static final long[] jjtoSkip = { 0x190L, 0x0L, }; - static final long[] jjtoSpecial = { 0x80L, 0x0L, }; - static final long[] jjtoMore = { 0x26cL, 0x0L, }; - protected CharStream input_stream; - private final int[] jjrounds = new int[517]; - private final int[] jjstateSet = new int[1034]; - private final StringBuilder jjimage = new StringBuilder(); - private StringBuilder image = jjimage; - private int jjimageLen; - private int lengthOfMatch; - protected char curChar; + } while(i != startsAt); + } + if (kind != 0x7fffffff) + { + jjmatchedKind = kind; + jjmatchedPos = curPos; + kind = 0x7fffffff; + } + ++curPos; + if ((i = jjnewStateCnt) == (startsAt = 4 - (jjnewStateCnt = startsAt))) + return curPos; + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { return curPos; } + } +} +private int jjMoveStringLiteralDfa0_2() +{ + switch(curChar) + { + case 42: + return jjMoveStringLiteralDfa1_2(0x80L); + default : + return 1; + } +} +private int jjMoveStringLiteralDfa1_2(long active0) +{ + try { curChar = input_stream.readChar(); } + catch(java.io.IOException e) { + return 1; + } + switch(curChar) + { + case 47: + if ((active0 & 0x80L) != 0L) + return jjStopAtPos(1, 7); + break; + default : + return 2; + } + return 2; +} +static final int[] jjnextStates = { + 457, 458, 459, 460, 461, 462, 268, 463, 464, 465, 271, 466, 467, 468, 274, 469, + 470, 471, 277, 472, 473, 474, 280, 475, 476, 477, 283, 478, 479, 480, 286, 481, + 482, 483, 289, 484, 485, 486, 293, 487, 488, 489, 297, 490, 491, 492, 300, 493, + 494, 495, 304, 496, 497, 498, 308, 499, 500, 501, 313, 502, 503, 504, 316, 505, + 506, 507, 318, 508, 509, 510, 321, 511, 512, 513, 325, 514, 515, 516, 327, 328, + 339, 340, 266, 267, 269, 272, 275, 278, 281, 284, 287, 290, 294, 298, 301, 305, + 309, 314, 317, 319, 322, 326, 262, 263, 243, 250, 251, 260, 79, 80, 91, 92, + 62, 63, 64, 66, 45, 46, 47, 49, 230, 231, 232, 233, 326, 327, 328, 339, + 340, 7, 8, 20, 21, 9, 10, 11, 9, 14, 10, 11, 15, 9, 16, 14, + 10, 11, 17, 18, 19, 9, 14, 10, 11, 9, 16, 14, 10, 11, 9, 16, + 14, 10, 11, 17, 9, 16, 14, 10, 11, 17, 18, 14, 9, 10, 11, 23, + 24, 14, 9, 10, 11, 25, 26, 27, 14, 9, 10, 11, 24, 14, 9, 10, + 11, 24, 14, 9, 10, 11, 25, 24, 14, 9, 10, 11, 25, 26, 45, 52, + 46, 47, 49, 53, 45, 54, 52, 46, 47, 49, 55, 56, 57, 45, 52, 46, + 47, 49, 45, 54, 52, 46, 47, 49, 45, 54, 52, 46, 47, 49, 55, 45, + 54, 52, 46, 47, 49, 55, 56, 62, 69, 63, 64, 66, 70, 62, 71, 69, + 63, 64, 66, 72, 73, 74, 62, 69, 63, 64, 66, 62, 71, 69, 63, 64, + 66, 62, 71, 69, 63, 64, 66, 72, 62, 71, 69, 63, 64, 66, 72, 73, + 81, 85, 82, 86, 81, 87, 85, 82, 88, 89, 90, 81, 85, 82, 81, 87, + 85, 82, 81, 87, 85, 82, 88, 81, 87, 85, 82, 88, 89, 85, 81, 82, + 94, 95, 85, 81, 82, 96, 97, 98, 85, 81, 82, 95, 85, 81, 82, 95, + 85, 81, 82, 96, 95, 85, 81, 82, 96, 97, 100, 104, 101, 105, 100, 106, + 104, 101, 107, 108, 109, 100, 104, 101, 100, 106, 104, 101, 100, 106, 104, 101, + 107, 100, 106, 104, 101, 107, 108, 113, 117, 114, 118, 113, 119, 117, 114, 120, + 121, 122, 113, 117, 114, 113, 119, 117, 114, 113, 119, 117, 114, 120, 113, 119, + 117, 114, 120, 121, 117, 113, 114, 126, 127, 117, 113, 114, 128, 129, 130, 117, + 113, 114, 127, 117, 113, 114, 127, 117, 113, 114, 128, 127, 117, 113, 114, 128, + 129, 132, 133, 140, 141, 148, 149, 220, 224, 221, 225, 220, 226, 224, 221, 227, + 228, 229, 220, 224, 221, 220, 226, 224, 221, 220, 226, 224, 221, 227, 220, 226, + 224, 221, 227, 228, 230, 232, 233, 236, 237, 230, 238, 232, 233, 236, 239, 240, + 241, 230, 232, 233, 236, 230, 238, 232, 233, 236, 230, 238, 232, 233, 236, 239, + 230, 238, 232, 233, 236, 239, 240, 329, 333, 330, 334, 329, 335, 333, 330, 336, + 337, 338, 329, 333, 330, 329, 335, 333, 330, 329, 335, 333, 330, 336, 329, 335, + 333, 330, 336, 337, 333, 329, 330, 342, 343, 333, 329, 330, 344, 345, 346, 333, + 329, 330, 343, 333, 329, 330, 343, 333, 329, 330, 344, 343, 333, 329, 330, 344, + 345, 349, 361, 378, 351, 352, 395, 349, 350, 351, 352, 349, 351, 352, 355, 356, + 349, 357, 351, 352, 355, 358, 359, 360, 349, 351, 352, 355, 349, 357, 351, 352, + 355, 349, 357, 351, 352, 355, 358, 349, 357, 351, 352, 355, 358, 359, 362, 363, + 364, 366, 362, 369, 363, 364, 366, 370, 362, 371, 369, 363, 364, 366, 372, 373, + 374, 362, 369, 363, 364, 366, 362, 371, 369, 363, 364, 366, 362, 371, 369, 363, + 364, 366, 372, 362, 371, 369, 363, 364, 366, 372, 373, 379, 380, 381, 383, 379, + 386, 380, 381, 383, 387, 379, 388, 386, 380, 381, 383, 389, 390, 391, 379, 386, + 380, 381, 383, 379, 388, 386, 380, 381, 383, 379, 388, 386, 380, 381, 383, 389, + 379, 388, 386, 380, 381, 383, 389, 390, 349, 361, 378, 350, 351, 352, 395, 399, + 405, 401, 402, 403, 404, 401, 402, 403, 406, 410, 414, 418, 422, 426, 401, 424, + 425, 401, 427, 428, 429, 401, 427, 428, 409, 435, 436, 437, 409, 435, 436, 439, + 432, 440, 441, 442, 439, 432, 440, 439, 432, 440, 441, 224, 220, 221, 445, 446, + 224, 220, 221, 447, 448, 449, 224, 220, 221, 446, 224, 220, 221, 446, 224, 220, + 221, 447, 446, 224, 220, 221, 447, 448, 230, 232, 233, 236, 451, 452, 230, 232, + 233, 236, 453, 454, 455, 452, 230, 232, 233, 236, 452, 230, 232, 233, 236, 453, + 452, 230, 232, 233, 236, 453, 454, 514, 327, 328, 339, 340, 220, 230, 231, 232, + 233, 221, 222, 444, 234, 450, 166, 179, 190, 206, 218, 397, 398, 430, 111, 112, + 123, 124, 48, 58, 60, 59, 50, 51, 65, 75, 77, 76, 67, 68, 102, 103, + 353, 354, 365, 375, 377, 376, 367, 368, 382, 392, 394, 393, 384, 385, +}; - /** Constructor. */ - public ParserTokenManager(CharStream stream) { - input_stream = stream; - } +/** Token literal values. */ +public static final String[] jjstrLiteralImages = { +"", null, null, null, null, null, null, null, null, null, "\74\41\55\55", +"\55\55\76", "\173", "\175", "\174\75", "\136\75", "\44\75", "\52\75", "\176\75", "\75", +"\53", "\55", "\54", "\73", "\76", "\176", "\74", "\57", "\133", "\135", "\52", +"\45", "\46", "\56", "\50", "\51", "\75\75", "\174\174", "\46\46", "\41\75", "\72", +null, null, null, null, null, null, null, null, null, null, null, null, null, null, +null, null, null, null, null, null, null, null, null, null, null, null, null, null, +null, null, null, null, null, null, null, null, null, null, null, null, null, null, +null, null, null, null, null, null, null, null, null, null, null, null, null, null, +null, null, null, null, null, null, null, null, null, null, null, null, null, null, +null, null, null, null, null, null, null, null, null, null, null, }; - /** Constructor. */ - public ParserTokenManager(CharStream stream, int lexState) { - this(stream); - SwitchTo(lexState); - } +/** Lexer state names. */ +public static final String[] lexStateNames = { + "DEFAULT", + "IN_SINGLE_LINE_COMMENT", + "IN_FORMAL_COMMENT", + "IN_MULTI_LINE_COMMENT", +}; - /** Reinitialise parser. */ - public void ReInit(CharStream stream) { - jjmatchedPos = jjnewStateCnt = 0; - curLexState = defaultLexState; - input_stream = stream; - ReInitRounds(); - } +/** Lex State array. */ +public static final int[] jjnewLexState = { + -1, -1, 1, -1, 0, 2, 3, 0, 0, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; +static final long[] jjtoToken = { + 0xfff807fffffffc03L, 0x3f007ffffffefffL, +}; +static final long[] jjtoSkip = { + 0x190L, 0x0L, +}; +static final long[] jjtoSpecial = { + 0x80L, 0x0L, +}; +static final long[] jjtoMore = { + 0x26cL, 0x0L, +}; +protected CharStream input_stream; +private final int[] jjrounds = new int[517]; +private final int[] jjstateSet = new int[1034]; +private final StringBuilder jjimage = new StringBuilder(); +private StringBuilder image = jjimage; +private int jjimageLen; +private int lengthOfMatch; +protected char curChar; +/** Constructor. */ +public ParserTokenManager(CharStream stream){ + input_stream = stream; +} - private void ReInitRounds() { - int i; - jjround = 0x80000001; - for (i = 517; i-- > 0;) { - jjrounds[i] = 0x80000000; - } - } +/** Constructor. */ +public ParserTokenManager(CharStream stream, int lexState){ + this(stream); + SwitchTo(lexState); +} - /** Reinitialise parser. */ - public void ReInit(CharStream stream, int lexState) { - ReInit(stream); - SwitchTo(lexState); - } +/** Reinitialise parser. */ +public void ReInit(CharStream stream) +{ + jjmatchedPos = jjnewStateCnt = 0; + curLexState = defaultLexState; + input_stream = stream; + ReInitRounds(); +} +private void ReInitRounds() +{ + int i; + jjround = 0x80000001; + for (i = 517; i-- > 0;) + jjrounds[i] = 0x80000000; +} - /** Switch to specified lex state. */ - public void SwitchTo(int lexState) { - if (lexState >= 4 || lexState < 0) { - throw new TokenMgrError("Error: Ignoring invalid lexical state : " - + lexState + ". State unchanged.", - TokenMgrError.INVALID_LEXICAL_STATE); - } else { - curLexState = lexState; - } - } +/** Reinitialise parser. */ +public void ReInit(CharStream stream, int lexState) +{ + ReInit(stream); + SwitchTo(lexState); +} - protected Token jjFillToken() { - final Token t; - final String curTokenImage; - final int beginLine; - final int endLine; - final int beginColumn; - final int endColumn; - String im = jjstrLiteralImages[jjmatchedKind]; - curTokenImage = (im == null) ? input_stream.GetImage() : im; - beginLine = input_stream.getBeginLine(); - beginColumn = input_stream.getBeginColumn(); - endLine = input_stream.getEndLine(); - endColumn = input_stream.getEndColumn(); - t = Token.newToken(jjmatchedKind, curTokenImage); +/** Switch to specified lex state. */ +public void SwitchTo(int lexState) +{ + if (lexState >= 4 || lexState < 0) + throw new TokenMgrError("Error: Ignoring invalid lexical state : " + lexState + ". State unchanged.", TokenMgrError.INVALID_LEXICAL_STATE); + else + curLexState = lexState; +} - t.beginLine = beginLine; - t.endLine = endLine; - t.beginColumn = beginColumn; - t.endColumn = endColumn; +protected Token jjFillToken() +{ + final Token t; + final String curTokenImage; + final int beginLine; + final int endLine; + final int beginColumn; + final int endColumn; + String im = jjstrLiteralImages[jjmatchedKind]; + curTokenImage = (im == null) ? input_stream.GetImage() : im; + beginLine = input_stream.getBeginLine(); + beginColumn = input_stream.getBeginColumn(); + endLine = input_stream.getEndLine(); + endColumn = input_stream.getEndColumn(); + t = Token.newToken(jjmatchedKind, curTokenImage); - return t; - } + t.beginLine = beginLine; + t.endLine = endLine; + t.beginColumn = beginColumn; + t.endColumn = endColumn; - int curLexState = 0; - int defaultLexState = 0; - int jjnewStateCnt; - int jjround; - int jjmatchedPos; - int jjmatchedKind; + return t; +} - /** Get the next Token. */ - public Token getNextToken() { - Token specialToken = null; - Token matchedToken; - int curPos = 0; +int curLexState = 0; +int defaultLexState = 0; +int jjnewStateCnt; +int jjround; +int jjmatchedPos; +int jjmatchedKind; - EOFLoop: for (;;) { - try { - curChar = input_stream.BeginToken(); - } catch (java.io.IOException e) { - jjmatchedKind = 0; - matchedToken = jjFillToken(); - matchedToken.specialToken = specialToken; - return matchedToken; - } - image = jjimage; - image.setLength(0); - jjimageLen = 0; +/** Get the next Token. */ +public Token getNextToken() +{ + Token specialToken = null; + Token matchedToken; + int curPos = 0; - for (;;) { - switch (curLexState) { - case 0: - jjmatchedKind = 0x7fffffff; - jjmatchedPos = 0; - curPos = jjMoveStringLiteralDfa0_0(); - if (jjmatchedPos == 0 && jjmatchedKind > 119) { - jjmatchedKind = 119; - } - break; - case 1: - jjmatchedKind = 0x7fffffff; - jjmatchedPos = 0; - curPos = jjMoveStringLiteralDfa0_1(); - break; - case 2: - jjmatchedKind = 0x7fffffff; - jjmatchedPos = 0; - curPos = jjMoveStringLiteralDfa0_2(); - if (jjmatchedPos == 0 && jjmatchedKind > 9) { - jjmatchedKind = 9; - } - break; - case 3: - jjmatchedKind = 0x7fffffff; - jjmatchedPos = 0; - curPos = jjMoveStringLiteralDfa0_3(); - if (jjmatchedPos == 0 && jjmatchedKind > 9) { - jjmatchedKind = 9; - } - break; - } - if (jjmatchedKind != 0x7fffffff) { - if (jjmatchedPos + 1 < curPos) { - input_stream.backup(curPos - jjmatchedPos - 1); - } - if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) { - matchedToken = jjFillToken(); - matchedToken.specialToken = specialToken; - TokenLexicalActions(matchedToken); - if (jjnewLexState[jjmatchedKind] != -1) { - curLexState = jjnewLexState[jjmatchedKind]; - } - return matchedToken; - } else if ((jjtoSkip[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) { - if ((jjtoSpecial[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) { - matchedToken = jjFillToken(); - if (specialToken == null) { - specialToken = matchedToken; - } else { - matchedToken.specialToken = specialToken; - specialToken = (specialToken.next = matchedToken); - } - SkipLexicalActions(matchedToken); - } else { - SkipLexicalActions(null); - } - if (jjnewLexState[jjmatchedKind] != -1) { - curLexState = jjnewLexState[jjmatchedKind]; - } - continue EOFLoop; - } - MoreLexicalActions(); - if (jjnewLexState[jjmatchedKind] != -1) { - curLexState = jjnewLexState[jjmatchedKind]; - } - curPos = 0; - jjmatchedKind = 0x7fffffff; - try { - curChar = input_stream.readChar(); - continue; - } catch (java.io.IOException e1) { - } - } - int error_line = input_stream.getEndLine(); - int error_column = input_stream.getEndColumn(); - String error_after = null; - boolean EOFSeen = false; - try { - input_stream.readChar(); - input_stream.backup(1); - } catch (java.io.IOException e1) { - EOFSeen = true; - error_after = curPos <= 1 ? "" : input_stream.GetImage(); - if (curChar == '\n' || curChar == '\r') { - error_line++; - error_column = 0; - } else { - error_column++; - } - } - if (!EOFSeen) { - input_stream.backup(1); - error_after = curPos <= 1 ? "" : input_stream.GetImage(); - } - throw new TokenMgrError(EOFSeen, curLexState, error_line, - error_column, error_after, curChar, - TokenMgrError.LEXICAL_ERROR); - } - } - } + EOFLoop : + for (;;) + { + try + { + curChar = input_stream.BeginToken(); + } + catch(java.io.IOException e) + { + jjmatchedKind = 0; + matchedToken = jjFillToken(); + matchedToken.specialToken = specialToken; + return matchedToken; + } + image = jjimage; + image.setLength(0); + jjimageLen = 0; - void SkipLexicalActions(Token matchedToken) { - switch (jjmatchedKind) { - default: - break; + for (;;) + { + switch(curLexState) + { + case 0: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_0(); + if (jjmatchedPos == 0 && jjmatchedKind > 121) + { + jjmatchedKind = 121; + } + break; + case 1: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_1(); + break; + case 2: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_2(); + if (jjmatchedPos == 0 && jjmatchedKind > 9) + { + jjmatchedKind = 9; + } + break; + case 3: + jjmatchedKind = 0x7fffffff; + jjmatchedPos = 0; + curPos = jjMoveStringLiteralDfa0_3(); + if (jjmatchedPos == 0 && jjmatchedKind > 9) + { + jjmatchedKind = 9; + } + break; + } + if (jjmatchedKind != 0x7fffffff) + { + if (jjmatchedPos + 1 < curPos) + input_stream.backup(curPos - jjmatchedPos - 1); + if ((jjtoToken[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) + { + matchedToken = jjFillToken(); + matchedToken.specialToken = specialToken; + TokenLexicalActions(matchedToken); + if (jjnewLexState[jjmatchedKind] != -1) + curLexState = jjnewLexState[jjmatchedKind]; + return matchedToken; } - } - - void MoreLexicalActions() { - jjimageLen += (lengthOfMatch = jjmatchedPos + 1); - switch (jjmatchedKind) { - case 5: - image.append(input_stream.GetSuffix(jjimageLen)); - jjimageLen = 0; - input_stream.backup(1); - break; - default: - break; + else if ((jjtoSkip[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) + { + if ((jjtoSpecial[jjmatchedKind >> 6] & (1L << (jjmatchedKind & 077))) != 0L) + { + matchedToken = jjFillToken(); + if (specialToken == null) + specialToken = matchedToken; + else + { + matchedToken.specialToken = specialToken; + specialToken = (specialToken.next = matchedToken); + } + SkipLexicalActions(matchedToken); + } + else + SkipLexicalActions(null); + if (jjnewLexState[jjmatchedKind] != -1) + curLexState = jjnewLexState[jjmatchedKind]; + continue EOFLoop; } - } - - void TokenLexicalActions(Token matchedToken) { - switch (jjmatchedKind) { - case 1: - image.append(input_stream.GetSuffix(jjimageLen - + (lengthOfMatch = jjmatchedPos + 1))); - image = Parser.SPACE; - break; - default: - break; + MoreLexicalActions(); + if (jjnewLexState[jjmatchedKind] != -1) + curLexState = jjnewLexState[jjmatchedKind]; + curPos = 0; + jjmatchedKind = 0x7fffffff; + try { + curChar = input_stream.readChar(); + continue; } - } - - private void jjCheckNAdd(int state) { - if (jjrounds[state] != jjround) { - jjstateSet[jjnewStateCnt++] = state; - jjrounds[state] = jjround; + catch (java.io.IOException e1) { } + } + int error_line = input_stream.getEndLine(); + int error_column = input_stream.getEndColumn(); + String error_after = null; + boolean EOFSeen = false; + try { input_stream.readChar(); input_stream.backup(1); } + catch (java.io.IOException e1) { + EOFSeen = true; + error_after = curPos <= 1 ? "" : input_stream.GetImage(); + if (curChar == '\n' || curChar == '\r') { + error_line++; + error_column = 0; } - } - - private void jjAddStates(int start, int end) { - do { - jjstateSet[jjnewStateCnt++] = jjnextStates[start]; - } while (start++ != end); - } + else + error_column++; + } + if (!EOFSeen) { + input_stream.backup(1); + error_after = curPos <= 1 ? "" : input_stream.GetImage(); + } + throw new TokenMgrError(EOFSeen, curLexState, error_line, error_column, error_after, curChar, TokenMgrError.LEXICAL_ERROR); + } + } +} - private void jjCheckNAddTwoStates(int state1, int state2) { - jjCheckNAdd(state1); - jjCheckNAdd(state2); - } +void SkipLexicalActions(Token matchedToken) +{ + switch(jjmatchedKind) + { + default : + break; + } +} +void MoreLexicalActions() +{ + jjimageLen += (lengthOfMatch = jjmatchedPos + 1); + switch(jjmatchedKind) + { + case 5 : + image.append(input_stream.GetSuffix(jjimageLen)); + jjimageLen = 0; + input_stream.backup(1); + break; + default : + break; + } +} +void TokenLexicalActions(Token matchedToken) +{ + switch(jjmatchedKind) + { + case 1 : + image.append(input_stream.GetSuffix(jjimageLen + (lengthOfMatch = jjmatchedPos + 1))); + image = Parser.SPACE; + break; + default : + break; + } +} +private void jjCheckNAdd(int state) +{ + if (jjrounds[state] != jjround) + { + jjstateSet[jjnewStateCnt++] = state; + jjrounds[state] = jjround; + } +} +private void jjAddStates(int start, int end) +{ + do { + jjstateSet[jjnewStateCnt++] = jjnextStates[start]; + } while (start++ != end); +} +private void jjCheckNAddTwoStates(int state1, int state2) +{ + jjCheckNAdd(state1); + jjCheckNAdd(state2); +} - private void jjCheckNAddStates(int start, int end) { - do { - jjCheckNAdd(jjnextStates[start]); - } while (start++ != end); - } +private void jjCheckNAddStates(int start, int end) +{ + do { + jjCheckNAdd(jjnextStates[start]); + } while (start++ != end); +} } diff --git a/theme-compiler/src/com/vaadin/sass/internal/parser/SCSSLexicalUnit.java b/theme-compiler/src/com/vaadin/sass/internal/parser/SCSSLexicalUnit.java index 935e4e5abd..709d1d3576 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/parser/SCSSLexicalUnit.java +++ b/theme-compiler/src/com/vaadin/sass/internal/parser/SCSSLexicalUnit.java @@ -19,6 +19,8 @@ import org.w3c.css.sac.LexicalUnit; public interface SCSSLexicalUnit extends LexicalUnit { static final short SCSS_VARIABLE = 100; + static final short SCSS_OPERATOR_LEFT_PAREN = 101; + static final short SCSS_OPERATOR_RIGHT_PAREN = 102; static final short SAC_LEM = 200; static final short SAC_REM = 201; @@ -31,4 +33,6 @@ public interface SCSSLexicalUnit extends LexicalUnit { LexicalUnitImpl multiply(LexicalUnitImpl another); + LexicalUnitImpl modulo(LexicalUnitImpl another); + } diff --git a/theme-compiler/src/com/vaadin/sass/internal/resolver/ClassloaderResolver.java b/theme-compiler/src/com/vaadin/sass/internal/resolver/ClassloaderResolver.java index 7ab789ca3f..8711a0a3e9 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/resolver/ClassloaderResolver.java +++ b/theme-compiler/src/com/vaadin/sass/internal/resolver/ClassloaderResolver.java @@ -40,6 +40,12 @@ public class ClassloaderResolver implements ScssStylesheetResolver { // Ensure only "/" is used, also in Windows fileName = fileName.replace(File.separatorChar, '/'); + // Filename should be a relative path starting with VAADIN/... + int vaadinIdx = fileName.lastIndexOf("VAADIN/"); + if (vaadinIdx > -1) { + fileName = fileName.substring(vaadinIdx); + } + // Can the classloader find it? InputStream is = getClass().getClassLoader().getResourceAsStream( fileName); diff --git a/theme-compiler/src/com/vaadin/sass/internal/resolver/VaadinResolver.java b/theme-compiler/src/com/vaadin/sass/internal/resolver/VaadinResolver.java index f51201da06..fec16a54c8 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/resolver/VaadinResolver.java +++ b/theme-compiler/src/com/vaadin/sass/internal/resolver/VaadinResolver.java @@ -15,8 +15,8 @@ */ package com.vaadin.sass.internal.resolver; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.io.File; +import java.util.Stack; import org.w3c.css.sac.InputSource; @@ -24,41 +24,67 @@ public class VaadinResolver implements ScssStylesheetResolver { @Override public InputSource resolve(String identifier) { - if (identifier.endsWith(".css")) { - // CSS support mainly for testing, don't load from classpath etc - ScssStylesheetResolver resolver = new FilesystemResolver(); - return resolver.resolve(identifier); - } + + // Remove extra "." and ".." + identifier = normalize(identifier); InputSource source = null; - Pattern pattern = Pattern - .compile("\\.\\.\\/([^\\/]+)\\/([^\\/]+\\.scss)"); - Matcher matcher = pattern.matcher(identifier); + // Can we find the scss from the file system? + ScssStylesheetResolver resolver = new FilesystemResolver(); + source = resolver.resolve(identifier); - if (matcher.find()) { - // theme include - ScssStylesheetResolver resolver = new FilesystemResolver(); + if (source == null) { + // How about the classpath? + resolver = new ClassloaderResolver(); source = resolver.resolve(identifier); + } - if (source == null) { - String themeName = matcher.group(1); - String fileName = matcher.group(2); - resolver = new ClassloaderResolver(); - String id = "VAADIN/themes/" + themeName + "/" + fileName; - source = resolver.resolve(id); - } + return source; + } - } else { - ScssStylesheetResolver resolver = new FilesystemResolver(); - source = resolver.resolve(identifier); + /** + * Normalizes "." and ".." from the path string where parent path segments + * can be removed. Preserve leading "..". + * + * @param path + * A relative or absolute file path + * @return The normalized path + */ + private static String normalize(String path) { - if (source == null) { - resolver = new ClassloaderResolver(); - source = resolver.resolve(identifier); + // Ensure only "/" is used, also in Windows + path = path.replace(File.separatorChar, '/'); + + // Split into segments + String[] segments = path.split("/"); + Stack<String> result = new Stack<String>(); + + // Replace '.' and '..' segments + for (int i = 0; i < segments.length; i++) { + if (segments[i].equals(".")) { + // Segments marked '.' are ignored + + } else if (segments[i].equals("..") && !result.isEmpty() + && !result.lastElement().equals("..")) { + // If segment is ".." then remove the previous iff the previous + // element is not a ".." and the result stack is not empty + result.pop(); + } else { + // Other segments are just added to the stack + result.push(segments[i]); } } - return source; + // Reconstruct path + StringBuilder pathBuilder = new StringBuilder(); + for (int i = 0; i < result.size(); i++) { + if (i > 0) { + pathBuilder.append("/"); + } + pathBuilder.append(result.get(i)); + } + return pathBuilder.toString(); } + } diff --git a/theme-compiler/src/com/vaadin/sass/internal/tree/ContentNode.java b/theme-compiler/src/com/vaadin/sass/internal/tree/ContentNode.java new file mode 100644 index 0000000000..10cb1599c1 --- /dev/null +++ b/theme-compiler/src/com/vaadin/sass/internal/tree/ContentNode.java @@ -0,0 +1,33 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +/** + * ContentNode represents a {@literal @}content in a SCSS tree. + */ +package com.vaadin.sass.internal.tree; + +public class ContentNode extends Node { + + @Override + public void traverse() { + /* + * ContentNode is basically just a placeholder for some content which + * will be included. So for traverse of this node, it does nothing. it + * will be replaced when traversing MixinDefNode which contains it. + */ + } + +} diff --git a/theme-compiler/src/com/vaadin/sass/internal/tree/MixinDefNode.java b/theme-compiler/src/com/vaadin/sass/internal/tree/MixinDefNode.java index d3dce12c48..bae1475076 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/tree/MixinDefNode.java +++ b/theme-compiler/src/com/vaadin/sass/internal/tree/MixinDefNode.java @@ -85,4 +85,38 @@ public class MixinDefNode extends Node implements IVariableNode { } } + /** + * This should only happen on a cloned MixinDefNode, since it changes the + * Node itself. + * + * @param mixinNode + * @return + */ + public MixinDefNode replaceContentDirective(MixinNode mixinNode) { + return findAndReplaceContentNodeInChildren(this, mixinNode); + } + + private MixinDefNode findAndReplaceContentNodeInChildren(Node node, + MixinNode mixinNode) { + ContentNode contentNode = null; + for (Node child : new ArrayList<Node>(node.getChildren())) { + if (child instanceof ContentNode) { + contentNode = (ContentNode) child; + replaceContentNode(contentNode, mixinNode); + } else { + findAndReplaceContentNodeInChildren(child, mixinNode); + } + } + return this; + } + + public MixinDefNode replaceContentNode(ContentNode contentNode, + MixinNode mixinNode) { + if (contentNode != null) { + contentNode.getParentNode().appendChildrenAfter( + DeepCopy.copy(mixinNode.getChildren()), contentNode); + contentNode.getParentNode().removeChild(contentNode); + } + return this; + } } diff --git a/theme-compiler/src/com/vaadin/sass/internal/tree/MixinNode.java b/theme-compiler/src/com/vaadin/sass/internal/tree/MixinNode.java index 755c2d5c88..e702bc8577 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/tree/MixinNode.java +++ b/theme-compiler/src/com/vaadin/sass/internal/tree/MixinNode.java @@ -30,10 +30,13 @@ public class MixinNode extends Node implements IVariableNode { private String name; private ArrayList<LexicalUnitImpl> arglist; - public MixinNode(String name, Collection<LexicalUnitImpl> args) { - super(); + public MixinNode(String name) { this.name = name; arglist = new ArrayList<LexicalUnitImpl>(); + } + + public MixinNode(String name, Collection<LexicalUnitImpl> args) { + this(name); if (args != null && !args.isEmpty()) { arglist.addAll(args); } @@ -69,7 +72,8 @@ public class MixinNode extends Node implements IVariableNode { for (final LexicalUnitImpl arg : new ArrayList<LexicalUnitImpl>( arglist)) { LexicalUnitImpl unit = arg; - // only perform replace in the value if separate argument name + // only perform replace in the value if separate argument + // name // and value if (unit.getNextLexicalUnit() != null) { unit = unit.getNextLexicalUnit(); @@ -89,7 +93,15 @@ public class MixinNode extends Node implements IVariableNode { name = var.getExpr().toString(); } } + } + } + protected void replaceVariablesForChildren() { + for (Node child : getChildren()) { + if (child instanceof IVariableNode) { + ((IVariableNode) child).replaceVariables(ScssStylesheet + .getVariables()); + } } } @@ -101,6 +113,7 @@ public class MixinNode extends Node implements IVariableNode { .openVariableScope(); replaceVariables(ScssStylesheet.getVariables()); + replaceVariablesForChildren(); MixinNodeHandler.traverse(this); ScssStylesheet.closeVariableScope(variableScope); diff --git a/theme-compiler/src/com/vaadin/sass/internal/tree/Node.java b/theme-compiler/src/com/vaadin/sass/internal/tree/Node.java index 9a2cc6371e..98b701d5a9 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/tree/Node.java +++ b/theme-compiler/src/com/vaadin/sass/internal/tree/Node.java @@ -45,6 +45,23 @@ public abstract class Node implements Serializable { } } + public void appendChildrenAfter(Collection<Node> childrenNodes, Node after) { + if (childrenNodes != null && !childrenNodes.isEmpty()) { + int index = children.indexOf(after); + if (index != -1) { + children.addAll(index, childrenNodes); + for (final Node child : childrenNodes) { + if (child.getParentNode() != null) { + child.getParentNode().removeChild(child); + } + child.setParentNode(this); + } + } else { + throw new NullPointerException("after-node was not found"); + } + } + } + public void appendChild(Node node) { if (node != null) { children.add(node); diff --git a/theme-compiler/src/com/vaadin/sass/internal/tree/RuleNode.java b/theme-compiler/src/com/vaadin/sass/internal/tree/RuleNode.java index a78d9d66d2..19880e4ce5 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/tree/RuleNode.java +++ b/theme-compiler/src/com/vaadin/sass/internal/tree/RuleNode.java @@ -20,6 +20,7 @@ import java.util.ArrayList; import java.util.regex.Pattern; import com.vaadin.sass.internal.ScssStylesheet; +import com.vaadin.sass.internal.expression.ArithmeticExpressionEvaluator; import com.vaadin.sass.internal.parser.LexicalUnitImpl; import com.vaadin.sass.internal.util.StringUtil; @@ -140,6 +141,20 @@ public class RuleNode extends Node implements IVariableNode { @Override public void traverse() { - replaceVariables(ScssStylesheet.getVariables()); + /* + * "replaceVariables(ScssStylesheet.getVariables());" seems duplicated + * and can be extracted out of if, but it is not. + * containsArithmeticalOperator must be called before replaceVariables. + * Because for the "/" operator, it needs to see if its predecessor or + * successor is a Variable or not, to determine it is an arithmetic + * operator. + */ + if (ArithmeticExpressionEvaluator.get().containsArithmeticalOperator( + value)) { + replaceVariables(ScssStylesheet.getVariables()); + value = ArithmeticExpressionEvaluator.get().evaluate(value); + } else { + replaceVariables(ScssStylesheet.getVariables()); + } } } diff --git a/theme-compiler/src/com/vaadin/sass/internal/tree/VariableNode.java b/theme-compiler/src/com/vaadin/sass/internal/tree/VariableNode.java index 90be727f88..f2499d72ab 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/tree/VariableNode.java +++ b/theme-compiler/src/com/vaadin/sass/internal/tree/VariableNode.java @@ -19,6 +19,7 @@ package com.vaadin.sass.internal.tree; import java.util.ArrayList; import com.vaadin.sass.internal.ScssStylesheet; +import com.vaadin.sass.internal.expression.ArithmeticExpressionEvaluator; import com.vaadin.sass.internal.parser.LexicalUnitImpl; import com.vaadin.sass.internal.util.StringUtil; import com.vaadin.sass.internal.visitor.VariableNodeHandler; @@ -101,7 +102,21 @@ public class VariableNode extends Node implements IVariableNode { @Override public void traverse() { - replaceVariables(ScssStylesheet.getVariables()); + /* + * "replaceVariables(ScssStylesheet.getVariables());" seems duplicated + * and can be extracted out of if, but it is not. + * containsArithmeticalOperator must be called before replaceVariables. + * Because for the "/" operator, it needs to see if its predecessor or + * successor is a Variable or not, to determine it is an arithmetic + * operator. + */ + if (ArithmeticExpressionEvaluator.get().containsArithmeticalOperator( + expr)) { + replaceVariables(ScssStylesheet.getVariables()); + expr = ArithmeticExpressionEvaluator.get().evaluate(expr); + } else { + replaceVariables(ScssStylesheet.getVariables()); + } VariableNodeHandler.traverse(this); } } diff --git a/theme-compiler/src/com/vaadin/sass/internal/util/DeepCopy.java b/theme-compiler/src/com/vaadin/sass/internal/util/DeepCopy.java index 6b16183588..bc30ffdd6c 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/util/DeepCopy.java +++ b/theme-compiler/src/com/vaadin/sass/internal/util/DeepCopy.java @@ -19,6 +19,9 @@ package com.vaadin.sass.internal.util; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.util.Collection; +import java.util.LinkedList; +import java.util.List; /** * Utility for making deep copies (vs. clone()'s shallow copies) of objects. @@ -70,4 +73,11 @@ public class DeepCopy { } } + public static <T> Collection<T> copy(Collection<T> objects) { + List<T> copies = new LinkedList<T>(); + for (T object : objects) { + copies.add((T) copy(object)); + } + return copies; + } }
\ No newline at end of file diff --git a/theme-compiler/src/com/vaadin/sass/internal/visitor/ImportNodeHandler.java b/theme-compiler/src/com/vaadin/sass/internal/visitor/ImportNodeHandler.java index 5593241297..e356ed3525 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/visitor/ImportNodeHandler.java +++ b/theme-compiler/src/com/vaadin/sass/internal/visitor/ImportNodeHandler.java @@ -67,12 +67,9 @@ public class ImportNodeHandler { updateUrlInImportedSheet(imported, prefix); } - Node pre = importNode; - for (Node importedChild : new ArrayList<Node>( - imported.getChildren())) { - node.appendChild(importedChild, pre); - pre = importedChild; - } + node.appendChildrenAfter( + new ArrayList<Node>(imported.getChildren()), + importNode); node.removeChild(importNode); } catch (CSSException e) { e.printStackTrace(); diff --git a/theme-compiler/src/com/vaadin/sass/internal/visitor/MixinNodeHandler.java b/theme-compiler/src/com/vaadin/sass/internal/visitor/MixinNodeHandler.java index 4e54416522..feb1d7e622 100644 --- a/theme-compiler/src/com/vaadin/sass/internal/visitor/MixinNodeHandler.java +++ b/theme-compiler/src/com/vaadin/sass/internal/visitor/MixinNodeHandler.java @@ -45,19 +45,19 @@ public class MixinNodeHandler { private static void replaceMixinNode(MixinNode mixinNode, MixinDefNode mixinDef) { - Node pre = mixinNode; - MixinDefNode defClone = (MixinDefNode) DeepCopy.copy(mixinDef); defClone.traverse(); + defClone.replaceContentDirective(mixinNode); + if (mixinDef.getArglist().isEmpty()) { - for (Node child : new ArrayList<Node>(defClone.getChildren())) { - mixinNode.getParentNode().appendChild(child, pre); - pre = child; - } + mixinNode.getParentNode().appendChildrenAfter( + new ArrayList<Node>(defClone.getChildren()), mixinNode); } else { - - replacePossibleArguments(mixinNode, defClone); + if (mixinNode.getArglist() != null + && !mixinNode.getArglist().isEmpty()) { + replacePossibleArguments(mixinNode, defClone); + } Node previous = mixinNode; for (final Node child : new ArrayList<Node>(defClone.getChildren())) { @@ -81,7 +81,6 @@ public class MixinNodeHandler { */ private static void replacePossibleArguments(MixinNode mixinNode, MixinDefNode def) { - if (mixinNode.getArglist().size() > 0) { ArrayList<VariableNode> remainingNodes = new ArrayList<VariableNode>( def.getArglist()); diff --git a/theme-compiler/tests/resources/automatic/css/basic_arithmetics.css b/theme-compiler/tests/resources/automatic/css/basic_arithmetics.css new file mode 100644 index 0000000000..9fd33f2efe --- /dev/null +++ b/theme-compiler/tests/resources/automatic/css/basic_arithmetics.css @@ -0,0 +1,31 @@ +.foo { + font: 10px / 8px; + font: 5px; + margin-left: 9px; +} + +.foo { + size: 1; +} + +.foo { + bar: 8; + bar: 8; + bar: 12; +} + +.foo { + bar: 2 3; + bar: 5; + bar: 5; +} + +.foo { + bar: 2 -3; + bar: -1; + bar: -1; +} + +.foo { + bar: 14; +}
\ No newline at end of file diff --git a/theme-compiler/tests/resources/automatic/css/mixin-content-directive-with-vars.css b/theme-compiler/tests/resources/automatic/css/mixin-content-directive-with-vars.css new file mode 100644 index 0000000000..799d6ae90c --- /dev/null +++ b/theme-compiler/tests/resources/automatic/css/mixin-content-directive-with-vars.css @@ -0,0 +1,5 @@ +.colors { + background-color: blue; + color: white; + border-color: blue; +}
\ No newline at end of file diff --git a/theme-compiler/tests/resources/automatic/css/mixin-content-directive.css b/theme-compiler/tests/resources/automatic/css/mixin-content-directive.css new file mode 100644 index 0000000000..07813d1c99 --- /dev/null +++ b/theme-compiler/tests/resources/automatic/css/mixin-content-directive.css @@ -0,0 +1,20 @@ +.foobar { + color: red; +} + +.foobar { + background-color: blue; +} + +* html #logo { + background-image: url(/logo.gif); +} + +* html .link { + color: blue; +} + +.foobar { + color: red; + color: red; +}
\ No newline at end of file diff --git a/theme-compiler/tests/resources/automatic/scss/basic_arithmetics.scss b/theme-compiler/tests/resources/automatic/scss/basic_arithmetics.scss new file mode 100644 index 0000000000..cc913fe048 --- /dev/null +++ b/theme-compiler/tests/resources/automatic/scss/basic_arithmetics.scss @@ -0,0 +1,44 @@ +/* +*supports: +* 1. standard arithmetic operations (+, -, *, /, %) +* 2. / is treated as css operator, unless one of its operands is variable or there is another binary arithmetic operator +*limits: +* 1. cannot mix arithmetic and css operations, e.g. "margin: 1px + 3px 2px" will fail +* 2. space between add and minus operator and their following operand is mandatory. e.g. "1 + 2" is valid, "1+2" is not +* 3. parenthesis is not supported now. +*/ + +$div: 10px; +.foo { + font: 10px/8px; // Plain CSS, no division + font: $div/2; // Uses a variable, does division + margin-left: 5px + 8px/2px; //Uses +, does division +} + +.foo{ + size: 5 % 2; // modular +} + +$mul: 2*4; //valid multiply in variable +$mul1: 2 * 4; //valid multiply in variable +.foo{ + bar: $mul; + bar: $mul1; + bar: 3*4; //valid multiply in declaration +} + +.foo { + bar: 2 +3; //'+' is regarded as an unary operator, because no space between '+' and '3' + bar: 2+ 3; //valid add expression + bar: 2 + 3; //beautiful valid add expression +} + +.foo { + bar: 2 -3; //'-' is regarded as an unary operator, because no space between '-' and '3' + bar: 2 - 3; //beautiful valid minus expression + bar: 2- 3; //valid minus expression +} + +.foo { + bar: 2 + 3 * 4; // combinations +}
\ No newline at end of file diff --git a/theme-compiler/tests/resources/automatic/scss/mixin-content-directive-with-vars.scss b/theme-compiler/tests/resources/automatic/scss/mixin-content-directive-with-vars.scss new file mode 100644 index 0000000000..e7e0c3b7e6 --- /dev/null +++ b/theme-compiler/tests/resources/automatic/scss/mixin-content-directive-with-vars.scss @@ -0,0 +1,9 @@ +$color: white; +@mixin colors($color: blue) { + background-color: $color; + @content; + border-color: $color; +} +.colors { + @include colors { color: $color; } +}
\ No newline at end of file diff --git a/theme-compiler/tests/resources/automatic/scss/mixin-content-directive.scss b/theme-compiler/tests/resources/automatic/scss/mixin-content-directive.scss new file mode 100644 index 0000000000..71217cb814 --- /dev/null +++ b/theme-compiler/tests/resources/automatic/scss/mixin-content-directive.scss @@ -0,0 +1,40 @@ +@mixin my-mixin { + .foobar { + @content; + } +} + +@include my-mixin { + color: red; +} + +@include my-mixin { + background-color: blue; +} + +@mixin apply-to-ie6-only { + * html { + @content; + } +} +@include apply-to-ie6-only { + #logo { + background-image: url(/logo.gif); + } +} +@include apply-to-ie6-only { + .link { + color: blue; + } +} + +@mixin mixin-multi-contents { + .foobar { + @content; + @content; + } +} + +@include mixin-multi-contents { + color: red; +}
\ No newline at end of file diff --git a/theme-compiler/tests/src/com/vaadin/sass/internal/expression/ArithmeticExpressionEvaluatorTest.java b/theme-compiler/tests/src/com/vaadin/sass/internal/expression/ArithmeticExpressionEvaluatorTest.java new file mode 100644 index 0000000000..8978eb812e --- /dev/null +++ b/theme-compiler/tests/src/com/vaadin/sass/internal/expression/ArithmeticExpressionEvaluatorTest.java @@ -0,0 +1,125 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.sass.internal.expression; + +import org.junit.Assert; +import org.junit.Test; +import org.w3c.css.sac.LexicalUnit; + +import com.vaadin.sass.internal.expression.exception.IncompatibleUnitsException; +import com.vaadin.sass.internal.parser.LexicalUnitImpl; + +public class ArithmeticExpressionEvaluatorTest { + private ArithmeticExpressionEvaluator evaluator = new ArithmeticExpressionEvaluator(); + + @Test + public void testPrecedenceSameAsAppearOrder() { + // 2 * 3 - 4 = 2 + LexicalUnitImpl operand2 = LexicalUnitImpl.createInteger(0, 0, null, 2); + LexicalUnitImpl operatorMultiply = LexicalUnitImpl.createMultiply(0, 0, + operand2); + LexicalUnitImpl operand3 = LexicalUnitImpl.createInteger(0, 0, + operatorMultiply, 3); + LexicalUnitImpl operatorMinus = LexicalUnitImpl.createMinus(0, 0, + operand3); + LexicalUnitImpl operand4 = LexicalUnitImpl.createInteger(0, 0, + operatorMinus, 4); + LexicalUnitImpl result = evaluator.evaluate(operand2); + Assert.assertEquals(2, result.getIntegerValue()); + } + + @Test + public void testPrecedenceDifferFromAppearOrder() { + // 2 - 3 * 4 = -10 + LexicalUnitImpl operand2 = LexicalUnitImpl.createInteger(0, 0, null, 2); + LexicalUnitImpl operatorMinus = LexicalUnitImpl.createMinus(0, 0, + operand2); + LexicalUnitImpl operand3 = LexicalUnitImpl.createInteger(0, 0, + operatorMinus, 3); + LexicalUnitImpl operatorMultiply = LexicalUnitImpl.createMultiply(0, 0, + operand3); + LexicalUnitImpl operand4 = LexicalUnitImpl.createInteger(0, 0, + operatorMultiply, 4); + LexicalUnitImpl result = evaluator.evaluate(operand2); + Assert.assertEquals(-10, result.getIntegerValue()); + } + + @Test(expected = IncompatibleUnitsException.class) + public void testIncompatibleUnit() { + // 2cm - 3px + LexicalUnitImpl operand2 = LexicalUnitImpl.createCM(0, 0, null, 2); + LexicalUnitImpl operatorMinus = LexicalUnitImpl.createMinus(0, 0, + operand2); + LexicalUnitImpl operand3 = LexicalUnitImpl.createPX(0, 0, + operatorMinus, 3); + evaluator.evaluate(operand2); + } + + @Test + public void testMultiplyWithUnitInfirstOperand() { + // 2cm * 3 = 6cm + LexicalUnitImpl operand2cm = LexicalUnitImpl.createCM(0, 0, null, 2); + LexicalUnitImpl operatorMultiply = LexicalUnitImpl.createMultiply(0, 0, + operand2cm); + LexicalUnitImpl operand3 = LexicalUnitImpl.createInteger(0, 0, + operatorMultiply, 3); + LexicalUnitImpl result = evaluator.evaluate(operand2cm); + Assert.assertEquals(6, result.getIntegerValue()); + Assert.assertEquals(LexicalUnit.SAC_CENTIMETER, + result.getLexicalUnitType()); + } + + @Test + public void testMultiplyWithUnitInSecondOperand() { + // 2 * 3cm = 6cm + LexicalUnitImpl operand2 = LexicalUnitImpl.createInteger(0, 0, null, 2); + LexicalUnitImpl operatorMultiply = LexicalUnitImpl.createMultiply(0, 0, + operand2); + LexicalUnitImpl operand3cm = LexicalUnitImpl.createCM(0, 0, + operatorMultiply, 3); + LexicalUnitImpl result = evaluator.evaluate(operand2); + Assert.assertEquals(6, result.getIntegerValue()); + Assert.assertEquals(LexicalUnit.SAC_CENTIMETER, + result.getLexicalUnitType()); + } + + @Test + public void testDivideWithSameUnit() { + // 4cm / 2cm = 2 + LexicalUnitImpl operand4cm = LexicalUnitImpl.createCM(0, 0, null, 4); + LexicalUnitImpl operatorDivide = LexicalUnitImpl.createSlash(0, 0, + operand4cm); + LexicalUnitImpl operand2cm = LexicalUnitImpl.createCM(0, 0, + operatorDivide, 2); + LexicalUnitImpl result = evaluator.evaluate(operand4cm); + Assert.assertEquals(2, result.getIntegerValue()); + Assert.assertEquals(LexicalUnit.SAC_REAL, result.getLexicalUnitType()); + } + + @Test + public void testDivideDenominatorWithoutUnit() { + // 4cm / 2 = 2cm + LexicalUnitImpl operand4cm = LexicalUnitImpl.createCM(0, 0, null, 4); + LexicalUnitImpl operatorDivide = LexicalUnitImpl.createSlash(0, 0, + operand4cm); + LexicalUnitImpl operand2 = LexicalUnitImpl.createInteger(0, 0, + operatorDivide, 2); + LexicalUnitImpl result = evaluator.evaluate(operand4cm); + Assert.assertEquals(2, result.getIntegerValue()); + Assert.assertEquals(LexicalUnit.SAC_CENTIMETER, + result.getLexicalUnitType()); + } +} diff --git a/theme-compiler/tests/src/com/vaadin/sass/resolvers/VaadinResolverTest.java b/theme-compiler/tests/src/com/vaadin/sass/resolvers/VaadinResolverTest.java new file mode 100644 index 0000000000..59b49888c2 --- /dev/null +++ b/theme-compiler/tests/src/com/vaadin/sass/resolvers/VaadinResolverTest.java @@ -0,0 +1,83 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +/** + * + */ +package com.vaadin.sass.resolvers; + +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +import java.lang.reflect.Method; + +import org.junit.Assert; +import org.junit.Test; + +import com.vaadin.sass.internal.resolver.VaadinResolver; + +public class VaadinResolverTest { + + @Test + public void testPathNormalization() throws Exception { + + VaadinResolver resolver = new VaadinResolver(); + + Method normalizeMethod = VaadinResolver.class.getDeclaredMethod( + "normalize", String.class); + normalizeMethod.setAccessible(true); + + String identifier, result; + + identifier = "a/b/../../../a b/b.scss"; + result = (String) normalizeMethod.invoke(resolver, identifier); + Assert.assertEquals("../a b/b.scss", result); + + identifier = "./a/b/../c/d/.././e.scss"; + result = (String) normalizeMethod.invoke(resolver, identifier); + Assert.assertEquals("a/c/e.scss", result); + + identifier = "/äåäåäääå/:;:;:;/???????/- -/e.scss"; + result = (String) normalizeMethod.invoke(resolver, identifier); + Assert.assertEquals("/äåäåäääå/:;:;:;/???????/- -/e.scss", result); + + identifier = "."; + result = (String) normalizeMethod.invoke(resolver, identifier); + Assert.assertEquals("", result); + + identifier = "../.."; + result = (String) normalizeMethod.invoke(resolver, identifier); + Assert.assertEquals("../..", result); + + identifier = "./../a.scss"; + result = (String) normalizeMethod.invoke(resolver, identifier); + Assert.assertEquals("../a.scss", result); + } + +} diff --git a/themes/build.xml b/themes/build.xml index 6e9e3c9951..75b3e5a903 100644 --- a/themes/build.xml +++ b/themes/build.xml @@ -99,8 +99,8 @@ <target name="checkstyle"> <echo>No java files in module</echo> </target> - <target name="tests" depends="checkstyle"> - <!--<antcall target="common.tests.run" />--> + <target name="test" depends="checkstyle"> + <!--<antcall target="common.test.run" />--> <echo>WHAT? No tests for ${module.name}!</echo> </target> diff --git a/uitest/build.xml b/uitest/build.xml index 53da0ae2e9..453fc26ea5 100644 --- a/uitest/build.xml +++ b/uitest/build.xml @@ -16,7 +16,7 @@ </path> <target name="dependencies"> - <!-- This is copied from common.xml to be able to add server.tests.source to the source path --> + <!-- This is copied from common.xml to be able to add server.test.source to the source path --> <ivy:resolve resolveid="common" conf="build, build-provided" /> <ivy:cachepath pathid="classpath.compile.dependencies" conf="build, build-provided" /> @@ -28,12 +28,12 @@ <property name="result.dir" location="result" /> <property name="src" location="${result.dir}/../src" /> <property name="classes" location="${result.dir}/classes" /> - <property name="server.tests.sources" location="${result.dir}/../../server/tests/src" /> + <property name="server.test.sources" location="${result.dir}/../../server/tests/src" /> <mkdir dir="${classes}" /> <!-- TODO: Get rid of this --> <javac destdir="${classes}" source="${vaadin.java.version}" target="${vaadin.java.version}" debug="true" encoding="UTF-8" includeantruntime="false"> - <src path="${server.tests.sources}" /> + <src path="${server.test.sources}" /> <include name="com/vaadin/tests/data/bean/**" /> <include name="com/vaadin/tests/VaadinClasses.java" /> <include name="com/vaadin/data/util/sqlcontainer/SQLTestsConstants.java" /> @@ -140,8 +140,8 @@ <target name="checkstyle"> <echo>Checkstyle is disabled for uitest for now</echo> </target> - <target name="tests" depends="checkstyle"> - <!--<antcall target="common.tests.run" />--> + <target name="test" depends="checkstyle"> + <!--<antcall target="common.test.run" />--> <echo>WHAT? No JUnit tests for ${module.name}!</echo> </target> diff --git a/uitest/integration-testscripts/common/integration_push_test.tpl b/uitest/integration-testscripts/common/integration_push_test.tpl new file mode 100644 index 0000000000..4e93f628e5 --- /dev/null +++ b/uitest/integration-testscripts/common/integration_push_test.tpl @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://vaadin-integration-tests:8080/" /> +<title>integration_test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">integration_test</td></tr> +</thead><tbody> +<tr> + <td>openAndWait</td> + <td>/demo/run-push/com.vaadin.tests.integration.IntegrationTestApplication?restartApplication</td> + <td></td> +</tr> +<tr> + <td>pause</td> + <td>1000</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>initial</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=demorunpushcomvaadintestsintegrationIntegrationTestApplication::/VVerticalLayout[0]/ChildComponentContainer[0]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td> + <td>51,13</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>finland</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/uitest/integration_tests.xml b/uitest/integration_tests.xml index f8d250554c..cb96834307 100644 --- a/uitest/integration_tests.xml +++ b/uitest/integration_tests.xml @@ -13,7 +13,7 @@ <fail unless="test.integration.antfile" message="test.integration.antfile must be set for integration tests to run" /> <!-- Test with these browsers --> - <property name="test_browsers" value="winxp-firefox11" /> + <property name="test_browsers" value="winxp-firefox17-esr" /> <!-- Path to key file. Default value --> <property name="sshkey.file" value="id_dsa" /> @@ -54,6 +54,23 @@ <fileset dir="." includes="test.xml" /> </subant> </target> + + <target name="integration-test-push-servlet"> + <fileset dir="integration-testscripts" id="html-test-files" includes="integration-test-${server-name}-push-servlet.html" /> + <pathconvert pathsep=" " property="testfiles" refid="html-test-files" /> + <subant target="run-tests" failonerror="false" antfile="test.xml"> + <property name="com.vaadin.testbench.lib.dir" value="${com.vaadin.testbench.lib.dir}" /> + <property name="com.vaadin.testbench.tester.host" value="${com.vaadin.testbench.tester.host}" /> + <property name="com.vaadin.testbench.deployment.url" value="${deployment.url}" /> + <property name="server.start.succeeded" value="1" /> + <property name="browsers" value="${test_browsers}" /> + <property name="testfiles" value="${testfiles}" /> + <property name="test-output-dir" value="${integration_test.dir}/result/integration-test-output/${server-name}" /> + <property name="retries" value="0" /> + + <fileset dir="." includes="test.xml" /> + </subant> + </target> <target name="integration-test-theme"> <subant target="run-tests" failonerror="false" antfile="test.xml"> @@ -427,6 +444,24 @@ </antcontrib:then> </antcontrib:if> + <!-- Run integration tests with push --> + <copy file="integration-testscripts/common/integration_push_test.tpl" tofile="integration-testscripts/integration-test-${target-server}-push-servlet.html" overwrite="true" /> + <antcall target="integration-test-push-servlet"> + <param name="server-name" value="${target-server}" /> + <param name="deployment.url" value="http://${target-host}:${target-port}" /> + </antcall> + + <!-- Run theme tests in all browsers if there's a property with the test files --> + <antcontrib:if> + <isset property="testfiles-theme" /> + <antcontrib:then> + <antcall target="integration-test-theme"> + <param name="server-name" value="${target-server}" /> + <param name="deployment.url" value="http://${target-host}:${target-port}" /> + </antcall> + </antcontrib:then> + </antcontrib:if> + <!-- timeout in five minutes --> <sshexec host="${target-host}" outputproperty="stop-output" timeout="600000" username="${user}" keyfile="${sshkey.file}" trust="yes" command="ant -f deploy.xml shutdown-and-cleanup" failonerror="false" /> <antcall target="echo-prefix"> diff --git a/uitest/ivy.xml b/uitest/ivy.xml index 4196cca4da..bb54232852 100644 --- a/uitest/ivy.xml +++ b/uitest/ivy.xml @@ -40,6 +40,8 @@ rev="${vaadin.version}" conf="build->build"></dependency> <dependency org="com.vaadin" name="vaadin-themes" rev="${vaadin.version}" conf="build->build"></dependency> + <dependency org="com.vaadin" name="vaadin-push" + rev="${vaadin.version}" conf="build->build"></dependency> <!-- For compiling TestingWidgetSet --> <dependency org="com.vaadin" name="vaadin-client-compiler" @@ -48,22 +50,23 @@ <!-- Newest Jetty does not work with Ivy currently (orbit -> jar mapping problem) --> <dependency org="org.eclipse.jetty" name="jetty-server" - rev="7.4.5.v20110725" conf="build, ide, jetty-run->default" /> - <!-- jetty-servlets needed in .war by ProxyTest, but not by jetty-runner --> + rev="7.5.0.v20110901" conf="ide, build-provided, jetty-run->default" /> + <!-- jetty-servlets needed by ProxyTest, but not by jetty-runner --> <dependency org="org.eclipse.jetty" name="jetty-servlets" - rev="7.4.5.v20110725" conf="build, ide->default" /> - <!-- <dependency org="org.mortbay.jetty" name="jetty-util" --> - <!-- rev="8.1.5.v20120716" conf="build,ide,jetty-run->default" /> --> + rev="7.5.0.v20110901" conf="ide, build-provided, jetty-run->default" /> + <dependency org="org.eclipse.jetty" name="jetty-websocket" + rev="7.5.0.v20110901" conf="ide, jetty-run->default" /> <dependency org="org.eclipse.jetty" name="jetty-webapp" - rev="7.4.5.v20110725" conf="build, ide,jetty-run->default" /> + rev="7.5.0.v20110901" conf="ide, build-provided, jetty-run->default" /> <dependency org="org.mortbay.jetty" name="jetty-runner" - rev="7.4.5.v20110725" conf="jetty-run->default" /> + rev="7.5.0.v20110901" conf="jetty-run->default" /> + <dependency org="junit" name="junit" rev="4.5" conf="build,ide -> default" /> <dependency org="commons-codec" name="commons-codec" rev="1.5" conf="build,ide->default" /> - <dependency org="commons-io" name="commons-io" rev="1.4" + <dependency org="commons-io" name="commons-io" rev="2.2" conf="build,ide->default" /> <!-- Mainly for SQLContainer tests --> <dependency org="org.hsqldb" name="hsqldb" rev="2.2.6" diff --git a/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java b/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java index ec060bc73a..070cd2834d 100644 --- a/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java +++ b/uitest/src/com/vaadin/launcher/DevelopmentServerLauncher.java @@ -69,9 +69,12 @@ public class DevelopmentServerLauncher { // Pass-through of arguments for Jetty final Map<String, String> serverArgs = parseArguments(args); + if (!serverArgs.containsKey("shutdownPort")) { + serverArgs.put("shutdownPort", "8889"); + } - if (serverArgs.containsKey("shutdownPort")) { - int port = Integer.parseInt(serverArgs.get("shutdownPort")); + int port = Integer.parseInt(serverArgs.get("shutdownPort")); + if (port > 0) { try { // Try to notify another instance that it's time to close Socket socket = new Socket((String) null, port); @@ -105,7 +108,7 @@ public class DevelopmentServerLauncher { try { assert false; - throw new RuntimeException("You should run " + System.err.println("You should run " + DevelopmentServerLauncher.class.getSimpleName() + " with assertions enabled. Add -ea as a VM argument."); } catch (AssertionError e) { diff --git a/uitest/src/com/vaadin/tests/application/ThreadLocalInstances.java b/uitest/src/com/vaadin/tests/application/ThreadLocalInstances.java index b6774ae56a..ed611117d0 100644 --- a/uitest/src/com/vaadin/tests/application/ThreadLocalInstances.java +++ b/uitest/src/com/vaadin/tests/application/ThreadLocalInstances.java @@ -52,7 +52,12 @@ public class ThreadLocalInstances extends AbstractTestCase { private final FlagSeResource resource = new FlagSeResource() { @Override public DownloadStream getStream() { - reportCurrentStatus("resource handler"); + ThreadLocalInstances.this.getContext().lock(); + try { + reportCurrentStatus("resource handler"); + } finally { + ThreadLocalInstances.this.getContext().unlock(); + } return super.getStream(); } }; diff --git a/uitest/src/com/vaadin/tests/applicationcontext/CloseSession.java b/uitest/src/com/vaadin/tests/applicationcontext/CloseSession.java index 446638f8a2..cca76cc9e3 100644 --- a/uitest/src/com/vaadin/tests/applicationcontext/CloseSession.java +++ b/uitest/src/com/vaadin/tests/applicationcontext/CloseSession.java @@ -18,13 +18,16 @@ package com.vaadin.tests.applicationcontext; import javax.servlet.http.HttpSession; +import com.vaadin.server.Page; import com.vaadin.server.VaadinRequest; import com.vaadin.server.VaadinService; +import com.vaadin.server.VaadinSession; import com.vaadin.server.WrappedHttpSession; import com.vaadin.tests.components.AbstractTestUI; import com.vaadin.tests.util.Log; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.UI; public class CloseSession extends AbstractTestUI { private static final String OLD_HASH_PARAM = "oldHash"; @@ -158,6 +161,17 @@ public class CloseSession extends AbstractTestUI { @Override public void detach() { super.detach(); - System.out.println("UI " + getUIId() + " detached"); + log.log("Detach of " + this + " (" + getUIId() + ")"); + boolean correctUI = (UI.getCurrent() == this); + boolean correctPage = (Page.getCurrent() == getPage()); + boolean correctVaadinSession = (VaadinSession.getCurrent() == getSession()); + boolean correctVaadinService = (VaadinService.getCurrent() == getSession() + .getService()); + log.log("UI.current correct in detach: " + correctUI); + log.log("Page.current correct in detach: " + correctPage); + log.log("VaadinSession.current correct in detach: " + + correctVaadinSession); + log.log("VaadinService.current correct in detach: " + + correctVaadinService); } } diff --git a/uitest/src/com/vaadin/tests/applicationcontext/CloseUI.java b/uitest/src/com/vaadin/tests/applicationcontext/CloseUI.java new file mode 100644 index 0000000000..bec8c0a10f --- /dev/null +++ b/uitest/src/com/vaadin/tests/applicationcontext/CloseUI.java @@ -0,0 +1,160 @@ +/* + * Copyright 2012 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tests.applicationcontext; + +import com.vaadin.server.Page; +import com.vaadin.server.VaadinRequest; +import com.vaadin.server.VaadinService; +import com.vaadin.server.VaadinSession; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.tests.util.Log; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.UI; + +public class CloseUI extends AbstractTestUI { + private static final String OLD_HASH_PARAM = "oldHash"; + private static final String OLD_SESSION_ID_PARAM = "oldSessionId"; + + private final Log log = new Log(6); + + @Override + protected void setup(VaadinRequest request) { + System.out.println("UI " + getUIId() + " inited"); + + final int sessionHash = getSession().hashCode(); + final String sessionId = request.getWrappedSession().getId(); + + log.log("Current session hashcode: " + sessionHash); + log.log("Current WrappedSession id: " + sessionId); + + // Log previous values to make it easier to see what has changed + String oldHashValue = request.getParameter(OLD_HASH_PARAM); + if (oldHashValue != null) { + log.log("Old session hashcode: " + oldHashValue); + log.log("Same hash as current? " + + oldHashValue.equals(Integer.toString(sessionHash))); + } + + String oldSessionId = request.getParameter(OLD_SESSION_ID_PARAM); + if (oldSessionId != null) { + log.log("Old WrappedSession id: " + oldSessionId); + log.log("Same WrappedSession id? " + oldSessionId.equals(sessionId)); + } + + addComponent(log); + addComponent(new Button("Log 'hello'", new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + log.log("Hello"); + } + })); + addComponent(new Button("Close UI", new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + close(); + } + })); + + addComponent(new Button("Close UI (background)", + new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + new UIRunSafelyThread(CloseUI.this) { + @Override + protected void runSafely() { + close(); + }; + }.start(); + } + })); + addComponent(new Button( + "Close UI and redirect to /statictestfiles/static.html", + new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + getPage().setLocation("/statictestfiles/static.html"); + close(); + } + })); + addComponent(new Button( + "Close UI and redirect to /statictestfiles/static.html (background)", + new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + new UIRunSafelyThread(CloseUI.this) { + + @Override + protected void runSafely() { + getPage().setLocation( + "/statictestfiles/static.html"); + close(); + } + }.start(); + } + })); + + } + + private abstract class RunSafelyThread extends Thread { + private UI ui; + + public RunSafelyThread(UI ui) { + this.ui = ui; + } + + @Override + public void run() { + ui.access(new Runnable() { + + @Override + public void run() { + runSafely(); + } + }); + } + + protected abstract void runSafely(); + } + + @Override + protected String getTestDescription() { + return "Test for closing the session and redirecting the user"; + } + + @Override + protected Integer getTicketNumber() { + return Integer.valueOf(9859); + } + + @Override + public void detach() { + super.detach(); + log.log("Detach of " + this + " (" + getUIId() + ")"); + boolean correctUI = (UI.getCurrent() == this); + boolean correctPage = (Page.getCurrent() == getPage()); + boolean correctVaadinSession = (VaadinSession.getCurrent() == getSession()); + boolean correctVaadinService = (VaadinService.getCurrent() == getSession() + .getService()); + log.log("UI.current correct in detach: " + correctUI); + log.log("Page.current correct in detach: " + correctPage); + log.log("VaadinSession.current correct in detach: " + + correctVaadinSession); + log.log("VaadinService.current correct in detach: " + + correctVaadinService); + } +} diff --git a/uitest/src/com/vaadin/tests/applicationcontext/RpcForClosedUI.html b/uitest/src/com/vaadin/tests/applicationcontext/RpcForClosedUI.html new file mode 100644 index 0000000000..642e31b22c --- /dev/null +++ b/uitest/src/com/vaadin/tests/applicationcontext/RpcForClosedUI.html @@ -0,0 +1,44 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://192.168.2.75:8888/" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/CloseUI?restartApplication</td> + <td></td> +</tr> +<!--Close the UI in a background thread--> +<tr> + <td>click</td> + <td>vaadin=runCloseUI::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Try to log 'hello'--> +<tr> + <td>click</td> + <td>vaadin=runCloseUI::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Ensure 'hello' was not logged--> +<tr> + <td>assertText</td> + <td>vaadin=runCloseUI::PID_SLog_row_0</td> + <td>2. Current WrappedSession id: *</td> +</tr> +<tr> + <td>assertTextNotPresent</td> + <td>Hello</td> + <td></td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/applicationcontext/UIRunSafelyThread.java b/uitest/src/com/vaadin/tests/applicationcontext/UIRunSafelyThread.java new file mode 100644 index 0000000000..ddc0f28664 --- /dev/null +++ b/uitest/src/com/vaadin/tests/applicationcontext/UIRunSafelyThread.java @@ -0,0 +1,24 @@ +package com.vaadin.tests.applicationcontext; + +import com.vaadin.ui.UI; + +public abstract class UIRunSafelyThread extends Thread { + private UI ui; + + public UIRunSafelyThread(UI ui) { + this.ui = ui; + } + + @Override + public void run() { + ui.access(new Runnable() { + + @Override + public void run() { + runSafely(); + } + }); + } + + protected abstract void runSafely(); +}
\ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/components/button/ButtonWithShortcutNotRendered.java b/uitest/src/com/vaadin/tests/components/button/ButtonWithShortcutNotRendered.java index b01e0a85d0..f866928054 100644 --- a/uitest/src/com/vaadin/tests/components/button/ButtonWithShortcutNotRendered.java +++ b/uitest/src/com/vaadin/tests/components/button/ButtonWithShortcutNotRendered.java @@ -83,6 +83,7 @@ public class ButtonWithShortcutNotRendered extends AbstractTestUI { addValueChangeListener(new Property.ValueChangeListener() { + @Override public void valueChange( com.vaadin.data.Property.ValueChangeEvent event) { final Item item = getItem(getValue()); @@ -162,6 +163,7 @@ public class ButtonWithShortcutNotRendered extends AbstractTestUI { } } + @Override public void buttonClick(ClickEvent event) { // NOP } diff --git a/uitest/src/com/vaadin/tests/components/button/ButtonsWaiAria.html b/uitest/src/com/vaadin/tests/components/button/ButtonsWaiAria.html new file mode 100644 index 0000000000..8d10438c1d --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/button/ButtonsWaiAria.html @@ -0,0 +1,77 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://127.0.0.1:8080/" /> +<title>ButtonsWaiAria</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">ButtonsWaiAria</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.button.ButtonsWaiAria?restartApplication</td> + <td></td> +</tr> +<tr> + <td>verifyElementPresent</td> + <td>vaadin=runcomvaadintestscomponentsbuttonButtonsWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VButton[0]/</td> + <td></td> +</tr> +<tr> + <td>assertAttribute</td> + <td>vaadin=runcomvaadintestscomponentsbuttonButtonsWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VButton[0]@role</td> + <td>button</td> +</tr> +<tr> + <td>verifyElementPresent</td> + <td>vaadin=runcomvaadintestscomponentsbuttonButtonsWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VButton[0]/</td> + <td></td> +</tr> +<tr> + <td>assertElementPresent</td> + <td>xpath=/html/body/div/div/div[2]/div/div[2]/div/div[5]/div/span/img[@alt='']</td> + <td></td> +</tr> +<tr> + <td>verifyElementPresent</td> + <td>vaadin=runcomvaadintestscomponentsbuttonButtonsWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VButton[0]/</td> + <td></td> +</tr> +<tr> + <td>assertAttribute</td> + <td>vaadin=runcomvaadintestscomponentsbuttonButtonsWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VButton[0]/domChild[0]/domChild[0]@alt</td> + <td>user icon</td> +</tr> +<tr> + <td>verifyElementPresent</td> + <td>vaadin=runcomvaadintestscomponentsbuttonButtonsWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[4]/VButton[0]/</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsbuttonButtonsWaiAria::PID_Scheckboxaction-Enabled/domChild[0]</td> + <td>7,7</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>vaadin=runcomvaadintestscomponentsbuttonButtonsWaiAria::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VButton[0]/@aria-disabled</td> + <td>true</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsbuttonButtonsWaiAria::PID_Scheckboxaction-Enabled/domChild[0]</td> + <td>7,7</td> +</tr> +<tr> + <td>assertElementNotPresent</td> + <td>/html/body/div/div/div[2]/div/div[2]/div/div[3]/div[@aria-disabled]</td> + <td></td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/button/ButtonsWaiAria.java b/uitest/src/com/vaadin/tests/components/button/ButtonsWaiAria.java new file mode 100644 index 0000000000..1208b8be3b --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/button/ButtonsWaiAria.java @@ -0,0 +1,51 @@ +package com.vaadin.tests.components.button; + +import com.vaadin.tests.components.ComponentTestCase; +import com.vaadin.ui.Button; +import com.vaadin.ui.NativeButton; + +public class ButtonsWaiAria extends ComponentTestCase<Button> { + + @Override + protected Class<Button> getTestClass() { + return Button.class; + } + + @Override + protected void initializeComponents() { + + Button l; + boolean nat = false; + + l = createButton("Default Button", nat); + addTestComponent(l); + + l = createButton("Icon Button, empty alt", nat); + l.setIcon(ICON_16_USER_PNG_CACHEABLE); + addTestComponent(l); + + l = createButton("Icon Button with alt", nat); + l.setIcon(ICON_16_USER_PNG_CACHEABLE, "user icon"); + addTestComponent(l); + + l = createButton("Tooltip Button", nat); + l.setDescription("Tooltip"); + addTestComponent(l); + } + + private Button createButton(String text, boolean nativeButton) { + Button b; + if (nativeButton) { + b = new NativeButton(text); + } else { + b = new Button(text); + } + + return b; + } + + @Override + protected String getDescription() { + return "A generic test for Buttons in different configurations"; + } +} diff --git a/uitest/src/com/vaadin/tests/components/calendar/BeanItemContainerTestUI.java b/uitest/src/com/vaadin/tests/components/calendar/BeanItemContainerTestUI.java new file mode 100644 index 0000000000..83fc4a03cb --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/BeanItemContainerTestUI.java @@ -0,0 +1,184 @@ +/** + * Copyright 2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.calendar; + +import java.util.Arrays; +import java.util.Date; + +import com.vaadin.data.fieldgroup.FieldGroup; +import com.vaadin.data.fieldgroup.FieldGroup.CommitException; +import com.vaadin.data.util.BeanItem; +import com.vaadin.data.util.BeanItemContainer; +import com.vaadin.event.Action; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.datefield.Resolution; +import com.vaadin.ui.Calendar; +import com.vaadin.ui.DateField; +import com.vaadin.ui.FormLayout; +import com.vaadin.ui.Table; +import com.vaadin.ui.TextField; +import com.vaadin.ui.UI; +import com.vaadin.ui.VerticalSplitPanel; +import com.vaadin.ui.Window; +import com.vaadin.ui.Window.CloseEvent; +import com.vaadin.ui.components.calendar.ContainerEventProvider; +import com.vaadin.ui.components.calendar.event.BasicEvent; + +public class BeanItemContainerTestUI extends UI { + + private Calendar calendar; + + private Table table; + + private BeanItemContainer<BasicEvent> events = new BeanItemContainer<BasicEvent>( + BasicEvent.class); + + @SuppressWarnings("deprecation") + @Override + protected void init(VaadinRequest request) { + VerticalSplitPanel content = new VerticalSplitPanel(); + content.setSizeFull(); + setContent(content); + + // Create Calendar + calendar = new Calendar(); + calendar.setImmediate(true); + calendar.setSizeFull(); + calendar.setContainerDataSource(events); + calendar.setStartDate(new Date(100, 1, 1)); + calendar.setEndDate(new Date(100, 2, 1)); + + content.addComponent(calendar); + + // Add event table connected to same data source + table = createTable(); + table.setContainerDataSource(events); + table.setVisibleColumns(new Object[] { "caption", "description", + "start", "end" }); + content.addComponent(table); + } + + /** + * Creates a table with some actions + * + * @return + */ + private Table createTable() { + Table table = new Table(); + table.setSizeFull(); + table.addActionHandler(new Action.Handler() { + + private final Action ADD = new Action("Add event"); + private final Action EDIT = new Action("Edit event"); + private final Action REMOVE = new Action("Remove event"); + + @Override + public void handleAction(Action action, Object sender, Object target) { + if (action == ADD) { + BasicEvent event = new BasicEvent(); + event.setStart(new Date(100, 1, 1)); + event.setEnd(new Date(100, 1, 1)); + editEvent(event); + } else if (action == EDIT) { + editEvent((BasicEvent) target); + } else if (action == REMOVE) { + events.removeItem(target); + } + } + + @Override + public Action[] getActions(Object target, Object sender) { + if (target == null) { + return new Action[] { ADD }; + } else { + return new Action[] { EDIT, REMOVE }; + } + } + }); + return table; + } + + /** + * Opens up a modal dialog window where an event can be modified + * + * @param event + * The event to modify + */ + private void editEvent(final BasicEvent event) { + Window modal = new Window("Add event"); + modal.setModal(true); + modal.setResizable(false); + modal.setDraggable(false); + modal.setWidth("300px"); + final FieldGroup fieldGroup = new FieldGroup(); + + FormLayout formLayout = new FormLayout(); + TextField captionField = new TextField("Caption"); + captionField.setImmediate(true); + TextField descriptionField = new TextField("Description"); + descriptionField.setImmediate(true); + DateField startField = new DateField("Start"); + startField.setResolution(Resolution.MINUTE); + startField.setImmediate(true); + DateField endField = new DateField("End"); + endField.setImmediate(true); + endField.setResolution(Resolution.MINUTE); + + formLayout.addComponent(captionField); + formLayout.addComponent(descriptionField); + formLayout.addComponent(startField); + formLayout.addComponent(endField); + + fieldGroup.bind(captionField, ContainerEventProvider.CAPTION_PROPERTY); + fieldGroup.bind(descriptionField, + ContainerEventProvider.DESCRIPTION_PROPERTY); + fieldGroup.bind(startField, ContainerEventProvider.STARTDATE_PROPERTY); + fieldGroup.bind(endField, ContainerEventProvider.ENDDATE_PROPERTY); + + fieldGroup.setItemDataSource(new BeanItem<BasicEvent>(event, Arrays + .asList(ContainerEventProvider.CAPTION_PROPERTY, + ContainerEventProvider.DESCRIPTION_PROPERTY, + ContainerEventProvider.STARTDATE_PROPERTY, + ContainerEventProvider.ENDDATE_PROPERTY))); + modal.setContent(formLayout); + modal.addCloseListener(new Window.CloseListener() { + @Override + public void windowClose(CloseEvent e) { + // Commit changes to bean + try { + fieldGroup.commit(); + } catch (CommitException e1) { + e1.printStackTrace(); + } + + if (events.containsId(event)) { + /* + * BeanItemContainer does not notify container listeners + * when the bean changes so we need to trigger a + * ItemSetChange event + */ + BasicEvent dummy = new BasicEvent(); + events.addBean(dummy); + events.removeItem(dummy); + + } else { + events.addBean(event); + } + } + }); + getUI().addWindow(modal); + } +} diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarActions.html b/uitest/src/com/vaadin/tests/components/calendar/CalendarActions.html new file mode 100644 index 0000000000..bec3f2aded --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarActions.html @@ -0,0 +1,51 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://localhost:8080/" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.calendar.CalendarActionsUI?restartApplication</td> + <td></td> +</tr> +<tr> + <td>contextMenuAt</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarActionsUI::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[7]</td> + <td>100,20</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>addEventContextMenu</td> +</tr> +<tr> + <td>contextMenuAt</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarActionsUI::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[2]/domChild[0]/domChild[8]</td> + <td>100,20</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>removeEventContextMenu</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarActionsUI::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[3]/domChild[0]/domChild[8]</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>NoContextMenu</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarActionsUI.java b/uitest/src/com/vaadin/tests/components/calendar/CalendarActionsUI.java new file mode 100644 index 0000000000..ee898e0790 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarActionsUI.java @@ -0,0 +1,110 @@ +/** + * Copyright 2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.calendar; + +import java.util.Date; +import java.util.Locale; + +import com.vaadin.event.Action; +import com.vaadin.server.VaadinRequest; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Calendar; +import com.vaadin.ui.GridLayout; +import com.vaadin.ui.UI; +import com.vaadin.ui.components.calendar.CalendarDateRange; +import com.vaadin.ui.components.calendar.event.BasicEvent; + +public class CalendarActionsUI extends UI { + + @SuppressWarnings("deprecation") + @Override + protected void init(VaadinRequest request) { + GridLayout content = new GridLayout(1, 2); + content.setSizeFull(); + setContent(content); + + final Calendar calendar = new Calendar(); + calendar.setLocale(new Locale("fi", "FI")); + + calendar.setSizeFull(); + calendar.setStartDate(new Date(100, 1, 1)); + calendar.setEndDate(new Date(100, 2, 1)); + + calendar.addActionHandler(new Action.Handler() { + + public final Action NEW_EVENT = new Action("Add event"); + public final Action EDIT_EVENT = new Action("Edit event"); + public final Action REMOVE_EVENT = new Action("Remove event"); + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.event.Action.Handler#handleAction(com.vaadin.event + * .Action, java.lang.Object, java.lang.Object) + */ + @Override + public void handleAction(Action action, Object sender, Object target) { + Date date = (Date) target; + if (action == NEW_EVENT) { + BasicEvent event = new BasicEvent("New event", + "Hello world", date, date); + calendar.addEvent(event); + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.event.Action.Handler#getActions(java.lang.Object, + * java.lang.Object) + */ + @Override + public Action[] getActions(Object target, Object sender) { + CalendarDateRange date = (CalendarDateRange) target; + + java.util.Calendar cal = java.util.Calendar.getInstance(); + cal.set(2000, 1, 1, 12, 0, 0); + + if (date.inRange(cal.getTime())) { + return new Action[] { NEW_EVENT, }; + } + + cal.add(java.util.Calendar.DAY_OF_WEEK, 1); + + if (date.inRange(cal.getTime())) { + return new Action[] { REMOVE_EVENT }; + } + + return null; + } + }); + + content.addComponent(calendar); + + content.addComponent(new Button("Set week view", + new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + calendar.setEndDate(new Date(100, 1, 7)); + } + })); + + content.setRowExpandRatio(0, 1); + + } +} diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarBasicNavigation.html b/uitest/src/com/vaadin/tests/components/calendar/CalendarBasicNavigation.html new file mode 100644 index 0000000000..cccb88bfb2 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarBasicNavigation.html @@ -0,0 +1,236 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>basicNavigation</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">basicNavigation</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&width=1000px&height=600px&restartApplication</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td> + <td>Jan 2000</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td> + <td>Feb 2000</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td> + <td>Mar 2000</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td> + <td>Feb 2000</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td> + <td>Jan 2000</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>26</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[5]/domChild[6]/domChild[0]/domChild[0]</td> + <td>5</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td> + <td>9,44</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td> + <td>Sunday 1/9/00</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[7]/domChild[0]</td> + <td>Saturday 1/15/00</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>1 AM</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[23]</td> + <td>11 PM</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[8]/domChild[0]</td> + <td>4,6</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[8]/domChild[0]</td> + <td>4,5</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td> + <td>Sunday 1/23/00</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[7]/domChild[0]</td> + <td>Saturday 1/29/00</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>9,7</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>9,7</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td> + <td>Sunday 1/9/00</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[7]/domChild[0]</td> + <td>Saturday 1/15/00</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]</td> + <td>77,5</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td> + <td>Tuesday 1/11/00</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>8,8</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>10,9</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>10,9</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>10,9</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>10,9</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>10,9</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>10,9</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>10,9</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>10,9</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>10,9</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>10,9</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td> + <td>Friday 12/31/99</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[3]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]</td> + <td>Sunday 1/30/00</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[7]/domChild[0]</td> + <td>Saturday 2/5/00</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>26</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarEventSizingNoOverlap.html b/uitest/src/com/vaadin/tests/components/calendar/CalendarEventSizingNoOverlap.html new file mode 100644 index 0000000000..dcf6c1ec53 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarEventSizingNoOverlap.html @@ -0,0 +1,110 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>eventSizingNoOverlap</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">eventSizingNoOverlap</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&firstDay=1&lastDay=5&firstHour=8&lastHour=16&restartApplication</td> + <td></td> +</tr> +<!-- Go to week view --> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td> + <td>4,57</td> +</tr> +<!-- Open add-event popup, enter event between 12:45-13:15 --> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[0]/Slot[6]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/9/00 12:45 PM</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/9/00 13:15 PM</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>12:45-13:15</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!-- Open add-event popup, enter event between 13:15-13:25 --> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[0]/Slot[6]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/9/00 13:15 PM</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/9/00 13:25 PM</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>13:15-13:25</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!-- Open add-event popup, enter event between 13:25-13:55 --> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[0]/Slot[6]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/9/00 13:25 PM</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/9/00 13:55 PM</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>13:25-13:55</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td></td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarMidnightEventsTest.html b/uitest/src/com/vaadin/tests/components/calendar/CalendarMidnightEventsTest.html new file mode 100644 index 0000000000..8991e1983d --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarMidnightEventsTest.html @@ -0,0 +1,116 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>midnightEventsTest</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">midnightEventsTest</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&width=1000px&height=600px&secondsResolution&restartApplication</td> + <td></td> +</tr> +<!--Add event from 0:00 to 0:00--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[1]/domChild[1]/domChild[0]/domChild[2]</td> + <td></td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/3/00 12:00:00 AM</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/4/00 12:00:00 AM</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Midnight to midnight</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Add event from 00:00 to 00:00 on the same day--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[1]/domChild[1]/domChild[0]/domChild[2]</td> + <td>84,10</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/7/00 12:00:00 AM</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Zero-length midnight event</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td></td> +</tr> +<!--Go to weekly view--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[0]</td> + <td>10,48</td> +</tr> +<!--Assert zero-length event exists--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]/domChild[0]/domChild[48]/domChild[0]</td> + <td>50,4</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Zero-length midnight event</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Assert that the the all day event does not overflow to the next day by checking that the first time-cell is empty--> +<tr> + <td>dragAndDrop</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]/domChild[0]/domChild[5]</td> + <td>+0,+5</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[1]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td></td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarMonthlyViewNewEvent.html b/uitest/src/com/vaadin/tests/components/calendar/CalendarMonthlyViewNewEvent.html new file mode 100644 index 0000000000..760beef6c0 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarMonthlyViewNewEvent.html @@ -0,0 +1,170 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>monthlyViewNewEvent</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">monthlyViewNewEvent</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&width=1000px&height=600px&restartApplication</td> + <td></td> +</tr> +<!--Create new event with the button--> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[0]/Slot[6]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td> + <td>10,5</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>52,8</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>12/26/99</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/1/00</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Test event</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Test description</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Open previously created event, assert values--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>91,9</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>12/26/99</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/1/00</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td> + <td>on</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Test event</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Test description</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td> + <td>Green</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Open previously created event, assert values and shorten the event--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>41,8</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>12/26/99</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/1/00</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td> + <td>on</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Test event</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Test description</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td> + <td>Green</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>12/27/99</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>12/31/99</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Click the calendar where the event previously was, assert new event creation--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[3]</td> + <td>107,8</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarMonthlyViewNewEvents.html b/uitest/src/com/vaadin/tests/components/calendar/CalendarMonthlyViewNewEvents.html new file mode 100644 index 0000000000..68690f81b4 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarMonthlyViewNewEvents.html @@ -0,0 +1,410 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>monthlyViewNewEvents</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">monthlyViewNewEvents</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&width=1000px&height=600px&restartApplication</td> + <td></td> +</tr> +<!--Create new event by dragging, make it all-day--> +<tr> + <td>drag</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[2]</td> + <td></td> +</tr> +<tr> + <td>drop</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[5]/domChild[0]/domChild[2]</td> + <td>98,83</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>12/27/99</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>12/31/99</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td> + <td>on</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Test event</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Description</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Create new event by dragging--> +<tr> + <td>drag</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td></td> +</tr> +<tr> + <td>drop</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[3]/domChild[0]</td> + <td>38,8</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td> + <td>7,9</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Second test event</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Desc</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Create new event by dragging, make it blue--> +<tr> + <td>drag</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[4]/domChild[0]/domChild[4]</td> + <td></td> +</tr> +<tr> + <td>drop</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[6]/domChild[0]/domChild[3]</td> + <td>125,80</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td> + <td>10,8</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Third test event</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Testing</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]#button</td> + <td>6,9</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::Root/VFilterSelect$SuggestionPopup[0]/VFilterSelect$SuggestionMenu[0]#item2</td> + <td>49,10</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[3]/domChild[0]/domChild[4]</td> + <td>45,85</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td> + <td>5,9</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Fourth test event</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Fourth event</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]#button</td> + <td>13,18</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::Root/VFilterSelect$SuggestionPopup[0]/VFilterSelect$SuggestionMenu[0]#item3</td> + <td>57,9</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[1]/VHorizontalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Start asserting the previously entered events--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]</td> + <td>12,7</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>12/27/99</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>12/31/99</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td> + <td>on</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Test event</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Description</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td> + <td>Green</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[1]</td> + <td>72,6</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>12/27/99</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>12/31/99</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[2]</td> + <td>44,10</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>12/26/99</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>12/29/99</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Second test event</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Desc</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td> + <td>Green</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[2]</td> + <td>79,5</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>12/26/99</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>12/29/99</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Second test event</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Desc</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td> + <td>Green</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[4]/domChild[0]/domChild[2]</td> + <td>47,9</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>12/30/99</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/1/00</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Third test event</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Testing</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td> + <td>Blue</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[4]/domChild[0]/domChild[2]</td> + <td>72,9</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>12/30/99</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/1/00</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Third test event</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Testing</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td> + <td>Blue</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[3]/domChild[0]/domChild[3]</td> + <td>37,9</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>12/29/99</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>12/29/99</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Fourth test event</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Fourth event</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td> + <td>Red</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td> + <td></td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarNotifications.html b/uitest/src/com/vaadin/tests/components/calendar/CalendarNotifications.html new file mode 100644 index 0000000000..15bf29a0fd --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarNotifications.html @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://localhost:8080/" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.calendar.NotificationTestUI?restartApplication</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarNotificationTestUI::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[2]/domChild[0]/domChild[0]</td> + <td>83,11</td> +</tr> +<tr> + <td>mouseMove</td> + <td>vaadin=runcomvaadintestscomponentscalendarNotificationTestUI::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[1]/domChild[3]/domChild[0]/domChild[3]</td> + <td>10,10</td> +</tr> +<tr> + <td>closeNotification</td> + <td>//div[@id='runcomvaadintestscomponentscalendarNotificationTestUI-1842310749-overlays']/div</td> + <td>0,0</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>no-notification</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTest1000x600px.html b/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTest1000x600px.html new file mode 100644 index 0000000000..bd4c671361 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTest1000x600px.html @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>sizeTest1000x600px</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">sizeTest1000x600px</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&restartApplication&width=1000px&height=600px</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td> + <td>Jan 2000</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>month</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td> + <td>7,53</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>week</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTest100percentXundefined.html b/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTest100percentXundefined.html new file mode 100644 index 0000000000..64b5444d34 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTest100percentXundefined.html @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>sizeTest100percentXundefined</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">sizeTest100percentXundefined</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&restartApplication&width=100%25</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td> + <td>Jan 2000</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>month</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td> + <td>10,53</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>week</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTest100x100percent.html b/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTest100x100percent.html new file mode 100644 index 0000000000..0905034b13 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTest100x100percent.html @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>sizeTest100x100percent</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">sizeTest100x100percent</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&restartApplication&width=100%25&height=100%25</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td> + <td>Jan 2000</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>month</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td> + <td>12,54</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>week</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTest300pxXundefined.html b/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTest300pxXundefined.html new file mode 100644 index 0000000000..99327a01df --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTest300pxXundefined.html @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>sizeTest300pxXundefined</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">sizeTest300pxXundefined</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&restartApplication&width=300px</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td> + <td>Jan 2000</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>month</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td> + <td>11,52</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>week</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTestUndefinedX100percent.html b/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTestUndefinedX100percent.html new file mode 100644 index 0000000000..2e4c811ff1 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTestUndefinedX100percent.html @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>sizeTestUndefinedX100percent</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">sizeTestUndefinedX100percent</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&restartApplication&height=100%25</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td> + <td>Jan 2000</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>month</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td> + <td>9,53</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>week</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTestUndefinedX300px.html b/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTestUndefinedX300px.html new file mode 100644 index 0000000000..cca66c7145 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTestUndefinedX300px.html @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>sizeTestUndefinedX300px</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">sizeTestUndefinedX300px</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&restartApplication&height=300px</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td> + <td>Jan 2000</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>month</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td> + <td>9,26</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>week</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTestUndefinedXUndefined.html b/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTestUndefinedXUndefined.html new file mode 100644 index 0000000000..e4a8da691b --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarSizeTestUndefinedXUndefined.html @@ -0,0 +1,41 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>sizeTestUndefinedXUndefined</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">sizeTestUndefinedXUndefined</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&restartApplication&width=&height=</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[1]/VLabel[0]</td> + <td>Jan 2000</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>month</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td> + <td>8,52</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>week</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarTest.java b/uitest/src/com/vaadin/tests/components/calendar/CalendarTest.java new file mode 100644 index 0000000000..a1bcca2e4e --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarTest.java @@ -0,0 +1,1242 @@ +/** + * Copyright 2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.calendar; + +import java.text.DateFormatSymbols; +import java.util.Date; +import java.util.GregorianCalendar; +import java.util.Locale; +import java.util.Map; +import java.util.TimeZone; + +import com.vaadin.annotations.Theme; +import com.vaadin.data.Item; +import com.vaadin.data.Property; +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.data.fieldgroup.FieldGroup; +import com.vaadin.data.fieldgroup.FieldGroup.CommitException; +import com.vaadin.data.util.BeanItem; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.MarginInfo; +import com.vaadin.shared.ui.combobox.FilteringMode; +import com.vaadin.shared.ui.datefield.Resolution; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.Calendar; +import com.vaadin.ui.Calendar.TimeFormat; +import com.vaadin.ui.CheckBox; +import com.vaadin.ui.ComboBox; +import com.vaadin.ui.DateField; +import com.vaadin.ui.FormLayout; +import com.vaadin.ui.GridLayout; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Label; +import com.vaadin.ui.Layout; +import com.vaadin.ui.TextArea; +import com.vaadin.ui.TextField; +import com.vaadin.ui.UI; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.Window; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickEvent; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventClick; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.EventClickHandler; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.RangeSelectEvent; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.RangeSelectHandler; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClick; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.WeekClickHandler; +import com.vaadin.ui.components.calendar.event.BasicEvent; +import com.vaadin.ui.components.calendar.event.BasicEventProvider; +import com.vaadin.ui.components.calendar.event.CalendarEvent; +import com.vaadin.ui.components.calendar.handler.BasicDateClickHandler; +import com.vaadin.ui.components.calendar.handler.BasicWeekClickHandler; + +/** Calendar component test application */ +@Theme("tests-calendar") +public class CalendarTest extends UI { + + private static final long serialVersionUID = -5436777475398410597L; + + private static final String DEFAULT_ITEMID = "DEFAULT"; + + private enum Mode { + MONTH, WEEK, DAY; + } + + /** + * This Gregorian calendar is used to control dates and time inside of this + * test application. + */ + private GregorianCalendar calendar; + + /** Target calendar component that this test application is made for. */ + private Calendar calendarComponent; + + private Date currentMonthsFirstDate; + + private final Label captionLabel = new Label(""); + + private Button monthButton; + + private Button weekButton; + + private Button nextButton; + + private Button prevButton; + + private ComboBox timeZoneSelect; + + private ComboBox formatSelect; + + private ComboBox localeSelect; + + private CheckBox hideWeekendsButton; + + private CheckBox readOnlyButton; + + private TextField captionField; + + private Window scheduleEventPopup; + + private final FormLayout scheduleEventFieldLayout = new FormLayout(); + private FieldGroup scheduleEventFieldGroup = new FieldGroup(); + + private Button deleteEventButton; + + private Button applyEventButton; + + private Mode viewMode = Mode.MONTH; + + private BasicEventProvider dataSource; + + private Button addNewEvent; + + /* + * When testBench is set to true, CalendarTest will have static content that + * is more suitable for Vaadin TestBench testing. Calendar will use a static + * date Mon 10 Jan 2000. Enable by starting the application with a + * "testBench" parameter in the URL. + */ + private boolean testBench = false; + + private String calendarHeight = null; + + private String calendarWidth = null; + + private CheckBox disabledButton; + + private Integer firstHour; + + private Integer lastHour; + + private Integer firstDay; + + private Integer lastDay; + + private Locale defaultLocale = Locale.US; + + private boolean showWeeklyView; + + private boolean useSecondResolution; + + private DateField startDateField; + private DateField endDateField; + + @SuppressWarnings("serial") + @Override + public void init(VaadinRequest request) { + GridLayout layout = new GridLayout(); + layout.setSizeFull(); + layout.setMargin(true); + setContent(layout); + + handleURLParams(request.getParameterMap()); + + initContent(); + } + + private void handleURLParams(Map<String, String[]> parameters) { + testBench = parameters.containsKey("testBench") + || parameters.containsKey("?testBench"); + + if (parameters.containsKey("width")) { + calendarWidth = parameters.get("width")[0]; + } + + if (parameters.containsKey("height")) { + calendarHeight = parameters.get("height")[0]; + } + + if (parameters.containsKey("firstDay")) { + firstDay = Integer.parseInt(parameters.get("firstDay")[0]); + } + + if (parameters.containsKey("lastDay")) { + lastDay = Integer.parseInt(parameters.get("lastDay")[0]); + } + + if (parameters.containsKey("firstHour")) { + firstHour = Integer.parseInt(parameters.get("firstHour")[0]); + } + + if (parameters.containsKey("lastHour")) { + lastHour = Integer.parseInt(parameters.get("lastHour")[0]); + } + + if (parameters.containsKey("locale")) { + String localeArray[] = parameters.get("locale")[0].split("_"); + defaultLocale = new Locale(localeArray[0], localeArray[1]); + setLocale(defaultLocale); + } + + if (parameters.containsKey(("secondsResolution"))) { + useSecondResolution = true; + } + + showWeeklyView = parameters.containsKey("weekly"); + + } + + public void initContent() { + // Set default Locale for this application + if (testBench) { + setLocale(defaultLocale); + + } else { + setLocale(Locale.getDefault()); + } + + // Initialize locale, timezone and timeformat selects. + localeSelect = createLocaleSelect(); + timeZoneSelect = createTimeZoneSelect(); + formatSelect = createCalendarFormatSelect(); + + initCalendar(); + initLayoutContent(); + addInitialEvents(); + } + + private Date resolveFirstDateOfWeek(Date today, + java.util.Calendar currentCalendar) { + int firstDayOfWeek = currentCalendar.getFirstDayOfWeek(); + currentCalendar.setTime(today); + while (firstDayOfWeek != currentCalendar + .get(java.util.Calendar.DAY_OF_WEEK)) { + currentCalendar.add(java.util.Calendar.DATE, -1); + } + return currentCalendar.getTime(); + } + + private Date resolveLastDateOfWeek(Date today, + java.util.Calendar currentCalendar) { + currentCalendar.setTime(today); + currentCalendar.add(java.util.Calendar.DATE, 1); + int firstDayOfWeek = currentCalendar.getFirstDayOfWeek(); + // Roll to weeks last day using firstdayofweek. Roll until FDofW is + // found and then roll back one day. + while (firstDayOfWeek != currentCalendar + .get(java.util.Calendar.DAY_OF_WEEK)) { + currentCalendar.add(java.util.Calendar.DATE, 1); + } + currentCalendar.add(java.util.Calendar.DATE, -1); + return currentCalendar.getTime(); + } + + private void addInitialEvents() { + Date originalDate = calendar.getTime(); + Date today = getToday(); + + // Add a event that last a whole week + + Date start = resolveFirstDateOfWeek(today, calendar); + Date end = resolveLastDateOfWeek(today, calendar); + CalendarTestEvent event = getNewEvent("Whole week event", start, end); + event.setAllDay(true); + event.setStyleName("color4"); + event.setDescription("Description for the whole week event."); + dataSource.addEvent(event); + + // Add a allday event + calendar.setTime(start); + calendar.add(GregorianCalendar.DATE, 3); + start = calendar.getTime(); + end = start; + event = getNewEvent("Allday event", start, end); + event.setAllDay(true); + event.setDescription("Some description."); + event.setStyleName("color3"); + dataSource.addEvent(event); + + // Add a second allday event + calendar.add(GregorianCalendar.DATE, 1); + start = calendar.getTime(); + end = start; + event = getNewEvent("Second allday event", start, end); + event.setAllDay(true); + event.setDescription("Some description."); + event.setStyleName("color2"); + dataSource.addEvent(event); + + calendar.add(GregorianCalendar.DATE, -3); + calendar.set(GregorianCalendar.HOUR_OF_DAY, 9); + calendar.set(GregorianCalendar.MINUTE, 30); + start = calendar.getTime(); + calendar.add(GregorianCalendar.HOUR_OF_DAY, 5); + calendar.set(GregorianCalendar.MINUTE, 0); + end = calendar.getTime(); + event = getNewEvent("Appointment", start, end); + event.setWhere("Office"); + event.setStyleName("color1"); + event.setDescription("A longer description, which should display correctly."); + dataSource.addEvent(event); + + calendar.add(GregorianCalendar.DATE, 1); + calendar.set(GregorianCalendar.HOUR_OF_DAY, 11); + calendar.set(GregorianCalendar.MINUTE, 0); + start = calendar.getTime(); + calendar.add(GregorianCalendar.HOUR_OF_DAY, 8); + end = calendar.getTime(); + event = getNewEvent("Training", start, end); + event.setStyleName("color2"); + dataSource.addEvent(event); + + calendar.add(GregorianCalendar.DATE, 4); + calendar.set(GregorianCalendar.HOUR_OF_DAY, 9); + calendar.set(GregorianCalendar.MINUTE, 0); + start = calendar.getTime(); + calendar.add(GregorianCalendar.HOUR_OF_DAY, 9); + end = calendar.getTime(); + event = getNewEvent("Free time", start, end); + dataSource.addEvent(event); + + calendar.setTime(originalDate); + } + + private void initLayoutContent() { + initNavigationButtons(); + initHideWeekEndButton(); + initReadOnlyButton(); + initDisabledButton(); + initAddNewEventButton(); + + HorizontalLayout hl = new HorizontalLayout(); + hl.setWidth("100%"); + hl.setSpacing(true); + hl.setMargin(new MarginInfo(false, false, true, false)); + hl.addComponent(prevButton); + hl.addComponent(captionLabel); + hl.addComponent(monthButton); + hl.addComponent(weekButton); + hl.addComponent(nextButton); + hl.setComponentAlignment(prevButton, Alignment.MIDDLE_LEFT); + hl.setComponentAlignment(captionLabel, Alignment.MIDDLE_CENTER); + hl.setComponentAlignment(monthButton, Alignment.MIDDLE_CENTER); + hl.setComponentAlignment(weekButton, Alignment.MIDDLE_CENTER); + hl.setComponentAlignment(nextButton, Alignment.MIDDLE_RIGHT); + + monthButton.setVisible(viewMode == Mode.WEEK); + weekButton.setVisible(viewMode == Mode.DAY); + + HorizontalLayout controlPanel = new HorizontalLayout(); + controlPanel.setSpacing(true); + controlPanel.setMargin(new MarginInfo(false, false, true, false)); + controlPanel.setWidth("100%"); + controlPanel.addComponent(localeSelect); + controlPanel.addComponent(timeZoneSelect); + controlPanel.addComponent(formatSelect); + controlPanel.addComponent(hideWeekendsButton); + controlPanel.addComponent(readOnlyButton); + controlPanel.addComponent(disabledButton); + controlPanel.addComponent(addNewEvent); + + controlPanel.setComponentAlignment(timeZoneSelect, + Alignment.MIDDLE_LEFT); + controlPanel.setComponentAlignment(formatSelect, Alignment.MIDDLE_LEFT); + controlPanel.setComponentAlignment(localeSelect, Alignment.MIDDLE_LEFT); + controlPanel.setComponentAlignment(hideWeekendsButton, + Alignment.MIDDLE_LEFT); + controlPanel.setComponentAlignment(readOnlyButton, + Alignment.MIDDLE_LEFT); + controlPanel.setComponentAlignment(disabledButton, + Alignment.MIDDLE_LEFT); + controlPanel.setComponentAlignment(addNewEvent, Alignment.MIDDLE_LEFT); + + GridLayout layout = (GridLayout) getContent(); + layout.addComponent(controlPanel); + layout.addComponent(hl); + layout.addComponent(calendarComponent); + layout.setRowExpandRatio(layout.getRows() - 1, 1.0f); + } + + private void initNavigationButtons() { + monthButton = new Button("Month view", new ClickListener() { + + private static final long serialVersionUID = 1L; + + @Override + public void buttonClick(ClickEvent event) { + switchToMonthView(); + } + }); + + weekButton = new Button("Week view", new ClickListener() { + + private static final long serialVersionUID = 1L; + + @Override + public void buttonClick(ClickEvent event) { + // simulate week click + WeekClickHandler handler = (WeekClickHandler) calendarComponent + .getHandler(WeekClick.EVENT_ID); + handler.weekClick(new WeekClick(calendarComponent, calendar + .get(GregorianCalendar.WEEK_OF_YEAR), calendar + .get(GregorianCalendar.YEAR))); + } + }); + + nextButton = new Button("Next", new Button.ClickListener() { + private static final long serialVersionUID = 1L; + + @Override + public void buttonClick(ClickEvent event) { + handleNextButtonClick(); + } + }); + + prevButton = new Button("Prev", new Button.ClickListener() { + private static final long serialVersionUID = 1L; + + @Override + public void buttonClick(ClickEvent event) { + handlePreviousButtonClick(); + } + }); + } + + private void initHideWeekEndButton() { + hideWeekendsButton = new CheckBox("Hide weekends"); + hideWeekendsButton.setImmediate(true); + hideWeekendsButton + .addValueChangeListener(new Property.ValueChangeListener() { + + private static final long serialVersionUID = 1L; + + @Override + public void valueChange(ValueChangeEvent event) { + setWeekendsHidden(hideWeekendsButton.getValue()); + } + }); + } + + private void setWeekendsHidden(boolean weekendsHidden) { + if (weekendsHidden) { + int firstToShow = (GregorianCalendar.MONDAY - calendar + .getFirstDayOfWeek()) % 7; + calendarComponent.setFirstVisibleDayOfWeek(firstToShow + 1); + calendarComponent.setLastVisibleDayOfWeek(firstToShow + 5); + } else { + calendarComponent.setFirstVisibleDayOfWeek(1); + calendarComponent.setLastVisibleDayOfWeek(7); + } + + } + + private void initReadOnlyButton() { + readOnlyButton = new CheckBox("Read-only mode"); + readOnlyButton.setImmediate(true); + readOnlyButton + .addValueChangeListener(new Property.ValueChangeListener() { + + private static final long serialVersionUID = 1L; + + @Override + public void valueChange(ValueChangeEvent event) { + calendarComponent.setReadOnly(readOnlyButton.getValue()); + } + }); + } + + private void initDisabledButton() { + disabledButton = new CheckBox("Disabled"); + disabledButton.setImmediate(true); + disabledButton + .addValueChangeListener(new Property.ValueChangeListener() { + + private static final long serialVersionUID = 1L; + + @Override + public void valueChange(ValueChangeEvent event) { + calendarComponent.setEnabled(!disabledButton.getValue()); + } + }); + } + + public void initAddNewEventButton() { + addNewEvent = new Button("Add new event"); + addNewEvent.addClickListener(new Button.ClickListener() { + + private static final long serialVersionUID = -8307244759142541067L; + + @Override + public void buttonClick(ClickEvent event) { + Date start = getToday(); + start.setHours(0); + start.setMinutes(0); + start.setSeconds(0); + + Date end = getEndOfDay(calendar, start); + + showEventPopup(createNewEvent(start, end), true); + } + }); + } + + private void initFormFields(Layout formLayout, + Class<? extends CalendarEvent> eventClass) { + + startDateField = createDateField("Start date"); + endDateField = createDateField("End date"); + + final CheckBox allDayField = createCheckBox("All-day"); + allDayField.addValueChangeListener(new Property.ValueChangeListener() { + + private static final long serialVersionUID = -7104996493482558021L; + + @Override + public void valueChange(ValueChangeEvent event) { + Object value = event.getProperty().getValue(); + if (value instanceof Boolean && Boolean.TRUE.equals(value)) { + setFormDateResolution(Resolution.DAY); + + } else { + setFormDateResolution(Resolution.MINUTE); + } + } + + }); + + captionField = createTextField("Caption"); + final TextField whereField = createTextField("Where"); + final TextArea descriptionField = createTextArea("Description"); + descriptionField.setRows(3); + + final ComboBox styleNameField = createStyleNameComboBox(); + + formLayout.addComponent(startDateField); + formLayout.addComponent(endDateField); + formLayout.addComponent(allDayField); + formLayout.addComponent(captionField); + if (eventClass == CalendarTestEvent.class) { + formLayout.addComponent(whereField); + } + formLayout.addComponent(descriptionField); + formLayout.addComponent(styleNameField); + + scheduleEventFieldGroup.bind(startDateField, "start"); + scheduleEventFieldGroup.bind(endDateField, "end"); + scheduleEventFieldGroup.bind(captionField, "caption"); + scheduleEventFieldGroup.bind(descriptionField, "description"); + if (eventClass == CalendarTestEvent.class) { + scheduleEventFieldGroup.bind(whereField, "where"); + } + scheduleEventFieldGroup.bind(styleNameField, "styleName"); + scheduleEventFieldGroup.bind(allDayField, "allDay"); + } + + private CheckBox createCheckBox(String caption) { + CheckBox cb = new CheckBox(caption); + cb.setImmediate(true); + return cb; + } + + private TextField createTextField(String caption) { + TextField f = new TextField(caption); + f.setNullRepresentation(""); + return f; + } + + private TextArea createTextArea(String caption) { + TextArea f = new TextArea(caption); + f.setNullRepresentation(""); + return f; + } + + private DateField createDateField(String caption) { + DateField f = new DateField(caption); + if (useSecondResolution) { + f.setResolution(Resolution.SECOND); + } else { + f.setResolution(Resolution.MINUTE); + } + return f; + } + + private ComboBox createStyleNameComboBox() { + ComboBox s = new ComboBox("Color"); + s.addContainerProperty("c", String.class, ""); + s.setItemCaptionPropertyId("c"); + Item i = s.addItem("color1"); + i.getItemProperty("c").setValue("Green"); + i = s.addItem("color2"); + i.getItemProperty("c").setValue("Blue"); + i = s.addItem("color3"); + i.getItemProperty("c").setValue("Red"); + i = s.addItem("color4"); + i.getItemProperty("c").setValue("Orange"); + return s; + } + + private void initCalendar() { + dataSource = new BasicEventProvider(); + + calendarComponent = new Calendar(dataSource); + calendarComponent.setLocale(getLocale()); + calendarComponent.setImmediate(true); + + if (calendarWidth != null || calendarHeight != null) { + if (calendarHeight != null) { + calendarComponent.setHeight(calendarHeight); + } + if (calendarWidth != null) { + calendarComponent.setWidth(calendarWidth); + } + } else { + calendarComponent.setSizeFull(); + } + + if (firstHour != null && lastHour != null) { + calendarComponent.setFirstVisibleHourOfDay(firstHour); + calendarComponent.setLastVisibleHourOfDay(lastHour); + } + + if (firstDay != null && lastDay != null) { + calendarComponent.setFirstVisibleDayOfWeek(firstDay); + calendarComponent.setLastVisibleDayOfWeek(lastDay); + } + + Date today = getToday(); + calendar = new GregorianCalendar(getLocale()); + calendar.setTime(today); + + updateCaptionLabel(); + + if (!showWeeklyView) { + int rollAmount = calendar.get(GregorianCalendar.DAY_OF_MONTH) - 1; + calendar.add(GregorianCalendar.DAY_OF_MONTH, -rollAmount); + resetTime(false); + currentMonthsFirstDate = calendar.getTime(); + calendarComponent.setStartDate(currentMonthsFirstDate); + calendar.add(GregorianCalendar.MONTH, 1); + calendar.add(GregorianCalendar.DATE, -1); + calendarComponent.setEndDate(calendar.getTime()); + } + + addCalendarEventListeners(); + } + + private Date getToday() { + if (testBench) { + GregorianCalendar testDate = new GregorianCalendar(); + testDate.set(GregorianCalendar.YEAR, 2000); + testDate.set(GregorianCalendar.MONTH, 0); + testDate.set(GregorianCalendar.DATE, 10); + testDate.set(GregorianCalendar.HOUR_OF_DAY, 0); + testDate.set(GregorianCalendar.MINUTE, 0); + testDate.set(GregorianCalendar.SECOND, 0); + testDate.set(GregorianCalendar.MILLISECOND, 0); + return testDate.getTime(); + } + return new Date(); + } + + @SuppressWarnings("serial") + private void addCalendarEventListeners() { + // Register week clicks by changing the schedules start and end dates. + calendarComponent.setHandler(new BasicWeekClickHandler() { + + @Override + public void weekClick(WeekClick event) { + // let BasicWeekClickHandler handle calendar dates, and update + // only the other parts of UI here + super.weekClick(event); + updateCaptionLabel(); + switchToWeekView(); + } + }); + + calendarComponent.setHandler(new EventClickHandler() { + + @Override + public void eventClick(EventClick event) { + showEventPopup(event.getCalendarEvent(), false); + } + }); + + calendarComponent.setHandler(new BasicDateClickHandler() { + + @Override + public void dateClick(DateClickEvent event) { + // let BasicDateClickHandler handle calendar dates, and update + // only the other parts of UI here + super.dateClick(event); + switchToDayView(); + } + }); + + calendarComponent.setHandler(new RangeSelectHandler() { + + @Override + public void rangeSelect(RangeSelectEvent event) { + handleRangeSelect(event); + } + }); + } + + private ComboBox createTimeZoneSelect() { + ComboBox s = new ComboBox("Timezone"); + s.addContainerProperty("caption", String.class, ""); + s.setItemCaptionPropertyId("caption"); + s.setFilteringMode(FilteringMode.CONTAINS); + + Item i = s.addItem(DEFAULT_ITEMID); + i.getItemProperty("caption").setValue( + "Default (" + TimeZone.getDefault().getID() + ")"); + for (String id : TimeZone.getAvailableIDs()) { + if (!s.containsId(id)) { + i = s.addItem(id); + i.getItemProperty("caption").setValue(id); + } + } + + if (testBench) { + s.select("America/New_York"); + } else { + s.select(DEFAULT_ITEMID); + } + s.setImmediate(true); + s.addValueChangeListener(new ValueChangeListener() { + + private static final long serialVersionUID = 1L; + + @Override + public void valueChange(ValueChangeEvent event) { + + updateCalendarTimeZone(event.getProperty().getValue()); + } + }); + + return s; + } + + private ComboBox createCalendarFormatSelect() { + ComboBox s = new ComboBox("Calendar format"); + s.addContainerProperty("caption", String.class, ""); + s.setItemCaptionPropertyId("caption"); + + Item i = s.addItem(DEFAULT_ITEMID); + i.getItemProperty("caption").setValue("Default by locale"); + i = s.addItem(TimeFormat.Format12H); + i.getItemProperty("caption").setValue("12H"); + i = s.addItem(TimeFormat.Format24H); + i.getItemProperty("caption").setValue("24H"); + + s.select(DEFAULT_ITEMID); + s.setImmediate(true); + s.addValueChangeListener(new ValueChangeListener() { + + private static final long serialVersionUID = 1L; + + @Override + public void valueChange(ValueChangeEvent event) { + updateCalendarFormat(event.getProperty().getValue()); + } + }); + + return s; + } + + private ComboBox createLocaleSelect() { + ComboBox s = new ComboBox("Locale"); + s.addContainerProperty("caption", String.class, ""); + s.setItemCaptionPropertyId("caption"); + s.setFilteringMode(FilteringMode.CONTAINS); + + for (Locale l : Locale.getAvailableLocales()) { + if (!s.containsId(l)) { + Item i = s.addItem(l); + i.getItemProperty("caption").setValue(getLocaleItemCaption(l)); + } + } + + s.select(getLocale()); + s.setImmediate(true); + s.addValueChangeListener(new ValueChangeListener() { + + private static final long serialVersionUID = 1L; + + @Override + public void valueChange(ValueChangeEvent event) { + updateCalendarLocale((Locale) event.getProperty().getValue()); + } + }); + + return s; + } + + private void updateCalendarTimeZone(Object timezoneId) { + TimeZone tz = null; + if (!DEFAULT_ITEMID.equals(timezoneId)) { + tz = TimeZone.getTimeZone((String) timezoneId); + } + + // remember the week that was showing, so we can re-set it later + Date startDate = calendarComponent.getStartDate(); + calendar.setTime(startDate); + int weekNumber = calendar.get(java.util.Calendar.WEEK_OF_YEAR); + calendarComponent.setTimeZone(tz); + calendar.setTimeZone(calendarComponent.getTimeZone()); + + if (viewMode == Mode.WEEK) { + calendar.set(java.util.Calendar.WEEK_OF_YEAR, weekNumber); + calendar.set(java.util.Calendar.DAY_OF_WEEK, + calendar.getFirstDayOfWeek()); + + calendarComponent.setStartDate(calendar.getTime()); + calendar.add(java.util.Calendar.DATE, 6); + calendarComponent.setEndDate(calendar.getTime()); + } + } + + private void updateCalendarFormat(Object format) { + TimeFormat calFormat = null; + if (format instanceof TimeFormat) { + calFormat = (TimeFormat) format; + } + + calendarComponent.setTimeFormat(calFormat); + } + + private String getLocaleItemCaption(Locale l) { + String country = l.getDisplayCountry(getLocale()); + String language = l.getDisplayLanguage(getLocale()); + StringBuilder caption = new StringBuilder(country); + if (caption.length() != 0) { + caption.append(", "); + } + caption.append(language); + return caption.toString(); + } + + private void updateCalendarLocale(Locale l) { + int oldFirstDayOfWeek = calendar.getFirstDayOfWeek(); + setLocale(l); + calendarComponent.setLocale(l); + calendar = new GregorianCalendar(l); + int newFirstDayOfWeek = calendar.getFirstDayOfWeek(); + + // we are showing 1 week, and the first day of the week has changed + // update start and end dates so that the same week is showing + if (viewMode == Mode.WEEK && oldFirstDayOfWeek != newFirstDayOfWeek) { + calendar.setTime(calendarComponent.getStartDate()); + calendar.add(java.util.Calendar.DAY_OF_WEEK, 2); + // starting at the beginning of the week + calendar.set(GregorianCalendar.DAY_OF_WEEK, newFirstDayOfWeek); + Date start = calendar.getTime(); + + // ending at the end of the week + calendar.add(GregorianCalendar.DATE, 6); + Date end = calendar.getTime(); + + calendarComponent.setStartDate(start); + calendarComponent.setEndDate(end); + + // Week days depend on locale so this must be refreshed + setWeekendsHidden(hideWeekendsButton.getValue()); + } + + } + + private void handleNextButtonClick() { + switch (viewMode) { + case MONTH: + nextMonth(); + break; + case WEEK: + nextWeek(); + break; + case DAY: + nextDay(); + break; + } + } + + private void handlePreviousButtonClick() { + switch (viewMode) { + case MONTH: + previousMonth(); + break; + case WEEK: + previousWeek(); + break; + case DAY: + previousDay(); + break; + } + } + + private void handleRangeSelect(RangeSelectEvent event) { + Date start = event.getStart(); + Date end = event.getEnd(); + + /* + * If a range of dates is selected in monthly mode, we want it to end at + * the end of the last day. + */ + if (event.isMonthlyMode()) { + end = getEndOfDay(calendar, end); + } + + showEventPopup(createNewEvent(start, end), true); + } + + private void showEventPopup(CalendarEvent event, boolean newEvent) { + if (event == null) { + return; + } + + updateCalendarEventPopup(newEvent); + updateCalendarEventForm(event); + + if (!getWindows().contains(scheduleEventPopup)) { + addWindow(scheduleEventPopup); + } + } + + /* Initializes a modal window to edit schedule event. */ + private void createCalendarEventPopup() { + VerticalLayout layout = new VerticalLayout(); + layout.setMargin(true); + layout.setSpacing(true); + + scheduleEventPopup = new Window(null, layout); + scheduleEventPopup.setWidth("400px"); + scheduleEventPopup.setModal(true); + scheduleEventPopup.center(); + + layout.addComponent(scheduleEventFieldLayout); + + applyEventButton = new Button("Apply", new ClickListener() { + + private static final long serialVersionUID = 1L; + + @Override + public void buttonClick(ClickEvent event) { + try { + commitCalendarEvent(); + } catch (CommitException e) { + e.printStackTrace(); + } + } + }); + Button cancel = new Button("Cancel", new ClickListener() { + + private static final long serialVersionUID = 1L; + + @Override + public void buttonClick(ClickEvent event) { + discardCalendarEvent(); + } + }); + deleteEventButton = new Button("Delete", new ClickListener() { + + private static final long serialVersionUID = 1L; + + @Override + public void buttonClick(ClickEvent event) { + deleteCalendarEvent(); + } + }); + scheduleEventPopup.addCloseListener(new Window.CloseListener() { + + private static final long serialVersionUID = 1L; + + @Override + public void windowClose(Window.CloseEvent e) { + discardCalendarEvent(); + } + }); + + HorizontalLayout buttons = new HorizontalLayout(); + buttons.setSpacing(true); + buttons.addComponent(deleteEventButton); + buttons.addComponent(applyEventButton); + buttons.addComponent(cancel); + layout.addComponent(buttons); + layout.setComponentAlignment(buttons, Alignment.BOTTOM_RIGHT); + } + + private void updateCalendarEventPopup(boolean newEvent) { + if (scheduleEventPopup == null) { + createCalendarEventPopup(); + } + + if (newEvent) { + scheduleEventPopup.setCaption("New event"); + } else { + scheduleEventPopup.setCaption("Edit event"); + } + + deleteEventButton.setVisible(!newEvent); + deleteEventButton.setEnabled(!calendarComponent.isReadOnly()); + applyEventButton.setEnabled(!calendarComponent.isReadOnly()); + } + + private void updateCalendarEventForm(CalendarEvent event) { + BeanItem<CalendarEvent> item = new BeanItem<CalendarEvent>(event); + scheduleEventFieldLayout.removeAllComponents(); + scheduleEventFieldGroup = new FieldGroup(); + initFormFields(scheduleEventFieldLayout, event.getClass()); + scheduleEventFieldGroup.setBuffered(true); + scheduleEventFieldGroup.setItemDataSource(item); + } + + private void setFormDateResolution(Resolution resolution) { + if (startDateField != null && endDateField != null) { + startDateField.setResolution(resolution); + endDateField.setResolution(resolution); + } + } + + private CalendarEvent createNewEvent(Date startDate, Date endDate) { + + BasicEvent event = new BasicEvent(); + event.setCaption(""); + event.setStart(startDate); + event.setEnd(endDate); + event.setStyleName("color1"); + return event; + } + + /* Removes the event from the data source and fires change event. */ + private void deleteCalendarEvent() { + BasicEvent event = getFormCalendarEvent(); + if (dataSource.containsEvent(event)) { + dataSource.removeEvent(event); + } + removeWindow(scheduleEventPopup); + } + + /* Adds/updates the event in the data source and fires change event. */ + private void commitCalendarEvent() throws CommitException { + scheduleEventFieldGroup.commit(); + BasicEvent event = getFormCalendarEvent(); + if (event.getEnd() == null) { + event.setEnd(event.getStart()); + } + if (!dataSource.containsEvent(event)) { + dataSource.addEvent(event); + } + + removeWindow(scheduleEventPopup); + } + + private void discardCalendarEvent() { + scheduleEventFieldGroup.discard(); + removeWindow(scheduleEventPopup); + } + + @SuppressWarnings("unchecked") + private BasicEvent getFormCalendarEvent() { + BeanItem<CalendarEvent> item = (BeanItem<CalendarEvent>) scheduleEventFieldGroup + .getItemDataSource(); + CalendarEvent event = item.getBean(); + return (BasicEvent) event; + } + + private void nextMonth() { + rollMonth(1); + } + + private void previousMonth() { + rollMonth(-1); + } + + private void nextWeek() { + rollWeek(1); + } + + private void previousWeek() { + rollWeek(-1); + } + + private void nextDay() { + rollDate(1); + } + + private void previousDay() { + rollDate(-1); + } + + private void rollMonth(int direction) { + calendar.setTime(currentMonthsFirstDate); + calendar.add(GregorianCalendar.MONTH, direction); + resetTime(false); + currentMonthsFirstDate = calendar.getTime(); + calendarComponent.setStartDate(currentMonthsFirstDate); + + updateCaptionLabel(); + + calendar.add(GregorianCalendar.MONTH, 1); + calendar.add(GregorianCalendar.DATE, -1); + resetCalendarTime(true); + } + + private void rollWeek(int direction) { + calendar.add(GregorianCalendar.WEEK_OF_YEAR, direction); + calendar.set(GregorianCalendar.DAY_OF_WEEK, + calendar.getFirstDayOfWeek()); + resetCalendarTime(false); + resetTime(true); + calendar.add(GregorianCalendar.DATE, 6); + calendarComponent.setEndDate(calendar.getTime()); + } + + private void rollDate(int direction) { + calendar.add(GregorianCalendar.DATE, direction); + resetCalendarTime(false); + resetCalendarTime(true); + } + + private void updateCaptionLabel() { + DateFormatSymbols s = new DateFormatSymbols(getLocale()); + String month = s.getShortMonths()[calendar.get(GregorianCalendar.MONTH)]; + captionLabel.setValue(month + " " + + calendar.get(GregorianCalendar.YEAR)); + } + + private CalendarTestEvent getNewEvent(String caption, Date start, Date end) { + CalendarTestEvent event = new CalendarTestEvent(); + event.setCaption(caption); + event.setStart(start); + event.setEnd(end); + + return event; + } + + /* + * Switch the view to week view. + */ + public void switchToWeekView() { + viewMode = Mode.WEEK; + weekButton.setVisible(false); + monthButton.setVisible(true); + } + + /* + * Switch the Calendar component's start and end date range to the target + * month only. (sample range: 01.01.2010 00:00.000 - 31.01.2010 23:59.999) + */ + public void switchToMonthView() { + viewMode = Mode.MONTH; + monthButton.setVisible(false); + weekButton.setVisible(false); + + calendar.setTime(currentMonthsFirstDate); + calendarComponent.setStartDate(currentMonthsFirstDate); + + updateCaptionLabel(); + + calendar.add(GregorianCalendar.MONTH, 1); + calendar.add(GregorianCalendar.DATE, -1); + resetCalendarTime(true); + } + + /* + * Switch to day view (week view with a single day visible). + */ + public void switchToDayView() { + viewMode = Mode.DAY; + monthButton.setVisible(true); + weekButton.setVisible(true); + } + + private void resetCalendarTime(boolean resetEndTime) { + resetTime(resetEndTime); + if (resetEndTime) { + calendarComponent.setEndDate(calendar.getTime()); + } else { + calendarComponent.setStartDate(calendar.getTime()); + updateCaptionLabel(); + } + } + + /* + * Resets the calendar time (hour, minute second and millisecond) either to + * zero or maximum value. + */ + private void resetTime(boolean max) { + if (max) { + calendar.set(GregorianCalendar.HOUR_OF_DAY, + calendar.getMaximum(GregorianCalendar.HOUR_OF_DAY)); + calendar.set(GregorianCalendar.MINUTE, + calendar.getMaximum(GregorianCalendar.MINUTE)); + calendar.set(GregorianCalendar.SECOND, + calendar.getMaximum(GregorianCalendar.SECOND)); + calendar.set(GregorianCalendar.MILLISECOND, + calendar.getMaximum(GregorianCalendar.MILLISECOND)); + } else { + calendar.set(GregorianCalendar.HOUR_OF_DAY, 0); + calendar.set(GregorianCalendar.MINUTE, 0); + calendar.set(GregorianCalendar.SECOND, 0); + calendar.set(GregorianCalendar.MILLISECOND, 0); + } + } + + private static Date getEndOfDay(java.util.Calendar calendar, Date date) { + java.util.Calendar calendarClone = (java.util.Calendar) calendar + .clone(); + + calendarClone.setTime(date); + calendarClone.set(java.util.Calendar.MILLISECOND, + calendarClone.getActualMaximum(java.util.Calendar.MILLISECOND)); + calendarClone.set(java.util.Calendar.SECOND, + calendarClone.getActualMaximum(java.util.Calendar.SECOND)); + calendarClone.set(java.util.Calendar.MINUTE, + calendarClone.getActualMaximum(java.util.Calendar.MINUTE)); + calendarClone.set(java.util.Calendar.HOUR, + calendarClone.getActualMaximum(java.util.Calendar.HOUR)); + calendarClone.set(java.util.Calendar.HOUR_OF_DAY, + calendarClone.getActualMaximum(java.util.Calendar.HOUR_OF_DAY)); + + return calendarClone.getTime(); + } + + private static Date getStartOfDay(java.util.Calendar calendar, Date date) { + java.util.Calendar calendarClone = (java.util.Calendar) calendar + .clone(); + + calendarClone.setTime(date); + calendarClone.set(java.util.Calendar.MILLISECOND, 0); + calendarClone.set(java.util.Calendar.SECOND, 0); + calendarClone.set(java.util.Calendar.MINUTE, 0); + calendarClone.set(java.util.Calendar.HOUR, 0); + calendarClone.set(java.util.Calendar.HOUR_OF_DAY, 0); + + return calendarClone.getTime(); + } +} diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarTestEvent.java b/uitest/src/com/vaadin/tests/components/calendar/CalendarTestEvent.java new file mode 100644 index 0000000000..29b8f62403 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarTestEvent.java @@ -0,0 +1,48 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.calendar; + +import com.vaadin.ui.components.calendar.event.BasicEvent; + +/** + * Test CalendarEvent implementation. + * + * @see com.vaadin.addon.calendar.test.ui.Calendar.Event + */ +public class CalendarTestEvent extends BasicEvent { + + private static final long serialVersionUID = 2820133201983036866L; + private String where; + private Object data; + + public String getWhere() { + return where; + } + + public void setWhere(String where) { + this.where = where; + fireEventChange(); + } + + public Object getData() { + return data; + } + + public void setData(Object data) { + this.data = data; + fireEventChange(); + } +} diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarVisibleHours24H.html b/uitest/src/com/vaadin/tests/components/calendar/CalendarVisibleHours24H.html new file mode 100644 index 0000000000..6b7bb26b76 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarVisibleHours24H.html @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>visibleHours24H</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">visibleHours24H</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&firstDay=1&lastDay=5&firstHour=8&lastHour=16&restartApplication</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[1]/domChild[1]/domChild[0]/domChild[0]</td> + <td>9,55</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[0]/Slot[2]/VFilterSelect[0]#button</td> + <td>14,5</td> +</tr> +<tr> + <td>mouseClick</td> + <td>//div[@id='VAADIN_COMBOBOX_OPTIONLIST']/div/div[2]/table/tbody/tr[4]/td</td> + <td>120,15</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>9:00</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[8]</td> + <td>16:00</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/calendar/CalendarWeeklyViewNewEvents.html b/uitest/src/com/vaadin/tests/components/calendar/CalendarWeeklyViewNewEvents.html new file mode 100644 index 0000000000..fd51a0daad --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/CalendarWeeklyViewNewEvents.html @@ -0,0 +1,657 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>weeklyViewNewEvents</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">weeklyViewNewEvents</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.calendar.CalendarTest?testBench&width=1000px&height=600px&restartApplication</td> + <td></td> +</tr> +<!--Go to weekly view--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[1]/domChild[2]/domChild[0]/domChild[0]</td> + <td>3,49</td> +</tr> +<!--Assert the default event contents--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[2]/domChild[0]/domChild[48]/domChild[0]/domChild[0]</td> + <td>26,5</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/10/00 09:30 AM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/10/00 02:00 PM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td> + <td>off</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Appointment</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[1]</td> + <td>Office</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>A longer description, which should display correctly.</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td> + <td>Green</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[48]/domChild[0]/domChild[0]</td> + <td>21,12</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/11/00 11:00 AM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/11/00 07:00 PM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Training</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td> + <td>Blue</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[7]/domChild[0]/domChild[48]/domChild[0]/domChild[0]</td> + <td>19,9</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/15/00 09:00 AM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/15/00 06:00 PM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Free time</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[6]/domChild[0]/domChild[0]</td> + <td>22,6</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/9/00</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/15/00</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VCheckBox[0]/domChild[0]</td> + <td>on</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Whole week event</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Description for the whole week event.</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td> + <td>Orange</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Assert the all-day events--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[1]</td> + <td>78,3</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/12/00</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/12/00</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Allday event</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Some description.</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td> + <td>Red</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[4]/domChild[0]/domChild[1]</td> + <td>57,7</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/13/00</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/13/00</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Second allday event</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Some description.</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td> + <td>Blue</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Enter new event--> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[0]/Slot[6]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/13/00 9:00 AM</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/13/00 2:00 PM</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Test event</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Test event description</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[1]</td> + <td>3,15</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::Root/VFilterSelect$SuggestionPopup[0]/VFilterSelect$SuggestionMenu[0]#item4</td> + <td>36,6</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Assert previously created event--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[5]/domChild[0]/domChild[48]/domChild[0]</td> + <td>47,21</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/13/00 09:00 AM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/13/00 02:00 PM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Test event</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Test event description</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VFilterSelect[0]/domChild[0]</td> + <td>Orange</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> + <td>8,9</td> +</tr> +<!--Edit previously created events and change properties--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[5]/domChild[0]/domChild[48]/domChild[0]</td> + <td>33,16</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#popupButton</td> + <td>10,11</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::Root/VOverlay[0]/VCalendarPanel[0]#day11</td> + <td>16,11</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#popupButton</td> + <td>14,14</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::Root/VOverlay[0]/VCalendarPanel[0]#day11</td> + <td>14,10</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Assert the edited values--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[49]/domChild[0]</td> + <td>34,21</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/11/00 09:00 AM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/11/00 02:00 PM</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Create new event--> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[0]/Slot[6]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/11/00 10:00 AM</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/11/00 8:00 PM</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Test event 2</td> +</tr> +<tr> + <td>enterCharacter</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextArea[0]</td> + <td>Second test event</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Assert previously created event still exists in the right place (as multiple events occupy the same time)--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[50]/domChild[0]</td> + <td>7,73</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/11/00 09:00 AM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/11/00 02:00 PM</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Assert previously created event still exists in the right place (as multiple events occupy the same time)--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[49]/domChild[0]/domChild[0]</td> + <td>12,32</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/11/00 11:00 AM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/11/00 07:00 PM</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> + <td>11,8</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[48]/domChild[1]</td> + <td>4,9</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/11/00 10:00 AM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/11/00 08:00 PM</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[50]/domChild[0]</td> + <td>14,71</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/11/00 09:00 AM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/11/00 02:00 PM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Test event</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[48]/domChild[0]</td> + <td>16,111</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/11/00 10:00 AM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/11/00 08:00 PM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Test event 2</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[48]/domChild[0]</td> + <td>14,209</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/11/00 10:00 AM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/11/00 08:00 PM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Test event 2</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[49]/domChild[0]/domChild[0]</td> + <td>20,113</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/11/00 11:00 AM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/11/00 07:00 PM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Training</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> + <td>7,8</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[3]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]/domChild[50]/domChild[0]</td> + <td>21,87</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[0]#field</td> + <td>1/11/00 09:00 AM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VPopupCalendar[1]#field</td> + <td>1/11/00 02:00 PM</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Test event</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> + <td>12,10</td> +</tr> +<!--Go to monthly view and assert inserted events--> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VHorizontalLayout[1]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[2]/domChild[2]/domChild[0]/domChild[4]</td> + <td>36,10</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Test event</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[2]/domChild[2]/domChild[0]/domChild[3]</td> + <td>53,6</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Training</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[2]/domChild[2]/domChild[0]/domChild[2]/domChild[0]</td> + <td>48,7</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Test event 2</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VGridLayout[0]/VCalendar[0]/domChild[0]/domChild[1]/domChild[1]/domChild[0]/domChild[1]/domChild[2]/domChild[4]/domChild[0]/domChild[1]</td> + <td>50,6</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VFormLayout[0]/VFormLayout$VFormLayoutTable[0]/VTextField[0]</td> + <td>Whole week event</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentscalendarCalendarTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/calendar/HiddenFwdBackButtons.java b/uitest/src/com/vaadin/tests/components/calendar/HiddenFwdBackButtons.java new file mode 100644 index 0000000000..8b789098e6 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/HiddenFwdBackButtons.java @@ -0,0 +1,61 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.calendar; + +import java.util.Date; +import java.util.Locale; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.Calendar; +import com.vaadin.ui.GridLayout; +import com.vaadin.ui.UI; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.BackwardHandler; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.ForwardHandler; + +public class HiddenFwdBackButtons extends UI { + + @SuppressWarnings("deprecation") + @Override + protected void init(VaadinRequest request) { + GridLayout content = new GridLayout(1, 2); + content.setSizeFull(); + setContent(content); + + final Calendar calendar = new Calendar(); + calendar.setLocale(new Locale("fi", "FI")); + + calendar.setSizeFull(); + calendar.setStartDate(new Date(100, 1, 1)); + calendar.setEndDate(new Date(100, 1, 7)); + content.addComponent(calendar); + Button button = new Button("Hide forward and back buttons"); + button.addClickListener(new ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + // This should hide the forward and back navigation buttons + calendar.setHandler((BackwardHandler) null); + calendar.setHandler((ForwardHandler) null); + } + }); + content.addComponent(button); + + content.setRowExpandRatio(0, 1); + + } +} diff --git a/uitest/src/com/vaadin/tests/components/calendar/NotificationTestUI.java b/uitest/src/com/vaadin/tests/components/calendar/NotificationTestUI.java new file mode 100644 index 0000000000..6e5718a652 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/calendar/NotificationTestUI.java @@ -0,0 +1,104 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.calendar; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Calendar; +import com.vaadin.ui.GridLayout; +import com.vaadin.ui.Notification; +import com.vaadin.ui.UI; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickEvent; +import com.vaadin.ui.components.calendar.CalendarComponentEvents.DateClickHandler; +import com.vaadin.ui.components.calendar.event.BasicEvent; +import com.vaadin.ui.components.calendar.event.CalendarEvent; +import com.vaadin.ui.components.calendar.event.CalendarEventProvider; + +public class NotificationTestUI extends UI { + + private DummyEventProvider provider; + + private static class DummyEventProvider implements CalendarEventProvider { + + private int index; + private List<CalendarEvent> events = new ArrayList<CalendarEvent>(); + + public void addEvent(Date date) { + BasicEvent e = new BasicEvent(); + e.setAllDay(true); + e.setStart(date); + e.setEnd(date); + e.setCaption("Some event " + ++index); + events.add(e); + } + + @Override + public List<CalendarEvent> getEvents(Date startDate, Date endDate) { + return events; + } + + } + + @Override + protected void init(com.vaadin.server.VaadinRequest request) { + GridLayout content = new GridLayout(1, 2); + content.setSizeFull(); + content.setRowExpandRatio(1, 1.0f); + setContent(content); + final Button btn = new Button("Show working notification", + new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + Notification + .show("This will disappear when you move your mouse!"); + } + }); + content.addComponent(btn); + + provider = new DummyEventProvider(); + final Calendar cal = new Calendar(provider); + cal.setLocale(Locale.US); + cal.setSizeFull(); + cal.setHandler(new DateClickHandler() { + @Override + public void dateClick(DateClickEvent event) { + provider.addEvent(event.getDate()); + Notification + .show("This should disappear, but if wont unless clicked."); + + // this requestRepaint call interferes with the notification + cal.markAsDirty(); + } + }); + content.addComponent(cal); + + java.util.Calendar javaCal = java.util.Calendar.getInstance(); + javaCal.set(java.util.Calendar.YEAR, 2000); + javaCal.set(java.util.Calendar.MONTH, 0); + javaCal.set(java.util.Calendar.DAY_OF_MONTH, 1); + Date start = javaCal.getTime(); + javaCal.set(java.util.Calendar.DAY_OF_MONTH, 31); + Date end = javaCal.getTime(); + + cal.setStartDate(start); + cal.setEndDate(end); + } +} diff --git a/uitest/src/com/vaadin/tests/components/checkbox/CheckBoxRevertValueChange.java b/uitest/src/com/vaadin/tests/components/checkbox/CheckBoxRevertValueChange.java index 5b086fb935..cc26cf8845 100644 --- a/uitest/src/com/vaadin/tests/components/checkbox/CheckBoxRevertValueChange.java +++ b/uitest/src/com/vaadin/tests/components/checkbox/CheckBoxRevertValueChange.java @@ -29,6 +29,7 @@ public class CheckBoxRevertValueChange extends AbstractTestUIWithLog { final CheckBox alwaysUnchecked = new CheckBox("You may not check me"); alwaysUnchecked .addValueChangeListener(new Property.ValueChangeListener() { + @Override public void valueChange(Property.ValueChangeEvent event) { if (alwaysUnchecked.getValue()) { log("I said no checking!"); @@ -40,6 +41,7 @@ public class CheckBoxRevertValueChange extends AbstractTestUIWithLog { alwaysChecked.setValue(true); alwaysChecked .addValueChangeListener(new Property.ValueChangeListener() { + @Override public void valueChange(Property.ValueChangeEvent event) { if (!alwaysChecked.getValue()) { log("I said no unchecking!"); diff --git a/uitest/src/com/vaadin/tests/components/combobox/ComboBoxDuplicateCaption.java b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxDuplicateCaption.java index 3c1e8a27d6..bd71850a89 100644 --- a/uitest/src/com/vaadin/tests/components/combobox/ComboBoxDuplicateCaption.java +++ b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxDuplicateCaption.java @@ -38,6 +38,7 @@ public class ComboBoxDuplicateCaption extends TestBase { box.setImmediate(true); box.addValueChangeListener(new ValueChangeListener() { + @Override public void valueChange( com.vaadin.data.Property.ValueChangeEvent event) { Person p = (Person) event.getProperty().getValue(); diff --git a/uitest/src/com/vaadin/tests/components/combobox/ComboBoxSQLContainerFilteredValueChange.java b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxSQLContainerFilteredValueChange.java index 23a75ae56e..75010f0ea9 100644 --- a/uitest/src/com/vaadin/tests/components/combobox/ComboBoxSQLContainerFilteredValueChange.java +++ b/uitest/src/com/vaadin/tests/components/combobox/ComboBoxSQLContainerFilteredValueChange.java @@ -53,6 +53,7 @@ public class ComboBoxSQLContainerFilteredValueChange extends TestBase { myCombo.setWidth("100.0%"); myCombo.setHeight("-1px"); myCombo.addListener(new Property.ValueChangeListener() { + @Override public void valueChange(ValueChangeEvent event) { selectedLabel.setValue("Selected: " + event.getProperty().getValue()); @@ -72,6 +73,7 @@ public class ComboBoxSQLContainerFilteredValueChange extends TestBase { /** * (Re)creates the test table + * * @param connectionPool */ private void createTestTable(JDBCConnectionPool connectionPool) { @@ -97,6 +99,7 @@ public class ComboBoxSQLContainerFilteredValueChange extends TestBase { /** * Adds test data to the test table + * * @param connectionPool * @throws SQLException */ @@ -111,7 +114,7 @@ public class ComboBoxSQLContainerFilteredValueChange extends TestBase { statement.executeUpdate("INSERT INTO mytable VALUES(2, 'A1')"); statement.executeUpdate("INSERT INTO mytable VALUES(3, 'B0')"); statement.executeUpdate("INSERT INTO mytable VALUES(4, 'B1')"); - + statement.close(); conn.commit(); } catch (SQLException e) { diff --git a/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges.java b/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges.java new file mode 100644 index 0000000000..2daa3e3f96 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges.java @@ -0,0 +1,270 @@ +package com.vaadin.tests.components.datefield; + +import java.util.Calendar; +import java.util.Date; +import java.util.Locale; + +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.MarginInfo; +import com.vaadin.shared.ui.datefield.Resolution; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.CheckBox; +import com.vaadin.ui.DateField; +import com.vaadin.ui.GridLayout; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.InlineDateField; +import com.vaadin.ui.Label; +import com.vaadin.ui.NativeSelect; +import com.vaadin.ui.VerticalLayout; + +public class DateFieldRanges extends AbstractTestUI { + + @Override + protected Integer getTicketNumber() { + return 6241; + } + + private Label label = new Label(); + private NativeSelect resoSelect = new NativeSelect("Resolution"); + private DateField fromRange = new DateField("Range start"); + private DateField toRange = new DateField("Range end"); + private DateField valueDF = new DateField("Value"); + private CheckBox immediateCB = new CheckBox("Immediate"); + private Button recreate = new Button("Recreate static datefields"); + private Button clearRangeButton = new Button("Clear range"); + + private GridLayout currentStaticContainer; + + private DateField inlineDynamicDateField; + private DateField dynamicDateField; + + private Calendar createCalendar() { + Calendar c = Calendar.getInstance(); + c.set(2013, 3, 26, 6, 1, 12); + return c; + } + + private Date newDate() { + return createCalendar().getTime(); + } + + private void initializeControlFields() { + resoSelect.addItem(Resolution.MINUTE); + resoSelect.addItem(Resolution.SECOND); + resoSelect.addItem(Resolution.HOUR); + resoSelect.addItem(Resolution.DAY); + resoSelect.addItem(Resolution.MONTH); + resoSelect.addItem(Resolution.YEAR); + resoSelect.setImmediate(true); + resoSelect.setValue(Resolution.DAY); + resoSelect.addValueChangeListener(new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + + Resolution r = (Resolution) resoSelect.getValue(); + inlineDynamicDateField.setResolution(r); + dynamicDateField.setResolution(r); + + } + }); + + fromRange.setValue(null); + fromRange.setImmediate(true); + fromRange.addValueChangeListener(new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + + inlineDynamicDateField.setRangeStart(fromRange.getValue()); + dynamicDateField.setRangeStart(fromRange.getValue()); + + } + }); + + toRange.setValue(null); + toRange.setImmediate(true); + toRange.addValueChangeListener(new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + + inlineDynamicDateField.setRangeEnd(toRange.getValue()); + dynamicDateField.setRangeEnd(toRange.getValue()); + + } + }); + + valueDF.setValue(null); + valueDF.setImmediate(true); + valueDF.addValueChangeListener(new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + + inlineDynamicDateField.setValue(valueDF.getValue()); + dynamicDateField.setValue(valueDF.getValue()); + + } + }); + + immediateCB.setValue(true); + immediateCB.setImmediate(true); + immediateCB.addValueChangeListener(new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + + inlineDynamicDateField.setImmediate(immediateCB.getValue()); + dynamicDateField.setImmediate(immediateCB.getValue()); + + } + }); + + recreate.addClickListener(new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + GridLayout newContainer = createStaticFields(); + replaceComponent(currentStaticContainer, newContainer); + currentStaticContainer = newContainer; + } + }); + + clearRangeButton.addClickListener(new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + fromRange.setValue(null); + toRange.setValue(null); + } + }); + + Calendar startCal = createCalendar(); + Calendar endCal = createCalendar(); + endCal.add(Calendar.DATE, 30); + + dynamicDateField = createDateField(startCal.getTime(), + endCal.getTime(), null, Resolution.DAY, false); + inlineDynamicDateField = createDateField(startCal.getTime(), + endCal.getTime(), null, Resolution.DAY, true); + + resoSelect.setId("resoSelect"); + fromRange.setId("fromRange"); + toRange.setId("toRange"); + valueDF.setId("valueDF"); + immediateCB.setId("immediateCB"); + recreate.setId("recreate"); + clearRangeButton.setId("clearRangeButton"); + dynamicDateField.setId("dynamicDateField"); + inlineDynamicDateField.setId("inlineDynamicDateField"); + + } + + @Override + protected void setup(VaadinRequest request) { + setLocale(new Locale("en", "US")); + getLayout().setWidth(100, Unit.PERCENTAGE); + getLayout().setHeight(null); + getLayout().setMargin(new MarginInfo(true, false, false, false)); + getLayout().setSpacing(true); + + initializeControlFields(); + + GridLayout gl = new GridLayout(2, 2); + gl.setSpacing(true); + + gl.addComponent(dynamicDateField); + gl.addComponent(inlineDynamicDateField); + + HorizontalLayout hl = new HorizontalLayout(); + hl.setSpacing(true); + hl.addComponent(resoSelect); + hl.addComponent(fromRange); + hl.addComponent(toRange); + hl.addComponent(valueDF); + hl.addComponent(immediateCB); + hl.addComponent(recreate); + hl.addComponent(clearRangeButton); + addComponent(hl); + addComponent(new Label("Dynamic DateFields")); + addComponent(gl); + currentStaticContainer = createStaticFields(); + addComponent(new Label("Static DateFields")); + addComponent(currentStaticContainer); + + addComponent(label); + + } + + private GridLayout createStaticFields() { + Calendar startCal = createCalendar(); + Calendar endCal = createCalendar(); + endCal.add(Calendar.DATE, 30); + GridLayout gl = new GridLayout(2, 2); + gl.setSpacing(true); + DateField df = createDateField(startCal.getTime(), endCal.getTime(), + null, Resolution.DAY, false); + gl.addComponent(df); + DateField inline = createDateField(startCal.getTime(), + endCal.getTime(), null, Resolution.DAY, true); + gl.addComponent(inline); + inline.setId("staticInline"); + VerticalLayout vl = new VerticalLayout(); + + return gl; + } + + private DateField createDateField(Date rangeStart, Date rangeEnd, + Date value, Resolution resolution, boolean inline) { + + DateField df = null; + + if (inline) { + df = new InlineDateField(); + } else { + df = new DateField(); + } + + final DateField gg = df; + updateValuesForDateField(df); + + df.addValueChangeListener(new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + label.setValue((gg.getValue() == null ? "Nothing" : gg + .getValue().toString()) + + " selected. isValid: " + + gg.isValid()); + } + }); + return df; + } + + @Override + protected String getTestDescription() { + return "Not defined yet"; + + } + + private void updateValuesForDateField(DateField df) { + Date fromVal = fromRange.getValue(); + Date toVal = toRange.getValue(); + Date value = valueDF.getValue(); + Resolution r = (Resolution) resoSelect.getValue(); + boolean immediate = immediateCB.getValue(); + + df.setValue(value); + df.setResolution(r); + df.setRangeStart(fromVal); + df.setRangeEnd(toVal); + df.setImmediate(immediate); + + } + +} diff --git a/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_ChangingRangeSoValueFallsOutsideRangeCausesOutOfRangeExceptionIfImmediateField.html b/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_ChangingRangeSoValueFallsOutsideRangeCausesOutOfRangeExceptionIfImmediateField.html new file mode 100644 index 0000000000..ca5c006e82 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_ChangingRangeSoValueFallsOutsideRangeCausesOutOfRangeExceptionIfImmediateField.html @@ -0,0 +1,186 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.datefield.DateFieldRanges?restartApplication</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>59,10</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>4/4/13</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>36,13</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>2/2/13</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>48,13</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>5/5/13</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>50,7</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[8]</td> + <td>12,14</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::PID_SinlineDynamicDateField/VCalendarPanel[0]#header</td> + <td>April 2013</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#popupButton</td> + <td>14,15</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[15]</td> + <td>14,11</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[15]</td> + <td>14,11</td> +</tr> +<tr> + <td>mouseClick</td> + <td>//table[@id='PID_VAADIN_POPUPCAL']/tbody/tr[2]/td/table/tbody/tr[4]/td[4]/span</td> + <td>18,12</td> +</tr> +<tr> + <td>assertCSSClass</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VGridLayout[0]/domChild[2]/domChild[0]/domChild[0]</td> + <td>v-errorindicator</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::PID_SinlineDynamicDateField/VCalendarPanel[0]#header</td> + <td>March 2013</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[5]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertTextNotPresent</td> + <td>April 2013</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[7]</td> + <td>15,7</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[7]</td> + <td>15,7</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[6]</td> + <td>14,14</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[12]</td> + <td>18,11</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[12]</td> + <td>12,10</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[11]</td> + <td>17,14</td> +</tr> +<tr> + <td>assertTextPresent</td> + <td>February 2013</td> + <td></td> +</tr> +<tr> + <td>assertCSSClass</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[4]/VGridLayout[0]/domChild[2]/domChild[0]/domChild[0]</td> + <td>v-errorindicator</td> +</tr> +<tr> + <td>mouseClick</td> + <td>//div[@id='staticInline']/table/tbody/tr[2]/td/table/tbody/tr[2]/td[5]/span</td> + <td>14,7</td> +</tr> +<tr> + <td>mouseClick</td> + <td>//div[@id='inlineDynamicDateField']/table/tbody/tr[2]/td/table/tbody/tr[2]/td[5]/span</td> + <td>15,6</td> +</tr> +<tr> + <td>assertCSSClass</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VGridLayout[0]/domChild[2]/domChild[0]/domChild[0]</td> + <td>v-errorindicator</td> +</tr> +<tr> + <td>assertCSSClass</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[4]/VGridLayout[0]/domChild[2]/domChild[0]/domChild[0]</td> + <td>v-errorindicator</td> +</tr> +<tr> + <td>mouseClick</td> + <td>//div[@id='staticInline']/table/tbody/tr[2]/td/table/tbody/tr[2]/td[8]/span</td> + <td>14,8</td> +</tr> +<tr> + <td>mouseClick</td> + <td>//div[@id='inlineDynamicDateField']/table/tbody/tr[2]/td/table/tbody/tr[2]/td[8]/span</td> + <td>19,4</td> +</tr> +<tr> + <td>assertNotCSSClass</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VGridLayout[0]/domChild[2]/domChild[0]/domChild[0]</td> + <td>v-errorindicator</td> +</tr> +<tr> + <td>assertNotCSSClass</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[4]/VGridLayout[0]/domChild[2]/domChild[0]/domChild[0]</td> + <td>v-errorindicator</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_EndRangeEarlierThanStartRangeCausesException.html b/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_EndRangeEarlierThanStartRangeCausesException.html new file mode 100644 index 0000000000..b19f519cb1 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_EndRangeEarlierThanStartRangeCausesException.html @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.datefield.DateFieldRanges?restartApplication</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>44,6</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>4</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>4</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>4/4/13</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>41,14</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>3</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>3</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>3/3/13</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>33,9</td> +</tr> +<tr> + <td>assertCSSClass</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/domChild[0]/domChild[0]/domChild[1]</td> + <td>v-errorindicator</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_InitialDatesOutsideRange.html b/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_InitialDatesOutsideRange.html new file mode 100644 index 0000000000..eeeda5270b --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_InitialDatesOutsideRange.html @@ -0,0 +1,121 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.datefield.DateFieldRanges</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>39,14</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>1</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>1</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>1/1/13</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>31,13</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>1</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>2</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>1/2/11</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>111</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>1</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>1/1/10</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>52,12</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[5]/VButton[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertCSSClass</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[4]/VGridLayout[0]/domChild[2]/domChild[0]/domChild[0]</td> + <td>v-errorindicator</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_MonthChangeMeansFocusDayRolledInsideRange.html b/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_MonthChangeMeansFocusDayRolledInsideRange.html new file mode 100644 index 0000000000..6e1ca024cb --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_MonthChangeMeansFocusDayRolledInsideRange.html @@ -0,0 +1,100 @@ +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.datefield.DateFieldRanges</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>27,11</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>1</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>1</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>1/1/10</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>47,10</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>2</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>2</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>2/2/10</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>15,8</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VGridLayout[0]/VPopupCalendar[0]#popupButton</td> + <td>9,14</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>id=PID_VAADIN_POPUPCAL</td> + <td>left</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>id=PID_VAADIN_POPUPCAL</td> + <td>left</td> +</tr> +<tr> + <td>assertText</td> + <td>//table[@id='PID_VAADIN_POPUPCAL']/tbody/tr/td[3]/span</td> + <td>January 2010</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>id=PID_VAADIN_POPUPCAL</td> + <td>right</td> +</tr> +<tr> + <td>assertText</td> + <td>//table[@id='PID_VAADIN_POPUPCAL']/tbody/tr/td[3]/span</td> + <td>February 2010</td> +</tr> diff --git a/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_NextYearClickableIfRangeAcceptsFractionOfNextYear.html b/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_NextYearClickableIfRangeAcceptsFractionOfNextYear.html new file mode 100644 index 0000000000..f5d7ee97ca --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_NextYearClickableIfRangeAcceptsFractionOfNextYear.html @@ -0,0 +1,191 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.datefield.DateFieldRanges</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>37,13</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>1</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>1</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>1/1/15</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>35,7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>12</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>12</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>12/12/14</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>40,10</td> +</tr> +<tr> + <td>assertTextPresent</td> + <td>December 2014</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[8]</td> + <td>11,8</td> +</tr> +<tr> + <td>assertTextPresent</td> + <td>January 2015</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[7]</td> + <td>13,10</td> +</tr> +<tr> + <td>assertTextPresent</td> + <td>January 2015</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[8]</td> + <td>6,7</td> +</tr> +<tr> + <td>assertTextPresent</td> + <td>January 2015</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[6]</td> + <td>15,7</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[5]</td> + <td>8,8</td> +</tr> +<tr> + <td>assertTextPresent</td> + <td>December 2013</td> + <td></td> +</tr> +<tr> + <td>select</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::PID_SresoSelect/domChild[0]</td> + <td>label=MONTH</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[8]</td> + <td>13,11</td> +</tr> +<tr> + <td>assertTextPresent</td> + <td>January 2015</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[6]</td> + <td>12,13</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[5]</td> + <td>12,13</td> +</tr> +<tr> + <td>assertTextPresent</td> + <td>December 2013</td> + <td></td> +</tr> +<tr> + <td>select</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::PID_SresoSelect/domChild[0]</td> + <td>label=YEAR</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[6]</td> + <td>8,12</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[6]</td> + <td>8,12</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[6]</td> + <td>8,12</td> +</tr> +<tr> + <td>assertTextPresent</td> + <td>2015</td> + <td></td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_PrevYearClickableIfRangeAcceptsFractionOfPrevYear.html b/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_PrevYearClickableIfRangeAcceptsFractionOfPrevYear.html new file mode 100644 index 0000000000..4c671b266d --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_PrevYearClickableIfRangeAcceptsFractionOfPrevYear.html @@ -0,0 +1,146 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.datefield.DateFieldRanges</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>33,10</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>12</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>12</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>12/12/10</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>32,9</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>1</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>1</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>11</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>enter</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>1/1/11</td> +</tr> +<tr> + <td>assertTextPresent</td> + <td>January 2011</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[5]</td> + <td>13,12</td> +</tr> +<tr> + <td>assertTextPresent</td> + <td>December 2010</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[6]</td> + <td>9,7</td> +</tr> +<tr> + <td>assertTextPresent</td> + <td>December 2010</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[5]</td> + <td>5,0</td> +</tr> +<tr> + <td>assertTextPresent</td> + <td>December 2010</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[7]</td> + <td>16,9</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[7]</td> + <td>16,9</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[8]</td> + <td>19,8</td> +</tr> +<tr> + <td>assertTextPresent</td> + <td>February 2012</td> + <td></td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_SettingValueOutsideRangeCausesException.html b/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_SettingValueOutsideRangeCausesException.html new file mode 100644 index 0000000000..c3f7e021a8 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/datefield/DateFieldRanges_SettingValueOutsideRangeCausesException.html @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.datefield.DateFieldRanges</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>36,8</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>4</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>4</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#field</td> + <td>4/4/12</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>40,11</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>3</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>3</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>3/3/12</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>40,5</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>5</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>5</td> +</tr> +<tr> + <td>pressSpecialKey</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>shift 7</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>5/5/12</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]</td> + <td>393,89</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsdatefieldDateFieldRanges::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[3]/VPopupCalendar[0]#popupButton</td> + <td>10,8</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=(//button[@type='button'])[17]</td> + <td>12,11</td> +</tr> +<tr> + <td>mouseClick</td> + <td>//table[@id='PID_VAADIN_POPUPCAL']/tbody/tr[2]/td/table/tbody/tr[4]/td[5]/span</td> + <td>13,9</td> +</tr> +<tr> + <td>assertTextPresent</td> + <td>May 2012</td> + <td></td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/orderedlayout/VerticalRelativeSizeWithoutExpand.java b/uitest/src/com/vaadin/tests/components/orderedlayout/VerticalRelativeSizeWithoutExpand.java index 86525da3ef..0ac9a008d2 100755 --- a/uitest/src/com/vaadin/tests/components/orderedlayout/VerticalRelativeSizeWithoutExpand.java +++ b/uitest/src/com/vaadin/tests/components/orderedlayout/VerticalRelativeSizeWithoutExpand.java @@ -1,4 +1,5 @@ package com.vaadin.tests.components.orderedlayout; + import com.vaadin.data.util.BeanItemContainer; import com.vaadin.server.VaadinRequest; import com.vaadin.ui.Panel; diff --git a/uitest/src/com/vaadin/tests/components/page/PageReload.html b/uitest/src/com/vaadin/tests/components/page/PageReload.html new file mode 100644 index 0000000000..20d61a48f1 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/page/PageReload.html @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.page.PageReload?restartApplication</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentspagePageReload::PID_SLog_row_0</td> + <td>1. UI id: 0</td> +</tr> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.page.PageReload</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentspagePageReload::PID_SLog_row_0</td> + <td>1. UI id: 1</td> +</tr> +<tr> + <td>clickAndWait</td> + <td>vaadin=runcomvaadintestscomponentspagePageReload::/VVerticalLayout[0]/Slot[2]/VVerticalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentspagePageReload::PID_SLog_row_0</td> + <td>1. UI id: 2</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/page/PageReload.java b/uitest/src/com/vaadin/tests/components/page/PageReload.java new file mode 100644 index 0000000000..c1799b29e3 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/page/PageReload.java @@ -0,0 +1,34 @@ +package com.vaadin.tests.components.page; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; + +public class PageReload extends AbstractTestUIWithLog { + + @Override + protected void setup(VaadinRequest request) { + Button b = new Button("Press to reload"); + b.addClickListener(new ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + getPage().reload(); + } + }); + addComponent(b); + log("UI id: " + getUIId()); + } + + @Override + protected String getTestDescription() { + return "Tests Page.reload(). Click button to refresh the page."; + } + + @Override + protected Integer getTicketNumber() { + return 10250; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/popupview/ClickingWhilePopupOpen.html b/uitest/src/com/vaadin/tests/components/popupview/ClickingWhilePopupOpen.html index 81676f6599..43f1c2e8ed 100644 --- a/uitest/src/com/vaadin/tests/components/popupview/ClickingWhilePopupOpen.html +++ b/uitest/src/com/vaadin/tests/components/popupview/ClickingWhilePopupOpen.html @@ -28,7 +28,7 @@ </tr> <tr> <td>assertTextNotPresent</td> - <td>IllegalStateException: Cannot set a new parent without first clearing the old parent</td> + <td>Uncaught client side exception</td> <td></td> </tr> diff --git a/uitest/src/com/vaadin/tests/components/richtextarea/RichTextAreaEmptyString.java b/uitest/src/com/vaadin/tests/components/richtextarea/RichTextAreaEmptyString.java index 5eddf9dc6d..01dc10220f 100644 --- a/uitest/src/com/vaadin/tests/components/richtextarea/RichTextAreaEmptyString.java +++ b/uitest/src/com/vaadin/tests/components/richtextarea/RichTextAreaEmptyString.java @@ -29,6 +29,7 @@ public class RichTextAreaEmptyString extends TestBase { final Button b = new Button("get area value", new ClickListener() { + @Override public void buttonClick(ClickEvent event) { l.setValue(area.getValue()); } diff --git a/uitest/src/com/vaadin/tests/components/richtextarea/RichTextAreaPreventsTextFieldAccess.java b/uitest/src/com/vaadin/tests/components/richtextarea/RichTextAreaPreventsTextFieldAccess.java index f4ad149dd1..c3433c3054 100644 --- a/uitest/src/com/vaadin/tests/components/richtextarea/RichTextAreaPreventsTextFieldAccess.java +++ b/uitest/src/com/vaadin/tests/components/richtextarea/RichTextAreaPreventsTextFieldAccess.java @@ -50,6 +50,7 @@ public class RichTextAreaPreventsTextFieldAccess extends TestBase { Button addWindowButton = new Button("Open RichTextArea-Dialog"); addWindowButton.addClickListener(new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { getMainWindow().addWindow(subWindow); @@ -60,6 +61,7 @@ public class RichTextAreaPreventsTextFieldAccess extends TestBase { Button removeWindowButton = new Button("removeWindowButton"); removeWindowButton.addClickListener(new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { getMainWindow().removeWindow(subWindow); @@ -70,6 +72,7 @@ public class RichTextAreaPreventsTextFieldAccess extends TestBase { Button focusButton = new Button("Set focus on TextField"); focusButton.addClickListener(new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { testField.focus(); @@ -80,6 +83,7 @@ public class RichTextAreaPreventsTextFieldAccess extends TestBase { Button removeRTA = new Button("Remove RTA"); removeRTA.addClickListener(new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { wLayout.removeComponent(rText); diff --git a/uitest/src/com/vaadin/tests/components/select/OptionGroupBaseSelects.java b/uitest/src/com/vaadin/tests/components/select/OptionGroupBaseSelects.java index f2dee69cbf..0df82688d1 100644 --- a/uitest/src/com/vaadin/tests/components/select/OptionGroupBaseSelects.java +++ b/uitest/src/com/vaadin/tests/components/select/OptionGroupBaseSelects.java @@ -29,6 +29,7 @@ public class OptionGroupBaseSelects extends ComponentTestCase<HorizontalLayout> CheckBox cb = new CheckBox("Switch Selects ReadOnly", false); cb.addListener(new ValueChangeListener() { + @Override public void valueChange(ValueChangeEvent event) { for (Iterator<Component> it = layout.getComponentIterator(); it .hasNext();) { @@ -42,6 +43,7 @@ public class OptionGroupBaseSelects extends ComponentTestCase<HorizontalLayout> CheckBox cb2 = new CheckBox("Switch Selects Enabled", true); cb2.addListener(new ValueChangeListener() { + @Override public void valueChange(ValueChangeEvent event) { for (Iterator<Component> it = layout.getComponentIterator(); it .hasNext();) { diff --git a/uitest/src/com/vaadin/tests/components/table/ColumnCollapsingAndColumnExpansion.html b/uitest/src/com/vaadin/tests/components/table/ColumnCollapsingAndColumnExpansion.html index 9e529836b6..4dc63721a1 100644 --- a/uitest/src/com/vaadin/tests/components/table/ColumnCollapsingAndColumnExpansion.html +++ b/uitest/src/com/vaadin/tests/components/table/ColumnCollapsingAndColumnExpansion.html @@ -13,12 +13,7 @@ </thead><tbody> <tr> <td>open</td> - <td>/run/com.vaadin.tests.components.table.ColumnCollapsingAndColumnExpansion</td> - <td></td> -</tr> -<tr> - <td>waitForVaadin</td> - <td></td> + <td>/run/com.vaadin.tests.components.table.ColumnCollapsingAndColumnExpansion?restartApplication</td> <td></td> </tr> <!--Initial state, all 3 columns visible--> @@ -32,32 +27,17 @@ <td>vaadin=runcomvaadintestscomponentstableColumnCollapsingAndColumnExpansion::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VScrollTable[0]/domChild[0]/domChild[1]</td> <td></td> </tr> -<tr> - <td>waitForVaadin</td> - <td></td> - <td></td> -</tr> <!--Hide 'col2' through table interface--> <tr> - <td>click</td> - <td>//td[@id='gwt-uid-2']/span/div</td> - <td></td> -</tr> -<tr> - <td>waitForVaadin</td> - <td></td> - <td></td> + <td>mouseClick</td> + <td>//tr[2]/td/span/div</td> + <td>23,2</td> </tr> <tr> <td>screenCapture</td> <td></td> <td>col2-hidden</td> </tr> -<tr> - <td>waitForVaadin</td> - <td></td> - <td></td> -</tr> <!--Hide 'Col1' using button--> <tr> <td>enterCharacter</td> @@ -65,62 +45,32 @@ <td>Col1</td> </tr> <tr> - <td>waitForVaadin</td> - <td></td> - <td></td> -</tr> -<tr> <td>click</td> <td>vaadin=runcomvaadintestscomponentstableColumnCollapsingAndColumnExpansion::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[1]/VButton[0]/domChild[0]/domChild[0]</td> <td></td> </tr> <tr> - <td>waitForVaadin</td> - <td></td> - <td></td> -</tr> -<tr> <td>screenCapture</td> <td></td> <td>col1-col2-hidden</td> </tr> <!--Show 'col2' using action handler--> <tr> - <td>waitForVaadin</td> - <td></td> - <td></td> -</tr> -<tr> <td>contextmenu</td> <td>vaadin=runcomvaadintestscomponentstableColumnCollapsingAndColumnExpansion::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[0]/VScrollTable[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[2]/domChild[0]/domChild[0]</td> <td></td> </tr> <tr> - <td>waitForVaadin</td> - <td></td> - <td></td> -</tr> -<tr> <td>mouseClick</td> <td>vaadin=runcomvaadintestscomponentstableColumnCollapsingAndColumnExpansion::/VContextMenu[0]#option0</td> <td>11,6</td> </tr> <tr> - <td>waitForVaadin</td> - <td></td> - <td></td> -</tr> -<tr> <td>enterCharacter</td> <td>vaadin=runcomvaadintestscomponentstableColumnCollapsingAndColumnExpansion::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[0]/VTextField[0]</td> <td>Col2</td> </tr> <tr> - <td>waitForVaadin</td> - <td></td> - <td></td> -</tr> -<tr> <td>screenCapture</td> <td></td> <td>col1-hidden</td> @@ -132,27 +82,16 @@ <td>Col1</td> </tr> <tr> - <td>waitForVaadin</td> - <td></td> - <td></td> -</tr> -<tr> <td>click</td> <td>vaadin=runcomvaadintestscomponentstableColumnCollapsingAndColumnExpansion::/VVerticalLayout[0]/ChildComponentContainer[1]/VVerticalLayout[0]/ChildComponentContainer[1]/VHorizontalLayout[0]/ChildComponentContainer[2]/VButton[0]/domChild[0]/domChild[0]</td> <td></td> </tr> -<tr> - <td>waitForVaadin</td> - <td></td> - <td></td> -</tr> <!--We should now be back at the initial state, all 3 columns visible--> <tr> <td>screenCapture</td> <td></td> <td>col1-col2-col3-visible-again</td> </tr> - </tbody></table> </body> </html> diff --git a/uitest/src/com/vaadin/tests/components/table/EmptyRowsWhenScrolling.java b/uitest/src/com/vaadin/tests/components/table/EmptyRowsWhenScrolling.java index c1ae9b4118..3bc0d3dd1f 100644 --- a/uitest/src/com/vaadin/tests/components/table/EmptyRowsWhenScrolling.java +++ b/uitest/src/com/vaadin/tests/components/table/EmptyRowsWhenScrolling.java @@ -93,6 +93,7 @@ public class EmptyRowsWhenScrolling extends UI { table.setVisibleColumns(new String[] { "image", "id", "col1", "col2", "col3", "col4" }); table.addGeneratedColumn("image", new ColumnGenerator() { + @Override public Object generateCell(Table source, Object itemId, Object columnId) { int imgNum = new Random().nextInt(5) + 1; @@ -112,6 +113,7 @@ public class EmptyRowsWhenScrolling extends UI { image.setWidth("50px"); image.setHeight("50px"); image.addClickListener(new com.vaadin.event.MouseEvents.ClickListener() { + @Override public void click( com.vaadin.event.MouseEvents.ClickEvent event) { Notification.show("Image clicked!"); @@ -123,6 +125,7 @@ public class EmptyRowsWhenScrolling extends UI { // Refresh table button getBtnRefreshTable().addClickListener(new ClickListener() { + @Override public void buttonClick(ClickEvent event) { table.refreshRowCache(); } diff --git a/uitest/src/com/vaadin/tests/components/table/LargeSelectionCausesNPE.java b/uitest/src/com/vaadin/tests/components/table/LargeSelectionCausesNPE.java index fb782b8ded..b6ee62ea59 100644 --- a/uitest/src/com/vaadin/tests/components/table/LargeSelectionCausesNPE.java +++ b/uitest/src/com/vaadin/tests/components/table/LargeSelectionCausesNPE.java @@ -105,6 +105,7 @@ public class LargeSelectionCausesNPE extends TestBase { } Table.ValueChangeListener valueChangeListener = new Table.ValueChangeListener() { + @Override public void valueChange(ValueChangeEvent event) { // in multiselect mode, a Set of itemIds is returned, // in singleselect mode the itemId is returned directly @@ -119,6 +120,7 @@ public class LargeSelectionCausesNPE extends TestBase { Button.ClickListener clickListener = new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { Property nameProperty = table.getContainerProperty(0, NAME); if (("0").equals(nameLabel.getValue())) { @@ -148,6 +150,7 @@ public class LargeSelectionCausesNPE extends TestBase { ColumnGenerator columnGenerator = new ColumnGenerator() { + @Override public Object generateCell(Table source, Object itemId, Object columnId) { Label label = new Label(); diff --git a/uitest/src/com/vaadin/tests/components/table/TableColumnWidthsAndExpandRatios.java b/uitest/src/com/vaadin/tests/components/table/TableColumnWidthsAndExpandRatios.java index 747c99468f..b1ecb3fc10 100644 --- a/uitest/src/com/vaadin/tests/components/table/TableColumnWidthsAndExpandRatios.java +++ b/uitest/src/com/vaadin/tests/components/table/TableColumnWidthsAndExpandRatios.java @@ -38,10 +38,11 @@ public class TableColumnWidthsAndExpandRatios extends TestBase { return new NativeButton("Reset " + property + " width", new Button.ClickListener() { - public void buttonClick(ClickEvent event) { - table.setColumnWidth(property, -1); - } - }); + @Override + public void buttonClick(ClickEvent event) { + table.setColumnWidth(property, -1); + } + }); } @Override diff --git a/uitest/src/com/vaadin/tests/components/table/TableInSubWindowMemoryLeak.java b/uitest/src/com/vaadin/tests/components/table/TableInSubWindowMemoryLeak.java index dcaabf98d6..c0c8876fca 100644 --- a/uitest/src/com/vaadin/tests/components/table/TableInSubWindowMemoryLeak.java +++ b/uitest/src/com/vaadin/tests/components/table/TableInSubWindowMemoryLeak.java @@ -20,6 +20,7 @@ public class TableInSubWindowMemoryLeak extends TestBase { final Button openButton = new Button("open me"); openButton.addClickListener(new ClickListener() { + @Override public void buttonClick(final ClickEvent event) { final Window window = new Window("Simple Window"); window.setModal(true); @@ -29,6 +30,7 @@ public class TableInSubWindowMemoryLeak extends TestBase { window.setContent(table); UI.getCurrent().addWindow(window); window.addCloseListener(new CloseListener() { + @Override public void windowClose(final CloseEvent e) { window.setContent(new Label()); UI.getCurrent().removeWindow(window); @@ -40,6 +42,7 @@ public class TableInSubWindowMemoryLeak extends TestBase { final Button openButton2 = new Button("open me without Table"); openButton2.addClickListener(new ClickListener() { + @Override public void buttonClick(final ClickEvent event) { final Window window = new Window("Simple Window"); window.setModal(true); @@ -47,6 +50,7 @@ public class TableInSubWindowMemoryLeak extends TestBase { window.setWidth("200px"); UI.getCurrent().addWindow(window); window.addCloseListener(new CloseListener() { + @Override public void windowClose(final CloseEvent e) { UI.getCurrent().removeWindow(window); } diff --git a/uitest/src/com/vaadin/tests/components/table/TableRowScrolledBottom.java b/uitest/src/com/vaadin/tests/components/table/TableRowScrolledBottom.java index 9823fc1859..7d48dfa11e 100644 --- a/uitest/src/com/vaadin/tests/components/table/TableRowScrolledBottom.java +++ b/uitest/src/com/vaadin/tests/components/table/TableRowScrolledBottom.java @@ -5,7 +5,6 @@ import com.vaadin.tests.components.TestBase; import com.vaadin.ui.Button; import com.vaadin.ui.Label; import com.vaadin.ui.Table; -import com.vaadin.ui.VerticalLayout; public class TableRowScrolledBottom extends TestBase { diff --git a/uitest/src/com/vaadin/tests/components/table/TableWithBrokenGeneratorAndContainer.java b/uitest/src/com/vaadin/tests/components/table/TableWithBrokenGeneratorAndContainer.java index 9c5ce9dc0c..efa1b1bdab 100644 --- a/uitest/src/com/vaadin/tests/components/table/TableWithBrokenGeneratorAndContainer.java +++ b/uitest/src/com/vaadin/tests/components/table/TableWithBrokenGeneratorAndContainer.java @@ -78,6 +78,7 @@ public class TableWithBrokenGeneratorAndContainer extends TestBase { this.brokenInterval = brokenInterval; } + @Override public Object generateCell(Table source, Object itemId, Object columnId) { if (counter++ % brokenInterval == 0 && Boolean.TRUE.equals(brokenGenerator.getValue())) { @@ -97,6 +98,7 @@ public class TableWithBrokenGeneratorAndContainer extends TestBase { clearTableOnError.setImmediate(true); clearTableOnError.addValueChangeListener(new ValueChangeListener() { + @Override public void valueChange(ValueChangeEvent event) { Boolean value = clearTableOnError.getValue(); setErrorHandler(value != null ? value : false); @@ -110,6 +112,7 @@ public class TableWithBrokenGeneratorAndContainer extends TestBase { Button refreshTableCache = new Button("Refresh table cache", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { table.markAsDirty(); table.refreshRowCache(); diff --git a/uitest/src/com/vaadin/tests/components/table/ValueAfterClearingContainer.java b/uitest/src/com/vaadin/tests/components/table/ValueAfterClearingContainer.java index f378c146ea..b0622e748c 100644 --- a/uitest/src/com/vaadin/tests/components/table/ValueAfterClearingContainer.java +++ b/uitest/src/com/vaadin/tests/components/table/ValueAfterClearingContainer.java @@ -26,6 +26,7 @@ public class ValueAfterClearingContainer extends TestBase { table.setImmediate(true); table.addValueChangeListener(new ValueChangeListener() { + @Override public void valueChange(ValueChangeEvent event) { log.log("Value changed to " + event.getProperty().getValue()); } @@ -38,6 +39,7 @@ public class ValueAfterClearingContainer extends TestBase { multiselect.setId("multiselect"); multiselect.addValueChangeListener(new ValueChangeListener() { + @Override public void valueChange(ValueChangeEvent event) { Boolean value = multiselect.getValue(); table.setMultiSelect(value == null ? false : value); @@ -46,6 +48,7 @@ public class ValueAfterClearingContainer extends TestBase { addComponent(multiselect); Button addItemsButton = new Button("Add table items", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { if (!table.getItemIds().isEmpty()) { Notification @@ -65,6 +68,7 @@ public class ValueAfterClearingContainer extends TestBase { Button showValueButton = new Button("Show value", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { log.log("Table selection: " + table.getValue()); } @@ -74,6 +78,7 @@ public class ValueAfterClearingContainer extends TestBase { Button removeItemsFromTableButton = new Button( "Remove items from table", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { table.removeAllItems(); } @@ -83,6 +88,7 @@ public class ValueAfterClearingContainer extends TestBase { Button removeItemsFromContainerButton = new Button( "Remove items from container", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { table.getContainerDataSource().removeAllItems(); } @@ -92,6 +98,7 @@ public class ValueAfterClearingContainer extends TestBase { Button removeItemsFromContainerAndSanitizeButton = new Button( "Remove items from container and sanitize", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { table.getContainerDataSource().removeAllItems(); table.sanitizeSelection(); @@ -102,6 +109,7 @@ public class ValueAfterClearingContainer extends TestBase { addComponent(removeItemsFromContainerAndSanitizeButton); Button removeSelectedFromTableButton = new Button( "Remove selected item from table", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { Object selection = table.getValue(); if (selection == null) { @@ -117,6 +125,7 @@ public class ValueAfterClearingContainer extends TestBase { Button removeSelectedFromContainer = new Button( "Remove selected item from container", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { Object selection = table.getValue(); if (selection == null) { diff --git a/uitest/src/com/vaadin/tests/components/table/ViewPortCalculation.java b/uitest/src/com/vaadin/tests/components/table/ViewPortCalculation.java index 878dd0d3c4..de32ea1fc0 100644 --- a/uitest/src/com/vaadin/tests/components/table/ViewPortCalculation.java +++ b/uitest/src/com/vaadin/tests/components/table/ViewPortCalculation.java @@ -43,6 +43,7 @@ public class ViewPortCalculation extends TestBase { } table.setCellStyleGenerator(new CellStyleGenerator() { + @Override public String getStyle(Table source, Object itemId, Object propertyId) { if (itemId.equals(lastDoubleClickedItemId)) { @@ -53,6 +54,7 @@ public class ViewPortCalculation extends TestBase { }); table.addItemClickListener(new ItemClickListener() { + @Override public void itemClick(ItemClickEvent event) { if (event.isDoubleClick()) { lastDoubleClickedItemId = event.getItemId(); diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/ExtraScrollbarsInTabSheet.java b/uitest/src/com/vaadin/tests/components/tabsheet/ExtraScrollbarsInTabSheet.java index 2917eccbfb..fffc766e7c 100755 --- a/uitest/src/com/vaadin/tests/components/tabsheet/ExtraScrollbarsInTabSheet.java +++ b/uitest/src/com/vaadin/tests/components/tabsheet/ExtraScrollbarsInTabSheet.java @@ -1,4 +1,5 @@ package com.vaadin.tests.components.tabsheet;
+
import com.vaadin.annotations.Theme;
import com.vaadin.server.VaadinRequest;
import com.vaadin.ui.HorizontalSplitPanel;
diff --git a/uitest/src/com/vaadin/tests/components/tabsheet/HiddenTabSheetBrowserResize.java b/uitest/src/com/vaadin/tests/components/tabsheet/HiddenTabSheetBrowserResize.java index 0fdb579997..eac786d9b3 100644 --- a/uitest/src/com/vaadin/tests/components/tabsheet/HiddenTabSheetBrowserResize.java +++ b/uitest/src/com/vaadin/tests/components/tabsheet/HiddenTabSheetBrowserResize.java @@ -17,6 +17,7 @@ public class HiddenTabSheetBrowserResize extends TestBase { Button toggleButton = new Button("Toggle TabSheet", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { tabSheet.setVisible(!tabSheet.isVisible()); } diff --git a/uitest/src/com/vaadin/tests/components/textarea/ScrollCursor.java b/uitest/src/com/vaadin/tests/components/textarea/ScrollCursor.java index c95731d94f..154a30a64b 100644 --- a/uitest/src/com/vaadin/tests/components/textarea/ScrollCursor.java +++ b/uitest/src/com/vaadin/tests/components/textarea/ScrollCursor.java @@ -27,6 +27,7 @@ public class ScrollCursor extends TestBase { Button button = new Button("Scroll"); button.addListener(new ClickListener() { + @Override public void buttonClick(ClickEvent event) { textArea.setCursorPosition(getPosition()); } @@ -34,6 +35,7 @@ public class ScrollCursor extends TestBase { Button wrap = new Button("Set wrap"); wrap.addListener(new ClickListener() { + @Override public void buttonClick(ClickEvent event) { textArea.setWordwrap(false); } @@ -42,6 +44,7 @@ public class ScrollCursor extends TestBase { Button toBegin = new Button("To begin"); toBegin.addListener(new ClickListener() { + @Override public void buttonClick(ClickEvent event) { position = 3; } @@ -50,6 +53,7 @@ public class ScrollCursor extends TestBase { Button toMiddle = new Button("To middle"); toMiddle.addListener(new ClickListener() { + @Override public void buttonClick(ClickEvent event) { position = 130; } @@ -58,6 +62,7 @@ public class ScrollCursor extends TestBase { Button toEnd = new Button("To end"); toEnd.addListener(new ClickListener() { + @Override public void buttonClick(ClickEvent event) { position = textArea.getValue().toString().length(); } diff --git a/uitest/src/com/vaadin/tests/components/textfield/TextFieldMaxLengthRemovedFromDOM.java b/uitest/src/com/vaadin/tests/components/textfield/TextFieldMaxLengthRemovedFromDOM.java index 28ff20c174..049b08d4e8 100644 --- a/uitest/src/com/vaadin/tests/components/textfield/TextFieldMaxLengthRemovedFromDOM.java +++ b/uitest/src/com/vaadin/tests/components/textfield/TextFieldMaxLengthRemovedFromDOM.java @@ -17,6 +17,7 @@ public class TextFieldMaxLengthRemovedFromDOM extends TestBase { tf.addFocusListener(new FieldEvents.FocusListener() { + @Override public void focus(FocusEvent event) { // Resetting Max length should not remove maxlength attribute tf.setMaxLength(11); diff --git a/uitest/src/com/vaadin/tests/components/tree/SimpleTree.html b/uitest/src/com/vaadin/tests/components/tree/SimpleTree.html new file mode 100644 index 0000000000..a446f67726 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/tree/SimpleTree.html @@ -0,0 +1,270 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>SimpleTree</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">SimpleTree</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.tree.SimpleTree?restartApplication</td> + <td></td> +</tr> +<!--Caption--> +<tr> + <td>verifyElementPresent</td> + <td>vaadin=runcomvaadintestscomponentstreeSimpleTree::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VTree[0]</td> + <td></td> +</tr> +<tr> + <td>storeAttribute</td> + <td>vaadin=runcomvaadintestscomponentstreeSimpleTree::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/domChild[0]/domChild[0]@id</td> + <td>captionid</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>vaadin=runcomvaadintestscomponentstreeSimpleTree::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/domChild[0]/domChild[1]/domChild[0]@aria-labelledby</td> + <td>${captionid}</td> +</tr> +<tr> + <td>storeAttribute</td> + <td>vaadin=runcomvaadintestscomponentstreeSimpleTree::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/domChild[0]/domChild[1]/domChild[0]@id</td> + <td>treeid</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>vaadin=runcomvaadintestscomponentstreeSimpleTree::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/domChild[0]/domChild[0]@for</td> + <td>${treeid}</td> +</tr> +<!--Tree--> +<tr> + <td>verifyElementPresent</td> + <td>xpath=/html/body/div/div/div[2]/div/div[2]/div/div/div/div[@aria-multiselectable='false']</td> + <td></td> +</tr> +<tr> + <td>assertElementPresent</td> + <td>xpath=/html/body/div/div/div[2]/div/div[2]/div/div/div/div[2]/div[@role='tree']</td> + <td></td> +</tr> +<!--Treeitem--> +<tr> + <td>storeAttribute</td> + <td>vaadin=runcomvaadintestscomponentstreeSimpleTree::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]@id</td> + <td>captionid</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>vaadin=runcomvaadintestscomponentstreeSimpleTree::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]@aria-labelledby</td> + <td>${captionid}</td> +</tr> +<tr> + <td>storeAttribute</td> + <td>vaadin=runcomvaadintestscomponentstreeSimpleTree::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]@id</td> + <td>treeitem10id</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>vaadin=runcomvaadintestscomponentstreeSimpleTree::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]/domChild[0]@for</td> + <td>${treeitem10id}</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>${treeitem10id}@aria-level</td> + <td>1</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>xpath=//div[@id='${treeitem10id}']/div[2]@role</td> + <td>group</td> +</tr> +<tr> + <td>storeAttribute</td> + <td>xpath=//div[@id='${treeitem10id}']/div[2]/div[1]@id</td> + <td>treeitem11id</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>${treeitem11id}@role</td> + <td>treeitem</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>${treeitem11id}@aria-level</td> + <td>2</td> +</tr> +<!--Open/Close--> +<tr> + <td>assertAttribute</td> + <td>${treeitem10id}@aria-expanded</td> + <td>true</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=//div[@id='${treeitem10id}']</td> + <td>5,5</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>${treeitem10id}@aria-expanded</td> + <td>false</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=//div[@id='${treeitem10id}']</td> + <td>5,5</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>${treeitem10id}@aria-expanded</td> + <td>true</td> +</tr> +<!--Root Selected--> +<tr> + <td>assertAttribute</td> + <td>${treeitem10id}@aria-selected</td> + <td>false</td> +</tr> +<tr> + <td>mouseClick</td> + <td>xpath=//div[@id='${treeitem10id}']/div[1]/div[1]/span[1]</td> + <td></td> +</tr> +<tr> + <td>storeAttribute</td> + <td>vaadin=runcomvaadintestscomponentstreeSimpleTree::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]@id</td> + <td>treeitem10id</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>${treeitem10id}@aria-selected</td> + <td>true</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>xpath=//div[@id='${treeitem10id}']/div[2]/div[1]@aria-selected</td> + <td>false</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>xpath=//div[@id='${treeitem10id}']/div[2]/div[2]@aria-selected</td> + <td>false</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>xpath=//div[@id='${treeitem10id}']/div[2]/div[3]@aria-selected</td> + <td>false</td> +</tr> +<!--First child selected--> +<tr> + <td>mouseClick</td> + <td>xpath=//div[@id='${treeitem10id}']/div[2]/div[1]/div[1]/div[1]/span[1]</td> + <td></td> +</tr> +<tr> + <td>storeAttribute</td> + <td>vaadin=runcomvaadintestscomponentstreeSimpleTree::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]@id</td> + <td>treeitem10id</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>${treeitem10id}@aria-selected</td> + <td>false</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>xpath=//div[@id='${treeitem10id}']/div[2]/div[1]@aria-selected</td> + <td>true</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>xpath=//div[@id='${treeitem10id}']/div[2]/div[2]@aria-selected</td> + <td>false</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>xpath=//div[@id='${treeitem10id}']/div[2]/div[3]@aria-selected</td> + <td>false</td> +</tr> +<!--Last child selected--> +<tr> + <td>mouseClick</td> + <td>xpath=//div[@id='${treeitem10id}']/div[2]/div[3]/div[1]/div[1]/span[1]</td> + <td></td> +</tr> +<tr> + <td>storeAttribute</td> + <td>vaadin=runcomvaadintestscomponentstreeSimpleTree::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]@id</td> + <td>treeitem10id</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>${treeitem10id}@aria-selected</td> + <td>false</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>xpath=//div[@id='${treeitem10id}']/div[2]/div[1]@aria-selected</td> + <td>false</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>xpath=//div[@id='${treeitem10id}']/div[2]/div[2]@aria-selected</td> + <td>false</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>xpath=//div[@id='${treeitem10id}']/div[2]/div[3]@aria-selected</td> + <td>true</td> +</tr> +<!--Another root selected--> +<tr> + <td>assertAttribute</td> + <td>vaadin=runcomvaadintestscomponentstreeSimpleTree::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/domChild[0]/domChild[1]/domChild[0]/domChild[1]@aria-selected</td> + <td>false</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentstreeSimpleTree::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/domChild[0]/domChild[1]/domChild[0]/domChild[1]/domChild[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertAttribute</td> + <td>vaadin=runcomvaadintestscomponentstreeSimpleTree::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/domChild[0]/domChild[1]/domChild[0]/domChild[1]@aria-selected</td> + <td>true</td> +</tr> +<tr> + <td>storeAttribute</td> + <td>vaadin=runcomvaadintestscomponentstreeSimpleTree::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/domChild[0]/domChild[1]/domChild[0]/domChild[0]@id</td> + <td>treeitem10id</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>${treeitem10id}@aria-selected</td> + <td>false</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>xpath=//div[@id='${treeitem10id}']/div[2]/div[1]@aria-selected</td> + <td>false</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>xpath=//div[@id='${treeitem10id}']/div[2]/div[2]@aria-selected</td> + <td>false</td> +</tr> +<tr> + <td>assertAttribute</td> + <td>xpath=//div[@id='${treeitem10id}']/div[2]/div[3]@aria-selected</td> + <td>false</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/tree/SimpleTree.java b/uitest/src/com/vaadin/tests/components/tree/SimpleTree.java new file mode 100644 index 0000000000..2fd3f05dbb --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/tree/SimpleTree.java @@ -0,0 +1,122 @@ +package com.vaadin.tests.components.tree; + +import java.util.Date; + +import com.vaadin.data.Item; +import com.vaadin.data.util.HierarchicalContainer; +import com.vaadin.event.Action; +import com.vaadin.server.ThemeResource; +import com.vaadin.tests.components.TestBase; +import com.vaadin.ui.AbstractSelect; +import com.vaadin.ui.Tree; + +public class SimpleTree extends TestBase implements Action.Handler { + private static final String[][] hardware = { // + { "Desktops", "Dell OptiPlex GX240", "Dell OptiPlex GX260", + "Dell OptiPlex GX280" }, + { "Monitors", "Benq T190HD", "Benq T220HD", "Benq T240HD" }, + { "Laptops", "IBM ThinkPad T40", "IBM ThinkPad T43", + "IBM ThinkPad T60" } }; + + ThemeResource notCachedFolderIconLargeOther = new ThemeResource( + "../runo/icons/16/ok.png?" + new Date().getTime()); + ThemeResource notCachedFolderIconLarge = new ThemeResource( + "../runo/icons/16/folder.png?" + new Date().getTime()); + + // Actions for the context menu + private static final Action ACTION_ADD = new Action("Add child item"); + private static final Action ACTION_DELETE = new Action("Delete"); + private static final Action[] ACTIONS = new Action[] { ACTION_ADD, + ACTION_DELETE }; + + private Tree tree; + + @Override + public void setup() { + // Create the Tree,a dd to layout + tree = new Tree("Hardware Inventory"); + addComponent(tree); + + // Contents from a (prefilled example) hierarchical container: + tree.setContainerDataSource(getHardwareContainer()); + + // Add actions (context menu) + tree.addActionHandler(this); + + // Cause valueChange immediately when the user selects + tree.setImmediate(true); + + // Set tree to show the 'name' property as caption for items + tree.setItemCaptionPropertyId("name"); + tree.setItemCaptionMode(AbstractSelect.ITEM_CAPTION_MODE_PROPERTY); + + tree.setItemIcon(9, notCachedFolderIconLargeOther, "First Choice"); + tree.setItemIcon(11, notCachedFolderIconLarge); + + // Expand whole tree + for (Object id : tree.rootItemIds()) { + tree.expandItemsRecursively(id); + } + } + + public static HierarchicalContainer getHardwareContainer() { + Item item = null; + int itemId = 0; // Increasing numbering for itemId:s + + // Create new container + HierarchicalContainer hwContainer = new HierarchicalContainer(); + // Create containerproperty for name + hwContainer.addContainerProperty("name", String.class, null); + // Create containerproperty for icon + hwContainer.addContainerProperty("icon", ThemeResource.class, + new ThemeResource("../runo/icons/16/document.png")); + for (int i = 0; i < hardware.length; i++) { + // Add new item + item = hwContainer.addItem(itemId); + // Add name property for item + item.getItemProperty("name").setValue(hardware[i][0]); + // Allow children + hwContainer.setChildrenAllowed(itemId, true); + itemId++; + for (int j = 1; j < hardware[i].length; j++) { + if (j == 1) { + item.getItemProperty("icon").setValue( + new ThemeResource("../runo/icons/16/folder.png")); + } + + // Add child items + item = hwContainer.addItem(itemId); + item.getItemProperty("name").setValue(hardware[i][j]); + hwContainer.setParent(itemId, itemId - j); + + hwContainer.setChildrenAllowed(itemId, false); + if (j == 2) { + hwContainer.setChildrenAllowed(itemId, true); + } + + itemId++; + } + } + return hwContainer; + } + + @Override + protected String getDescription() { + return "Sample Tree for testing WAI-ARIA functionality"; + } + + @Override + protected Integer getTicketNumber() { + return 0; + } + + @Override + public Action[] getActions(Object target, Object sender) { + return ACTIONS; + } + + @Override + public void handleAction(Action action, Object sender, Object target) { + System.out.println("Action: " + action.getCaption()); + } +}
\ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/components/tree/TreeItemClickAndValueChange.html b/uitest/src/com/vaadin/tests/components/tree/TreeItemClickAndValueChange.html index bf83a1acdb..83508c9478 100644 --- a/uitest/src/com/vaadin/tests/components/tree/TreeItemClickAndValueChange.html +++ b/uitest/src/com/vaadin/tests/components/tree/TreeItemClickAndValueChange.html @@ -64,7 +64,7 @@ <tr> <td>assertText</td> <td>vaadin=runTrees::PID_SLog_row_1</td> - <td>4. left click on source: [Item 1], client: [*];, relative: [-1,-1], itemId: Item 2, propertyId: null</td> + <td>4. left click on source: com.vaadin.ui.Tree@*, client: [*];, relative: [-1,-1], itemId: Item 2, propertyId: null</td> </tr> <tr> <td>assertText</td> @@ -74,9 +74,8 @@ <tr> <td>assertText</td> <td>vaadin=runTrees::PID_SLog_row_3</td> - <td>2. left click on source: [], client: [*];, relative: [-1,-1], itemId: Item 1, propertyId: null</td> + <td>2. left click on source: com.vaadin.ui.Tree@*, client: [*];, relative: [-1,-1], itemId: Item 1, propertyId: null</td> </tr> - </tbody></table> </body> </html> diff --git a/uitest/src/com/vaadin/tests/components/treetable/TreeTableCacheOnPartialUpdates.java b/uitest/src/com/vaadin/tests/components/treetable/TreeTableCacheOnPartialUpdates.java index f792a32f8f..85a69702a4 100644 --- a/uitest/src/com/vaadin/tests/components/treetable/TreeTableCacheOnPartialUpdates.java +++ b/uitest/src/com/vaadin/tests/components/treetable/TreeTableCacheOnPartialUpdates.java @@ -90,6 +90,7 @@ public class TreeTableCacheOnPartialUpdates extends TestBase { } public class Col4ColumnGenerator implements ColumnGenerator { + @Override public Component generateCell(final com.vaadin.ui.Table source, final Object itemId, Object columnId) { TestBean tb = (TestBean) itemId; @@ -98,6 +99,7 @@ public class TreeTableCacheOnPartialUpdates extends TestBase { btnCol4.setId("cacheTestButtonToggle-" + tb.getCol1() + "-" + tb.getCol2()); btnCol4.addClickListener(new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { treeTable.setCollapsed(itemId, !treeTable.isCollapsed(itemId)); diff --git a/uitest/src/com/vaadin/tests/components/treetable/TreeTableExtraScrollbar.java b/uitest/src/com/vaadin/tests/components/treetable/TreeTableExtraScrollbar.java index 4af0da158d..79c967914f 100644 --- a/uitest/src/com/vaadin/tests/components/treetable/TreeTableExtraScrollbar.java +++ b/uitest/src/com/vaadin/tests/components/treetable/TreeTableExtraScrollbar.java @@ -49,6 +49,7 @@ public class TreeTableExtraScrollbar extends TestBase { button.setId("button"); button.addClickListener(new ClickListener() { + @Override public void buttonClick(ClickEvent event) { table.addItem(new TestObject("name 6-1", "value 6-1")); table.addItem(new TestObject("name 6-2", "value 6-2")); @@ -68,12 +69,14 @@ public class TreeTableExtraScrollbar extends TestBase { } private class EmptyColumnGenerator implements Table.ColumnGenerator { + @Override public Object generateCell(Table table, Object itemId, Object columnId) { return null; } } private class TypeColumnGenerator implements Table.ColumnGenerator { + @Override public Object generateCell(Table table, Object itemId, Object columnId) { if (itemId instanceof TestObject) { return new Label(((TestObject) itemId).getValue()); diff --git a/uitest/src/com/vaadin/tests/components/treetable/TreeTableExtraScrollbarWithChildren.java b/uitest/src/com/vaadin/tests/components/treetable/TreeTableExtraScrollbarWithChildren.java index cad33e242f..0dc98b2c2e 100644 --- a/uitest/src/com/vaadin/tests/components/treetable/TreeTableExtraScrollbarWithChildren.java +++ b/uitest/src/com/vaadin/tests/components/treetable/TreeTableExtraScrollbarWithChildren.java @@ -62,6 +62,7 @@ public class TreeTableExtraScrollbarWithChildren extends TestBase { button.setId("button"); button.addClickListener(new ClickListener() { + @Override public void buttonClick(ClickEvent event) { table.setCollapsed(parent, !table.isCollapsed(parent)); Notification.show("collapsed: " + table.isCollapsed(parent)); @@ -73,6 +74,7 @@ public class TreeTableExtraScrollbarWithChildren extends TestBase { } private class HierarchyColumnGenerator implements Table.ColumnGenerator { + @Override public Object generateCell(Table table, Object itemId, Object columnId) { Label label = new Label("this should be mostly hidden"); label.setSizeUndefined(); @@ -81,6 +83,7 @@ public class TreeTableExtraScrollbarWithChildren extends TestBase { } private class TypeColumnGenerator implements Table.ColumnGenerator { + @Override public Object generateCell(Table table, Object itemId, Object columnId) { if (itemId instanceof TestObject) { return new Label(((TestObject) itemId).getValue()); diff --git a/uitest/src/com/vaadin/tests/components/treetable/TreeTableInternalError.java b/uitest/src/com/vaadin/tests/components/treetable/TreeTableInternalError.java index f6d7f11eb7..1b510f1ac5 100644 --- a/uitest/src/com/vaadin/tests/components/treetable/TreeTableInternalError.java +++ b/uitest/src/com/vaadin/tests/components/treetable/TreeTableInternalError.java @@ -30,6 +30,7 @@ public class TreeTableInternalError extends TestBase { Button button = new Button("Resize") { { addClickListener(new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { t.setHeight("300px"); } @@ -64,12 +65,14 @@ public class TreeTableInternalError extends TestBase { } public class ButtonColumnGenerator implements ColumnGenerator { + @Override public Component generateCell(final com.vaadin.ui.Table source, final Object itemId, Object columnId) { String identifier = "Expand/Collapse"; Button btnCol = new NativeButton(identifier); btnCol.setId("cacheTestButtonToggle-" + itemId); btnCol.addClickListener(new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { t.setCollapsed(itemId, !t.isCollapsed(itemId)); } diff --git a/uitest/src/com/vaadin/tests/components/ui/LoadingIndicatorConfigurationTest.java b/uitest/src/com/vaadin/tests/components/ui/LoadingIndicatorConfigurationTest.java new file mode 100644 index 0000000000..3c857a8753 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/ui/LoadingIndicatorConfigurationTest.java @@ -0,0 +1,96 @@ +package com.vaadin.tests.components.ui; + +import com.vaadin.data.Property; +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.NativeButton; +import com.vaadin.ui.TextField; + +public class LoadingIndicatorConfigurationTest extends AbstractTestUIWithLog { + + private TextField firstDelay; + private TextField secondDelay; + private TextField thirdDelay; + + @Override + protected void setup(VaadinRequest request) { + final TextField delayField = new TextField("Delay (ms)"); + delayField.setConverter(Integer.class); + delayField.setConvertedValue(1000); + + NativeButton delayButton = new NativeButton("Wait"); + delayButton.addClickListener(new ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + try { + Thread.sleep((Integer) delayField.getConvertedValue()); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + }); + + firstDelay = createIntegerTextField("First delay (ms)", + getState().loadingIndicatorConfiguration.firstDelay); + firstDelay.addValueChangeListener(new Property.ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + getLoadingIndicatorConfiguration().setFirstDelay( + (Integer) firstDelay.getConvertedValue()); + } + }); + secondDelay = createIntegerTextField("Second delay (ms)", + getState().loadingIndicatorConfiguration.secondDelay); + secondDelay.addValueChangeListener(new Property.ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + getLoadingIndicatorConfiguration().setSecondDelay( + (Integer) secondDelay.getConvertedValue()); + } + }); + thirdDelay = createIntegerTextField("Third delay (ms)", + getState().loadingIndicatorConfiguration.thirdDelay); + thirdDelay.addValueChangeListener(new Property.ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + getLoadingIndicatorConfiguration().setThirdDelay( + (Integer) thirdDelay.getConvertedValue()); + } + }); + + getLayout().addComponents(firstDelay, secondDelay, thirdDelay); + + HorizontalLayout hl = new HorizontalLayout(); + hl.setMargin(true); + hl.setDefaultComponentAlignment(Alignment.BOTTOM_RIGHT); + hl.addComponents(delayField, delayButton); + addComponent(hl); + + } + + private TextField createIntegerTextField(String caption, int initialValue) { + TextField tf = new TextField(caption); + tf.setId(caption); + tf.setConverter(Integer.class); + tf.setImmediate(true); + tf.setConvertedValue(initialValue); + return tf; + } + + @Override + protected String getTestDescription() { + return "Tests that loading indicator delay can be configured"; + } + + @Override + protected Integer getTicketNumber() { + return 7448; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/ui/TooltipConfiguration.html b/uitest/src/com/vaadin/tests/components/ui/TooltipConfiguration.html new file mode 100644 index 0000000000..e41cf5e176 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/ui/TooltipConfiguration.html @@ -0,0 +1,145 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://localhost:8888/" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.ui.TooltipConfiguration?restartApplication</td> + <td></td> +</tr> +<!--Short close delay--> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::PID_SClose timeout</td> + <td>0</td> +</tr> +<tr> + <td>mouseMoveAt</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::PID_SshortTooltip</td> + <td>0,0</td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td> + <td>This is a short tooltip</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::</td> + <td></td> +</tr> +<tr> + <td>assertElementNotPresent</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td> + <td></td> +</tr> +<!--Long close delay--> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::PID_SClose timeout</td> + <td>3000</td> +</tr> +<tr> + <td>mouseMoveAt</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::</td> + <td>0,0</td> +</tr> +<tr> + <td>mouseMoveAt</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::PID_SshortTooltip</td> + <td>0,0</td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td> + <td>This is a short tooltip</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::</td> + <td></td> +</tr> +<tr> + <td>assertElementPresent</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::PID_SClose timeout</td> + <td>60,9</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::PID_SClose timeout</td> + <td>0</td> +</tr> +<!--Max width 500--> +<tr> + <td>mouseMoveAt</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::PID_SlongTooltip</td> + <td>0,0</td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td> + <td></td> +</tr> +<tr> + <td>assertElementWidth</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td> + <td>500</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::</td> + <td></td> +</tr> +<!--Max width 100--> +<tr> + <td>type</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::PID_SMax width</td> + <td>100</td> +</tr> +<tr> + <td>mouseMoveAt</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::</td> + <td>0,0</td> +</tr> +<tr> + <td>mouseMoveAt</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::PID_SlongTooltip</td> + <td>0,0</td> +</tr> +<tr> + <td>waitForElementPresent</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td> + <td></td> +</tr> +<tr> + <td>assertElementWidth</td> + <td>vaadin=runcomvaadintestscomponentsuiTooltipConfiguration::Root/VTooltip[0]/FlowPanel[0]/domChild[1]</td> + <td>100</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/ui/TooltipConfiguration.java b/uitest/src/com/vaadin/tests/components/ui/TooltipConfiguration.java new file mode 100644 index 0000000000..4d201d2a1a --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/ui/TooltipConfiguration.java @@ -0,0 +1,107 @@ +package com.vaadin.tests.components.ui; + +import com.vaadin.data.Property; +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.tests.util.LoremIpsum; +import com.vaadin.ui.NativeButton; +import com.vaadin.ui.TextField; + +public class TooltipConfiguration extends AbstractTestUIWithLog { + + private TextField closeTimeout; + private TextField quickOpenTimeout; + private TextField maxWidth; + private TextField openDelay; + private TextField quickOpenDelay; + + @Override + protected void setup(VaadinRequest request) { + NativeButton componentWithShortTooltip = new NativeButton( + "Short tooltip"); + componentWithShortTooltip.setDescription("This is a short tooltip"); + componentWithShortTooltip.setId("shortTooltip"); + + NativeButton componentWithLongTooltip = new NativeButton("Long tooltip"); + componentWithLongTooltip.setId("longTooltip"); + componentWithLongTooltip.setDescription(LoremIpsum.get(5000)); + + closeTimeout = createIntegerTextField("Close timeout", + getState().tooltipConfiguration.closeTimeout); + closeTimeout.addValueChangeListener(new Property.ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + getTooltipConfiguration().setCloseTimeout( + (Integer) closeTimeout.getConvertedValue()); + } + }); + maxWidth = createIntegerTextField("Max width", + getState().tooltipConfiguration.maxWidth); + maxWidth.addValueChangeListener(new Property.ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + getTooltipConfiguration().setMaxWidth( + (Integer) maxWidth.getConvertedValue()); + } + }); + openDelay = createIntegerTextField("Open delay", + getState().tooltipConfiguration.openDelay); + openDelay.addValueChangeListener(new Property.ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + getTooltipConfiguration().setOpenDelay( + (Integer) openDelay.getConvertedValue()); + } + }); + + quickOpenDelay = createIntegerTextField("Quick open delay", + getState().tooltipConfiguration.quickOpenDelay); + quickOpenDelay + .addValueChangeListener(new Property.ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + getTooltipConfiguration().setQuickOpenDelay( + (Integer) quickOpenDelay.getConvertedValue()); + } + }); + + quickOpenTimeout = createIntegerTextField("Quick open timeout", + getState().tooltipConfiguration.quickOpenTimeout); + quickOpenTimeout + .addValueChangeListener(new Property.ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + getTooltipConfiguration().setQuickOpenTimeout( + (Integer) quickOpenTimeout.getConvertedValue()); + } + }); + + getLayout().addComponents(closeTimeout, openDelay, quickOpenDelay, + quickOpenTimeout, maxWidth); + + getLayout().addComponents(componentWithShortTooltip, + componentWithLongTooltip); + + } + + private TextField createIntegerTextField(String caption, int initialValue) { + TextField tf = new TextField(caption); + tf.setId(caption); + tf.setConverter(Integer.class); + tf.setImmediate(true); + tf.setConvertedValue(initialValue); + return tf; + } + + @Override + protected String getTestDescription() { + return "Tests that tooltip delays can be configured"; + } + + @Override + protected Integer getTicketNumber() { + return 8065; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/ui/UIInitException.html b/uitest/src/com/vaadin/tests/components/ui/UIInitException.html index c2b1b33059..68b11e7942 100644 --- a/uitest/src/com/vaadin/tests/components/ui/UIInitException.html +++ b/uitest/src/com/vaadin/tests/components/ui/UIInitException.html @@ -17,9 +17,9 @@ <td></td> </tr> <tr> - <td>assertText</td> - <td>//html/body/div/pre</td> + <td>assertTextPresent</td> <td>Catch me if you can</td> + <td></td> </tr> </tbody></table> </body> diff --git a/uitest/src/com/vaadin/tests/components/ui/UIPolling.html b/uitest/src/com/vaadin/tests/components/ui/UIPolling.html new file mode 100644 index 0000000000..f770bae009 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/ui/UIPolling.html @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://localhost:8888/run/" /> +<title>WindowMaximizeRestoreTest</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">WindowMaximizeRestoreTest</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/UIPolling?restartApplication</td> + <td></td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runUIPolling::/VVerticalLayout[0]/Slot[2]/VVerticalLayout[0]/Slot[0]/VTextField[0]</td> + <td>500</td> +</tr> +<tr> + <td>pause</td> + <td>2000</td> + <td></td> +</tr> +<!--Ensure polling has taken place--> +<tr> + <td>assertTextPresent</td> + <td>2. 1000ms has passed</td> + <td></td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runUIPolling::/VVerticalLayout[0]/Slot[2]/VVerticalLayout[0]/Slot[0]/VTextField[0]</td> + <td>-1</td> +</tr> +<tr> + <td>pause</td> + <td>2000</td> + <td></td> +</tr> +<!--Ensure polling has stopped--> +<tr> + <td>assertTextNotPresent</td> + <td>8. 4000ms has passed</td> + <td></td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/ui/UIPolling.java b/uitest/src/com/vaadin/tests/components/ui/UIPolling.java new file mode 100644 index 0000000000..48671191ca --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/ui/UIPolling.java @@ -0,0 +1,90 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.components.ui; + +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.data.util.MethodProperty; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.tests.util.Log; +import com.vaadin.ui.TextField; + +public class UIPolling extends AbstractTestUIWithLog { + + protected static final long SLEEP_TIME = 500; + + private class BackgroundLogger extends Thread { + + @Override + public void run() { + int i = 0; + while (true) { + i++; + try { + Thread.sleep(SLEEP_TIME); + } catch (InterruptedException e) { + } + final int iteration = i; + access(new Runnable() { + @Override + public void run() { + log.log((iteration * SLEEP_TIME) + "ms has passed"); + } + }); + } + } + } + + private BackgroundLogger logger = null; + + @Override + protected void setup(VaadinRequest request) { + log = new Log(20); + log.setNumberLogRows(true); + TextField pollingInterval = new TextField("Poll interval", + new MethodProperty<Integer>(this, "pollInterval")); + pollingInterval.setImmediate(true); + pollingInterval.setValue("-1"); + pollingInterval.addValueChangeListener(new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + if (logger != null) { + logger.stop(); + logger = null; + } + if (getPollInterval() >= 0) { + logger = new BackgroundLogger(); + logger.start(); + } + } + }); + addComponent(pollingInterval); + + } + + @Override + protected String getTestDescription() { + return "Tests the polling feature of UI. Set the polling interval using the text field. Enabling polling will at the same time start a background thread which logs every 500ms"; + } + + @Override + protected Integer getTicketNumber() { + return 11495; + } + +} diff --git a/uitest/src/com/vaadin/tests/components/ui/UISerialization.java b/uitest/src/com/vaadin/tests/components/ui/UISerialization.java index 5f3d8d97de..90021a0bf4 100644 --- a/uitest/src/com/vaadin/tests/components/ui/UISerialization.java +++ b/uitest/src/com/vaadin/tests/components/ui/UISerialization.java @@ -20,15 +20,19 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; +import java.io.PrintWriter; import java.io.Serializable; +import java.io.StringWriter; import java.util.Date; import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.label.ContentMode; import com.vaadin.tests.components.AbstractTestUI; import com.vaadin.tests.util.Log; import com.vaadin.ui.Button; import com.vaadin.ui.Button.ClickEvent; import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.Label; public class UISerialization extends AbstractTestUI { @@ -42,21 +46,29 @@ public class UISerialization extends AbstractTestUI { @Override public void buttonClick(ClickEvent event) { Date d = new Date(); - byte[] result = serialize(UISerialization.this); - long elapsed = new Date().getTime() - d.getTime(); - log.log("Serialized UI in " + elapsed + "ms into " - + result.length + " bytes"); - Object diffStateBefore = getConnectorTracker().getDiffState( - UISerialization.this); - UISerialization app = (UISerialization) deserialize(result); - log.log("Deserialized UI in " + elapsed + "ms"); - Object diffStateAfter = getConnectorTracker().getDiffState( - UISerialization.this); - if (diffStateBefore.equals(diffStateAfter)) { - log.log("Diff states match, size: " - + diffStateBefore.toString().length()); - } else { - log.log("Diff states do not match"); + try { + byte[] result = serialize(UISerialization.this); + long elapsed = new Date().getTime() - d.getTime(); + log.log("Serialized UI in " + elapsed + "ms into " + + result.length + " bytes"); + Object diffStateBefore = getConnectorTracker() + .getDiffState(UISerialization.this); + UISerialization app = (UISerialization) deserialize(result); + log.log("Deserialized UI in " + elapsed + "ms"); + Object diffStateAfter = getConnectorTracker().getDiffState( + UISerialization.this); + if (diffStateBefore.equals(diffStateAfter)) { + log.log("Diff states match, size: " + + diffStateBefore.toString().length()); + } else { + log.log("Diff states do not match"); + } + } catch (Exception e) { + log.log("Exception caught: " + e.getMessage()); + StringWriter sw = new StringWriter(); + e.printStackTrace(new PrintWriter(sw)); + addComponent(new Label(sw.toString(), + ContentMode.PREFORMATTED)); } } @@ -64,20 +76,16 @@ public class UISerialization extends AbstractTestUI { } protected void serializeInstance(Class<?> cls) - throws InstantiationException, IllegalAccessException { + throws InstantiationException, IllegalAccessException, IOException { serialize((Serializable) cls.newInstance()); } - protected byte[] serialize(Serializable serializable) { + protected byte[] serialize(Serializable serializable) throws IOException { ByteArrayOutputStream os = new ByteArrayOutputStream(); ObjectOutputStream oos; - try { - oos = new ObjectOutputStream(os); - oos.writeObject(serializable); - return os.toByteArray(); - } catch (IOException e) { - throw new RuntimeException("Serialization failed", e); - } + oos = new ObjectOutputStream(os); + oos.writeObject(serializable); + return os.toByteArray(); } protected Object deserialize(byte[] result) { diff --git a/uitest/src/com/vaadin/tests/components/uitest/BackButtonTest.java b/uitest/src/com/vaadin/tests/components/uitest/BackButtonTest.java index d5bac0d509..7e7a084eed 100644 --- a/uitest/src/com/vaadin/tests/components/uitest/BackButtonTest.java +++ b/uitest/src/com/vaadin/tests/components/uitest/BackButtonTest.java @@ -57,6 +57,7 @@ public class BackButtonTest extends AbstractTestUI { addComponent(l); Button b = new Button("Go to Page 2", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { l.setCaption("Data from Page 1 : " + value); getPage().setUriFragment("page2"); @@ -85,6 +86,7 @@ public class BackButtonTest extends AbstractTestUI { addComponent(f); f.addValueChangeListener(new ValueChangeListener() { + @Override public void valueChange(ValueChangeEvent event) { value = f.getValue(); p1.l.setCaption("Data from Page 2 : " + value); @@ -92,6 +94,7 @@ public class BackButtonTest extends AbstractTestUI { }); Button b = new Button("Go Back", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { getPage().setUriFragment("page1"); } diff --git a/uitest/src/com/vaadin/tests/components/uitest/base_theme_test.html b/uitest/src/com/vaadin/tests/components/uitest/base_theme_test.html index 0308a09a16..614ae7bcda 100644 --- a/uitest/src/com/vaadin/tests/components/uitest/base_theme_test.html +++ b/uitest/src/com/vaadin/tests/components/uitest/base_theme_test.html @@ -289,7 +289,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>9,8</td> </tr> <tr> @@ -304,7 +304,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>11,6</td> </tr> <tr> @@ -319,7 +319,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>8,5</td> </tr> <tr> @@ -334,7 +334,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>9,6</td> </tr> <tr> @@ -349,7 +349,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>10,7</td> </tr> <tr> diff --git a/uitest/src/com/vaadin/tests/components/uitest/chameleon_theme_test.html b/uitest/src/com/vaadin/tests/components/uitest/chameleon_theme_test.html index fb00029d8f..7d9ffc65b8 100644 --- a/uitest/src/com/vaadin/tests/components/uitest/chameleon_theme_test.html +++ b/uitest/src/com/vaadin/tests/components/uitest/chameleon_theme_test.html @@ -289,7 +289,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>9,8</td> </tr> <tr> @@ -304,7 +304,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>11,6</td> </tr> <tr> @@ -319,7 +319,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>8,5</td> </tr> <tr> @@ -334,7 +334,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>9,6</td> </tr> <tr> @@ -349,7 +349,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>10,7</td> </tr> <tr> diff --git a/uitest/src/com/vaadin/tests/components/uitest/liferay_theme_test.html b/uitest/src/com/vaadin/tests/components/uitest/liferay_theme_test.html index 193c648916..d0ee96c7ef 100644 --- a/uitest/src/com/vaadin/tests/components/uitest/liferay_theme_test.html +++ b/uitest/src/com/vaadin/tests/components/uitest/liferay_theme_test.html @@ -289,7 +289,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>9,8</td> </tr> <tr> @@ -304,7 +304,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>11,6</td> </tr> <tr> @@ -319,7 +319,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>8,5</td> </tr> <tr> @@ -334,7 +334,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>9,6</td> </tr> <tr> @@ -349,7 +349,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>10,7</td> </tr> <tr> diff --git a/uitest/src/com/vaadin/tests/components/uitest/reindeer_theme_test.html b/uitest/src/com/vaadin/tests/components/uitest/reindeer_theme_test.html index b1340831bc..a330f5bf61 100644 --- a/uitest/src/com/vaadin/tests/components/uitest/reindeer_theme_test.html +++ b/uitest/src/com/vaadin/tests/components/uitest/reindeer_theme_test.html @@ -289,7 +289,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>9,8</td> </tr> <tr> @@ -304,7 +304,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>11,6</td> </tr> <tr> @@ -319,7 +319,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>8,5</td> </tr> <tr> @@ -334,7 +334,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>9,6</td> </tr> <tr> @@ -349,7 +349,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>10,7</td> </tr> <tr> diff --git a/uitest/src/com/vaadin/tests/components/uitest/runo_theme_test.html b/uitest/src/com/vaadin/tests/components/uitest/runo_theme_test.html index fadc503abd..61ba58a0e6 100644 --- a/uitest/src/com/vaadin/tests/components/uitest/runo_theme_test.html +++ b/uitest/src/com/vaadin/tests/components/uitest/runo_theme_test.html @@ -289,7 +289,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>9,8</td> </tr> <tr> @@ -304,7 +304,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>11,6</td> </tr> <tr> @@ -319,7 +319,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>8,5</td> </tr> <tr> @@ -334,7 +334,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>9,6</td> </tr> <tr> @@ -349,7 +349,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runThemeTestUI::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>10,7</td> </tr> <tr> diff --git a/uitest/src/com/vaadin/tests/components/upload/TestFileUploadSize.java b/uitest/src/com/vaadin/tests/components/upload/TestFileUploadSize.java index 32f5c93bfd..178f8f9393 100644 --- a/uitest/src/com/vaadin/tests/components/upload/TestFileUploadSize.java +++ b/uitest/src/com/vaadin/tests/components/upload/TestFileUploadSize.java @@ -28,6 +28,7 @@ public class TestFileUploadSize extends TestBase implements Receiver { Upload u = new Upload("Upload", new Upload.Receiver() { + @Override public OutputStream receiveUpload(String filename, String mimeType) { return baos; } @@ -35,12 +36,14 @@ public class TestFileUploadSize extends TestBase implements Receiver { u.setId("UPL"); u.addStartedListener(new Upload.StartedListener() { + @Override public void uploadStarted(StartedEvent event) { expectedSize.setValue(String.valueOf(event.getContentLength())); } }); u.addFinishedListener(new Upload.FinishedListener() { + @Override public void uploadFinished(FinishedEvent event) { label.setValue("Upload finished. Name: " + event.getFilename()); receivedSize.setValue(String.valueOf(baos.size())); @@ -62,6 +65,7 @@ public class TestFileUploadSize extends TestBase implements Receiver { addComponent(u); } + @Override public OutputStream receiveUpload(String filename, String MIMEType) { Notification.show("Receiving upload"); return new ByteArrayOutputStream(); diff --git a/uitest/src/com/vaadin/tests/components/window/CenteredWindowWithUndefinedSize.java b/uitest/src/com/vaadin/tests/components/window/CenteredWindowWithUndefinedSize.java index efe7eb20bf..42fab79376 100644 --- a/uitest/src/com/vaadin/tests/components/window/CenteredWindowWithUndefinedSize.java +++ b/uitest/src/com/vaadin/tests/components/window/CenteredWindowWithUndefinedSize.java @@ -9,7 +9,7 @@ public class CenteredWindowWithUndefinedSize extends TestBase { @Override protected String getDescription() { - return "The centered sub-window with undefined height and a 100% high layout should be rendered in the center of the screen and not in the top-left corner."; + return "The centered sub-window with undefined height and a undefined high layout should be rendered in the center of the screen and not in the top-left corner."; } @Override @@ -23,7 +23,7 @@ public class CenteredWindowWithUndefinedSize extends TestBase { layout.setMargin(true); Window centered = new Window("A window", layout); centered.setSizeUndefined(); - layout.setSizeFull(); + layout.setSizeUndefined(); centered.center(); Label l = new Label("This window should be centered"); diff --git a/uitest/src/com/vaadin/tests/components/window/CloseSubWindow.html b/uitest/src/com/vaadin/tests/components/window/CloseSubWindow.html index 6e3eb906b7..ac81dfdefb 100644 --- a/uitest/src/com/vaadin/tests/components/window/CloseSubWindow.html +++ b/uitest/src/com/vaadin/tests/components/window/CloseSubWindow.html @@ -17,11 +17,6 @@ <td></td> </tr> <tr> - <td>waitForVaadin</td> - <td></td> - <td></td> -</tr> -<tr> <td>click</td> <td>vaadin=runcomvaadintestscomponentswindowCloseSubWindow::PID_Sopensub/domChild[0]/domChild[0]</td> <td></td> @@ -45,7 +40,7 @@ <!--Click close in title bar--> <tr> <td>click</td> - <td>vaadin=runcomvaadintestscomponentswindowCloseSubWindow::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runcomvaadintestscomponentswindowCloseSubWindow::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td></td> </tr> <tr> diff --git a/uitest/src/com/vaadin/tests/components/window/CloseSubWindow.java b/uitest/src/com/vaadin/tests/components/window/CloseSubWindow.java index e2a59b6005..6aad3e9170 100644 --- a/uitest/src/com/vaadin/tests/components/window/CloseSubWindow.java +++ b/uitest/src/com/vaadin/tests/components/window/CloseSubWindow.java @@ -33,7 +33,7 @@ public class CloseSubWindow extends TestBase { private Window createClosableSubWindow(final String title) { VerticalLayout layout = new VerticalLayout(); layout.setMargin(true); - layout.setSizeFull(); + layout.setSizeUndefined(); final Window window = new Window(title, layout); window.setSizeUndefined(); window.setClosable(true); diff --git a/uitest/src/com/vaadin/tests/components/window/LegacyWindowOpenTest.java b/uitest/src/com/vaadin/tests/components/window/LegacyWindowOpenTest.java index 175c3f6d8a..ad36e04d88 100644 --- a/uitest/src/com/vaadin/tests/components/window/LegacyWindowOpenTest.java +++ b/uitest/src/com/vaadin/tests/components/window/LegacyWindowOpenTest.java @@ -19,6 +19,7 @@ public class LegacyWindowOpenTest extends TestBase { addComponent(new Button("Window.open _blank always as popup", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { win.open(r, "_blank", true); } @@ -26,6 +27,7 @@ public class LegacyWindowOpenTest extends TestBase { addComponent(new Button("Window.open _blank NOT always as popup", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { win.open(r, "_blank", false); } @@ -33,6 +35,7 @@ public class LegacyWindowOpenTest extends TestBase { addComponent(new Button("Window.open _new always as popup", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { win.open(r, "_new", true); } @@ -40,6 +43,7 @@ public class LegacyWindowOpenTest extends TestBase { addComponent(new Button("Window.open _new NOT always as popup", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { win.open(r, "_new", false); } @@ -47,6 +51,7 @@ public class LegacyWindowOpenTest extends TestBase { addComponent(new Button( "Window execute Javascript window.open(www.google.com, _blank)", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { win.executeJavaScript("window.open(\"http://www.google.com\", \"_blank\");"); } @@ -54,6 +59,7 @@ public class LegacyWindowOpenTest extends TestBase { addComponent(new Button( "Window execute Javascript window.open(www.google.com, _blank, resizable=yes,menubar=yes,toolbar=yes,directories=yes,location=yes,scrollbars=yes,status=yes)", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { win.executeJavaScript("window.open(\"http://www.google.com\", \"_blank\", \"resizable=yes,menubar=yes,toolbar=yes,directories=yes,location=yes,scrollbars=yes,status=yes\");"); } diff --git a/uitest/src/com/vaadin/tests/components/window/PageOpenTest.java b/uitest/src/com/vaadin/tests/components/window/PageOpenTest.java index 2dbc24cb66..a566b09cdc 100644 --- a/uitest/src/com/vaadin/tests/components/window/PageOpenTest.java +++ b/uitest/src/com/vaadin/tests/components/window/PageOpenTest.java @@ -20,6 +20,7 @@ public class PageOpenTest extends AbstractTestUI { addComponent(new Button("Page.open _blank always as popup", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { page.open(url, "_blank", true); } @@ -27,6 +28,7 @@ public class PageOpenTest extends AbstractTestUI { addComponent(new Button("Page.open _blank NOT always as popup", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { page.open(url, "_blank", false); } @@ -34,6 +36,7 @@ public class PageOpenTest extends AbstractTestUI { addComponent(new Button("Page.open _new always as popup", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { page.open(url, "_new", true); } @@ -41,6 +44,7 @@ public class PageOpenTest extends AbstractTestUI { addComponent(new Button("Page.open _new NOT always as popup", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { page.open(url, "_new", false); } @@ -48,6 +52,7 @@ public class PageOpenTest extends AbstractTestUI { addComponent(new Button( "Execute Javascript window.open(www.google.com, _blank)", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { JavaScript .getCurrent() @@ -58,6 +63,7 @@ public class PageOpenTest extends AbstractTestUI { addComponent(new Button( "Execute Javascript window.open(www.google.com, _blank, resizable=yes,menubar=yes,toolbar=yes,directories=yes,location=yes,scrollbars=yes,status=yes)", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { JavaScript .getCurrent() diff --git a/uitest/src/com/vaadin/tests/components/window/SubWindowOrder.html b/uitest/src/com/vaadin/tests/components/window/SubWindowOrder.html index 0476de6c35..8374a90b52 100644 --- a/uitest/src/com/vaadin/tests/components/window/SubWindowOrder.html +++ b/uitest/src/com/vaadin/tests/components/window/SubWindowOrder.html @@ -90,7 +90,7 @@ <!--Close window 4, which is the topmost window--> <tr> <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentswindowSubWindowOrder::/VWindow[3]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runcomvaadintestscomponentswindowSubWindowOrder::/VWindow[3]/domChild[0]/domChild[0]/domChild[2]</td> <td>11,15</td> </tr> <tr> @@ -101,7 +101,7 @@ <!--Close Dialog 3 (topmost)--> <tr> <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentswindowSubWindowOrder::/VWindow[2]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runcomvaadintestscomponentswindowSubWindowOrder::/VWindow[2]/domChild[0]/domChild[0]/domChild[2]</td> <td>6,8</td> </tr> <!--Make Dialog 5 (topmost) non-modal--> @@ -139,7 +139,7 @@ <!--Close dialog 5--> <tr> <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentswindowSubWindowOrder::/VWindow[2]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runcomvaadintestscomponentswindowSubWindowOrder::/VWindow[2]/domChild[0]/domChild[0]/domChild[2]</td> <td>10,5</td> </tr> <tr> diff --git a/uitest/src/com/vaadin/tests/components/window/WindowMaximizeRestoreTest.html b/uitest/src/com/vaadin/tests/components/window/WindowMaximizeRestoreTest.html new file mode 100644 index 0000000000..945564f298 --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/window/WindowMaximizeRestoreTest.html @@ -0,0 +1,239 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://localhost:8888/run/" /> +<title>WindowMaximizeRestoreTest</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">WindowMaximizeRestoreTest</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.components.window.WindowMaximizeRestoreTest?restartApplication</td> + <td></td> +</tr> +<!--Test maximize-restore button--> +<tr> + <td>assertCSSClass</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>v-window-maximizebox</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>Window 1</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>7,8</td> +</tr> +<tr> + <td>assertCSSClass</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>v-window-restorebox</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>9,7</td> +</tr> +<tr> + <td>assertCSSClass</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>v-window-maximizebox</td> +</tr> +<!--test double click on header--> +<tr> + <td>doubleClickAt</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertCSSClass</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>v-window-restorebox</td> +</tr> +<tr> + <td>doubleClickAt</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertCSSClass</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>v-window-maximizebox</td> +</tr> +<!--Resizable = false should hide max-restore button--> +<tr> + <td>assertVisible</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td></td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[1]/VCheckBox[0]/domChild[0]</td> + <td>8,3</td> +</tr> +<tr> + <td>assertNotVisible</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td></td> +</tr> +<!--Test server side max-restore--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VNativeButton[0]</td> + <td>34,6</td> +</tr> +<tr> + <td>assertCSSClass</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>v-window-restorebox</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VNativeButton[0]</td> + <td>34,6</td> +</tr> +<tr> + <td>assertCSSClass</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>v-window-maximizebox</td> +</tr> +<!--test double click on header doesn't work--> +<tr> + <td>doubleClickAt</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertCSSClass</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>v-window-maximizebox</td> +</tr> +<tr> + <td>doubleClickAt</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertCSSClass</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>v-window-maximizebox</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[1]/VCheckBox[0]/domChild[0]</td> + <td>8,3</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[4]/VNativeButton[0]</td> + <td>26,9</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--test two windows with screen shot--> +<tr> + <td>screenCapture</td> + <td></td> + <td>window-2-original-pos-window-1-centered</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>10,8</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>window-1-maximized-on-top-of-window-2</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VNativeButton[0]</td> + <td>43,12</td> +</tr> +<!--Remove the following two commands once #11737 is fixed--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VNativeButton[0]</td> + <td>43,12</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[0]/VNativeButton[0]</td> + <td>43,12</td> +</tr> +<!--maximize window 2 content--> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[1]/FocusableScrollPanel[0]/VVerticalLayout[0]/Slot[3]/VNativeButton[0]</td> + <td>100,9</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>window-2-original-pos-window-1-centered-again</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[1]/domChild[0]/domChild[0]/domChild[1]</td> + <td>6,11</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>window-2-maximized-on-top-of-window-1</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[1]/domChild[0]/domChild[0]/domChild[2]</td> + <td>7,5</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>window-2-closed-window-1-centered</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VFilterSelect[0]/domChild[1]</td> + <td>1,17</td> +</tr> +<tr> + <td>mouseClick</td> + <td>//div[@id='VAADIN_COMBOBOX_OPTIONLIST']/div/div[2]/table/tbody/tr[2]/td</td> + <td>122,6</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>window-2-added-maximized-on-top-of-window-1</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[1]/domChild[0]/domChild[0]/domChild[1]</td> + <td>6,11</td> +</tr> +<tr> + <td>doubleClickAt</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowMaximizeRestoreTest::/VWindow[0]/domChild[0]/domChild[0]/domChild[0]/domChild[0]</td> + <td>113,10</td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>window-1-maximized-with-doubleclick</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/components/window/WindowMaximizeRestoreTest.java b/uitest/src/com/vaadin/tests/components/window/WindowMaximizeRestoreTest.java new file mode 100644 index 0000000000..a8102a7d1a --- /dev/null +++ b/uitest/src/com/vaadin/tests/components/window/WindowMaximizeRestoreTest.java @@ -0,0 +1,165 @@ +package com.vaadin.tests.components.window; + +import com.vaadin.data.Item; +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.window.WindowMode; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.CheckBox; +import com.vaadin.ui.ComboBox; +import com.vaadin.ui.ComponentContainer; +import com.vaadin.ui.NativeButton; +import com.vaadin.ui.UI; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.Window; +import com.vaadin.ui.Window.CloseEvent; +import com.vaadin.ui.Window.CloseListener; +import com.vaadin.ui.Window.WindowModeChangeEvent; +import com.vaadin.ui.Window.WindowModeChangeListener; + +public class WindowMaximizeRestoreTest extends AbstractTestUI { + Button.ClickListener addListener = new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + addWindow(createNewWindow()); + } + }; + + @Override + protected void setup(VaadinRequest request) { + Button addButton = new Button("Add new Window"); + addButton.addListener(addListener); + addComponent(addButton); + + addWindowAgain = new ComboBox("Add Window Again"); + addWindowAgain.setBuffered(false); + addWindowAgain.setImmediate(true); + addWindowAgain.addValueChangeListener(new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + + Object value = event.getProperty().getValue(); + if (value != null && value instanceof Window) { + UI.getCurrent().addWindow((Window) value); + addWindowAgain.removeItem(value); + } + } + }); + addComponent(addWindowAgain); + + addWindow(createNewWindow()); + } + + private int windowCount = 0; + private ComboBox addWindowAgain; + + private Window createNewWindow() { + final Window w = new Window("Window " + (++windowCount)); + final VerticalLayout content = new VerticalLayout(); + w.setContent(content); + w.setData(windowCount); + w.setWidth("200px"); + w.setHeight("300px"); + w.setPositionX(200); + w.setPositionY(200); + final NativeButton maximize = new NativeButton("Maximize"); + Button.ClickListener listener = new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + if (w.getWindowMode() == WindowMode.MAXIMIZED) { + w.setWindowMode(WindowMode.NORMAL); + maximize.setCaption("Maximize"); + } else { + w.setWindowMode(WindowMode.MAXIMIZED); + maximize.setCaption("Restore"); + } + } + + }; + maximize.addClickListener(listener); + ((ComponentContainer) w.getContent()).addComponent(maximize); + + w.addWindowModeChangeListener(new WindowModeChangeListener() { + + @Override + public void windowModeChanged(WindowModeChangeEvent event) { + WindowMode state = (event.getWindow().getWindowMode()); + if (state == WindowMode.NORMAL) { + w.setCaption("Window " + w.getData() + " Normal"); + maximize.setCaption("Maximize"); + } else if (state == WindowMode.MAXIMIZED) { + w.setCaption("Window " + w.getData() + " Maximized"); + maximize.setCaption("Restore"); + } + } + }); + final CheckBox resizeable = new CheckBox("Resizeable"); + resizeable.setValue(w.isResizable()); + resizeable.addValueChangeListener(new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + w.setResizable(resizeable.getValue()); + } + }); + ((ComponentContainer) w.getContent()).addComponent(resizeable); + final CheckBox closeable = new CheckBox("Closeable"); + closeable.setValue(w.isClosable()); + closeable.addValueChangeListener(new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + w.setClosable(closeable.getValue()); + } + }); + ((ComponentContainer) w.getContent()).addComponent(closeable); + NativeButton contentFull = new NativeButton("Set Content Size Full", + new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + w.getContent().setSizeFull(); + } + }); + contentFull.setWidth("100%"); + ((ComponentContainer) w.getContent()).addComponent(contentFull); + + NativeButton center = new NativeButton("Center"); + center.addClickListener(new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + w.center(); + } + }); + ((ComponentContainer) w.getContent()).addComponent(center); + + w.addCloseListener(new CloseListener() { + + @Override + public void windowClose(CloseEvent e) { + Item item = addWindowAgain.addItem(w); + addWindowAgain.setItemCaption(w, "Window " + + w.getData().toString()); + } + }); + + return w; + } + + @Override + protected Integer getTicketNumber() { + return 3400; + } + + @Override + protected String getTestDescription() { + return "Tests the default maximize & restore funtionality. Max. makes window 100%*100% and pos(0, 0), and restore returns it to the values that are set in windows state."; + } +} diff --git a/uitest/src/com/vaadin/tests/components/window/WindowWithInvalidCloseListener.html b/uitest/src/com/vaadin/tests/components/window/WindowWithInvalidCloseListener.html index 923276b613..fa63e5e1e6 100644 --- a/uitest/src/com/vaadin/tests/components/window/WindowWithInvalidCloseListener.html +++ b/uitest/src/com/vaadin/tests/components/window/WindowWithInvalidCloseListener.html @@ -18,7 +18,7 @@ </tr> <tr> <td>mouseClick</td> - <td>vaadin=runcomvaadintestscomponentswindowWindowWithInvalidCloseListener::/VWindow[0]/domChild[0]/domChild[0]/domChild[1]</td> + <td>vaadin=runcomvaadintestscomponentswindowWindowWithInvalidCloseListener::/VWindow[0]/domChild[0]/domChild[0]/domChild[2]</td> <td>6,7</td> </tr> <tr> diff --git a/uitest/src/com/vaadin/tests/containers/sqlcontainer/TableQueryWithNonUniqueFirstPrimaryKey.java b/uitest/src/com/vaadin/tests/containers/sqlcontainer/TableQueryWithNonUniqueFirstPrimaryKey.java index fa84c7cbb8..da3476610b 100644 --- a/uitest/src/com/vaadin/tests/containers/sqlcontainer/TableQueryWithNonUniqueFirstPrimaryKey.java +++ b/uitest/src/com/vaadin/tests/containers/sqlcontainer/TableQueryWithNonUniqueFirstPrimaryKey.java @@ -53,6 +53,7 @@ public class TableQueryWithNonUniqueFirstPrimaryKey extends LegacyApplication { myCombo.setWidth("100.0%"); myCombo.setHeight("-1px"); myCombo.addValueChangeListener(new Property.ValueChangeListener() { + @Override public void valueChange(ValueChangeEvent event) { if (myCombo.getValue() != null) { Item item = myCombo.getItem(event.getProperty() diff --git a/uitest/src/com/vaadin/tests/debug/DebugWindowPresent.html b/uitest/src/com/vaadin/tests/debug/DebugWindowPresent.html index 11640ef6c3..ff5c731e5c 100644 --- a/uitest/src/com/vaadin/tests/debug/DebugWindowPresent.html +++ b/uitest/src/com/vaadin/tests/debug/DebugWindowPresent.html @@ -18,7 +18,7 @@ </tr> <tr> <td>assertElementPresent</td> - <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugConsole[0]</td> + <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugWindow[0]</td> <td></td> </tr> <tr> @@ -28,7 +28,7 @@ </tr> <tr> <td>assertElementPresent</td> - <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugConsole[0]</td> + <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugWindow[0]</td> <td></td> </tr> <tr> @@ -38,7 +38,7 @@ </tr> <tr> <td>assertElementNotPresent</td> - <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugConsole[0]</td> + <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugWindow[0]</td> <td></td> </tr> <tr> @@ -48,7 +48,7 @@ </tr> <tr> <td>assertElementNotPresent</td> - <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugConsole[0]</td> + <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugWindow[0]</td> <td></td> </tr> <tr> @@ -58,7 +58,7 @@ </tr> <tr> <td>assertElementNotPresent</td> - <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugConsole[0]</td> + <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugWindow[0]</td> <td></td> </tr> <tr> @@ -68,7 +68,7 @@ </tr> <tr> <td>assertElementNotPresent</td> - <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugConsole[0]</td> + <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugWindow[0]</td> <td></td> </tr> <tr> @@ -78,7 +78,7 @@ </tr> <tr> <td>assertElementNotPresent</td> - <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugConsole[0]</td> + <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugWindow[0]</td> <td></td> </tr> <tr> @@ -88,7 +88,7 @@ </tr> <tr> <td>assertElementNotPresent</td> - <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugConsole[0]</td> + <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugWindow[0]</td> <td></td> </tr> <tr> @@ -98,7 +98,7 @@ </tr> <tr> <td>assertElementNotPresent</td> - <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugConsole[0]</td> + <td>vaadin=runcomvaadintestsdebugDebugWindowPresent::Root/VDebugWindow[0]</td> <td></td> </tr> </tbody></table> diff --git a/uitest/src/com/vaadin/tests/debug/HierarchyAfterAnalyzeLayouts.html b/uitest/src/com/vaadin/tests/debug/HierarchyAfterAnalyzeLayouts.html index 03d8f34651..a0ebb3bf27 100644 --- a/uitest/src/com/vaadin/tests/debug/HierarchyAfterAnalyzeLayouts.html +++ b/uitest/src/com/vaadin/tests/debug/HierarchyAfterAnalyzeLayouts.html @@ -17,14 +17,19 @@ <td></td> </tr> <tr> - <td>mouseClick</td> - <td>vaadin=runcomvaadintestsdebugHierarchyAfterAnalyzeLayouts::Root/VDebugConsole[0]/FlowPanel[0]/HorizontalPanel[0]/domChild[0]/domChild[0]/domChild[3]/domChild[0]</td> + <td>click</td> + <td>vaadin=runcomvaadintestsdebugHierarchyAfterAnalyzeLayouts::Root/VDebugWindow[0]/FlowPanel[0]/FlowPanel[0]/FlowPanel[0]/domChild[1]</td> <td>18,9</td> </tr> <tr> - <td>assertTextPresent</td> - <td>Layouts analyzed on server, total top level problems: 0</td> - <td></td> + <td>click</td> + <td>vaadin=runcomvaadintestsdebugHierarchyAfterAnalyzeLayouts::Root/VDebugWindow[0]/FlowPanel[0]/FlowPanel[0]/FlowPanel[2]/FlowPanel[0]/domChild[2]</td> + <td>18,9</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runcomvaadintestsdebugHierarchyAfterAnalyzeLayouts::Root/VDebugWindow[0]/FlowPanel[0]/SimplePanel[0]/FlowPanel[0]/domChild[0]</td> + <td>Layouts analyzed, no top level problems</td> </tr> <tr> <td>assertElementPresent</td> diff --git a/uitest/src/com/vaadin/tests/fieldgroup/DateForm.html b/uitest/src/com/vaadin/tests/fieldgroup/DateForm.html new file mode 100644 index 0000000000..f141091805 --- /dev/null +++ b/uitest/src/com/vaadin/tests/fieldgroup/DateForm.html @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://localhost:8888/" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.fieldgroup.DateForm?restartApplication</td> + <td></td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestsfieldgroupDateForm::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VPopupCalendar[0]#field</td> + <td>1/20/84</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestsfieldgroupDateForm::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VPopupCalendar[0]#field</td> + <td>1/20/84</td> +</tr> +<tr> + <td>assertCSSClass</td> + <td>vaadin=runcomvaadintestsfieldgroupDateForm::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VDateFieldCalendar[0]/VCalendarPanel[0]#day20</td> + <td>v-inline-datefield-calendarpanel-day-selected</td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runcomvaadintestsfieldgroupDateForm::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[4]/VTextField[0]</td> + <td>Jan 20, 1984 4:34:49 PM</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/fieldgroup/DateForm.java b/uitest/src/com/vaadin/tests/fieldgroup/DateForm.java new file mode 100644 index 0000000000..3064856db9 --- /dev/null +++ b/uitest/src/com/vaadin/tests/fieldgroup/DateForm.java @@ -0,0 +1,152 @@ +package com.vaadin.tests.fieldgroup; + +import java.util.Date; +import java.util.Locale; + +import com.vaadin.data.fieldgroup.BeanFieldGroup; +import com.vaadin.data.fieldgroup.FieldGroup; +import com.vaadin.data.fieldgroup.FieldGroup.CommitException; +import com.vaadin.data.fieldgroup.PropertyId; +import com.vaadin.data.util.BeanItem; +import com.vaadin.tests.components.TestBase; +import com.vaadin.tests.data.bean.Person; +import com.vaadin.tests.util.Log; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.DateField; +import com.vaadin.ui.InlineDateField; +import com.vaadin.ui.Notification; +import com.vaadin.ui.PopupDateField; +import com.vaadin.ui.TextField; + +public class DateForm extends TestBase { + + private Log log = new Log(5); + @PropertyId("date1") + private DateField dateField; + @PropertyId("date2") + private PopupDateField popupDateField; + @PropertyId("date3") + private InlineDateField inlineDateField; + @PropertyId("date4") + private TextField textField; + + public static class DateObject { + private Date date1, date2, date3, date4; + + public DateObject(Date date1, Date date2, Date date3, Date date4) { + super(); + this.date1 = date1; + this.date2 = date2; + this.date3 = date3; + this.date4 = date4; + } + + public Date getDate1() { + return date1; + } + + public void setDate1(Date date1) { + this.date1 = date1; + } + + public Date getDate2() { + return date2; + } + + public void setDate2(Date date2) { + this.date2 = date2; + } + + public Date getDate3() { + return date3; + } + + public void setDate3(Date date3) { + this.date3 = date3; + } + + public Date getDate4() { + return date4; + } + + public void setDate4(Date date4) { + this.date4 = date4; + } + + } + + @Override + protected void setup() { + getMainWindow().setLocale(Locale.US); + addComponent(log); + final FieldGroup fieldGroup = new BeanFieldGroup<DateObject>( + DateObject.class); + fieldGroup.setBuffered(true); + + fieldGroup.buildAndBindMemberFields(this); + textField.setWidth("20em"); + addComponent(dateField); + addComponent(popupDateField); + addComponent(inlineDateField); + addComponent(textField); + + Button commitButton = new Button("Commit", new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + String msg = "Commit succesful"; + try { + fieldGroup.commit(); + } catch (CommitException e) { + msg = "Commit failed: " + e.getMessage(); + } + Notification.show(msg); + log.log(msg); + + } + }); + Button discardButton = new Button("Discard", + new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + fieldGroup.discard(); + log.log("Discarded changes"); + + } + }); + Button showBean = new Button("Show bean values", + new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + log.log(getPerson(fieldGroup).toString()); + + } + }); + addComponent(commitButton); + addComponent(discardButton); + addComponent(showBean); + + DateObject d = new DateObject(new Date(443457289789L), new Date( + 443457289789L), new Date(443457289789L), + new Date(443457289789L)); + fieldGroup.setItemDataSource(new BeanItem<DateObject>(d)); + } + + public static Person getPerson(FieldGroup binder) { + return ((BeanItem<Person>) binder.getItemDataSource()).getBean(); + } + + @Override + protected String getDescription() { + return "Ensure FieldGroupFieldFactory supports Dates"; + } + + @Override + protected Integer getTicketNumber() { + return 8539; + } + +} diff --git a/uitest/src/com/vaadin/tests/layouts/CaptionsInLayoutsWaiAria.java b/uitest/src/com/vaadin/tests/layouts/CaptionsInLayoutsWaiAria.java new file mode 100644 index 0000000000..ec323f2db2 --- /dev/null +++ b/uitest/src/com/vaadin/tests/layouts/CaptionsInLayoutsWaiAria.java @@ -0,0 +1,375 @@ +package com.vaadin.tests.layouts; + +import java.util.ArrayList; +import java.util.List; + +import com.vaadin.data.Item; +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.server.ThemeResource; +import com.vaadin.server.UserError; +import com.vaadin.tests.components.TestBase; +import com.vaadin.ui.AbstractField; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.CheckBox; +import com.vaadin.ui.ComboBox; +import com.vaadin.ui.Component; +import com.vaadin.ui.CssLayout; +import com.vaadin.ui.DateField; +import com.vaadin.ui.FormLayout; +import com.vaadin.ui.GridLayout; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Layout; +import com.vaadin.ui.NativeSelect; +import com.vaadin.ui.OptionGroup; +import com.vaadin.ui.PasswordField; +import com.vaadin.ui.TextArea; +import com.vaadin.ui.TextField; +import com.vaadin.ui.VerticalLayout; + +public class CaptionsInLayoutsWaiAria extends TestBase { + + private static final Object CAPTION = "CAPTION"; + private static final Object CLASS = "C"; + private static final Object WIDTH = "W"; + + private NativeSelect layoutSelect; + private Layout layout; + private VerticalLayout verticalLayout; + private HorizontalLayout horizontalLayout; + private GridLayout gridLayout; + private FormLayout formLayout; + private List<AbstractField<?>> components = new ArrayList<AbstractField<?>>(); + private CssLayout cssLayout; + private HorizontalLayout layoutParent = new HorizontalLayout(); + + @Override + protected void setup() { + // setTheme("tests-tickets"); + addComponent(createLayoutSelect()); + addComponent(toggleRequired()); + // addComponent(toggleCaptions()); + // addComponent(toggleError()); + addComponent(toggleIcon()); + addComponent(toggleReadOnly()); + addComponent(toggleInvalid()); + addComponent(toggleEnabled()); + addComponent(addCaptionText()); + // layoutParent.addComponent(new + // NativeButton("Button right of layout")); + addComponent(layoutParent); + // addComponent(new NativeButton("Button below layout")); + createComponents(); + layoutSelect.setValue(layoutSelect.getItemIds().iterator().next()); + } + + private Component addCaptionText() { + Button b = new Button("Add caption text"); + b.addListener(new ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + prependCaptions("a"); + } + }); + return b; + } + + protected void prependCaptions(String prepend) { + for (AbstractField<?> c : components) { + c.setCaption(prepend + c.getCaption()); + } + + } + + private Component toggleRequired() { + CheckBox requiredToggle = new CheckBox(); + requiredToggle.setImmediate(true); + requiredToggle.setCaption("Required"); + requiredToggle.addListener(new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + setRequired((Boolean) event.getProperty().getValue()); + } + }); + return requiredToggle; + } + + private Component toggleIcon() { + CheckBox iconToggle = new CheckBox(); + iconToggle.setImmediate(true); + iconToggle.setCaption("Icons"); + iconToggle.addListener(new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + setIcon((Boolean) event.getProperty().getValue()); + } + }); + return iconToggle; + } + + private Component toggleReadOnly() { + CheckBox readOnlyToggle = new CheckBox(); + readOnlyToggle.setImmediate(true); + readOnlyToggle.setCaption("Read only"); + readOnlyToggle.addValueChangeListener(new ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + setReadOnly((Boolean) event.getProperty().getValue()); + } + }); + + return readOnlyToggle; + } + + private Component toggleEnabled() { + CheckBox enabledToggle = new CheckBox(); + enabledToggle.setImmediate(true); + enabledToggle.setValue(true); + enabledToggle.setCaption("Enabled"); + enabledToggle.addValueChangeListener(new ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + setEnabled((Boolean) event.getProperty().getValue()); + } + }); + + return enabledToggle; + } + + private Component toggleInvalid() { + CheckBox invalid = new CheckBox("Invalid"); + invalid.setImmediate(true); + invalid.addValueChangeListener(new ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + setInvalid((Boolean) event.getProperty().getValue()); + } + }); + + return invalid; + } + + protected void setInvalid(boolean value) { + UserError userError = null; + if (value) { + userError = new UserError( + "Der eingegebene Wert ist nicht zulässig!"); + } + + for (AbstractField<?> c : components) { + c.setComponentError(userError); + } + } + + protected void setRequired(boolean value) { + for (AbstractField<?> c : components) { + c.setRequired(value); + } + + } + + protected void setIcon(boolean value) { + for (AbstractField<?> c : components) { + if (!value) { + c.setIcon(null); + } else { + c.setIcon(new ThemeResource("../runo/icons/16/ok.png")); + } + } + + } + + protected void setReadOnly(boolean value) { + for (AbstractField<?> c : components) { + c.setReadOnly(value); + } + } + + protected void setEnabled(boolean value) { + for (AbstractField<?> c : components) { + c.setEnabled(value); + } + } + + private Component toggleError() { + CheckBox errorToggle = new CheckBox(); + errorToggle.setImmediate(true); + errorToggle.setCaption("Error"); + errorToggle.addListener(new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + setError((Boolean) event.getProperty().getValue()); + } + }); + return errorToggle; + } + + protected void setError(boolean value) { + for (AbstractField<?> c : components) { + if (value) { + c.setComponentError(new UserError("error")); + } else { + c.setComponentError(null); + + } + } + + } + + private void createComponents() { + components.add(new TextField("Default TextBox")); + components.add(new TextArea("Default TextArea.")); + // components.add(new RichTextArea("Default RichtTextArea")); + components.add(new PasswordField("Default Password")); + components.add(new DateField("Default DateField")); + + // PopupDateField popupDateField = new + // PopupDateField("Default DateField"); + // popupDateField.setTextFieldEnabled(false); + // components.add(popupDateField); + + components.add(new CheckBox("Default CheckBox")); + + ComboBox comboBox = new ComboBox("Default ComboBox"); + comboBox.addItem("Item1"); + components.add(comboBox); + + OptionGroup radioGroup = new OptionGroup("Single Items"); + radioGroup.addItem("Single Item 1"); + radioGroup.addItem("Single Item 2"); + radioGroup.setMultiSelect(false); + components.add(radioGroup); + + OptionGroup checkGroup = new OptionGroup("Multi Items"); + checkGroup.addItem("Multi Item 1"); + checkGroup.addItem("Multi Item 2"); + checkGroup.setMultiSelect(true); + components.add(checkGroup); + + // Tree tree = new Tree(); + // tree.setCaption("tree"); + // tree.addItem("single item"); + // components.add(tree); + } + + private void setLayout(Layout newLayout) { + if (layout == null) { + layoutParent.addComponent(newLayout, 0); + } else { + layoutParent.replaceComponent(layout, newLayout); + } + layout = newLayout; + + for (Component c : components) { + if (c.getParent() != layout) { + layout.addComponent(c); + } + } + + } + + private Layout getLayout(String caption, + Class<? extends Layout> layoutClass, String width) { + Layout l; + if (layoutClass == VerticalLayout.class) { + if (verticalLayout == null) { + verticalLayout = new VerticalLayout(); + verticalLayout.setStyleName("borders"); + } + l = verticalLayout; + } else if (layoutClass == HorizontalLayout.class) { + if (horizontalLayout == null) { + horizontalLayout = new HorizontalLayout(); + horizontalLayout.setStyleName("borders"); + } + l = horizontalLayout; + } else if (layoutClass == GridLayout.class) { + if (gridLayout == null) { + gridLayout = new GridLayout(); + gridLayout.setStyleName("borders"); + } + l = gridLayout; + } else if (layoutClass == CssLayout.class) { + if (cssLayout == null) { + cssLayout = new CssLayout(); + cssLayout.setStyleName("borders"); + } + l = cssLayout; + } else if (layoutClass == FormLayout.class) { + if (formLayout == null) { + formLayout = new FormLayout(); + formLayout.setStyleName("borders"); + } + l = formLayout; + } else { + return null; + } + + l.setCaption(caption); + if (width.equals("auto")) { + width = null; + } + + l.setWidth(width); + + // addComponent(l); + + return l; + } + + private Component createLayoutSelect() { + layoutSelect = new NativeSelect("Layout"); + layoutSelect.addContainerProperty(CAPTION, String.class, ""); + layoutSelect.addContainerProperty(CLASS, Class.class, ""); + layoutSelect.addContainerProperty(WIDTH, String.class, ""); + layoutSelect.setItemCaptionPropertyId(CAPTION); + layoutSelect.setNullSelectionAllowed(false); + + for (Class<?> cls : new Class[] { HorizontalLayout.class, + VerticalLayout.class, GridLayout.class, CssLayout.class, + FormLayout.class }) { + for (String width : new String[] { "auto" }) { + Object id = layoutSelect.addItem(); + Item i = layoutSelect.getItem(id); + i.getItemProperty(CAPTION).setValue( + cls.getSimpleName() + ", " + width); + i.getItemProperty(CLASS).setValue(cls); + i.getItemProperty(WIDTH).setValue(width); + } + + } + layoutSelect.setImmediate(true); + layoutSelect.addListener(new ValueChangeListener() { + + @Override + @SuppressWarnings("unchecked") + public void valueChange(ValueChangeEvent event) { + Item i = layoutSelect.getItem(event.getProperty().getValue()); + + setLayout(getLayout((String) i.getItemProperty(CAPTION) + .getValue(), (Class<? extends Layout>) i + .getItemProperty(CLASS).getValue(), (String) i + .getItemProperty(WIDTH).getValue())); + } + }); + + return layoutSelect; + } + + @Override + protected String getDescription() { + return "Tests what happens when the caption changes in various layouts. Behavior should be consistent."; + } + + @Override + protected Integer getTicketNumber() { + return 5424; + } + +} diff --git a/uitest/src/com/vaadin/tests/layouts/VerticalLayoutSlotExpansionAndAlignment.java b/uitest/src/com/vaadin/tests/layouts/VerticalLayoutSlotExpansionAndAlignment.java index bba8ccf120..fe2dd6cea8 100644 --- a/uitest/src/com/vaadin/tests/layouts/VerticalLayoutSlotExpansionAndAlignment.java +++ b/uitest/src/com/vaadin/tests/layouts/VerticalLayoutSlotExpansionAndAlignment.java @@ -1,6 +1,5 @@ package com.vaadin.tests.layouts; -import com.vaadin.annotations.Theme; import com.vaadin.server.VaadinRequest; import com.vaadin.ui.Alignment; import com.vaadin.ui.HorizontalLayout; diff --git a/uitest/src/com/vaadin/tests/layouts/layouttester/LayoutTesterApplication.java b/uitest/src/com/vaadin/tests/layouts/layouttester/LayoutTesterApplication.java index 2294b1909b..72863895a1 100644 --- a/uitest/src/com/vaadin/tests/layouts/layouttester/LayoutTesterApplication.java +++ b/uitest/src/com/vaadin/tests/layouts/layouttester/LayoutTesterApplication.java @@ -19,9 +19,14 @@ import com.vaadin.ui.themes.Reindeer; public class LayoutTesterApplication extends AbstractTestCase { Button nextButton = new Button("Next"); private int layoutIndex = -1; - private int layoutCount = 1; - private Method[] layoutGetters; + private static final String[] layoutGetters = new String[] { + "getCaptionsTests", "getIconsTests", + "getRequiredErrorIndicatorsTests", "getAlignmentTests", + "getExpandRatiosTests", "getMarginSpacingTests", + "getComponentAddReplaceMoveTests", "getComponentSizingTests", + "getLayoutSizingTests" }; + private LegacyWindow mainWindow; private NativeSelect layoutSelector; @@ -29,33 +34,32 @@ public class LayoutTesterApplication extends AbstractTestCase { public void init() { mainWindow = new LegacyWindow("LayoutTesterApplication"); setMainWindow(mainWindow); - loadLayoutGetters(); - nextLaytout(); + nextLayout(); nextButton.addListener(new Button.ClickListener() { private static final long serialVersionUID = -1577298910202253538L; @Override public void buttonClick(ClickEvent event) { - nextLaytout(); + nextLayout(); } }); } - private void nextLaytout() { + private void nextLayout() { try { mainWindow.removeAllComponents(); HorizontalLayout vlo = new HorizontalLayout(); vlo.setSpacing(true); ++layoutIndex; - if (layoutIndex >= layoutCount) { + if (layoutIndex >= layoutGetters.length) { layoutIndex = 0; } mainWindow.addComponent(vlo); vlo.addComponent(nextButton); vlo.addComponent(getLayoutTypeSelect()); - vlo.addComponent(new UndefWideLabel(layoutGetters[layoutIndex] - .getName())); + vlo.addComponent(new UndefWideLabel(getLayoutGetterMethod( + layoutGetters[layoutIndex]).getName())); Layout lo = null; if (layoutSelector.getValue() == VerticalLayout.class) { @@ -75,24 +79,34 @@ public class LayoutTesterApplication extends AbstractTestCase { } } - public void loadLayoutGetters() { - layoutGetters = AbstractLayoutTests.class.getDeclaredMethods(); - layoutCount = layoutGetters.length; + private Method getLayoutGetterMethod(String method) { + try { + return AbstractLayoutTests.class.getDeclaredMethod(method); + } catch (SecurityException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } } public Layout getVerticalTestLayout(int index) throws Exception { VerticalLayoutTests vlotest = new VerticalLayoutTests(this); - return (Layout) layoutGetters[index].invoke(vlotest, (Object[]) null); + return (Layout) getLayoutGetterMethod(layoutGetters[index]).invoke( + vlotest, (Object[]) null); } public Layout getHorizontalTestLayout(int index) throws Exception { HorizontalLayoutTests hlotest = new HorizontalLayoutTests(this); - return (Layout) layoutGetters[index].invoke(hlotest, (Object[]) null); + return (Layout) getLayoutGetterMethod(layoutGetters[index]).invoke( + hlotest, (Object[]) null); } public Layout getGridTestLayout(int index) throws Exception { GridLayoutTests hlotest = new GridLayoutTests(this); - return (Layout) layoutGetters[index].invoke(hlotest, (Object[]) null); + return (Layout) getLayoutGetterMethod(layoutGetters[index]).invoke( + hlotest, (Object[]) null); } private NativeSelect getLayoutTypeSelect() { @@ -110,7 +124,7 @@ public class LayoutTesterApplication extends AbstractTestCase { @Override public void valueChange(ValueChangeEvent event) { layoutIndex = -1; - nextLaytout(); + nextLayout(); } }); } diff --git a/uitest/src/com/vaadin/tests/minitutorials/broadcastingmessages/Broadcaster.java b/uitest/src/com/vaadin/tests/minitutorials/broadcastingmessages/Broadcaster.java new file mode 100644 index 0000000000..57ad0d97ba --- /dev/null +++ b/uitest/src/com/vaadin/tests/minitutorials/broadcastingmessages/Broadcaster.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package com.vaadin.tests.minitutorials.broadcastingmessages; + +import java.util.ArrayList; +import java.util.List; + +public class Broadcaster { + + private static List<BroadcastListener> listeners = new ArrayList<BroadcastListener>(); + + public synchronized static void register(BroadcastListener listener) { + listeners.add(listener); + } + + public synchronized static void unregister(BroadcastListener listener) { + listeners.remove(listener); + } + + private synchronized static List<BroadcastListener> getListeners() { + List<BroadcastListener> listenerCopy = new ArrayList<BroadcastListener>(); + listenerCopy.addAll(listeners); + return listenerCopy; + } + + public static void broadcast(final String message) { + // Make a copy of the listener list while synchronized, can't be + // synchronized while firing the event or we would have to fire each + // event in a separate thread. + final List<BroadcastListener> listenerCopy = getListeners(); + + // We spawn another thread to avoid potential deadlocks with + // multiple UIs locked simultaneously + Thread eventThread = new Thread() { + @Override + public void run() { + for (BroadcastListener listener : listenerCopy) { + listener.receiveBroadcast(message); + } + } + }; + eventThread.start(); + } + + public interface BroadcastListener { + public void receiveBroadcast(String message); + } + +} diff --git a/uitest/src/com/vaadin/tests/minitutorials/broadcastingmessages/BroadcasterUI.java b/uitest/src/com/vaadin/tests/minitutorials/broadcastingmessages/BroadcasterUI.java new file mode 100644 index 0000000000..06ead20db3 --- /dev/null +++ b/uitest/src/com/vaadin/tests/minitutorials/broadcastingmessages/BroadcasterUI.java @@ -0,0 +1,57 @@ +package com.vaadin.tests.minitutorials.broadcastingmessages; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.minitutorials.broadcastingmessages.Broadcaster.BroadcastListener; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Notification; +import com.vaadin.ui.Notification.Type; +import com.vaadin.ui.TextArea; +import com.vaadin.ui.UI; +import com.vaadin.ui.VerticalLayout; + +public class BroadcasterUI extends UI implements BroadcastListener { + + @Override + protected void init(VaadinRequest request) { + final VerticalLayout layout = new VerticalLayout(); + layout.setMargin(true); + setContent(layout); + + final TextArea message = new TextArea("", + "The system is going down for maintenance in 10 minutes"); + layout.addComponent(message); + + final Button button = new Button("Broadcast"); + layout.addComponent(button); + button.addClickListener(new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + Broadcaster.broadcast(message.getValue()); + } + }); + + // Register broadcast listener + Broadcaster.register(this); + } + + @Override + public void detach() { + Broadcaster.unregister(this); + super.detach(); + } + + @Override + public void receiveBroadcast(final String message) { + access(new Runnable() { + @Override + public void run() { + Notification n = new Notification("Message received", message, + Type.TRAY_NOTIFICATION); + n.show(getPage()); + } + }); + + } + +}
\ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/minitutorials/v70/SimpleLoginMainView.java b/uitest/src/com/vaadin/tests/minitutorials/v70/SimpleLoginMainView.java new file mode 100644 index 0000000000..cb59aa1608 --- /dev/null +++ b/uitest/src/com/vaadin/tests/minitutorials/v70/SimpleLoginMainView.java @@ -0,0 +1,42 @@ +package com.vaadin.tests.minitutorials.v70; + +import com.vaadin.navigator.View; +import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.CssLayout; +import com.vaadin.ui.CustomComponent; +import com.vaadin.ui.Label; + +public class SimpleLoginMainView extends CustomComponent implements View { + + public static final String NAME = ""; + + Label text = new Label(); + + Button logout = new Button("Logout", new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + + // "Logout" the user + getSession().setAttribute("user", null); + + // Refresh this view, should redirect to login view + getUI().getNavigator().navigateTo(NAME); + } + }); + + public SimpleLoginMainView() { + setCompositionRoot(new CssLayout(text, logout)); + } + + @Override + public void enter(ViewChangeEvent event) { + // Get the user name from the session + String username = String.valueOf(getSession().getAttribute("user")); + + // And show the username + text.setValue("Hello " + username); + } +} diff --git a/uitest/src/com/vaadin/tests/minitutorials/v70/SimpleLoginUI.java b/uitest/src/com/vaadin/tests/minitutorials/v70/SimpleLoginUI.java new file mode 100644 index 0000000000..1f94d43abe --- /dev/null +++ b/uitest/src/com/vaadin/tests/minitutorials/v70/SimpleLoginUI.java @@ -0,0 +1,64 @@ +package com.vaadin.tests.minitutorials.v70; + +import com.vaadin.navigator.Navigator; +import com.vaadin.navigator.ViewChangeListener; +import com.vaadin.server.VaadinRequest; +import com.vaadin.ui.UI; + +public class SimpleLoginUI extends UI { + + @Override + protected void init(VaadinRequest request) { + + /* + * Create a new instance of the navigator. The navigator will attach + * itself automatically to this view. + */ + new Navigator(this, this); + + /* + * The initial log view where the user can login to the application + */ + getNavigator().addView(SimpleLoginView.NAME, SimpleLoginView.class); + + /* + * Add the main view of the application + */ + getNavigator().addView(SimpleLoginMainView.NAME, + SimpleLoginMainView.class); + + /* + * We use a view change handler to ensure the user is always redirected + * to the login view if the user is not logged in. + */ + getNavigator().addViewChangeListener(new ViewChangeListener() { + + @Override + public boolean beforeViewChange(ViewChangeEvent event) { + + // Check if a user has logged in + boolean isLoggedIn = getSession().getAttribute("user") != null; + boolean isLoginView = event.getNewView() instanceof SimpleLoginView; + + if (!isLoggedIn && !isLoginView) { + // Redirect to login view always if a user has not yet + // logged in + getNavigator().navigateTo(SimpleLoginView.NAME); + return false; + + } else if (isLoggedIn && isLoginView) { + // If someone tries to access to login view while logged in, + // then cancel + return false; + } + + return true; + } + + @Override + public void afterViewChange(ViewChangeEvent event) { + + } + }); + } +} diff --git a/uitest/src/com/vaadin/tests/minitutorials/v70/SimpleLoginView.java b/uitest/src/com/vaadin/tests/minitutorials/v70/SimpleLoginView.java new file mode 100644 index 0000000000..3ff1c2df40 --- /dev/null +++ b/uitest/src/com/vaadin/tests/minitutorials/v70/SimpleLoginView.java @@ -0,0 +1,137 @@ +package com.vaadin.tests.minitutorials.v70; + +import com.vaadin.data.validator.AbstractValidator; +import com.vaadin.data.validator.EmailValidator; +import com.vaadin.navigator.View; +import com.vaadin.navigator.ViewChangeListener.ViewChangeEvent; +import com.vaadin.shared.ui.MarginInfo; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.CustomComponent; +import com.vaadin.ui.PasswordField; +import com.vaadin.ui.TextField; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.themes.Reindeer; + +public class SimpleLoginView extends CustomComponent implements View, + Button.ClickListener { + + public static final String NAME = "login"; + + private final TextField user; + + private final PasswordField password; + + private final Button loginButton; + + public SimpleLoginView() { + setSizeFull(); + + // Create the user input field + user = new TextField("User:"); + user.setWidth("300px"); + user.setRequired(true); + user.setInputPrompt("Your username (eg. joe@email.com)"); + user.addValidator(new EmailValidator( + "Username must be an email address")); + user.setInvalidAllowed(false); + + // Create the password input field + password = new PasswordField("Password:"); + password.setWidth("300px"); + password.addValidator(new PasswordValidator()); + password.setRequired(true); + password.setValue(""); + password.setNullRepresentation(""); + + // Create login button + loginButton = new Button("Login", this); + + // Add both to a panel + VerticalLayout fields = new VerticalLayout(user, password, loginButton); + fields.setCaption("Please login to access the application. (test@test.com/passw0rd)"); + fields.setSpacing(true); + fields.setMargin(new MarginInfo(true, true, true, false)); + fields.setSizeUndefined(); + + // The view root layout + VerticalLayout viewLayout = new VerticalLayout(fields); + viewLayout.setSizeFull(); + viewLayout.setComponentAlignment(fields, Alignment.MIDDLE_CENTER); + viewLayout.setStyleName(Reindeer.LAYOUT_BLUE); + setCompositionRoot(viewLayout); + } + + @Override + public void enter(ViewChangeEvent event) { + // focus the username field when user arrives to the login view + user.focus(); + } + + /* + * Validator for validating the passwords + */ + private static final class PasswordValidator extends + AbstractValidator<String> { + + public PasswordValidator() { + super("The password provided is not valid"); + } + + @Override + protected boolean isValidValue(String value) { + /* + * Password must be at least 8 characters long and contain at least + * one number + */ + if (value != null + && (value.length() < 8 || !value.matches(".*\\d.*"))) { + return false; + } + return true; + } + + @Override + public Class<String> getType() { + return String.class; + } + } + + @Override + public void buttonClick(ClickEvent event) { + + /* + * Validate the fields using the navigator. By using validors for the + * fields we reduce the amount of queries we have to use to the database + * for wrongly entered passwords + */ + if (!user.isValid() || !password.isValid()) { + return; + } + + String username = user.getValue(); + String password = this.password.getValue(); + + /* + * Validate username and password with database here. For examples sake + * I use a dummy username and password. + */ + boolean isValid = username.equals("test@test.com") + && password.equals("passw0rd"); + + if (isValid) { + // Store the current user in the service session + getSession().setAttribute("user", username); + + // Navigate to main view + getUI().getNavigator().navigateTo(SimpleLoginMainView.NAME); + + } else { + + // Wrong password clear the password field and refocuses it + this.password.setValue(null); + this.password.focus(); + } + } +} diff --git a/uitest/src/com/vaadin/tests/minitutorials/v71beta/CSSInjectWithColorpicker.java b/uitest/src/com/vaadin/tests/minitutorials/v71beta/CSSInjectWithColorpicker.java new file mode 100644 index 0000000000..63e43b29f1 --- /dev/null +++ b/uitest/src/com/vaadin/tests/minitutorials/v71beta/CSSInjectWithColorpicker.java @@ -0,0 +1,234 @@ +package com.vaadin.tests.minitutorials.v71beta; + +import java.util.Arrays; + +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.server.Page; +import com.vaadin.server.Page.Styles; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.MarginInfo; +import com.vaadin.shared.ui.colorpicker.Color; +import com.vaadin.shared.ui.label.ContentMode; +import com.vaadin.ui.Alignment; +import com.vaadin.ui.ColorPicker; +import com.vaadin.ui.ComboBox; +import com.vaadin.ui.Component; +import com.vaadin.ui.HorizontalLayout; +import com.vaadin.ui.Label; +import com.vaadin.ui.Panel; +import com.vaadin.ui.TextArea; +import com.vaadin.ui.UI; +import com.vaadin.ui.VerticalLayout; +import com.vaadin.ui.components.colorpicker.ColorChangeEvent; +import com.vaadin.ui.components.colorpicker.ColorChangeListener; + +public class CSSInjectWithColorpicker extends UI { + + @Override + protected void init(VaadinRequest request) { + + // Create a text editor + Component editor = createEditor("Lorem ipsum dolor sit amet, lacus pharetra sed, sit a " + + "tortor. Id aliquam lorem pede, orci ut enim metus, diam nulla mi " + + "suspendisse tempor tortor. Eleifend lorem proin, morbi vel diam ut. " + + "Tempor est tellus vitae, pretium condimentum facilisis sit. Sagittis " + + "quam, ac urna eros est cras id cras, eleifend eu mattis nec." + + "Lorem ipsum dolor sit amet, lacus pharetra sed, sit a " + + "tortor. Id aliquam lorem pede, orci ut enim metus, diam nulla mi " + + "suspendisse tempor tortor. Eleifend lorem proin, morbi vel diam ut. " + + "Tempor est tellus vitae, pretium condimentum facilisis sit. Sagittis " + + "quam, ac urna eros est cras id cras, eleifend eu mattis nec." + + "Lorem ipsum dolor sit amet, lacus pharetra sed, sit a " + + "tortor. Id aliquam lorem pede, orci ut enim metus, diam nulla mi " + + "suspendisse tempor tortor. Eleifend lorem proin, morbi vel diam ut. " + + "Tempor est tellus vitae, pretium condimentum facilisis sit. Sagittis " + + "quam, ac urna eros est cras id cras, eleifend eu mattis nec." + + "Lorem ipsum dolor sit amet, lacus pharetra sed, sit a " + + "tortor. Id aliquam lorem pede, orci ut enim metus, diam nulla mi " + + "suspendisse tempor tortor. Eleifend lorem proin, morbi vel diam ut. " + + "Tempor est tellus vitae, pretium condimentum facilisis sit. Sagittis " + + "quam, ac urna eros est cras id cras, eleifend eu mattis nec."); + + VerticalLayout content = new VerticalLayout(editor); + content.setMargin(true); + setContent(content); + } + + /** + * Creates a text editor for visually editing text + * + * @param text + * The text editor + * @return + */ + private Component createEditor(String text) { + + Panel editor = new Panel("Text Editor"); + editor.setWidth("580px"); + + VerticalLayout panelContent = new VerticalLayout(); + panelContent.setSpacing(true); + panelContent.setMargin(new MarginInfo(true, false, false, false)); + editor.setContent(panelContent); + + // Create the toolbar + HorizontalLayout toolbar = new HorizontalLayout(); + toolbar.setSpacing(true); + toolbar.setMargin(new MarginInfo(false, false, false, true)); + + // Create the font family selector + toolbar.addComponent(createFontSelect()); + + // Create the font size selector + toolbar.addComponent(createFontSizeSelect()); + + // Create the text color selector + toolbar.addComponent(createTextColorSelect()); + + // Create the background color selector + toolbar.addComponent(createBackgroundColorSelect()); + + panelContent.addComponent(toolbar); + panelContent.setComponentAlignment(toolbar, Alignment.MIDDLE_LEFT); + + // Spacer between toolbar and text + panelContent.addComponent(new Label("<hr/>", ContentMode.HTML)); + + // The text to edit + TextArea textLabel = new TextArea(null, text); + textLabel.setWidth("100%"); + textLabel.setHeight("200px"); + + // IMPORTANT: We are here setting the style name of the label, we are + // going to use this in our injected styles to target the label + textLabel.setStyleName("text-label"); + + panelContent.addComponent(textLabel); + + return editor; + } + + /** + * Creates a background color select dialog + */ + private Component createBackgroundColorSelect() { + ColorPicker bgColor = new ColorPicker("Background", Color.WHITE); + bgColor.setWidth("110px"); + bgColor.setCaption("Background"); + bgColor.addColorChangeListener(new ColorChangeListener() { + + @Override + public void colorChanged(ColorChangeEvent event) { + + // Get the new background color + Color color = event.getColor(); + + // Get the stylesheet of the page + Styles styles = Page.getCurrent().getStyles(); + + // inject the new background color + styles.add(".v-app .v-textarea.text-label { background-color:" + + color.getCSS() + "; }"); + } + }); + return bgColor; + } + + /** + * Create a text color selction dialog + */ + private Component createTextColorSelect() { + + // Colorpicker for changing text color + ColorPicker textColor = new ColorPicker("Color", Color.BLACK); + textColor.setWidth("110px"); + textColor.setCaption("Color"); + textColor.addColorChangeListener(new ColorChangeListener() { + + @Override + public void colorChanged(ColorChangeEvent event) { + + // Get the new text color + Color color = event.getColor(); + + // Get the stylesheet of the page + Styles styles = Page.getCurrent().getStyles(); + + // inject the new color as a style + styles.add(".v-app .v-textarea.text-label { color:" + + color.getCSS() + "; }"); + } + }); + + return textColor; + } + + /** + * Creates a font family selection dialog + */ + private Component createFontSelect() { + final ComboBox select = new ComboBox(null, Arrays.asList("Arial", + "Helvetica", "Verdana", "Courier", "Times", "sans-serif")); + select.setValue("Arial"); + select.setWidth("200px"); + select.setInputPrompt("Font"); + select.setDescription("Font"); + select.setImmediate(true); + select.setNullSelectionAllowed(false); + select.setNewItemsAllowed(false); + + select.addValueChangeListener(new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + // Get the new font family + String fontFamily = select.getValue().toString(); + + // Get the stylesheet of the page + Styles styles = Page.getCurrent().getStyles(); + + // inject the new font size as a style. We need .v-app to + // override Vaadin's default styles here + styles.add(".v-app .v-textarea.text-label { font-family:" + + fontFamily + "; }"); + } + }); + + return select; + } + + /** + * Creates a font size selection control + */ + private Component createFontSizeSelect() { + + final ComboBox select = new ComboBox(null, Arrays.asList(8, 9, 10, 12, + 14, 16, 20, 25, 30, 40, 50)); + select.setWidth("100px"); + select.setValue(12); + select.setInputPrompt("Font size"); + select.setDescription("Font size"); + select.setImmediate(true); + select.setNullSelectionAllowed(false); + select.setNewItemsAllowed(false); + select.addValueChangeListener(new ValueChangeListener() { + + @Override + public void valueChange(ValueChangeEvent event) { + // Get the new font size + Integer fontSize = (Integer) select.getValue(); + + // Get the stylesheet of the page + Styles styles = Page.getCurrent().getStyles(); + + // inject the new font size as a style. We need .v-app to + // override Vaadin's default styles here + styles.add(".v-app .v-textarea.text-label { font-size:" + + String.valueOf(fontSize) + "px; }"); + } + }); + + return select; + } +} diff --git a/uitest/src/com/vaadin/tests/minitutorials/v7a1/AutoGeneratingForm.java b/uitest/src/com/vaadin/tests/minitutorials/v7a1/AutoGeneratingForm.java index 5547c1077e..a2723beab3 100644 --- a/uitest/src/com/vaadin/tests/minitutorials/v7a1/AutoGeneratingForm.java +++ b/uitest/src/com/vaadin/tests/minitutorials/v7a1/AutoGeneratingForm.java @@ -46,7 +46,7 @@ public class AutoGeneratingForm extends UI { fieldGroup.setItemDataSource(new BeanItem<Person>(new Person("John", "Doe", 34))); - // Loop through the properties, build fields for them and add the fields + // Loop through the properties, build fields for them and add the fields // to this root for (Object propertyId : fieldGroup.getUnboundPropertyIds()) { layout.addComponent(fieldGroup.buildAndBind(propertyId)); diff --git a/uitest/src/com/vaadin/tests/minitutorials/v7b6/OpeningUIInPopup.java b/uitest/src/com/vaadin/tests/minitutorials/v7b6/OpeningUIInPopup.java index 2152e05f14..da9c73dd94 100644 --- a/uitest/src/com/vaadin/tests/minitutorials/v7b6/OpeningUIInPopup.java +++ b/uitest/src/com/vaadin/tests/minitutorials/v7b6/OpeningUIInPopup.java @@ -27,16 +27,17 @@ public class OpeningUIInPopup extends UI { protected void init(VaadinRequest request) { Button popupButton = new Button("Open popup with MyPopupUI"); - BrowserWindowOpener popupOpener = new BrowserWindowOpener(MyPopupUI.class); + BrowserWindowOpener popupOpener = new BrowserWindowOpener( + MyPopupUI.class); popupOpener.setFeatures("height=300,width=300"); popupOpener.extend(popupButton); - + // Add a parameter popupOpener.setParameter("foo", "bar"); // Set a fragment popupOpener.setUriFragment("myfragment"); - + setContent(popupButton); } diff --git a/uitest/src/com/vaadin/tests/minitutorials/v7b9/CountView.java b/uitest/src/com/vaadin/tests/minitutorials/v7b9/CountView.java index 7aaf810355..59708f2bc7 100644 --- a/uitest/src/com/vaadin/tests/minitutorials/v7b9/CountView.java +++ b/uitest/src/com/vaadin/tests/minitutorials/v7b9/CountView.java @@ -14,6 +14,7 @@ public class CountView extends Panel implements View { setContent(new Label("Created: " + count++)); } + @Override public void enter(ViewChangeEvent event) { } diff --git a/uitest/src/com/vaadin/tests/minitutorials/v7b9/LoginView.java b/uitest/src/com/vaadin/tests/minitutorials/v7b9/LoginView.java index 3aa3e42a58..28f8443440 100644 --- a/uitest/src/com/vaadin/tests/minitutorials/v7b9/LoginView.java +++ b/uitest/src/com/vaadin/tests/minitutorials/v7b9/LoginView.java @@ -28,6 +28,7 @@ public class LoginView extends Panel implements View { layout.addComponent(password); final Button login = new Button("Login", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { Notification.show("Ok, let's pretend you're " + email); diff --git a/uitest/src/com/vaadin/tests/minitutorials/v7b9/MainView.java b/uitest/src/com/vaadin/tests/minitutorials/v7b9/MainView.java index 3a1a685bbe..d37a39345f 100644 --- a/uitest/src/com/vaadin/tests/minitutorials/v7b9/MainView.java +++ b/uitest/src/com/vaadin/tests/minitutorials/v7b9/MainView.java @@ -42,6 +42,7 @@ public class MainView extends Panel implements View { layout.addComponent(lnk); logOut = new Button("Logout", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { ((NavigationtestUI) UI.getCurrent()).setLoggedInUser(null); diff --git a/uitest/src/com/vaadin/tests/minitutorials/v7b9/MainViewEarlierExample.java b/uitest/src/com/vaadin/tests/minitutorials/v7b9/MainViewEarlierExample.java index 0eac6a042e..861fd9f8a4 100644 --- a/uitest/src/com/vaadin/tests/minitutorials/v7b9/MainViewEarlierExample.java +++ b/uitest/src/com/vaadin/tests/minitutorials/v7b9/MainViewEarlierExample.java @@ -41,6 +41,7 @@ public class MainViewEarlierExample extends Panel implements View { // login/logout toggle so we can test this Button logInOut = new Button("Toggle login", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { Object user = ((NavigationtestUI) UI.getCurrent()) .getLoggedInUser(); diff --git a/uitest/src/com/vaadin/tests/minitutorials/v7b9/SettingsView.java b/uitest/src/com/vaadin/tests/minitutorials/v7b9/SettingsView.java index 61492adc39..74c4e68b93 100644 --- a/uitest/src/com/vaadin/tests/minitutorials/v7b9/SettingsView.java +++ b/uitest/src/com/vaadin/tests/minitutorials/v7b9/SettingsView.java @@ -43,6 +43,7 @@ public class SettingsView extends Panel implements View { date.setBuffered(true); // show buttons when date is changed date.addValueChangeListener(new ValueChangeListener() { + @Override public void valueChange(ValueChangeEvent event) { hideOrShowButtons(); pendingViewAndParameters = null; @@ -51,6 +52,7 @@ public class SettingsView extends Panel implements View { // commit the TextField changes when "Save" is clicked apply = new Button("Apply", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { date.commit(); hideOrShowButtons(); @@ -61,6 +63,7 @@ public class SettingsView extends Panel implements View { // Discard the TextField changes when "Cancel" is clicked cancel = new Button("Cancel", new Button.ClickListener() { + @Override public void buttonClick(ClickEvent event) { date.discard(); hideOrShowButtons(); @@ -72,6 +75,7 @@ public class SettingsView extends Panel implements View { // attach a listener so that we'll get asked isViewChangeAllowed? navigator.addViewChangeListener(new ViewChangeListener() { + @Override public boolean beforeViewChange(ViewChangeEvent event) { if (event.getOldView() == SettingsView.this && date.isModified()) { @@ -93,6 +97,7 @@ public class SettingsView extends Panel implements View { } } + @Override public void afterViewChange(ViewChangeEvent event) { pendingViewAndParameters = null; } diff --git a/uitest/src/com/vaadin/tests/push/BasicPush.html b/uitest/src/com/vaadin/tests/push/BasicPush.html new file mode 100644 index 0000000000..173ec90674 --- /dev/null +++ b/uitest/src/com/vaadin/tests/push/BasicPush.html @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://localhost:8888/" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run-push/com.vaadin.tests.push.BasicPush?restartApplication&debug</td> + <td></td> +</tr> +<!--Test client initiated push --> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VLabel[0]</td> + <td>0</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VLabel[0]</td> + <td>1</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VLabel[0]</td> + <td>4</td> +</tr> +<!--Test server initiated push--> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[5]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[4]/VLabel[0]</td> + <td>0</td> +</tr> +<tr> + <td>pause</td> + <td>3000</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[4]/VLabel[0]</td> + <td>1</td> +</tr> +<tr> + <td>pause</td> + <td>3000</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[4]/VLabel[0]</td> + <td>2</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/push/BasicPush.java b/uitest/src/com/vaadin/tests/push/BasicPush.java new file mode 100644 index 0000000000..b80d287a1d --- /dev/null +++ b/uitest/src/com/vaadin/tests/push/BasicPush.java @@ -0,0 +1,105 @@ +package com.vaadin.tests.push; + +import java.util.Date; +import java.util.Timer; +import java.util.TimerTask; + +import com.vaadin.annotations.Widgetset; +import com.vaadin.data.util.ObjectProperty; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.ui.label.ContentMode; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.tests.widgetset.TestingWidgetSet; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Label; + +@Widgetset(TestingWidgetSet.NAME) +public class BasicPush extends AbstractTestUI { + + private ObjectProperty<Integer> counter = new ObjectProperty<Integer>(0); + + private ObjectProperty<Integer> counter2 = new ObjectProperty<Integer>(0); + + private final Timer timer = new Timer(true); + + private final TimerTask task = new TimerTask() { + + @Override + public void run() { + access(new Runnable() { + @Override + public void run() { + counter2.setValue(counter2.getValue() + 1); + } + }); + } + }; + + @Override + protected void setup(VaadinRequest request) { + + spacer(); + + /* + * Client initiated push. + */ + Label lbl = new Label(counter); + lbl.setCaption("Client counter (click 'increment' to update):"); + addComponent(lbl); + + addComponent(new Button("Increment", new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + counter.setValue(counter.getValue() + 1); + } + })); + + spacer(); + + /* + * Server initiated push. + */ + lbl = new Label(counter2); + lbl.setCaption("Server counter (updates each 3s by server thread) :"); + addComponent(lbl); + + addComponent(new Button("Reset", new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + counter2.setValue(0); + } + })); + } + + @Override + protected String getTestDescription() { + return "This test tests the very basic operations of push. " + + "It tests that client initiated changes are " + + "recieved back to the client as well as server " + + "initiated changes are correctly updated to the client."; + } + + @Override + protected Integer getTicketNumber() { + return 11494; + } + + private void spacer() { + addComponent(new Label("<hr/>", ContentMode.HTML)); + } + + @Override + public void attach() { + super.attach(); + timer.scheduleAtFixedRate(task, new Date(), 3000); + } + + @Override + public void detach() { + super.detach(); + timer.cancel(); + } +} diff --git a/uitest/src/com/vaadin/tests/push/PushFromInit.html b/uitest/src/com/vaadin/tests/push/PushFromInit.html new file mode 100644 index 0000000000..d009eb3baf --- /dev/null +++ b/uitest/src/com/vaadin/tests/push/PushFromInit.html @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run-push/com.vaadin.tests.push.PushFromInit?debug&restartApplication</td> + <td></td> +</tr> +<tr> + <td>waitForText</td> + <td>vaadin=runpushcomvaadintestspushPushFromInit::PID_SLog_row_1</td> + <td>1. Logged in init</td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushPushFromInit::PID_SLog_row_0</td> + <td>2. Logged from background thread started in init</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/push/PushFromInit.java b/uitest/src/com/vaadin/tests/push/PushFromInit.java new file mode 100644 index 0000000000..4b442de499 --- /dev/null +++ b/uitest/src/com/vaadin/tests/push/PushFromInit.java @@ -0,0 +1,36 @@ +package com.vaadin.tests.push; + +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUIWithLog; +import com.vaadin.ui.Button; + +public class PushFromInit extends AbstractTestUIWithLog { + + @Override + protected void setup(VaadinRequest request) { + new Thread() { + @Override + public void run() { + access(new Runnable() { + @Override + public void run() { + log("Logged from background thread started in init"); + } + }); + } + }.start(); + log("Logged in init"); + addComponent(new Button("Sync")); + } + + @Override + protected String getTestDescription() { + return "Pusing something to a newly created UI should not cause race conditions"; + } + + @Override + protected Integer getTicketNumber() { + return Integer.valueOf(11529); + } + +} diff --git a/uitest/src/com/vaadin/tests/push/PushReattachedComponent.html b/uitest/src/com/vaadin/tests/push/PushReattachedComponent.html new file mode 100644 index 0000000000..e1f6a5f048 --- /dev/null +++ b/uitest/src/com/vaadin/tests/push/PushReattachedComponent.html @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://192.168.2.162:8888/" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run-push/com.vaadin.tests.components.panel.PanelChangeContents?restartApplication</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestscomponentspanelPanelChangeContents::/VVerticalLayout[0]/Slot[1]/VPanel[0]/VVerticalLayout[0]/Slot[0]/VLabel[0]</td> + <td>stats</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestscomponentspanelPanelChangeContents::/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[1]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestscomponentspanelPanelChangeContents::/VVerticalLayout[0]/Slot[1]/VPanel[0]/VVerticalLayout[0]/Slot[0]/VLabel[0]</td> + <td>companies</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestscomponentspanelPanelChangeContents::/VVerticalLayout[0]/Slot[0]/VHorizontalLayout[0]/Slot[0]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestscomponentspanelPanelChangeContents::/VVerticalLayout[0]/Slot[1]/VPanel[0]/VVerticalLayout[0]/Slot[0]/VLabel[0]</td> + <td>stats</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/push/RoundTripTest.java b/uitest/src/com/vaadin/tests/push/RoundTripTest.java new file mode 100644 index 0000000000..ee0bf6dcb1 --- /dev/null +++ b/uitest/src/com/vaadin/tests/push/RoundTripTest.java @@ -0,0 +1,77 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.push; + +import com.vaadin.annotations.Widgetset; +import com.vaadin.server.VaadinRequest; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.tests.widgetset.TestingWidgetSet; +import com.vaadin.tests.widgetset.server.RoundTripTester; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Button.ClickListener; +import com.vaadin.ui.TextField; + +@Widgetset(TestingWidgetSet.NAME) +public class RoundTripTest extends AbstractTestUI { + + @Override + protected void setup(VaadinRequest request) { + final RoundTripTester roundTripTester = new RoundTripTester(); + final TextField payloadSize = new TextField("Payload size (bytes)"); + payloadSize.setConverter(Integer.class); + payloadSize.setConvertedValue(10000); + if (request.getParameter("payload") != null) { + payloadSize.setValue(request.getParameter("payload")); + } + addComponent(payloadSize); + final TextField testDuration = new TextField("Test duration (ms)"); + testDuration.setConverter(Integer.class); + testDuration.setConvertedValue(10000); + addComponent(testDuration); + if (request.getParameter("duration") != null) { + testDuration.setValue(request.getParameter("duration")); + } + + Button start = new Button("Start test"); + start.addClickListener(new ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + roundTripTester.start( + (Integer) testDuration.getConvertedValue(), + (Integer) payloadSize.getConvertedValue()); + } + }); + addComponent(roundTripTester); + addComponent(start); + + if (request.getParameter("go") != null) { + start.click(); + } + } + + @Override + protected String getTestDescription() { + return "Tests how many roundtrips per second you can get using the given package size"; + } + + @Override + protected Integer getTicketNumber() { + return 11370; + } + +} diff --git a/uitest/src/com/vaadin/tests/push/StreamingPush.html b/uitest/src/com/vaadin/tests/push/StreamingPush.html new file mode 100644 index 0000000000..cf94a09c63 --- /dev/null +++ b/uitest/src/com/vaadin/tests/push/StreamingPush.html @@ -0,0 +1,88 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://localhost:8888/" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run-push/com.vaadin.tests.push.BasicPush?restartApplication&debug&transport=streaming</td> + <td></td> +</tr> +<!--Test client initiated push --> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VLabel[0]</td> + <td>0</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VLabel[0]</td> + <td>1</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VLabel[0]</td> + <td>4</td> +</tr> +<!--Test server initiated push--> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[5]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[4]/VLabel[0]</td> + <td>0</td> +</tr> +<tr> + <td>pause</td> + <td>3000</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[4]/VLabel[0]</td> + <td>1</td> +</tr> +<tr> + <td>pause</td> + <td>3000</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushBasicPush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[4]/VLabel[0]</td> + <td>2</td> +</tr> +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/push/TogglePush.html b/uitest/src/com/vaadin/tests/push/TogglePush.html new file mode 100644 index 0000000000..b752d2120c --- /dev/null +++ b/uitest/src/com/vaadin/tests/push/TogglePush.html @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run-push/com.vaadin.tests.push.TogglePush?restartApplication</td> + <td></td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestspushTogglePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>pause</td> + <td>2000</td> + <td></td> +</tr> +<!--Push is enabled, so text gets updated--> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushTogglePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VLabel[0]</td> + <td>Counter has been updated 1 times</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runpushcomvaadintestspushTogglePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VCheckBox[0]/domChild[0]</td> + <td>61,6</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestspushTogglePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>pause</td> + <td>2000</td> + <td></td> +</tr> +<!--Push is disabled, so text is not updated--> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushTogglePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VLabel[0]</td> + <td>Counter has been updated 1 times</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestspushTogglePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<!--Direct update is visible, and includes previous update--> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushTogglePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VLabel[0]</td> + <td>Counter has been updated 3 times</td> +</tr> +<tr> + <td>mouseClick</td> + <td>vaadin=runpushcomvaadintestspushTogglePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VCheckBox[0]/domChild[0]</td> + <td>61,3</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestspushTogglePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>pause</td> + <td>2000</td> + <td></td> +</tr> +<!--Push is enabled again, so text gets updated--> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushTogglePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VLabel[0]</td> + <td>Counter has been updated 4 times</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/push/TogglePush.java b/uitest/src/com/vaadin/tests/push/TogglePush.java new file mode 100644 index 0000000000..37687260dd --- /dev/null +++ b/uitest/src/com/vaadin/tests/push/TogglePush.java @@ -0,0 +1,85 @@ +package com.vaadin.tests.push; + +import java.util.Timer; +import java.util.TimerTask; + +import com.vaadin.data.Property.ValueChangeEvent; +import com.vaadin.data.Property.ValueChangeListener; +import com.vaadin.server.VaadinRequest; +import com.vaadin.shared.communication.PushMode; +import com.vaadin.tests.components.AbstractTestUI; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.CheckBox; +import com.vaadin.ui.Label; + +public class TogglePush extends AbstractTestUI { + private final Label counterLabel = new Label(); + private int counter = 0; + + @Override + protected void setup(VaadinRequest request) { + updateCounter(); + addComponent(counterLabel); + + setPushMode("disabled".equals(request.getParameter("push")) ? PushMode.DISABLED + : PushMode.AUTOMATIC); + + CheckBox pushSetting = new CheckBox("Push enabled"); + pushSetting.setValue(Boolean.valueOf(getPushMode().isEnabled())); + pushSetting.setImmediate(true); + pushSetting.addValueChangeListener(new ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + if (event.getProperty().getValue() == Boolean.TRUE) { + setPushMode(PushMode.AUTOMATIC); + } else { + setPushMode(PushMode.DISABLED); + } + } + }); + addComponent(pushSetting); + + addComponent(new Button("Update counter now", + new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + updateCounter(); + } + })); + + addComponent(new Button("Update counter in 1 sec", + new Button.ClickListener() { + @Override + public void buttonClick(ClickEvent event) { + new Timer().schedule(new TimerTask() { + @Override + public void run() { + access(new Runnable() { + @Override + public void run() { + updateCounter(); + } + }); + } + }, 1000); + } + })); + } + + public void updateCounter() { + counterLabel.setValue("Counter has been updated " + counter++ + + " times"); + } + + @Override + protected String getTestDescription() { + return "Basic test for enabling and disabling push on the fly."; + } + + @Override + protected Integer getTicketNumber() { + return Integer.valueOf(11506); + } + +} diff --git a/uitest/src/com/vaadin/tests/push/TogglePushInInit.html b/uitest/src/com/vaadin/tests/push/TogglePushInInit.html new file mode 100644 index 0000000000..c735f225e1 --- /dev/null +++ b/uitest/src/com/vaadin/tests/push/TogglePushInInit.html @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://localhost:8071/" /> +<title>TogglePushInInit</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">TogglePushInInit</td></tr> +</thead><tbody> +<!--Open with push disabled--> +<tr> + <td>open</td> + <td>/run-push/com.vaadin.tests.push.TogglePush?restartApplication&push=disabled</td> + <td></td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runpushcomvaadintestspushTogglePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VCheckBox[0]/domChild[0]</td> + <td>off</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestspushTogglePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>pause</td> + <td>2000</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushTogglePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VLabel[0]</td> + <td>Counter has been updated 0 times</td> +</tr> +<!--Open with push enabled--> +<tr> + <td>open</td> + <td>/run-push/com.vaadin.tests.push.TogglePush?restartApplication&push=enabled</td> + <td></td> +</tr> +<tr> + <td>assertValue</td> + <td>vaadin=runpushcomvaadintestspushTogglePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VCheckBox[0]/domChild[0]</td> + <td>on</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runpushcomvaadintestspushTogglePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[3]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>pause</td> + <td>2000</td> + <td></td> +</tr> +<tr> + <td>assertText</td> + <td>vaadin=runpushcomvaadintestspushTogglePush::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[0]/VLabel[0]</td> + <td>Counter has been updated 1 times</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/requesthandlers/AppResource404.html b/uitest/src/com/vaadin/tests/requesthandlers/AppResource404.html index 16f3db6a1a..543faa30dd 100644 --- a/uitest/src/com/vaadin/tests/requesthandlers/AppResource404.html +++ b/uitest/src/com/vaadin/tests/requesthandlers/AppResource404.html @@ -53,7 +53,12 @@ </tr> <tr> <td>assertTextPresent</td> - <td>/APP can not be found</td> + <td>HTTP ERROR 404</td> + <td></td> +</tr> +<tr> + <td>assertTextPresent</td> + <td>Problem accessing /run/APP/</td> <td></td> </tr> <tr> diff --git a/uitest/src/com/vaadin/tests/requesthandlers/AppResource404.java b/uitest/src/com/vaadin/tests/requesthandlers/AppResource404.java index dfd664c9cf..a6a5a5a084 100644 --- a/uitest/src/com/vaadin/tests/requesthandlers/AppResource404.java +++ b/uitest/src/com/vaadin/tests/requesthandlers/AppResource404.java @@ -23,8 +23,8 @@ public class AppResource404 extends TestBase { addComponent(new Link("Existing resource", resource)); addComponent(new Link("Non-existing resource", new ExternalResource( baseUrl + "/APP/connector/0/4/asdfasdf"))); - addComponent(new Link("/APP url that should give 404", - new ExternalResource(baseUrl + "/APP"))); + addComponent(new Link("/APP/ url that should give 404", + new ExternalResource(baseUrl + "/APP/"))); addComponent(new Link("/APPLE url that should go to UI providers", new ExternalResource(baseUrl + "/APPLE"))); } diff --git a/uitest/src/com/vaadin/tests/themes/CSSInjectTest.html b/uitest/src/com/vaadin/tests/themes/CSSInjectTest.html new file mode 100644 index 0000000000..05a0f256c2 --- /dev/null +++ b/uitest/src/com/vaadin/tests/themes/CSSInjectTest.html @@ -0,0 +1,57 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> +<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> +<head profile="http://selenium-ide.openqa.org/profiles/test-case"> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> +<link rel="selenium.base" href="http://localhost:8888/" /> +<title>New Test</title> +</head> +<body> +<table cellpadding="1" cellspacing="1" border="1"> +<thead> +<tr><td rowspan="1" colspan="3">New Test</td></tr> +</thead><tbody> +<tr> + <td>open</td> + <td>/run/com.vaadin.tests.themes.CSSInjectTest?restartApplication</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>hello-world-gray</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadinteststhemesCSSInjectTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VTextArea[0]</td> + <td>.hello{color:blue;}</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadinteststhemesCSSInjectTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>hello-blue</td> +</tr> +<tr> + <td>type</td> + <td>vaadin=runcomvaadinteststhemesCSSInjectTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[1]/VTextArea[0]</td> + <td>.world{color:red;}</td> +</tr> +<tr> + <td>click</td> + <td>vaadin=runcomvaadinteststhemesCSSInjectTest::/VVerticalLayout[0]/Slot[1]/VVerticalLayout[0]/Slot[2]/VButton[0]/domChild[0]/domChild[0]</td> + <td></td> +</tr> +<tr> + <td>screenCapture</td> + <td></td> + <td>world-red</td> +</tr> + +</tbody></table> +</body> +</html> diff --git a/uitest/src/com/vaadin/tests/themes/CSSInjectTest.java b/uitest/src/com/vaadin/tests/themes/CSSInjectTest.java new file mode 100644 index 0000000000..f4448bf326 --- /dev/null +++ b/uitest/src/com/vaadin/tests/themes/CSSInjectTest.java @@ -0,0 +1,87 @@ +package com.vaadin.tests.themes; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.UUID; + +import com.vaadin.server.Page; +import com.vaadin.server.Page.Styles; +import com.vaadin.server.StreamResource; +import com.vaadin.shared.ui.label.ContentMode; +import com.vaadin.tests.components.TestBase; +import com.vaadin.ui.Button; +import com.vaadin.ui.Button.ClickEvent; +import com.vaadin.ui.Label; +import com.vaadin.ui.TextArea; + +public class CSSInjectTest extends TestBase { + + @Override + protected void setup() { + + final Styles stylesheet = Page.getCurrent().getStyles(); + + // Inject some resources initially + stylesheet.add(new StreamResource(new StreamResource.StreamSource() { + + @Override + public InputStream getStream() { + return new ByteArrayInputStream( + ".hello, .world { color:silver; }".getBytes()); + } + }, "mystyles-" + System.currentTimeMillis() + ".css")); + + Label hello = new Label( + "<span class='hello'>Hello</span> <span class='world'>world</span>", + ContentMode.HTML); + addComponent(hello); + + final TextArea cssToInject = new TextArea(); + cssToInject.setImmediate(true); + addComponent(cssToInject); + + Button inject = new Button("Inject!", new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + stylesheet.add(cssToInject.getValue()); + cssToInject.setValue(""); + } + }); + addComponent(inject); + + Button injectRandom = new Button("Inject as resource!", + new Button.ClickListener() { + + @Override + public void buttonClick(ClickEvent event) { + + final String css = cssToInject.getValue(); + + stylesheet.add(new StreamResource( + new StreamResource.StreamSource() { + + @Override + public InputStream getStream() { + return new ByteArrayInputStream(css + .getBytes()); + } + }, UUID.randomUUID().toString() + ".css")); + + cssToInject.setValue(""); + } + }); + addComponent(injectRandom); + } + + @Override + protected String getDescription() { + return "Demonstrates how CSS injections can be used to theme the \"Hello world\" label below"; + } + + @Override + protected Integer getTicketNumber() { + return 5500; + } + +} diff --git a/uitest/src/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml b/uitest/src/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml index 919a4a5d69..1b47a86113 100644 --- a/uitest/src/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml +++ b/uitest/src/com/vaadin/tests/widgetset/TestingWidgetSet.gwt.xml @@ -7,5 +7,9 @@ <replace-with class="com.vaadin.tests.widgetset.client.CustomUIConnector"> <when-type-is class="com.vaadin.client.ui.ui.UIConnector" /> </replace-with> + + <replace-with class="com.vaadin.tests.widgetset.client.TestingPushConnection"> + <when-type-is class="com.vaadin.client.communication.PushConnection" /> + </replace-with> </module> diff --git a/uitest/src/com/vaadin/tests/widgetset/client/RoundTripTesterConnector.java b/uitest/src/com/vaadin/tests/widgetset/client/RoundTripTesterConnector.java new file mode 100644 index 0000000000..94972d92f4 --- /dev/null +++ b/uitest/src/com/vaadin/tests/widgetset/client/RoundTripTesterConnector.java @@ -0,0 +1,106 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.widgetset.client; + +import java.util.ArrayList; +import java.util.List; + +import com.google.gwt.core.client.Duration; +import com.google.gwt.user.client.ui.HTML; +import com.vaadin.client.ui.AbstractComponentConnector; +import com.vaadin.shared.ui.Connect; +import com.vaadin.tests.widgetset.server.RoundTripTester; + +@Connect(RoundTripTester.class) +public class RoundTripTesterConnector extends AbstractComponentConnector { + + private double lastPrintedTime = -1; + private int receivedPings = 0; + private List<Double> throughputData = new ArrayList<Double>(); + private int payloadSize = 0; + + @Override + protected void init() { + super.init(); + registerRpc(RoundTripTesterRpc.class, new RoundTripTesterRpc() { + + @Override + public void ping(int nr, String payload) { + getRpcProxy(RoundTripTesterRpc.class).ping(nr + 1, payload); + payloadSize = payload.length(); + + double now = Duration.currentTimeMillis(); + if (lastPrintedTime == -1) { + lastPrintedTime = now; + return; + } + receivedPings++; + + if (now - lastPrintedTime > 1000) { + double roundtripsPerSecond = receivedPings + / (now - lastPrintedTime) * 1000; + throughputData.add(roundtripsPerSecond); + getWidget().setText( + roundtripsPerSecond + " roundtrips/second"); + + lastPrintedTime = now; + receivedPings = 0; + } + + } + + @Override + public void done() { + String result = "Test results for payload of size " + + payloadSize + ":"; + double max = -1; + double min = 1239482038939.0; + double avg = 0; + + for (Double throughput : throughputData) { + if (throughput > max) { + max = throughput; + } + if (throughput < min) { + min = throughput; + } + + avg += throughput; + } + avg /= throughputData.size(); + + for (Double throughput : throughputData) { + result += "<br/>" + formatThroughput(throughput); + } + result += "<br/>Max: " + formatThroughput(max); + result += "<br/>Min: " + formatThroughput(min); + result += "<br/>Average: " + formatThroughput(avg); + getWidget().setHTML(result); + getRpcProxy(RoundTripTesterRpc.class).done(); + } + + private String formatThroughput(double throughput) { + return throughput + " roundtrips / second"; + } + }); + } + + @Override + public HTML getWidget() { + return (HTML) super.getWidget(); + } + +} diff --git a/uitest/src/com/vaadin/tests/widgetset/client/RoundTripTesterRpc.java b/uitest/src/com/vaadin/tests/widgetset/client/RoundTripTesterRpc.java new file mode 100644 index 0000000000..24c981e0c2 --- /dev/null +++ b/uitest/src/com/vaadin/tests/widgetset/client/RoundTripTesterRpc.java @@ -0,0 +1,25 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.widgetset.client; + +import com.vaadin.shared.communication.ClientRpc; +import com.vaadin.shared.communication.ServerRpc; + +public interface RoundTripTesterRpc extends ServerRpc, ClientRpc { + public void ping(int nr, String payload); + + public void done(); +} diff --git a/uitest/src/com/vaadin/tests/widgetset/client/TestingPushConnection.java b/uitest/src/com/vaadin/tests/widgetset/client/TestingPushConnection.java new file mode 100644 index 0000000000..8d00598907 --- /dev/null +++ b/uitest/src/com/vaadin/tests/widgetset/client/TestingPushConnection.java @@ -0,0 +1,30 @@ +package com.vaadin.tests.widgetset.client; + +import com.google.gwt.user.client.Window; +import com.vaadin.client.ApplicationConnection; +import com.vaadin.client.communication.AtmospherePushConnection; + +public class TestingPushConnection extends AtmospherePushConnection { + + private String transport; + + @Override + public void init(ApplicationConnection connection) { + super.init(connection); + transport = Window.Location.getParameter("transport"); + } + + /* + * Force transport + */ + @Override + protected AtmosphereConfiguration createConfig() { + AtmosphereConfiguration conf = super.createConfig(); + if (transport != null) { + conf.setTransport(transport); + conf.setFallbackTransport(transport); + } + return conf; + } + +} diff --git a/uitest/src/com/vaadin/tests/widgetset/server/RoundTripTester.java b/uitest/src/com/vaadin/tests/widgetset/server/RoundTripTester.java new file mode 100644 index 0000000000..c8e561e665 --- /dev/null +++ b/uitest/src/com/vaadin/tests/widgetset/server/RoundTripTester.java @@ -0,0 +1,60 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package com.vaadin.tests.widgetset.server; + +import com.vaadin.tests.util.LoremIpsum; +import com.vaadin.tests.widgetset.client.RoundTripTesterRpc; +import com.vaadin.ui.AbstractComponent; + +public class RoundTripTester extends AbstractComponent { + private long testStart = 0; + private long testEnd = 0; + + public RoundTripTester() { + registerRpc(new RoundTripTesterRpc() { + @Override + public void ping(int nr, String payload) { + if (System.currentTimeMillis() < testEnd) { + getRpcProxy(RoundTripTesterRpc.class).ping(nr + 1, payload); + } else { + getRpcProxy(RoundTripTesterRpc.class).done(); + } + } + + @Override + public void done() { + } + }); + } + + public void start(long testDuration, int payloadSize) { + testStart = System.currentTimeMillis(); + testEnd = testStart + testDuration; + getRpcProxy(RoundTripTesterRpc.class).ping(1, + generatePayload(payloadSize)); + } + + private String generatePayload(int payloadSize) { + StringBuilder sb = new StringBuilder(); + while (payloadSize > 10000) { + payloadSize -= 10000; + sb.append(LoremIpsum.get(10000)); + } + sb.append(LoremIpsum.get(payloadSize)); + return sb.toString(); + } + +} diff --git a/uitest/test.xml b/uitest/test.xml index f3cf2f0884..8228bd9d70 100644 --- a/uitest/test.xml +++ b/uitest/test.xml @@ -8,7 +8,7 @@ <!-- Configuration --> <!-- ================================================================== --> <!-- Browsers to use for testing --> - <property name="browsers-windows" value="winxp-ie8,win7-ie9,winxp-firefox17-esr,winxp-safari5,winxp-googlechrome21,winxp-opera12" /> + <property name="browsers-windows" value="winxp-ie8,win7-ie9,win7-ie10,winxp-firefox17-esr,winxp-safari5,winxp-googlechrome21,winxp-opera12" /> <property name="browsers-linux" value="linux-firefox3,linux-opera10,linux-googlechrome8" /> <property name="browsers-mac" value="osx-firefox3,osx-opera10,osx-googlechrome8,osx-safari4,osx-safari5" /> |